#include "milk.h"

/**
* projectM -- Milkdrop-esque visualisation SDK
* Copyright (C)2003-2009 projectM Team
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
* See 'LICENSE.txt' included within this release
*
*/

#ifdef SHR3D_ENVIRONMENT_MILK

#include "data.h"
#include "file.h"
#include "global.h"
#include "helper.h"
#include "opengl.h"
#include "shader.h"
#include "string_.h"

#ifdef SHR3D_SDL
#include "SDL.h"
#endif // SHR3D_SDL

#include <assert.h>
#include <cmath>
#include <cstdlib>
#include <ctype.h>
#include <errno.h>
#include <fstream>
#include <functional>
#include <iostream>
#include <locale>
#include <malloc.h>
#include <map>
#include <math.h>
#include <new>
#include <regex>
#include <set>
#include <sstream>
#include <stack>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include <sys/stat.h>
#include <sys/types.h>
#include <vector>


static const int _maxLineLength = 2048;

namespace M4
{
  static char* mprintf_valist(int size, const char* fmt, va_list args) {
    ASSERT(size > 0);
    char* res = nullptr;
    va_list tmp;

    while (1) {
      res = new char[size];
      if (!res) return nullptr;

      va_copy(tmp, args);
      int len = vsnprintf(res, size, fmt, tmp);
      va_end(tmp);

      if ((len >= 0) && (size >= len + 1)) {
        break;
      }

      delete[] res;

      if (len > -1) {
        size = len + 1;
      }
      else {
        size *= 2;
      }
    }

    return res;
  }

  bool String_Equal(const char* a, const char* b) {
    if (a == b) return true;
    if (a == nullptr || b == nullptr) return false;
    return strcmp(a, b) == 0;
  }

  class Allocator {
  public:
    template <typename T> T* New() {
      return (T*)malloc(sizeof(T));
    }
    template <typename T> T* New(size_t count) {
      return (T*)malloc(sizeof(T) * count);
    }
    template <typename T> void Delete(T* ptr) {
      free((void*)ptr);
    }
    template <typename T> T* Realloc(T* ptr, size_t count) {
      return (T*)realloc(ptr, sizeof(T) * count);
    }
  };

  int String_PrintfArgList(char* buffer, int size, const char* format, va_list args)
  {

    va_list tmp;
    va_copy(tmp, args);

    int n = vsnprintf(buffer, size, format, tmp);

    va_end(tmp);

    if (n < 0 || n > size) return -1;
    return n;
  }

  int String_Printf(char* buffer, int size, const char* format, ...)
  {

    va_list args;
    va_start(args, format);

    int n = String_PrintfArgList(buffer, size, format, args);

    va_end(args);

    return n;
  }

  // Engine/Log.h

  void Log_ErrorArgList(const char* format, va_list args)
  {
#if 1 // @@ Don't we need to do this?
    va_list tmp;
    va_copy(tmp, args);
    vprintf(format, args);
    va_end(tmp);
#else
    vprintf(format, args);
#endif
  }

  void Log_Error(const char* format, ...)
  {
    va_list args;
    va_start(args, format);
    Log_ErrorArgList(format, args);
    va_end(args);
  }

  // Engine/Array.h

  template <typename T>
  void ConstructRange(T* buffer, int new_size, int old_size) {
    for (int i = old_size; i < new_size; i++) {
      new(buffer + i) T; // placement new
    }
  }

  template <typename T>
  void ConstructRange(T* buffer, int new_size, int old_size, const T& val) {
    for (int i = old_size; i < new_size; i++) {
      new(buffer + i) T(val); // placement new
    }
  }

  template <typename T>
  void DestroyRange(T* buffer, int new_size, int old_size) {
    for (int i = new_size; i < old_size; i++) {
      (buffer + i)->~T(); // Explicit call to the destructor
    }
  }


  template <typename T>
  class Array {
  public:
    Array(Allocator* _allocator) : allocator(_allocator), buffer(nullptr), size(0), capacity(0) {}

    void PushBack(const T& val) {
      ASSERT(&val < buffer || &val >= buffer + size);

      int old_size = size;
      int new_size = size + 1;

      SetSize(new_size);

      ConstructRange(buffer, new_size, old_size, val);
    }
    T& PushBackNew() {
      int old_size = size;
      int new_size = size + 1;

      SetSize(new_size);

      ConstructRange(buffer, new_size, old_size);

      return buffer[old_size];
    }
    void Resize(int new_size) {
      int old_size = size;

      DestroyRange(buffer, new_size, old_size);

      SetSize(new_size);

      ConstructRange(buffer, new_size, old_size);
    }

    int GetSize() const { return size; }
    const T& operator[](int i) const { ASSERT(i < size); return buffer[i]; }
    T& operator[](int i) { ASSERT(i < size); return buffer[i]; }

  private:

    // Change array size.
    void SetSize(int new_size) {
      size = new_size;

      if (new_size > capacity) {
        int new_buffer_size;
        if (capacity == 0) {
          // first allocation is exact
          new_buffer_size = new_size;
        }
        else {
          // following allocations grow array by 25%
          new_buffer_size = new_size + (new_size >> 2);
        }

        SetCapacity(new_buffer_size);
      }
    }

    // Change array capacity.
    void SetCapacity(int new_capacity) {
      ASSERT(new_capacity >= size);

      if (new_capacity == 0) {
        // free the buffer.
        if (buffer != nullptr) {
          allocator->Delete<T>(buffer);
          buffer = nullptr;
        }
      }
      else {
        // realloc the buffer
        buffer = allocator->Realloc<T>(buffer, new_capacity);
      }

      capacity = new_capacity;
    }


  private:
    Allocator* allocator; // @@ Do we really have to keep a pointer to this?
    T* buffer;
    int size;
    int capacity;
  };



  // @@ Implement this with a hash table!
  struct StringPool {
    StringPool(Allocator* allocator) : stringArray(allocator) {
    }
    ~StringPool()
    {
      for (int i = 0; i < stringArray.GetSize(); i++) {
        free((void*)stringArray[i]);
        stringArray[i] = nullptr;
      }
    }

    const char* AddString(const char* string)
    {
      for (int i = 0; i < stringArray.GetSize(); i++) {
        if (String_Equal(stringArray[i], string)) return stringArray[i];
      }
#if _MSC_VER
      const char* dup = _strdup(string);
#else
      const char* dup = strdup(string);
#endif
      stringArray.PushBack(dup);
      return dup;
    }

    const char* AddStringFormatList(const char* fmt, va_list args)
    {
      va_list tmp;
      va_copy(tmp, args);
      const char* string = mprintf_valist(256, fmt, tmp);
      va_end(tmp);

      for (int i = 0; i < stringArray.GetSize(); i++) {
        if (String_Equal(stringArray[i], string)) {
          delete[] string;
          return stringArray[i];
        }
      }

      stringArray.PushBack(string);
      return string;
    }

    const char* AddStringFormat(const char* fmt, ...)
    {
      va_list args;
      va_start(args, fmt);
      const char* string = AddStringFormatList(fmt, args);
      va_end(args);

      return string;
    }

    bool GetContainsString(const char* string) const
    {
      for (int i = 0; i < stringArray.GetSize(); i++) {
        if (String_Equal(stringArray[i], string)) return true;
      }
      return false;
    }

    Array<const char*> stringArray;
  };



  int String_FormatFloat(char* buffer, int size, float value) {
    std::ostringstream oss;
    oss.imbue(std::locale("C"));
    oss << value;

    return String_Printf(buffer, size, "%s", oss.str().c_str());
  }

  bool String_EqualNoCase(const char* a, const char* b) {
    if (a == b) return true;
    if (a == nullptr || b == nullptr) return false;
#if _MSC_VER
    return _stricmp(a, b) == 0;
#else
    return strcasecmp(a, b) == 0;
#endif
  }

  static inline double iss_strtod(const char* in, char** end) {
    char* in_var = const_cast<char*>(in);
    double df;
    std::istringstream iss(in);
    iss.imbue(std::locale("C"));
    iss >> df;
    if (iss.fail()) {
      *end = in_var;
      return 0.0;
    }
    if (iss.eof()) {
      *end = in_var + strlen(in);
      return df;
    }

    std::streampos pos = iss.tellg();
    if (iss.fail()) {
      *end = in_var;
      return 0.0;
    }
    *end = in_var + pos;
    return df;
  }

  double String_ToDouble(const char* str, char** endptr) {
    return iss_strtod(str, endptr);
  }

  int String_ToInteger(const char* str, char** endptr) {
    return strtol(str, endptr, 10);
  }

  //int String_ToIntegerHex(const char * str, char ** endptr) {
  //	return strtol(str, endptr, 16);
  //}




} // M4 namespace




//=============================================================================
//
// Render/CodeWriter.h
//
// Created by Max McGuire (max@unknownworlds.com)
// Copyright (c) 2013, Unknown Worlds Entertainment, Inc.
//
//=============================================================================


#if defined(__GNUC__)
#define M4_PRINTF_ATTR(string_index, first_to_check) __attribute__((format(printf, string_index, first_to_check)))
#else
#define M4_PRINTF_ATTR(string_index, first_to_check)
#endif

namespace M4
{

  class Allocator;

  /**
   * This class is used for outputting code. It handles indentation and inserting #line markers
   * to match the desired output line numbers.
   */
  class CodeWriter
  {

  public:
    CodeWriter(bool writeFileNames = true)
    {
      m_currentLine = 1;
      m_currentFileName = nullptr;
      m_spacesPerIndent = 4;
      m_writeLines = false;
      m_writeFileNames = writeFileNames;
    }

    void BeginLine(int indent, const char* fileName = nullptr, int lineNumber = -1)
    {
      if (m_writeLines)
      {
        bool outputLine = false;
        bool outputFile = false;

        // Output a line number pragma if necessary.
        if (fileName != nullptr && m_currentFileName != fileName)
        {
          m_currentFileName = fileName;
          fileName = m_currentFileName;
          outputFile = true;
        }
        if (lineNumber != -1 && m_currentLine != lineNumber)
        {
          m_currentLine = lineNumber;
          outputLine = true;
        }
        if (outputLine || outputFile)
        {
          char buffer[256];
          String_Printf(buffer, sizeof(buffer), "#line %d", lineNumber);
          m_buffer += buffer;
          if (outputFile && m_writeFileNames)
          {
            m_buffer += " \"";
            m_buffer += fileName;
            m_buffer += "\"\n\n";
          }
          else
          {
            m_buffer += "\n\n";
          }
        }
      }

      // Handle the indentation.
      for (int i = 0; i < indent * m_spacesPerIndent; ++i)
      {
        m_buffer += " ";
      }
    }


    M4_PRINTF_ATTR(2, 3) void Write(const char* format, ...)
    {
      va_list args;
      va_start(args, format);

      char buffer[_maxLineLength];
      String_PrintfArgList(buffer, sizeof(buffer), format, args);

      m_buffer += buffer;

      va_end(args);
    }

    void EndLine(const char* text = nullptr)
    {
      if (text != nullptr)
      {
        m_buffer += text;
      }
      m_buffer += "\n";
      ++m_currentLine;
    }

    M4_PRINTF_ATTR(3, 4) void WriteLine(int indent, const char* format, ...)
    {
      va_list args;
      va_start(args, format);

      char buffer[_maxLineLength];

      int result = String_PrintfArgList(buffer, sizeof(buffer), format, args);
      ASSERT(result != -1);
      (void)result;

      for (int i = 0; i < indent * m_spacesPerIndent; ++i)
      {
        m_buffer += " ";
      }
      m_buffer += buffer;

      EndLine();

      va_end(args);
    }

    M4_PRINTF_ATTR(5, 6) void WriteLineTagged(int indent, const char* fileName, int lineNumber, const char* format, ...)
    {
      va_list args;
      va_start(args, format);

      BeginLine(indent, fileName, lineNumber);

      char buffer[_maxLineLength];
      int result = String_PrintfArgList(buffer, sizeof(buffer), format, args);
      ASSERT(result != -1);
      (void)result;

      m_buffer += buffer;

      EndLine();

      va_end(args);
    }

    const char* GetResult() const
    {
      return m_buffer.c_str();
    }

    void Reset()
    {
      m_buffer.clear();
    }

  private:

    std::string     m_buffer;
    int             m_currentLine;
    const char* m_currentFileName;
    int             m_spacesPerIndent;
    bool            m_writeLines;
    bool            m_writeFileNames;

  };

}


namespace M4
{

  enum HLSLNodeType
  {
    HLSLNodeType_Root,
    HLSLNodeType_Declaration,
    HLSLNodeType_Struct,
    HLSLNodeType_StructField,
    HLSLNodeType_Buffer,
    HLSLNodeType_BufferField,
    HLSLNodeType_Function,
    HLSLNodeType_Argument,
    HLSLNodeType_Macro,
    HLSLNodeType_ExpressionStatement,
    HLSLNodeType_Expression,
    HLSLNodeType_ReturnStatement,
    HLSLNodeType_DiscardStatement,
    HLSLNodeType_BreakStatement,
    HLSLNodeType_ContinueStatement,
    HLSLNodeType_IfStatement,
    HLSLNodeType_ForStatement,
    HLSLNodeType_WhileStatement,
    HLSLNodeType_BlockStatement,
    HLSLNodeType_UnaryExpression,
    HLSLNodeType_BinaryExpression,
    HLSLNodeType_ConditionalExpression,
    HLSLNodeType_CastingExpression,
    HLSLNodeType_LiteralExpression,
    HLSLNodeType_IdentifierExpression,
    HLSLNodeType_ConstructorExpression,
    HLSLNodeType_MemberAccess,
    HLSLNodeType_ArrayAccess,
    HLSLNodeType_FunctionCall,
    HLSLNodeType_StateAssignment,
    HLSLNodeType_SamplerState,
    HLSLNodeType_Pass,
    HLSLNodeType_Technique,
    HLSLNodeType_Attribute,
    HLSLNodeType_Pipeline,
    HLSLNodeType_Stage,
  };


  enum HLSLBaseType
  {
    HLSLBaseType_Unknown,
    HLSLBaseType_Void,
    HLSLBaseType_Float,
    HLSLBaseType_FirstNumeric = HLSLBaseType_Float,
    HLSLBaseType_Float2,
    HLSLBaseType_Float3,
    HLSLBaseType_Float4,

    HLSLBaseType_Float2x4,
    HLSLBaseType_Float2x3,
    HLSLBaseType_Float2x2,

    HLSLBaseType_Float3x4,
    HLSLBaseType_Float3x3,
    HLSLBaseType_Float3x2,

    HLSLBaseType_Float4x4,
    HLSLBaseType_Float4x3,
    HLSLBaseType_Float4x2,

    HLSLBaseType_Bool,
    HLSLBaseType_FirstInteger = HLSLBaseType_Bool,
    HLSLBaseType_Bool2,
    HLSLBaseType_Bool3,
    HLSLBaseType_Bool4,
    HLSLBaseType_Int,
    HLSLBaseType_Int2,
    HLSLBaseType_Int3,
    HLSLBaseType_Int4,
    HLSLBaseType_Uint,
    HLSLBaseType_Uint2,
    HLSLBaseType_Uint3,
    HLSLBaseType_Uint4,
    /*HLSLBaseType_Short,   // @@ Separate dimension from Base type, this is getting out of control.
    HLSLBaseType_Short2,
    HLSLBaseType_Short3,
    HLSLBaseType_Short4,
    HLSLBaseType_Ushort,
    HLSLBaseType_Ushort2,
    HLSLBaseType_Ushort3,
    HLSLBaseType_Ushort4,*/
    HLSLBaseType_LastInteger = HLSLBaseType_Uint4,
    HLSLBaseType_LastNumeric = HLSLBaseType_Uint4,
    HLSLBaseType_Texture,
    HLSLBaseType_Sampler,           // @@ use type inference to determine sampler type.
    HLSLBaseType_Sampler2D,
    HLSLBaseType_Sampler3D,
    HLSLBaseType_SamplerCube,
    HLSLBaseType_Sampler2DShadow,
    HLSLBaseType_Sampler2DMS,
    HLSLBaseType_Sampler2DArray,
    HLSLBaseType_UserDefined,       // struct
    HLSLBaseType_Expression,        // type argument for defined() sizeof() and typeof().
    HLSLBaseType_Auto,

    HLSLBaseType_Count,
    HLSLBaseType_NumericCount = HLSLBaseType_LastNumeric - HLSLBaseType_FirstNumeric + 1
  };



  enum NumericType
  {
    NumericType_Float,
    NumericType_Bool,
    NumericType_Int,
    NumericType_Uint,
    NumericType_Count,
    NumericType_NaN,
  };


  struct BaseTypeDescription
  {
    const char* typeName;
    NumericType     numericType;
    int             numComponents;
    int             numDimensions;
    int             height;
    int             binaryOpRank;
  };

  const BaseTypeDescription baseTypeDescriptions[HLSLBaseType_Count] =
  {
      { "unknown type",       NumericType_NaN,        0, 0, 0, -1 },      // HLSLBaseType_Unknown
      { "void",               NumericType_NaN,        0, 0, 0, -1 },      // HLSLBaseType_Void
      { "float",              NumericType_Float,      1, 0, 1,  0 },      // HLSLBaseType_Float
      { "float2",             NumericType_Float,      2, 1, 1,  0 },      // HLSLBaseType_Float2
      { "float3",             NumericType_Float,      3, 1, 1,  0 },      // HLSLBaseType_Float3
      { "float4",             NumericType_Float,      4, 1, 1,  0 },      // HLSLBaseType_Float4

      { "float2x4",			NumericType_Float,		2, 2, 4,  0 },		// HLSLBaseType_Float2x4
      { "float2x3",			NumericType_Float,		2, 2, 3,  0 },		// HLSLBaseType_Float2x3
      { "float2x2",			NumericType_Float,		2, 2, 2,  0 },		// HLSLBaseType_Float2x2

      { "float3x4",           NumericType_Float,      3, 2, 4,  0 },      // HLSLBaseType_Float3x4
      { "float3x3",           NumericType_Float,      3, 2, 3,  0 },      // HLSLBaseType_Float3x3
      { "float3x2",           NumericType_Float,      3, 2, 2,  0 },      // HLSLBaseType_Float3x2

      { "float4x4",           NumericType_Float,      4, 2, 4,  0 },      // HLSLBaseType_Float4x4
      { "float4x3",           NumericType_Float,      4, 2, 3,  0 },      // HLSLBaseType_Float4x3
      { "float4x2",           NumericType_Float,      4, 2, 2,  0 },      // HLSLBaseType_Float4x2

      { "bool",               NumericType_Bool,       1, 0, 1,  4 },      // HLSLBaseType_Bool
      { "bool2",				NumericType_Bool,		2, 1, 1,  4 },      // HLSLBaseType_Bool2
      { "bool3",				NumericType_Bool,		3, 1, 1,  4 },      // HLSLBaseType_Bool3
      { "bool4",				NumericType_Bool,		4, 1, 1,  4 },      // HLSLBaseType_Bool4

      { "int",                NumericType_Int,        1, 0, 1,  3 },      // HLSLBaseType_Int
      { "int2",               NumericType_Int,        2, 1, 1,  3 },      // HLSLBaseType_Int2
      { "int3",               NumericType_Int,        3, 1, 1,  3 },      // HLSLBaseType_Int3
      { "int4",               NumericType_Int,        4, 1, 1,  3 },      // HLSLBaseType_Int4

      { "uint",               NumericType_Uint,       1, 0, 1,  2 },      // HLSLBaseType_Uint
      { "uint2",              NumericType_Uint,       2, 1, 1,  2 },      // HLSLBaseType_Uint2
      { "uint3",              NumericType_Uint,       3, 1, 1,  2 },      // HLSLBaseType_Uint3
      { "uint4",              NumericType_Uint,       4, 1, 1,  2 },      // HLSLBaseType_Uint4

      { "texture",            NumericType_NaN,        1, 0, 0, -1 },      // HLSLBaseType_Texture
      { "sampler",            NumericType_NaN,        1, 0, 0, -1 },      // HLSLBaseType_Sampler
      { "sampler2D",          NumericType_NaN,        1, 0, 0, -1 },      // HLSLBaseType_Sampler2D
      { "sampler3D",          NumericType_NaN,        1, 0, 0, -1 },      // HLSLBaseType_Sampler3D
      { "samplerCUBE",        NumericType_NaN,        1, 0, 0, -1 },      // HLSLBaseType_SamplerCube
      { "sampler2DShadow",    NumericType_NaN,        1, 0, 0, -1 },      // HLSLBaseType_Sampler2DShadow
      { "sampler2DMS",        NumericType_NaN,        1, 0, 0, -1 },      // HLSLBaseType_Sampler2DMS
      { "sampler2DArray",     NumericType_NaN,        1, 0, 0, -1 },      // HLSLBaseType_Sampler2DArray
      { "user defined",       NumericType_NaN,        1, 0, 0, -1 },      // HLSLBaseType_UserDefined
      { "expression",         NumericType_NaN,        1, 0, 0, -1 }       // HLSLBaseType_Expression
  };

  extern const HLSLBaseType ScalarBaseType[HLSLBaseType_Count];

  inline bool IsSamplerType(HLSLBaseType baseType)
  {
    return baseType == HLSLBaseType_Sampler ||
      baseType == HLSLBaseType_Sampler2D ||
      baseType == HLSLBaseType_Sampler3D ||
      baseType == HLSLBaseType_SamplerCube ||
      baseType == HLSLBaseType_Sampler2DShadow ||
      baseType == HLSLBaseType_Sampler2DMS ||
      baseType == HLSLBaseType_Sampler2DArray;
  }

  inline bool IsMatrixType(HLSLBaseType baseType)
  {
    return baseType == HLSLBaseType_Float2x4 || baseType == HLSLBaseType_Float2x3 || baseType == HLSLBaseType_Float2x2 ||
      baseType == HLSLBaseType_Float3x4 || baseType == HLSLBaseType_Float3x3 || baseType == HLSLBaseType_Float3x2 ||
      baseType == HLSLBaseType_Float4x4 || baseType == HLSLBaseType_Float4x3 || baseType == HLSLBaseType_Float4x2;
  }

  inline bool IsScalarType(HLSLBaseType baseType)
  {
    return  baseType == HLSLBaseType_Float ||
      baseType == HLSLBaseType_Bool ||
      baseType == HLSLBaseType_Int ||
      baseType == HLSLBaseType_Uint;
  }

  inline bool IsVectorType(HLSLBaseType baseType)
  {
    return  baseType == HLSLBaseType_Float2 ||
      baseType == HLSLBaseType_Float3 ||
      baseType == HLSLBaseType_Float4 ||
      baseType == HLSLBaseType_Bool2 ||
      baseType == HLSLBaseType_Bool3 ||
      baseType == HLSLBaseType_Bool4 ||
      baseType == HLSLBaseType_Int2 ||
      baseType == HLSLBaseType_Int3 ||
      baseType == HLSLBaseType_Int4 ||
      baseType == HLSLBaseType_Uint2 ||
      baseType == HLSLBaseType_Uint3 ||
      baseType == HLSLBaseType_Uint4;
  }


  enum HLSLBinaryOp
  {
    HLSLBinaryOp_And,
    HLSLBinaryOp_Or,
    HLSLBinaryOp_Add,
    HLSLBinaryOp_Sub,
    HLSLBinaryOp_Mul,
    HLSLBinaryOp_Div,
    HLSLBinaryOp_Mod,
    HLSLBinaryOp_Less,
    HLSLBinaryOp_Greater,
    HLSLBinaryOp_LessEqual,
    HLSLBinaryOp_GreaterEqual,
    HLSLBinaryOp_Equal,
    HLSLBinaryOp_NotEqual,
    HLSLBinaryOp_BitAnd,
    HLSLBinaryOp_BitOr,
    HLSLBinaryOp_BitXor,
    HLSLBinaryOp_Assign,
    HLSLBinaryOp_AddAssign,
    HLSLBinaryOp_SubAssign,
    HLSLBinaryOp_MulAssign,
    HLSLBinaryOp_DivAssign,
  };

  inline bool IsCompareOp(HLSLBinaryOp op)
  {
    return op == HLSLBinaryOp_Less ||
      op == HLSLBinaryOp_Greater ||
      op == HLSLBinaryOp_LessEqual ||
      op == HLSLBinaryOp_GreaterEqual ||
      op == HLSLBinaryOp_Equal ||
      op == HLSLBinaryOp_NotEqual;
  }

  inline bool IsArithmeticOp(HLSLBinaryOp op)
  {
    return op == HLSLBinaryOp_Add ||
      op == HLSLBinaryOp_Sub ||
      op == HLSLBinaryOp_Mul ||
      op == HLSLBinaryOp_Div ||
      op == HLSLBinaryOp_Mod;
  }

  inline bool IsLogicOp(HLSLBinaryOp op)
  {
    return op == HLSLBinaryOp_And ||
      op == HLSLBinaryOp_Or;
  }

  inline bool IsAssignOp(HLSLBinaryOp op)
  {
    return op == HLSLBinaryOp_Assign ||
      op == HLSLBinaryOp_AddAssign ||
      op == HLSLBinaryOp_SubAssign ||
      op == HLSLBinaryOp_MulAssign ||
      op == HLSLBinaryOp_DivAssign;
  }


  enum HLSLUnaryOp
  {
    HLSLUnaryOp_Negative,       // -x
    HLSLUnaryOp_Positive,       // +x
    HLSLUnaryOp_Not,            // !x
    HLSLUnaryOp_PreIncrement,   // ++x
    HLSLUnaryOp_PreDecrement,   // --x
    HLSLUnaryOp_PostIncrement,  // x++
    HLSLUnaryOp_PostDecrement,  // x++
    HLSLUnaryOp_BitNot,         // ~x
  };

  enum HLSLArgumentModifier
  {
    HLSLArgumentModifier_None,
    HLSLArgumentModifier_In,
    HLSLArgumentModifier_Out,
    HLSLArgumentModifier_Inout,
    HLSLArgumentModifier_Uniform,
    HLSLArgumentModifier_Const,
  };

  enum HLSLTypeFlags
  {
    HLSLTypeFlag_None = 0,
    HLSLTypeFlag_Const = 0x01,
    HLSLTypeFlag_Static = 0x02,
    HLSLTypeFlag_Uniform = 0x04,
    //HLSLTypeFlag_Extern = 0x10,
    //HLSLTypeFlag_Volatile = 0x20,
    //HLSLTypeFlag_Shared = 0x40,
    //HLSLTypeFlag_Precise = 0x80,

    HLSLTypeFlag_Input = 0x100,
    HLSLTypeFlag_Output = 0x200,

    // Interpolation modifiers.
    HLSLTypeFlag_Linear = 0x10000,
    HLSLTypeFlag_Centroid = 0x20000,
    HLSLTypeFlag_NoInterpolation = 0x40000,
    HLSLTypeFlag_NoPerspective = 0x80000,
    HLSLTypeFlag_Sample = 0x100000,

    // Misc.
    HLSLTypeFlag_NoPromote = 0x200000,
  };

  enum HLSLAttributeType
  {
    HLSLAttributeType_Unknown,
    HLSLAttributeType_Unroll,
    HLSLAttributeType_Branch,
    HLSLAttributeType_Flatten,
    HLSLAttributeType_NoFastMath,
  };

  enum HLSLAddressSpace
  {
    HLSLAddressSpace_Undefined,
    HLSLAddressSpace_Constant,
    HLSLAddressSpace_Device,
    HLSLAddressSpace_Thread,
    HLSLAddressSpace_Shared,
  };


  struct HLSLNode;
  struct HLSLRoot;
  struct HLSLStatement;
  struct HLSLAttribute;
  struct HLSLDeclaration;
  struct HLSLStruct;
  struct HLSLStructField;
  struct HLSLBuffer;
  struct HLSLFunction;
  struct HLSLArgument;
  struct HLSLExpressionStatement;
  struct HLSLExpression;
  struct HLSLBinaryExpression;
  struct HLSLLiteralExpression;
  struct HLSLIdentifierExpression;
  struct HLSLConstructorExpression;
  struct HLSLFunctionCall;
  struct HLSLArrayAccess;
  struct HLSLAttribute;

  struct HLSLType
  {
    explicit HLSLType(HLSLBaseType _baseType = HLSLBaseType_Unknown)
    {
      baseType = _baseType;
      samplerType = HLSLBaseType_Float;
      typeName = nullptr;
      array = false;
      arraySize = nullptr;
      flags = 0;
      addressSpace = HLSLAddressSpace_Undefined;
    }
    HLSLBaseType        baseType;
    HLSLBaseType        samplerType;    // Half or Float
    const char* typeName;       // For user defined types.
    bool                array;
    HLSLExpression* arraySize;
    int                 flags;
    HLSLAddressSpace    addressSpace;
  };

  inline bool IsSamplerType(const HLSLType& type)
  {
    return IsSamplerType(type.baseType);
  }

  inline bool IsScalarType(const HLSLType& type)
  {
    return IsScalarType(type.baseType);
  }

  inline bool IsVectorType(const HLSLType& type)
  {
    return IsVectorType(type.baseType);
  }


  /** Base class for all nodes in the HLSL AST */
  struct HLSLNode
  {
    HLSLNodeType        nodeType;
    const char* fileName;
    int                 line;
  };

  struct HLSLRoot : public HLSLNode
  {
    static const HLSLNodeType s_type = HLSLNodeType_Root;
    HLSLRoot() { statement = nullptr; }
    HLSLStatement* statement;          // First statement.
  };

  struct HLSLStatement : public HLSLNode
  {
    HLSLStatement()
    {
      nextStatement = nullptr;
      attributes = nullptr;
      hidden = false;
    }
    HLSLStatement* nextStatement;      // Next statement in the block.
    HLSLAttribute* attributes;
    mutable bool        hidden;
  };

  struct HLSLAttribute : public HLSLNode
  {
    static const HLSLNodeType s_type = HLSLNodeType_Attribute;
    HLSLAttribute()
    {
      attributeType = HLSLAttributeType_Unknown;
      argument = nullptr;
      nextAttribute = nullptr;
    }
    HLSLAttributeType   attributeType;
    HLSLExpression* argument;
    HLSLAttribute* nextAttribute;
  };

  struct HLSLDeclaration : public HLSLStatement
  {
    static const HLSLNodeType s_type = HLSLNodeType_Declaration;
    HLSLDeclaration()
    {
      name = nullptr;
      registerName = nullptr;
      semantic = nullptr;
      nextDeclaration = nullptr;
      assignment = nullptr;
      buffer = nullptr;
    }
    const char* name;
    HLSLType            type;
    const char* registerName;       // @@ Store register index?
    const char* semantic;
    HLSLDeclaration* nextDeclaration;    // If multiple variables declared on a line.
    HLSLExpression* assignment;
    HLSLBuffer* buffer;
  };

  struct HLSLStruct : public HLSLStatement
  {
    static const HLSLNodeType s_type = HLSLNodeType_Struct;
    HLSLStruct()
    {
      name = nullptr;
      field = nullptr;
    }
    const char* name;
    HLSLStructField* field;              // First field in the structure.
  };

  struct HLSLStructField : public HLSLNode
  {
    static const HLSLNodeType s_type = HLSLNodeType_StructField;
    HLSLStructField()
    {
      name = nullptr;
      semantic = nullptr;
      sv_semantic = nullptr;
      nextField = nullptr;
      hidden = false;
    }
    const char* name;
    HLSLType            type;
    const char* semantic;
    const char* sv_semantic;
    HLSLStructField* nextField;      // Next field in the structure.
    bool                hidden;
  };

  /** A cbuffer or tbuffer declaration. */
  struct HLSLBuffer : public HLSLStatement
  {
    static const HLSLNodeType s_type = HLSLNodeType_Buffer;
    HLSLBuffer()
    {
      name = nullptr;
      registerName = nullptr;
      field = nullptr;
    }
    const char* name;
    const char* registerName;
    HLSLDeclaration* field;
  };


  /** Function declaration */
  struct HLSLFunction : public HLSLStatement
  {
    static const HLSLNodeType s_type = HLSLNodeType_Function;
    HLSLFunction()
    {
      name = nullptr;
      semantic = nullptr;
      sv_semantic = nullptr;
      statement = nullptr;
      argument = nullptr;
      numArguments = 0;
      numOutputArguments = 0;
      forward = nullptr;
    }
    const char* name;
    HLSLType            returnType;
    const char* semantic;
    const char* sv_semantic;
    int                 numArguments;
    int                 numOutputArguments;     // Includes out and inout arguments.
    HLSLArgument* argument;
    HLSLStatement* statement;
    HLSLFunction* forward; // Which HLSLFunction this one forward-declares
  };

  /** Declaration of an argument to a function. */
  struct HLSLArgument : public HLSLNode
  {
    static const HLSLNodeType s_type = HLSLNodeType_Argument;
    HLSLArgument()
    {
      name = nullptr;
      modifier = HLSLArgumentModifier_None;
      semantic = nullptr;
      sv_semantic = nullptr;
      defaultValue = nullptr;
      nextArgument = nullptr;
      hidden = false;
    }
    const char* name;
    HLSLArgumentModifier    modifier;
    HLSLType                type;
    const char* semantic;
    const char* sv_semantic;
    HLSLExpression* defaultValue;
    HLSLArgument* nextArgument;
    bool                    hidden;
  };

  /** Macro declaration */
  struct HLSLMacro : public HLSLStatement
  {
    static const HLSLNodeType s_type = HLSLNodeType_Macro;
    HLSLMacro()
    {
      name = nullptr;
      argument = nullptr;
      numArguments = 0;
      macroAliased = nullptr;
    }
    const char* name;
    HLSLArgument* argument;
    unsigned int        numArguments;
    std::string         value;
    HLSLMacro* macroAliased;
  };

  /** A expression which forms a complete statement. */
  struct HLSLExpressionStatement : public HLSLStatement
  {
    static const HLSLNodeType s_type = HLSLNodeType_ExpressionStatement;
    HLSLExpressionStatement()
    {
      expression = nullptr;
    }
    HLSLExpression* expression;
  };

  struct HLSLReturnStatement : public HLSLStatement
  {
    static const HLSLNodeType s_type = HLSLNodeType_ReturnStatement;
    HLSLReturnStatement()
    {
      expression = nullptr;
    }
    HLSLExpression* expression;
  };

  struct HLSLDiscardStatement : public HLSLStatement
  {
    static const HLSLNodeType s_type = HLSLNodeType_DiscardStatement;
  };

  struct HLSLBreakStatement : public HLSLStatement
  {
    static const HLSLNodeType s_type = HLSLNodeType_BreakStatement;
  };

  struct HLSLContinueStatement : public HLSLStatement
  {
    static const HLSLNodeType s_type = HLSLNodeType_ContinueStatement;
  };

  struct HLSLIfStatement : public HLSLStatement
  {
    static const HLSLNodeType s_type = HLSLNodeType_IfStatement;
    HLSLIfStatement()
    {
      condition = nullptr;
      statement = nullptr;
      elseStatement = nullptr;
      isStatic = false;
    }
    HLSLExpression* condition;
    HLSLStatement* statement;
    HLSLStatement* elseStatement;
    bool                isStatic;
  };

  struct HLSLForStatement : public HLSLStatement
  {
    static const HLSLNodeType s_type = HLSLNodeType_ForStatement;
    HLSLForStatement()
    {
      initialization = nullptr;
      condition = nullptr;
      increment = nullptr;
      statement = nullptr;
    }
    HLSLDeclaration* initialization;
    HLSLExpression* initializationWithoutType;
    HLSLExpression* condition;
    HLSLExpression* increment;
    HLSLStatement* statement;
  };

  struct HLSLWhileStatement : public HLSLStatement
  {
    static const HLSLNodeType s_type = HLSLNodeType_WhileStatement;
    HLSLWhileStatement()
    {
      condition = nullptr;
      statement = nullptr;
    }
    HLSLExpression* condition;
    HLSLStatement* statement;
  };

  struct HLSLBlockStatement : public HLSLStatement
  {
    static const HLSLNodeType s_type = HLSLNodeType_BlockStatement;
    HLSLBlockStatement()
    {
      statement = nullptr;
    }
    HLSLStatement* statement;
  };


  /** Base type for all types of expressions. */
  struct HLSLExpression : public HLSLNode
  {
    static const HLSLNodeType s_type = HLSLNodeType_Expression;
    HLSLExpression()
    {
      nextExpression = nullptr;
    }
    HLSLType            expressionType;
    HLSLExpression* nextExpression; // Used when the expression is part of a list, like in a function call.
  };

  struct HLSLUnaryExpression : public HLSLExpression
  {
    static const HLSLNodeType s_type = HLSLNodeType_UnaryExpression;
    HLSLUnaryExpression()
    {
      expression = nullptr;
    }
    HLSLUnaryOp         unaryOp;
    HLSLExpression* expression;
  };

  struct HLSLBinaryExpression : public HLSLExpression
  {
    static const HLSLNodeType s_type = HLSLNodeType_BinaryExpression;
    HLSLBinaryExpression()
    {
      expression1 = nullptr;
      expression2 = nullptr;
    }
    HLSLBinaryOp        binaryOp;
    HLSLExpression* expression1;
    HLSLExpression* expression2;
  };

  /** ? : construct */
  struct HLSLConditionalExpression : public HLSLExpression
  {
    static const HLSLNodeType s_type = HLSLNodeType_ConditionalExpression;
    HLSLConditionalExpression()
    {
      condition = nullptr;
      trueExpression = nullptr;
      falseExpression = nullptr;
    }
    HLSLExpression* condition;
    HLSLExpression* trueExpression;
    HLSLExpression* falseExpression;
  };

  struct HLSLCastingExpression : public HLSLExpression
  {
    static const HLSLNodeType s_type = HLSLNodeType_CastingExpression;
    HLSLCastingExpression()
    {
      expression = nullptr;
    }
    HLSLType            type;
    HLSLExpression* expression;
  };

  /** Float, integer, boolean, etc. literal constant. */
  struct HLSLLiteralExpression : public HLSLExpression
  {
    static const HLSLNodeType s_type = HLSLNodeType_LiteralExpression;
    HLSLBaseType        type;   // Note, not all types can be literals.
    union
    {
      bool            bValue;
      float           fValue;
      int             iValue;
    };
  };

  /** An identifier, typically a variable name or structure field name. */
  struct HLSLIdentifierExpression : public HLSLExpression
  {
    static const HLSLNodeType s_type = HLSLNodeType_IdentifierExpression;
    HLSLIdentifierExpression()
    {
      name = nullptr;
      global = false;
    }
    const char* name;
    bool                global; // This is a global variable.
  };

  /** float2(1, 2) */
  struct HLSLConstructorExpression : public HLSLExpression
  {
    static const HLSLNodeType s_type = HLSLNodeType_ConstructorExpression;
    HLSLConstructorExpression()
    {
      argument = nullptr;
    }
    HLSLType            type;
    HLSLExpression* argument;
  };

  /** object.member **/
  struct HLSLMemberAccess : public HLSLExpression
  {
    static const HLSLNodeType s_type = HLSLNodeType_MemberAccess;
    HLSLMemberAccess()
    {
      object = nullptr;
      field = nullptr;
      swizzle = false;
    }
    HLSLExpression* object;
    const char* field;
    bool                swizzle;
  };

  /** array[index] **/
  struct HLSLArrayAccess : public HLSLExpression
  {
    static const HLSLNodeType s_type = HLSLNodeType_ArrayAccess;
    HLSLArrayAccess()
    {
      array = nullptr;
      index = nullptr;
    }
    HLSLExpression* array;
    HLSLExpression* index;
  };

  struct HLSLFunctionCall : public HLSLExpression
  {
    static const HLSLNodeType s_type = HLSLNodeType_FunctionCall;
    HLSLFunctionCall()
    {
      function = nullptr;
      argument = nullptr;
      numArguments = 0;
    }
    const HLSLFunction* function;
    HLSLExpression* argument;
    int                 numArguments;
  };

  struct HLSLStateAssignment : public HLSLNode
  {
    static const HLSLNodeType s_type = HLSLNodeType_StateAssignment;
    HLSLStateAssignment()
    {
      stateName = nullptr;
      sValue = nullptr;
      nextStateAssignment = nullptr;
    }

    const char* stateName;
    int                     d3dRenderState;
    union {
      int                 iValue;
      float               fValue;
      const char* sValue;
    };
    HLSLStateAssignment* nextStateAssignment;
  };

  struct HLSLSamplerState : public HLSLExpression // @@ Does this need to be an expression? Does it have a type? I guess type is useful.
  {
    static const HLSLNodeType s_type = HLSLNodeType_SamplerState;
    HLSLSamplerState()
    {
      numStateAssignments = 0;
      stateAssignments = nullptr;
    }

    int                     numStateAssignments;
    HLSLStateAssignment* stateAssignments;
  };

  struct HLSLPass : public HLSLNode
  {
    static const HLSLNodeType s_type = HLSLNodeType_Pass;
    HLSLPass()
    {
      name = nullptr;
      numStateAssignments = 0;
      stateAssignments = nullptr;
      nextPass = nullptr;
    }

    const char* name;
    int                     numStateAssignments;
    HLSLStateAssignment* stateAssignments;
    HLSLPass* nextPass;
  };

  struct HLSLTechnique : public HLSLStatement
  {
    static const HLSLNodeType s_type = HLSLNodeType_Technique;
    HLSLTechnique()
    {
      name = nullptr;
      numPasses = 0;
      passes = nullptr;
    }

    const char* name;
    int                 numPasses;
    HLSLPass* passes;
  };

  struct HLSLPipeline : public HLSLStatement
  {
    static const HLSLNodeType s_type = HLSLNodeType_Pipeline;
    HLSLPipeline()
    {
      name = nullptr;
      numStateAssignments = 0;
      stateAssignments = nullptr;
    }

    const char* name;
    int                     numStateAssignments;
    HLSLStateAssignment* stateAssignments;
  };

  struct HLSLStage : public HLSLStatement
  {
    static const HLSLNodeType s_type = HLSLNodeType_Stage;
    HLSLStage()
    {
      name = nullptr;
      statement = nullptr;
      inputs = nullptr;
      outputs = nullptr;
    }

    const char* name;
    HLSLStatement* statement;
    HLSLDeclaration* inputs;
    HLSLDeclaration* outputs;
  };

  struct matrixCtor {
    HLSLBaseType matrixType;
    std::vector<HLSLBaseType> argumentTypes;

    bool operator==(const matrixCtor& other) const
    {
      return  matrixType == other.matrixType &&
        argumentTypes == other.argumentTypes;
    }

    bool operator<(const matrixCtor& other) const
    {
      if (matrixType < other.matrixType)
      {
        return true;
      }
      else if (matrixType > other.matrixType)
      {
        return false;
      }

      return argumentTypes < other.argumentTypes;
    }
  };


  /**
   * Abstract syntax tree for parsed HLSL code.
   */
  class HLSLTree
  {

  public:

    explicit HLSLTree(Allocator* allocator);
    ~HLSLTree();

    /** Adds a string to the string pool used by the tree. */
    const char* AddString(const char* string);
    const char* AddStringFormat(const char* string, ...);

    /** Returns true if the string is contained within the tree. */
    bool GetContainsString(const char* string) const;

    /** Returns the root block in the tree */
    HLSLRoot* GetRoot() const;

    /** Adds a new node to the tree with the specified type. */
    template <class T>
    T* AddNode(const char* fileName, int line)
    {
      HLSLNode* node = new (AllocateMemory(sizeof(T))) T();
      node->nodeType = T::s_type;
      node->fileName = fileName;
      node->line = line;
      return static_cast<T*>(node);
    }

    HLSLFunction* FindFunction(const char* name);
    HLSLDeclaration* FindGlobalDeclaration(const char* name, HLSLBuffer** buffer_out = nullptr);
    HLSLStruct* FindGlobalStruct(const char* name);
    HLSLTechnique* FindTechnique(const char* name);
    HLSLPipeline* FindFirstPipeline();
    HLSLPipeline* FindNextPipeline(HLSLPipeline* current);
    HLSLPipeline* FindPipeline(const char* name);
    HLSLBuffer* FindBuffer(const char* name);

    bool GetExpressionValue(HLSLExpression* expression, int& value);
    int GetExpressionValue(HLSLExpression* expression, float values[4]);

    bool NeedsFunction(const char* name);
    bool ReplaceUniformsAssignments();
    void EnumerateMatrixCtorsNeeded(std::vector<matrixCtor>& matrixCtors);

  private:

    void* AllocateMemory(size_t size);
    void  AllocatePage();

  private:

    static const size_t s_nodePageSize = 1024 * 4;

    struct NodePage
    {
      NodePage* next;
      char        buffer[s_nodePageSize];
    };

    Allocator* m_allocator;
    StringPool      m_stringPool;
    HLSLRoot* m_root;

    NodePage* m_firstPage;
    NodePage* m_currentPage;
    size_t          m_currentPageOffset;

  };



  class HLSLTreeVisitor
  {
  public:
    virtual void VisitType(HLSLType& type);

    virtual void VisitRoot(HLSLRoot* node);
    virtual void VisitTopLevelStatement(HLSLStatement* node);
    virtual void VisitStatements(HLSLStatement* statement);
    virtual void VisitStatement(HLSLStatement* node);
    virtual void VisitDeclaration(HLSLDeclaration* node);
    virtual void VisitStruct(HLSLStruct* node);
    virtual void VisitStructField(HLSLStructField* node);
    virtual void VisitBuffer(HLSLBuffer* node);
    //virtual void VisitBufferField(HLSLBufferField * node);
    virtual void VisitFunction(HLSLFunction* node);
    virtual void VisitArgument(HLSLArgument* node);
    virtual void VisitExpressionStatement(HLSLExpressionStatement* node);
    virtual void VisitExpression(HLSLExpression* node);
    virtual void VisitReturnStatement(HLSLReturnStatement* node);
    virtual void VisitDiscardStatement(HLSLDiscardStatement* node);
    virtual void VisitBreakStatement(HLSLBreakStatement* node);
    virtual void VisitContinueStatement(HLSLContinueStatement* node);
    virtual void VisitIfStatement(HLSLIfStatement* node);
    virtual void VisitForStatement(HLSLForStatement* node);
    virtual void VisitWhileStatement(HLSLWhileStatement* node);
    virtual void VisitBlockStatement(HLSLBlockStatement* node);
    virtual void VisitUnaryExpression(HLSLUnaryExpression* node);
    virtual void VisitBinaryExpression(HLSLBinaryExpression* node);
    virtual void VisitConditionalExpression(HLSLConditionalExpression* node);
    virtual void VisitCastingExpression(HLSLCastingExpression* node);
    virtual void VisitLiteralExpression(HLSLLiteralExpression* node);
    virtual void VisitIdentifierExpression(HLSLIdentifierExpression* node);
    virtual void VisitConstructorExpression(HLSLConstructorExpression* node);
    virtual void VisitMemberAccess(HLSLMemberAccess* node);
    virtual void VisitArrayAccess(HLSLArrayAccess* node);
    virtual void VisitFunctionCall(HLSLFunctionCall* node);
    virtual void VisitStateAssignment(HLSLStateAssignment* node);
    virtual void VisitSamplerState(HLSLSamplerState* node);
    virtual void VisitPass(HLSLPass* node);
    virtual void VisitTechnique(HLSLTechnique* node);
    virtual void VisitPipeline(HLSLPipeline* node);


    virtual void VisitFunctions(HLSLRoot* root);
    virtual void VisitParameters(HLSLRoot* root);

    HLSLFunction* FindFunction(HLSLRoot* root, const char* name);
    HLSLDeclaration* FindGlobalDeclaration(HLSLRoot* root, const char* name);
    HLSLStruct* FindGlobalStruct(HLSLRoot* root, const char* name);
  };


  // Tree transformations:
  extern void PruneTree(HLSLTree* tree, const char* entryName0, const char* entryName1 = nullptr);
  extern void SortTree(HLSLTree* tree);
  extern void GroupParameters(HLSLTree* tree);
  extern void HideUnusedArguments(HLSLFunction* function);
  extern bool EmulateAlphaTest(HLSLTree* tree, const char* entryName, float alphaRef = 0.5f);
  extern void FlattenExpressions(HLSLTree* tree);

  extern matrixCtor matrixCtorBuilder(HLSLType type, HLSLExpression* arguments);


} // M4

namespace M4
{

  HLSLTree::HLSLTree(Allocator* allocator) :
    m_allocator(allocator), m_stringPool(allocator)
  {
    m_firstPage = m_allocator->New<NodePage>();
    m_firstPage->next = nullptr;

    m_currentPage = m_firstPage;
    m_currentPageOffset = 0;

    m_root = AddNode<HLSLRoot>(nullptr, 1);
  }

  HLSLTree::~HLSLTree()
  {
    NodePage* page = m_firstPage;
    while (page != nullptr)
    {
      NodePage* next = page->next;
      m_allocator->Delete(page);
      page = next;
    }
  }

  void HLSLTree::AllocatePage()
  {
    NodePage* newPage = m_allocator->New<NodePage>();
    newPage->next = nullptr;
    m_currentPage->next = newPage;
    m_currentPageOffset = 0;
    m_currentPage = newPage;
  }

  const char* HLSLTree::AddString(const char* string)
  {
    return m_stringPool.AddString(string);
  }

  const char* HLSLTree::AddStringFormat(const char* format, ...)
  {
    va_list args;
    va_start(args, format);
    const char* string = m_stringPool.AddStringFormatList(format, args);
    va_end(args);
    return string;
  }

  bool HLSLTree::GetContainsString(const char* string) const
  {
    return m_stringPool.GetContainsString(string);
  }

  HLSLRoot* HLSLTree::GetRoot() const
  {
    return m_root;
  }

  void* HLSLTree::AllocateMemory(size_t size)
  {
    if (m_currentPageOffset + size > s_nodePageSize)
    {
      AllocatePage();
    }
    void* buffer = m_currentPage->buffer + m_currentPageOffset;
    m_currentPageOffset += size;
    return buffer;
  }

  // @@ This doesn't do any parameter matching. Simply returns the first function with that name.
  HLSLFunction* HLSLTree::FindFunction(const char* name)
  {
    HLSLStatement* statement = m_root->statement;
    while (statement != nullptr)
    {
      if (statement->nodeType == HLSLNodeType_Function)
      {
        HLSLFunction* function = (HLSLFunction*)statement;
        if (String_Equal(name, function->name))
        {
          return function;
        }
      }

      statement = statement->nextStatement;
    }

    return nullptr;
  }

  HLSLDeclaration* HLSLTree::FindGlobalDeclaration(const char* name, HLSLBuffer** buffer_out/*=nullptr*/)
  {
    HLSLStatement* statement = m_root->statement;
    while (statement != nullptr)
    {
      if (statement->nodeType == HLSLNodeType_Declaration)
      {
        HLSLDeclaration* declaration = (HLSLDeclaration*)statement;
        if (String_Equal(name, declaration->name))
        {
          if (buffer_out) *buffer_out = nullptr;
          return declaration;
        }
      }
      else if (statement->nodeType == HLSLNodeType_Buffer)
      {
        HLSLBuffer* buffer = (HLSLBuffer*)statement;

        HLSLDeclaration* field = buffer->field;
        while (field != nullptr)
        {
          ASSERT(field->nodeType == HLSLNodeType_Declaration);
          if (String_Equal(name, field->name))
          {
            if (buffer_out) *buffer_out = buffer;
            return field;
          }
          field = (HLSLDeclaration*)field->nextStatement;
        }
      }

      statement = statement->nextStatement;
    }

    if (buffer_out) *buffer_out = nullptr;
    return nullptr;
  }

  HLSLStruct* HLSLTree::FindGlobalStruct(const char* name)
  {
    HLSLStatement* statement = m_root->statement;
    while (statement != nullptr)
    {
      if (statement->nodeType == HLSLNodeType_Struct)
      {
        HLSLStruct* declaration = (HLSLStruct*)statement;
        if (String_Equal(name, declaration->name))
        {
          return declaration;
        }
      }

      statement = statement->nextStatement;
    }

    return nullptr;
  }

  HLSLTechnique* HLSLTree::FindTechnique(const char* name)
  {
    HLSLStatement* statement = m_root->statement;
    while (statement != nullptr)
    {
      if (statement->nodeType == HLSLNodeType_Technique)
      {
        HLSLTechnique* technique = (HLSLTechnique*)statement;
        if (String_Equal(name, technique->name))
        {
          return technique;
        }
      }

      statement = statement->nextStatement;
    }

    return nullptr;
  }

  HLSLPipeline* HLSLTree::FindFirstPipeline()
  {
    return FindNextPipeline(nullptr);
  }

  HLSLPipeline* HLSLTree::FindNextPipeline(HLSLPipeline* current)
  {
    HLSLStatement* statement = current ? current : m_root->statement;
    while (statement != nullptr)
    {
      if (statement->nodeType == HLSLNodeType_Pipeline)
      {
        return (HLSLPipeline*)statement;
      }

      statement = statement->nextStatement;
    }

    return nullptr;
  }

  HLSLPipeline* HLSLTree::FindPipeline(const char* name)
  {
    HLSLStatement* statement = m_root->statement;
    while (statement != nullptr)
    {
      if (statement->nodeType == HLSLNodeType_Pipeline)
      {
        HLSLPipeline* pipeline = (HLSLPipeline*)statement;
        if (String_Equal(name, pipeline->name))
        {
          return pipeline;
        }
      }

      statement = statement->nextStatement;
    }

    return nullptr;
  }

  HLSLBuffer* HLSLTree::FindBuffer(const char* name)
  {
    HLSLStatement* statement = m_root->statement;
    while (statement != nullptr)
    {
      if (statement->nodeType == HLSLNodeType_Buffer)
      {
        HLSLBuffer* buffer = (HLSLBuffer*)statement;
        if (String_Equal(name, buffer->name))
        {
          return buffer;
        }
      }

      statement = statement->nextStatement;
    }

    return nullptr;
  }



  bool HLSLTree::GetExpressionValue(HLSLExpression* expression, int& value)
  {
    ASSERT(expression != nullptr);

    // Expression must be constant.
    if ((expression->expressionType.flags & HLSLTypeFlag_Const) == 0)
    {
      return false;
    }

    // We are expecting an integer scalar. @@ Add support for type conversion from other scalar types.
    if (expression->expressionType.baseType != HLSLBaseType_Int &&
      expression->expressionType.baseType != HLSLBaseType_Bool)
    {
      return false;
    }

    if (expression->expressionType.array)
    {
      return false;
    }

    if (expression->nodeType == HLSLNodeType_BinaryExpression)
    {
      HLSLBinaryExpression* binaryExpression = (HLSLBinaryExpression*)expression;

      int value1, value2;
      if (!GetExpressionValue(binaryExpression->expression1, value1) ||
        !GetExpressionValue(binaryExpression->expression2, value2))
      {
        return false;
      }

      switch (binaryExpression->binaryOp)
      {
      case HLSLBinaryOp_And:
        value = value1 && value2;
        return true;
      case HLSLBinaryOp_Or:
        value = value1 || value2;
        return true;
      case HLSLBinaryOp_Add:
        value = value1 + value2;
        return true;
      case HLSLBinaryOp_Sub:
        value = value1 - value2;
        return true;
      case HLSLBinaryOp_Mul:
        value = value1 * value2;
        return true;
      case HLSLBinaryOp_Div:
        value = value1 / value2;
        return true;
      case HLSLBinaryOp_Mod:
        value = value1 % value2;
        return true;
      case HLSLBinaryOp_Less:
        value = value1 < value2;
        return true;
      case HLSLBinaryOp_Greater:
        value = value1 > value2;
        return true;
      case HLSLBinaryOp_LessEqual:
        value = value1 <= value2;
        return true;
      case HLSLBinaryOp_GreaterEqual:
        value = value1 >= value2;
        return true;
      case HLSLBinaryOp_Equal:
        value = value1 == value2;
        return true;
      case HLSLBinaryOp_NotEqual:
        value = value1 != value2;
        return true;
      case HLSLBinaryOp_BitAnd:
        value = value1 & value2;
        return true;
      case HLSLBinaryOp_BitOr:
        value = value1 | value2;
        return true;
      case HLSLBinaryOp_BitXor:
        value = value1 ^ value2;
        return true;
      case HLSLBinaryOp_Assign:
      case HLSLBinaryOp_AddAssign:
      case HLSLBinaryOp_SubAssign:
      case HLSLBinaryOp_MulAssign:
      case HLSLBinaryOp_DivAssign:
        // IC: These are not valid on non-constant expressions and should fail earlier when querying expression value.
        return false;
      }
    }
    else if (expression->nodeType == HLSLNodeType_UnaryExpression)
    {
      HLSLUnaryExpression* unaryExpression = (HLSLUnaryExpression*)expression;

      if (!GetExpressionValue(unaryExpression->expression, value))
      {
        return false;
      }

      switch (unaryExpression->unaryOp)
      {
      case HLSLUnaryOp_Negative:
        value = -value;
        return true;
      case HLSLUnaryOp_Positive:
        // nop.
        return true;
      case HLSLUnaryOp_Not:
        value = !value;
        return true;
      case HLSLUnaryOp_BitNot:
        value = ~value;
        return true;
      case HLSLUnaryOp_PostDecrement:
      case HLSLUnaryOp_PostIncrement:
      case HLSLUnaryOp_PreDecrement:
      case HLSLUnaryOp_PreIncrement:
        // IC: These are not valid on non-constant expressions and should fail earlier when querying expression value.
        return false;
      }
    }
    else if (expression->nodeType == HLSLNodeType_IdentifierExpression)
    {
      HLSLIdentifierExpression* identifier = (HLSLIdentifierExpression*)expression;

      HLSLDeclaration* declaration = FindGlobalDeclaration(identifier->name);
      if (declaration == nullptr)
      {
        return false;
      }
      if ((declaration->type.flags & HLSLTypeFlag_Const) == 0)
      {
        return false;
      }

      return GetExpressionValue(declaration->assignment, value);
    }
    else if (expression->nodeType == HLSLNodeType_LiteralExpression)
    {
      HLSLLiteralExpression* literal = (HLSLLiteralExpression*)expression;

      if (literal->expressionType.baseType == HLSLBaseType_Int) value = literal->iValue;
      else if (literal->expressionType.baseType == HLSLBaseType_Bool) value = (int)literal->bValue;
      else return false;

      return true;
    }

    return false;
  }

  bool HLSLTree::NeedsFunction(const char* name)
  {
    // Early out
    if (!GetContainsString(name))
      return false;

    struct NeedsFunctionVisitor : HLSLTreeVisitor
    {
      const char* name;
      bool result;

      virtual void VisitTopLevelStatement(HLSLStatement* node)
      {
        if (!node->hidden)
          HLSLTreeVisitor::VisitTopLevelStatement(node);
      }

      virtual void VisitFunctionCall(HLSLFunctionCall* node)
      {
        result = result || String_Equal(name, node->function->name);

        HLSLTreeVisitor::VisitFunctionCall(node);
      }
    };

    NeedsFunctionVisitor visitor;
    visitor.name = name;
    visitor.result = false;

    visitor.VisitRoot(m_root);

    return visitor.result;
  }

  int GetVectorDimension(HLSLType& type)
  {
    if (type.baseType >= HLSLBaseType_FirstNumeric &&
      type.baseType <= HLSLBaseType_LastNumeric)
    {
      if (type.baseType == HLSLBaseType_Float) return 1;
      if (type.baseType == HLSLBaseType_Float2) return 2;
      if (type.baseType == HLSLBaseType_Float3) return 3;
      if (type.baseType == HLSLBaseType_Float4) return 4;

    }
    return 0;
  }

  // Returns dimension, 0 if invalid.
  int HLSLTree::GetExpressionValue(HLSLExpression* expression, float values[4])
  {
    ASSERT(expression != nullptr);

    // Expression must be constant.
    if ((expression->expressionType.flags & HLSLTypeFlag_Const) == 0)
    {
      return 0;
    }

    if (expression->expressionType.baseType == HLSLBaseType_Int ||
      expression->expressionType.baseType == HLSLBaseType_Bool)
    {
      int int_value;
      if (GetExpressionValue(expression, int_value)) {
        for (int i = 0; i < 4; i++) values[i] = (float)int_value;   // @@ Warn if conversion is not exact.
        return 1;
      }

      return 0;
    }
    if (expression->expressionType.baseType >= HLSLBaseType_FirstInteger && expression->expressionType.baseType <= HLSLBaseType_LastInteger)
    {
      // @@ Add support for uints?
      // @@ Add support for int vectors?
      return 0;
    }
    if (expression->expressionType.baseType > HLSLBaseType_LastNumeric)
    {
      return 0;
    }

    // @@ Not supported yet, but we may need it?
    if (expression->expressionType.array)
    {
      return false;
    }

    if (expression->nodeType == HLSLNodeType_BinaryExpression)
    {
      HLSLBinaryExpression* binaryExpression = (HLSLBinaryExpression*)expression;
      int dim = GetVectorDimension(binaryExpression->expressionType);

      float values1[4], values2[4];
      int dim1 = GetExpressionValue(binaryExpression->expression1, values1);
      int dim2 = GetExpressionValue(binaryExpression->expression2, values2);

      if (dim1 == 0 || dim2 == 0)
      {
        return 0;
      }

      if (dim1 != dim2)
      {
        // Brodacast scalar to vector size.
        if (dim1 == 1)
        {
          for (int i = 1; i < dim2; i++) values1[i] = values1[0];
          dim1 = dim2;
        }
        else if (dim2 == 1)
        {
          for (int i = 1; i < dim1; i++) values2[i] = values2[0];
          dim2 = dim1;
        }
        else
        {
          return 0;
        }
      }
      ASSERT(dim == dim1);

      switch (binaryExpression->binaryOp)
      {
      case HLSLBinaryOp_Add:
        for (int i = 0; i < dim; i++) values[i] = values1[i] + values2[i];
        return dim;
      case HLSLBinaryOp_Sub:
        for (int i = 0; i < dim; i++) values[i] = values1[i] - values2[i];
        return dim;
      case HLSLBinaryOp_Mul:
        for (int i = 0; i < dim; i++) values[i] = values1[i] * values2[i];
        return dim;
      case HLSLBinaryOp_Div:
        for (int i = 0; i < dim; i++) values[i] = values1[i] / values2[i];
        return dim;
      case HLSLBinaryOp_Mod:
        for (int i = 0; i < dim; i++) values[i] = int(values1[i]) % int(values2[i]);
        return dim;
      default:
        return 0;
      }
    }
    else if (expression->nodeType == HLSLNodeType_UnaryExpression)
    {
      HLSLUnaryExpression* unaryExpression = (HLSLUnaryExpression*)expression;
      int dim = GetVectorDimension(unaryExpression->expressionType);

      int dim1 = GetExpressionValue(unaryExpression->expression, values);
      if (dim1 == 0)
      {
        return 0;
      }
      ASSERT(dim == dim1);

      switch (unaryExpression->unaryOp)
      {
      case HLSLUnaryOp_Negative:
        for (int i = 0; i < dim; i++) values[i] = -values[i];
        return dim;
      case HLSLUnaryOp_Positive:
        // nop.
        return dim;
      default:
        return 0;
      }
    }
    else if (expression->nodeType == HLSLNodeType_ConstructorExpression)
    {
      HLSLConstructorExpression* constructor = (HLSLConstructorExpression*)expression;

      int dim = GetVectorDimension(constructor->expressionType);

      int idx = 0;
      HLSLExpression* arg = constructor->argument;
      while (arg != nullptr)
      {
        float tmp[4];
        int n = GetExpressionValue(arg, tmp);
        for (int i = 0; i < n; i++) values[idx + i] = tmp[i];
        idx += n;

        arg = arg->nextExpression;
      }
      ASSERT(dim == idx);

      return dim;
    }
    else if (expression->nodeType == HLSLNodeType_IdentifierExpression)
    {
      HLSLIdentifierExpression* identifier = (HLSLIdentifierExpression*)expression;

      HLSLDeclaration* declaration = FindGlobalDeclaration(identifier->name);
      if (declaration == nullptr)
      {
        return 0;
      }
      if ((declaration->type.flags & HLSLTypeFlag_Const) == 0)
      {
        return 0;
      }

      return GetExpressionValue(declaration->assignment, values);
    }
    else if (expression->nodeType == HLSLNodeType_LiteralExpression)
    {
      HLSLLiteralExpression* literal = (HLSLLiteralExpression*)expression;

      if (literal->expressionType.baseType == HLSLBaseType_Float) values[0] = literal->fValue;
      else if (literal->expressionType.baseType == HLSLBaseType_Bool) values[0] = literal->bValue;
      else if (literal->expressionType.baseType == HLSLBaseType_Int) values[0] = (float)literal->iValue;  // @@ Warn if conversion is not exact.
      else return 0;

      return 1;
    }

    return 0;
  }

  bool HLSLTree::ReplaceUniformsAssignments()
  {
    struct ReplaceUniformsAssignmentsVisitor : HLSLTreeVisitor
    {
      HLSLTree* tree;
      std::map<std::string, HLSLDeclaration*> uniforms;
      std::map<std::string, std::string> uniformsReplaced;
      bool withinAssignment;

      virtual void VisitDeclaration(HLSLDeclaration* node)
      {
        HLSLTreeVisitor::VisitDeclaration(node);

        // Enumerate uniforms
        if (node->type.flags & HLSLTypeFlag_Uniform)
        {
          uniforms[node->name] = node;
        }
      }

      virtual void VisitFunction(HLSLFunction* node)
      {
        uniformsReplaced.clear();

        // Detect uniforms assignments
        HLSLTreeVisitor::VisitFunction(node);

        // Declare uniforms replacements
        std::map<std::string, std::string>::const_iterator iter = uniformsReplaced.cbegin();
        for (; iter != uniformsReplaced.cend(); ++iter)
        {
          HLSLDeclaration* uniformDeclaration = uniforms[iter->first];
          HLSLDeclaration* declaration = tree->AddNode<HLSLDeclaration>(node->fileName, node->line);

          declaration->name = tree->AddString(iter->second.c_str());
          declaration->type = uniformDeclaration->type;

          // Add declaration within function statements
          declaration->nextStatement = node->statement;
          node->statement = declaration;
        }
      }

      virtual void VisitBinaryExpression(HLSLBinaryExpression* node)
      {
        // Visit expression 2 first to not replace possible uniform reading
        VisitExpression(node->expression2);

        if (IsAssignOp(node->binaryOp))
        {
          withinAssignment = true;
        }

        VisitExpression(node->expression1);

        withinAssignment = false;
      }

      virtual void VisitIdentifierExpression(HLSLIdentifierExpression* node)
      {
        if (withinAssignment)
        {
          // Check if variable is a uniform
          if (uniforms.find(node->name) != uniforms.end())
          {
            // Check if variable is not already replaced
            if (uniformsReplaced.find(node->name) == uniformsReplaced.end())
            {
              std::string newName(node->name);
              do
              {
                newName.insert(0, "new");
              } while (tree->GetContainsString(newName.c_str()));

              uniformsReplaced[node->name] = newName;
            }
          }
        }

        // Check if variable need to be replaced
        if (uniformsReplaced.find(node->name) != uniformsReplaced.end())
        {
          // Replace
          node->name = tree->AddString(uniformsReplaced[node->name].c_str());
        }
      }
    };

    ReplaceUniformsAssignmentsVisitor visitor;
    visitor.tree = this;
    visitor.withinAssignment = false;
    visitor.VisitRoot(m_root);

    return true;
  }


  matrixCtor matrixCtorBuilder(HLSLType type, HLSLExpression* arguments) {
    matrixCtor ctor;

    ctor.matrixType = type.baseType;

    // Fetch all arguments
    HLSLExpression* argument = arguments;
    while (argument != nullptr)
    {
      ctor.argumentTypes.push_back(argument->expressionType.baseType);
      argument = argument->nextExpression;
    }

    return ctor;
  }

  void HLSLTree::EnumerateMatrixCtorsNeeded(std::vector<matrixCtor>& matrixCtors) {

    struct EnumerateMatrixCtorsVisitor : HLSLTreeVisitor
    {
      std::vector<matrixCtor> matrixCtorsNeeded;

      virtual void VisitConstructorExpression(HLSLConstructorExpression* node)
      {
        if (IsMatrixType(node->expressionType.baseType))
        {
          matrixCtor ctor = matrixCtorBuilder(node->expressionType, node->argument);

          if (std::find(matrixCtorsNeeded.cbegin(), matrixCtorsNeeded.cend(), ctor) == matrixCtorsNeeded.cend())
          {
            matrixCtorsNeeded.push_back(ctor);
          }
        }

        HLSLTreeVisitor::VisitConstructorExpression(node);
      }

      virtual void VisitDeclaration(HLSLDeclaration* node)
      {
        if (IsMatrixType(node->type.baseType) &&
          (node->type.flags & HLSLArgumentModifier_Uniform) == 0)
        {
          matrixCtor ctor = matrixCtorBuilder(node->type, node->assignment);

          // No special constructor needed if it already a matrix
          bool matrixArgument = false;
          for (HLSLBaseType& type : ctor.argumentTypes)
          {
            if (IsMatrixType(type))
            {
              matrixArgument = true;
              break;
            }
          }

          if (!matrixArgument &&
            std::find(matrixCtorsNeeded.cbegin(), matrixCtorsNeeded.cend(), ctor) == matrixCtorsNeeded.cend())
          {
            matrixCtorsNeeded.push_back(ctor);
          }
        }

        HLSLTreeVisitor::VisitDeclaration(node);
      }
    };

    EnumerateMatrixCtorsVisitor visitor;
    visitor.VisitRoot(m_root);

    matrixCtors = visitor.matrixCtorsNeeded;
  }


  void HLSLTreeVisitor::VisitType(HLSLType& type)
  {
  }

  void HLSLTreeVisitor::VisitRoot(HLSLRoot* root)
  {
    HLSLStatement* statement = root->statement;
    while (statement != nullptr) {
      VisitTopLevelStatement(statement);
      statement = statement->nextStatement;
    }
  }

  void HLSLTreeVisitor::VisitTopLevelStatement(HLSLStatement* node)
  {
    if (node->nodeType == HLSLNodeType_Declaration) {
      VisitDeclaration((HLSLDeclaration*)node);
    }
    else if (node->nodeType == HLSLNodeType_Struct) {
      VisitStruct((HLSLStruct*)node);
    }
    else if (node->nodeType == HLSLNodeType_Buffer) {
      VisitBuffer((HLSLBuffer*)node);
    }
    else if (node->nodeType == HLSLNodeType_Function) {
      VisitFunction((HLSLFunction*)node);
    }
    else if (node->nodeType == HLSLNodeType_Technique) {
      VisitTechnique((HLSLTechnique*)node);
    }
    else if (node->nodeType == HLSLNodeType_Pipeline) {
      VisitPipeline((HLSLPipeline*)node);
    }
    else {
      ASSERT(0);
    }
  }

  void HLSLTreeVisitor::VisitStatements(HLSLStatement* statement)
  {
    while (statement != nullptr) {
      VisitStatement(statement);
      statement = statement->nextStatement;
    }
  }

  void HLSLTreeVisitor::VisitStatement(HLSLStatement* node)
  {
    // Function statements
    if (node->nodeType == HLSLNodeType_Declaration) {
      VisitDeclaration((HLSLDeclaration*)node);
    }
    else if (node->nodeType == HLSLNodeType_ExpressionStatement) {
      VisitExpressionStatement((HLSLExpressionStatement*)node);
    }
    else if (node->nodeType == HLSLNodeType_ReturnStatement) {
      VisitReturnStatement((HLSLReturnStatement*)node);
    }
    else if (node->nodeType == HLSLNodeType_DiscardStatement) {
      VisitDiscardStatement((HLSLDiscardStatement*)node);
    }
    else if (node->nodeType == HLSLNodeType_BreakStatement) {
      VisitBreakStatement((HLSLBreakStatement*)node);
    }
    else if (node->nodeType == HLSLNodeType_ContinueStatement) {
      VisitContinueStatement((HLSLContinueStatement*)node);
    }
    else if (node->nodeType == HLSLNodeType_IfStatement) {
      VisitIfStatement((HLSLIfStatement*)node);
    }
    else if (node->nodeType == HLSLNodeType_ForStatement) {
      VisitForStatement((HLSLForStatement*)node);
    }
    else if (node->nodeType == HLSLNodeType_WhileStatement) {
      VisitWhileStatement((HLSLWhileStatement*)node);
    }
    else if (node->nodeType == HLSLNodeType_BlockStatement) {
      VisitBlockStatement((HLSLBlockStatement*)node);
    }
    else {
      ASSERT(0);
    }
  }

  void HLSLTreeVisitor::VisitDeclaration(HLSLDeclaration* node)
  {
    VisitType(node->type);
    /*do {
        VisitExpression(node->assignment);
        node = node->nextDeclaration;
    } while (node);*/
    if (node->assignment != nullptr) {
      VisitExpression(node->assignment);
    }
    if (node->nextDeclaration != nullptr) {
      VisitDeclaration(node->nextDeclaration);
    }
  }

  void HLSLTreeVisitor::VisitStruct(HLSLStruct* node)
  {
    HLSLStructField* field = node->field;
    while (field != nullptr) {
      VisitStructField(field);
      field = field->nextField;
    }
  }

  void HLSLTreeVisitor::VisitStructField(HLSLStructField* node)
  {
    VisitType(node->type);
  }

  void HLSLTreeVisitor::VisitBuffer(HLSLBuffer* node)
  {
    HLSLDeclaration* field = node->field;
    while (field != nullptr) {
      ASSERT(field->nodeType == HLSLNodeType_Declaration);
      VisitDeclaration(field);
      ASSERT(field->nextDeclaration == nullptr);
      field = (HLSLDeclaration*)field->nextStatement;
    }
  }

  /*void HLSLTreeVisitor::VisitBufferField(HLSLBufferField * node)
  {
      VisitType(node->type);
  }*/

  void HLSLTreeVisitor::VisitFunction(HLSLFunction* node)
  {
    VisitType(node->returnType);

    HLSLArgument* argument = node->argument;
    while (argument != nullptr) {
      VisitArgument(argument);
      argument = argument->nextArgument;
    }

    VisitStatements(node->statement);
  }

  void HLSLTreeVisitor::VisitArgument(HLSLArgument* node)
  {
    VisitType(node->type);
    if (node->defaultValue != nullptr) {
      VisitExpression(node->defaultValue);
    }
  }

  void HLSLTreeVisitor::VisitExpressionStatement(HLSLExpressionStatement* node)
  {
    VisitExpression(node->expression);
  }

  void HLSLTreeVisitor::VisitExpression(HLSLExpression* node)
  {
    VisitType(node->expressionType);

    if (node->nodeType == HLSLNodeType_UnaryExpression) {
      VisitUnaryExpression((HLSLUnaryExpression*)node);
    }
    else if (node->nodeType == HLSLNodeType_BinaryExpression) {
      VisitBinaryExpression((HLSLBinaryExpression*)node);
    }
    else if (node->nodeType == HLSLNodeType_ConditionalExpression) {
      VisitConditionalExpression((HLSLConditionalExpression*)node);
    }
    else if (node->nodeType == HLSLNodeType_CastingExpression) {
      VisitCastingExpression((HLSLCastingExpression*)node);
    }
    else if (node->nodeType == HLSLNodeType_LiteralExpression) {
      VisitLiteralExpression((HLSLLiteralExpression*)node);
    }
    else if (node->nodeType == HLSLNodeType_IdentifierExpression) {
      VisitIdentifierExpression((HLSLIdentifierExpression*)node);
    }
    else if (node->nodeType == HLSLNodeType_ConstructorExpression) {
      VisitConstructorExpression((HLSLConstructorExpression*)node);
    }
    else if (node->nodeType == HLSLNodeType_MemberAccess) {
      VisitMemberAccess((HLSLMemberAccess*)node);
    }
    else if (node->nodeType == HLSLNodeType_ArrayAccess) {
      VisitArrayAccess((HLSLArrayAccess*)node);
    }
    else if (node->nodeType == HLSLNodeType_FunctionCall) {
      VisitFunctionCall((HLSLFunctionCall*)node);
    }
    // Acoget-TODO: This was missing. Did adding it break anything?
    else if (node->nodeType == HLSLNodeType_SamplerState) {
      VisitSamplerState((HLSLSamplerState*)node);
    }
    else {
      ASSERT(0);
    }
  }

  void HLSLTreeVisitor::VisitReturnStatement(HLSLReturnStatement* node)
  {
    VisitExpression(node->expression);
  }

  void HLSLTreeVisitor::VisitDiscardStatement(HLSLDiscardStatement* node) {}
  void HLSLTreeVisitor::VisitBreakStatement(HLSLBreakStatement* node) {}
  void HLSLTreeVisitor::VisitContinueStatement(HLSLContinueStatement* node) {}

  void HLSLTreeVisitor::VisitIfStatement(HLSLIfStatement* node)
  {
    VisitExpression(node->condition);
    VisitStatements(node->statement);
    if (node->elseStatement) {
      VisitStatements(node->elseStatement);
    }
  }

  void HLSLTreeVisitor::VisitForStatement(HLSLForStatement* node)
  {
    if (node->initialization) {
      VisitDeclaration(node->initialization);
    }
    if (node->condition) {
      VisitExpression(node->condition);
    }
    if (node->increment) {
      VisitExpression(node->increment);
    }
    VisitStatements(node->statement);
  }

  void HLSLTreeVisitor::VisitWhileStatement(HLSLWhileStatement* node)
  {
    if (node->condition) {
      VisitExpression(node->condition);
    }
    VisitStatements(node->statement);
  }

  void HLSLTreeVisitor::VisitBlockStatement(HLSLBlockStatement* node)
  {
    VisitStatements(node->statement);
  }

  void HLSLTreeVisitor::VisitUnaryExpression(HLSLUnaryExpression* node)
  {
    VisitExpression(node->expression);
  }

  void HLSLTreeVisitor::VisitBinaryExpression(HLSLBinaryExpression* node)
  {
    VisitExpression(node->expression1);
    VisitExpression(node->expression2);
  }

  void HLSLTreeVisitor::VisitConditionalExpression(HLSLConditionalExpression* node)
  {
    VisitExpression(node->condition);
    VisitExpression(node->falseExpression);
    VisitExpression(node->trueExpression);
  }

  void HLSLTreeVisitor::VisitCastingExpression(HLSLCastingExpression* node)
  {
    VisitType(node->type);
    VisitExpression(node->expression);
  }

  void HLSLTreeVisitor::VisitLiteralExpression(HLSLLiteralExpression* node) {}
  void HLSLTreeVisitor::VisitIdentifierExpression(HLSLIdentifierExpression* node) {}

  void HLSLTreeVisitor::VisitConstructorExpression(HLSLConstructorExpression* node)
  {
    HLSLExpression* argument = node->argument;
    while (argument != nullptr) {
      VisitExpression(argument);
      argument = argument->nextExpression;
    }
  }

  void HLSLTreeVisitor::VisitMemberAccess(HLSLMemberAccess* node)
  {
    VisitExpression(node->object);
  }

  void HLSLTreeVisitor::VisitArrayAccess(HLSLArrayAccess* node)
  {
    VisitExpression(node->array);
    VisitExpression(node->index);
  }

  void HLSLTreeVisitor::VisitFunctionCall(HLSLFunctionCall* node)
  {
    HLSLExpression* argument = node->argument;
    while (argument != nullptr) {
      VisitExpression(argument);
      argument = argument->nextExpression;
    }
  }

  void HLSLTreeVisitor::VisitStateAssignment(HLSLStateAssignment* node) {}

  void HLSLTreeVisitor::VisitSamplerState(HLSLSamplerState* node)
  {
    HLSLStateAssignment* stateAssignment = node->stateAssignments;
    while (stateAssignment != nullptr) {
      VisitStateAssignment(stateAssignment);
      stateAssignment = stateAssignment->nextStateAssignment;
    }
  }

  void HLSLTreeVisitor::VisitPass(HLSLPass* node)
  {
    HLSLStateAssignment* stateAssignment = node->stateAssignments;
    while (stateAssignment != nullptr) {
      VisitStateAssignment(stateAssignment);
      stateAssignment = stateAssignment->nextStateAssignment;
    }
  }

  void HLSLTreeVisitor::VisitTechnique(HLSLTechnique* node)
  {
    HLSLPass* pass = node->passes;
    while (pass != nullptr) {
      VisitPass(pass);
      pass = pass->nextPass;
    }
  }

  void HLSLTreeVisitor::VisitPipeline(HLSLPipeline* node)
  {
    // @@ ?
  }

  void HLSLTreeVisitor::VisitFunctions(HLSLRoot* root)
  {
    HLSLStatement* statement = root->statement;
    while (statement != nullptr) {
      if (statement->nodeType == HLSLNodeType_Function) {
        VisitFunction((HLSLFunction*)statement);
      }

      statement = statement->nextStatement;
    }
  }

  void HLSLTreeVisitor::VisitParameters(HLSLRoot* root)
  {
    HLSLStatement* statement = root->statement;
    while (statement != nullptr) {
      if (statement->nodeType == HLSLNodeType_Declaration) {
        VisitDeclaration((HLSLDeclaration*)statement);
      }

      statement = statement->nextStatement;
    }
  }


  class ResetHiddenFlagVisitor : public HLSLTreeVisitor
  {
  public:
    virtual void VisitTopLevelStatement(HLSLStatement* statement)
    {
      statement->hidden = true;

      if (statement->nodeType == HLSLNodeType_Buffer)
      {
        VisitBuffer((HLSLBuffer*)statement);
      }
    }

    // Hide buffer fields.
    virtual void VisitDeclaration(HLSLDeclaration* node)
    {
      node->hidden = true;
    }

    virtual void VisitArgument(HLSLArgument* node)
    {
      node->hidden = false;   // Arguments are visible by default.
    }
  };

  class MarkVisibleStatementsVisitor : public HLSLTreeVisitor
  {
  public:
    HLSLTree* tree;
    MarkVisibleStatementsVisitor(HLSLTree* _tree) : tree(_tree) {}

    virtual void VisitFunction(HLSLFunction* node)
    {
      node->hidden = false;
      HLSLTreeVisitor::VisitFunction(node);

      if (node->forward)
        VisitFunction(node->forward);
    }

    virtual void VisitFunctionCall(HLSLFunctionCall* node)
    {
      HLSLTreeVisitor::VisitFunctionCall(node);

      if (node->function->hidden)
      {
        VisitFunction(const_cast<HLSLFunction*>(node->function));
      }
    }

    virtual void VisitIdentifierExpression(HLSLIdentifierExpression* node)
    {
      HLSLTreeVisitor::VisitIdentifierExpression(node);

      if (node->global)
      {
        HLSLDeclaration* declaration = tree->FindGlobalDeclaration(node->name);
        if (declaration != nullptr && declaration->hidden)
        {
          declaration->hidden = false;
          VisitDeclaration(declaration);
        }
      }
    }

    virtual void VisitType(HLSLType& type)
    {
      if (type.baseType == HLSLBaseType_UserDefined)
      {
        HLSLStruct* globalStruct = tree->FindGlobalStruct(type.typeName);
        if (globalStruct != nullptr)
        {
          globalStruct->hidden = false;
          VisitStruct(globalStruct);
        }
      }
    }

  };


  void PruneTree(HLSLTree* tree, const char* entryName0, const char* entryName1/*=nullptr*/)
  {
    HLSLRoot* root = tree->GetRoot();

    // Reset all flags.
    ResetHiddenFlagVisitor reset;
    reset.VisitRoot(root);

    // Mark all the statements necessary for these entrypoints.
    HLSLFunction* entry = tree->FindFunction(entryName0);
    if (entry != nullptr)
    {
      MarkVisibleStatementsVisitor mark(tree);
      mark.VisitFunction(entry);
    }

    if (entryName1 != nullptr)
    {
      entry = tree->FindFunction(entryName1);
      if (entry != nullptr)
      {
        MarkVisibleStatementsVisitor mark(tree);
        mark.VisitFunction(entry);
      }
    }

    // Mark buffers visible, if any of their fields is visible.
    HLSLStatement* statement = root->statement;
    while (statement != nullptr)
    {
      if (statement->nodeType == HLSLNodeType_Buffer)
      {
        HLSLBuffer* buffer = (HLSLBuffer*)statement;

        HLSLDeclaration* field = buffer->field;
        while (field != nullptr)
        {
          ASSERT(field->nodeType == HLSLNodeType_Declaration);
          if (!field->hidden)
          {
            buffer->hidden = false;
            break;
          }
          field = (HLSLDeclaration*)field->nextStatement;
        }
      }

      statement = statement->nextStatement;
    }
  }


  void SortTree(HLSLTree* tree)
  {
    // Stable sort so that statements are in this order:
    // structs, declarations, functions, techniques.
  // but their relative order is preserved.

    HLSLRoot* root = tree->GetRoot();

    HLSLStatement* structs = nullptr;
    HLSLStatement* lastStruct = nullptr;
    HLSLStatement* constDeclarations = nullptr;
    HLSLStatement* lastConstDeclaration = nullptr;
    HLSLStatement* declarations = nullptr;
    HLSLStatement* lastDeclaration = nullptr;
    HLSLStatement* functions = nullptr;
    HLSLStatement* lastFunction = nullptr;
    HLSLStatement* other = nullptr;
    HLSLStatement* lastOther = nullptr;

    HLSLStatement* statement = root->statement;
    while (statement != nullptr) {
      HLSLStatement* nextStatement = statement->nextStatement;
      statement->nextStatement = nullptr;

      if (statement->nodeType == HLSLNodeType_Struct) {
        if (structs == nullptr) structs = statement;
        if (lastStruct != nullptr) lastStruct->nextStatement = statement;
        lastStruct = statement;
      }
      else if (statement->nodeType == HLSLNodeType_Declaration || statement->nodeType == HLSLNodeType_Buffer) {
        if (statement->nodeType == HLSLNodeType_Declaration && (((HLSLDeclaration*)statement)->type.flags & HLSLTypeFlag_Const)) {
          if (constDeclarations == nullptr) constDeclarations = statement;
          if (lastConstDeclaration != nullptr) lastConstDeclaration->nextStatement = statement;
          lastConstDeclaration = statement;
        }
        else {
          if (declarations == nullptr) declarations = statement;
          if (lastDeclaration != nullptr) lastDeclaration->nextStatement = statement;
          lastDeclaration = statement;
        }
      }
      else if (statement->nodeType == HLSLNodeType_Function) {
        if (functions == nullptr) functions = statement;
        if (lastFunction != nullptr) lastFunction->nextStatement = statement;
        lastFunction = statement;
      }
      else {
        if (other == nullptr) other = statement;
        if (lastOther != nullptr) lastOther->nextStatement = statement;
        lastOther = statement;
      }

      statement = nextStatement;
    }

    // Chain all the statements in the order that we want.
    HLSLStatement* firstStatement = structs;
    HLSLStatement* lastStatement = lastStruct;

    if (constDeclarations != nullptr) {
      if (firstStatement == nullptr) firstStatement = constDeclarations;
      else lastStatement->nextStatement = constDeclarations;
      lastStatement = lastConstDeclaration;
    }

    if (declarations != nullptr) {
      if (firstStatement == nullptr) firstStatement = declarations;
      else lastStatement->nextStatement = declarations;
      lastStatement = lastDeclaration;
    }

    if (functions != nullptr) {
      if (firstStatement == nullptr) firstStatement = functions;
      else lastStatement->nextStatement = functions;
      lastStatement = lastFunction;
    }

    if (other != nullptr) {
      if (firstStatement == nullptr) firstStatement = other;
      else lastStatement->nextStatement = other;
      lastStatement = lastOther;
    }

    root->statement = firstStatement;
  }





  // First and last can be the same.
  void AddStatements(HLSLRoot* root, HLSLStatement* before, HLSLStatement* first, HLSLStatement* last)
  {
    if (before == nullptr) {
      last->nextStatement = root->statement;
      root->statement = first;
    }
    else {
      last->nextStatement = before->nextStatement;
      before->nextStatement = first;
    }
  }

  void AddSingleStatement(HLSLRoot* root, HLSLStatement* before, HLSLStatement* statement)
  {
    AddStatements(root, before, statement, statement);
  }



  // @@ This is very game-specific. Should be moved to pipeline_parser or somewhere else.
  void GroupParameters(HLSLTree* tree)
  {
    // Sort parameters based on semantic and group them in cbuffers.

    HLSLRoot* root = tree->GetRoot();

    HLSLDeclaration* firstPerItemDeclaration = nullptr;
    HLSLDeclaration* lastPerItemDeclaration = nullptr;

    HLSLDeclaration* instanceDataDeclaration = nullptr;

    HLSLDeclaration* firstPerPassDeclaration = nullptr;
    HLSLDeclaration* lastPerPassDeclaration = nullptr;

    HLSLDeclaration* firstPerItemSampler = nullptr;
    HLSLDeclaration* lastPerItemSampler = nullptr;

    HLSLDeclaration* firstPerPassSampler = nullptr;
    HLSLDeclaration* lastPerPassSampler = nullptr;

    HLSLStatement* statementBeforeBuffers = nullptr;

    HLSLStatement* previousStatement = nullptr;
    HLSLStatement* statement = root->statement;
    while (statement != nullptr)
    {
      HLSLStatement* nextStatement = statement->nextStatement;

      if (statement->nodeType == HLSLNodeType_Struct) // Do not remove this, or it will mess the else clause below.
      {
        statementBeforeBuffers = statement;
      }
      else if (statement->nodeType == HLSLNodeType_Declaration)
      {
        HLSLDeclaration* declaration = (HLSLDeclaration*)statement;

        // We insert buffers after the last const declaration.
        if ((declaration->type.flags & HLSLTypeFlag_Const) != 0)
        {
          statementBeforeBuffers = statement;
        }

        // Do not move samplers or static/const parameters.
        if ((declaration->type.flags & (HLSLTypeFlag_Static | HLSLTypeFlag_Const)) == 0)
        {
          // Unlink statement.
          statement->nextStatement = nullptr;
          if (previousStatement != nullptr) previousStatement->nextStatement = nextStatement;
          else root->statement = nextStatement;

          while (declaration != nullptr)
          {
            HLSLDeclaration* nextDeclaration = declaration->nextDeclaration;

            if (declaration->semantic != nullptr && String_EqualNoCase(declaration->semantic, "PER_INSTANCED_ITEM"))
            {
              ASSERT(instanceDataDeclaration == nullptr);
              instanceDataDeclaration = declaration;
            }
            else
            {
              // Select group based on type and semantic.
              HLSLDeclaration** first, ** last;
              if (declaration->semantic == nullptr || String_EqualNoCase(declaration->semantic, "PER_ITEM") || String_EqualNoCase(declaration->semantic, "PER_MATERIAL"))
              {
                if (IsSamplerType(declaration->type))
                {
                  first = &firstPerItemSampler;
                  last = &lastPerItemSampler;
                }
                else
                {
                  first = &firstPerItemDeclaration;
                  last = &lastPerItemDeclaration;
                }
              }
              else
              {
                if (IsSamplerType(declaration->type))
                {
                  first = &firstPerPassSampler;
                  last = &lastPerPassSampler;
                }
                else
                {
                  first = &firstPerPassDeclaration;
                  last = &lastPerPassDeclaration;
                }
              }

              // Add declaration to new list.
              if (*first == nullptr) *first = declaration;
              else (*last)->nextStatement = declaration;
              *last = declaration;
            }

            // Unlink from declaration list.
            declaration->nextDeclaration = nullptr;

            // Reset attributes.
            declaration->registerName = nullptr;
            //declaration->semantic = nullptr;         // @@ Don't do this!

            declaration = nextDeclaration;
          }
        }
      }
      /*else
      {
          if (statementBeforeBuffers == nullptr) {
              // This is the location where we will insert our buffers.
              statementBeforeBuffers = previousStatement;
          }
      }*/

      if (statement->nextStatement == nextStatement) {
        previousStatement = statement;
      }
      statement = nextStatement;
    }


    // Add instance data declaration at the end of the per_item buffer.
    if (instanceDataDeclaration != nullptr)
    {
      if (firstPerItemDeclaration == nullptr) firstPerItemDeclaration = instanceDataDeclaration;
      else lastPerItemDeclaration->nextStatement = instanceDataDeclaration;
    }


    // Add samplers.
    if (firstPerItemSampler != nullptr) {
      AddStatements(root, statementBeforeBuffers, firstPerItemSampler, lastPerItemSampler);
      statementBeforeBuffers = lastPerItemSampler;
    }
    if (firstPerPassSampler != nullptr) {
      AddStatements(root, statementBeforeBuffers, firstPerPassSampler, lastPerPassSampler);
      statementBeforeBuffers = lastPerPassSampler;
    }


    // @@ We are assuming per_item and per_pass buffers don't already exist. @@ We should assert on that.

    if (firstPerItemDeclaration != nullptr)
    {
      // Create buffer statement.
      HLSLBuffer* perItemBuffer = tree->AddNode<HLSLBuffer>(firstPerItemDeclaration->fileName, firstPerItemDeclaration->line - 1);
      perItemBuffer->name = tree->AddString("per_item");
      perItemBuffer->registerName = tree->AddString("b0");
      perItemBuffer->field = firstPerItemDeclaration;

      // Set declaration buffer pointers.
      HLSLDeclaration* field = perItemBuffer->field;
      while (field != nullptr)
      {
        field->buffer = perItemBuffer;
        field = (HLSLDeclaration*)field->nextStatement;
      }

      // Add buffer to statements.
      AddSingleStatement(root, statementBeforeBuffers, perItemBuffer);
      statementBeforeBuffers = perItemBuffer;
    }

    if (firstPerPassDeclaration != nullptr)
    {
      // Create buffer statement.
      HLSLBuffer* perPassBuffer = tree->AddNode<HLSLBuffer>(firstPerPassDeclaration->fileName, firstPerPassDeclaration->line - 1);
      perPassBuffer->name = tree->AddString("per_pass");
      perPassBuffer->registerName = tree->AddString("b1");
      perPassBuffer->field = firstPerPassDeclaration;

      // Set declaration buffer pointers.
      HLSLDeclaration* field = perPassBuffer->field;
      while (field != nullptr)
      {
        field->buffer = perPassBuffer;
        field = (HLSLDeclaration*)field->nextStatement;
      }

      // Add buffer to statements.
      AddSingleStatement(root, statementBeforeBuffers, perPassBuffer);
    }
  }


  class FindArgumentVisitor : public HLSLTreeVisitor
  {
  public:
    bool found;
    const char* name;

    FindArgumentVisitor()
    {
      found = false;
      name = nullptr;
    }

    bool FindArgument(const char* _name, HLSLFunction* function)
    {
      this->found = false;
      this->name = _name;
      VisitStatements(function->statement);
      return found;
    }

    virtual void VisitStatements(HLSLStatement* statement) override
    {
      while (statement != nullptr && !found)
      {
        VisitStatement(statement);
        statement = statement->nextStatement;
      }
    }

    virtual void VisitIdentifierExpression(HLSLIdentifierExpression* node) override
    {
      if (node->name == name)
      {
        found = true;
      }
    }
  };


  void HideUnusedArguments(HLSLFunction* function)
  {
    FindArgumentVisitor visitor;

    // For each argument.
    HLSLArgument* arg = function->argument;
    while (arg != nullptr)
    {
      if (!visitor.FindArgument(arg->name, function))
      {
        arg->hidden = true;
      }

      arg = arg->nextArgument;
    }
  }

  bool EmulateAlphaTest(HLSLTree* tree, const char* entryName, float alphaRef/*=0.5*/)
  {
    // Find all return statements of this entry point.
    HLSLFunction* entry = tree->FindFunction(entryName);
    if (entry != nullptr)
    {
      HLSLStatement** ptr = &entry->statement;
      HLSLStatement* statement = entry->statement;
      while (statement != nullptr)
      {
        if (statement->nodeType == HLSLNodeType_ReturnStatement)
        {
          HLSLReturnStatement* returnStatement = (HLSLReturnStatement*)statement;
          HLSLBaseType returnType = returnStatement->expression->expressionType.baseType;

          // Build statement: "if (%s.a < 0.5) discard;"

          HLSLDiscardStatement* discard = tree->AddNode<HLSLDiscardStatement>(statement->fileName, statement->line);

          HLSLExpression* alpha = nullptr;
          if (returnType == HLSLBaseType_Float4)
          {
            // @@ If return expression is a constructor, grab 4th argument.
            // That's not as easy, since we support 'float4(float3, float)' or 'float4(float, float3)', extracting
            // the latter is not that easy.
            /*if (returnStatement->expression->nodeType == HLSLNodeType_ConstructorExpression) {
                HLSLConstructorExpression * constructor = (HLSLConstructorExpression *)returnStatement->expression;
                //constructor->
            }
            */

            if (alpha == nullptr) {
              HLSLMemberAccess* access = tree->AddNode<HLSLMemberAccess>(statement->fileName, statement->line);
              access->expressionType = HLSLType(HLSLBaseType_Float);
              access->object = returnStatement->expression;     // @@ Is reference OK? Or should we clone expression?
              access->field = tree->AddString("a");
              access->swizzle = true;

              alpha = access;
            }
          }
          else if (returnType == HLSLBaseType_Float)
          {
            alpha = returnStatement->expression;     // @@ Is reference OK? Or should we clone expression?
          }
          else
          {
            return false;
          }

          HLSLLiteralExpression* threshold = tree->AddNode<HLSLLiteralExpression>(statement->fileName, statement->line);
          threshold->expressionType = HLSLType(HLSLBaseType_Float);
          threshold->fValue = alphaRef;
          threshold->type = HLSLBaseType_Float;

          HLSLBinaryExpression* condition = tree->AddNode<HLSLBinaryExpression>(statement->fileName, statement->line);
          condition->expressionType = HLSLType(HLSLBaseType_Bool);
          condition->binaryOp = HLSLBinaryOp_Less;
          condition->expression1 = alpha;
          condition->expression2 = threshold;

          // Insert statement.
          HLSLIfStatement* st = tree->AddNode<HLSLIfStatement>(statement->fileName, statement->line);
          st->condition = condition;
          st->statement = discard;
          st->nextStatement = statement;
          *ptr = st;
        }

        ptr = &statement->nextStatement;
        statement = statement->nextStatement;
      }
    }

    return true;
  }

  bool NeedsFlattening(HLSLExpression* expr, int level = 0) {
    if (expr == nullptr) {
      return false;
    }
    if (expr->nodeType == HLSLNodeType_UnaryExpression) {
      HLSLUnaryExpression* unaryExpr = (HLSLUnaryExpression*)expr;
      return NeedsFlattening(unaryExpr->expression, level + 1) || NeedsFlattening(expr->nextExpression, level);
    }
    else if (expr->nodeType == HLSLNodeType_BinaryExpression) {
      HLSLBinaryExpression* binaryExpr = (HLSLBinaryExpression*)expr;
      if (IsAssignOp(binaryExpr->binaryOp)) {
        return NeedsFlattening(binaryExpr->expression2, level + 1) || NeedsFlattening(expr->nextExpression, level);
      }
      else {
        return NeedsFlattening(binaryExpr->expression1, level + 1) || NeedsFlattening(binaryExpr->expression2, level + 1) || NeedsFlattening(expr->nextExpression, level);
      }
    }
    else if (expr->nodeType == HLSLNodeType_ConditionalExpression) {
      HLSLConditionalExpression* conditionalExpr = (HLSLConditionalExpression*)expr;
      return NeedsFlattening(conditionalExpr->condition, level + 1) || NeedsFlattening(conditionalExpr->trueExpression, level + 1) || NeedsFlattening(conditionalExpr->falseExpression, level + 1) || NeedsFlattening(expr->nextExpression, level);
    }
    else if (expr->nodeType == HLSLNodeType_CastingExpression) {
      HLSLCastingExpression* castingExpr = (HLSLCastingExpression*)expr;
      return NeedsFlattening(castingExpr->expression, level + 1) || NeedsFlattening(expr->nextExpression, level);
    }
    else if (expr->nodeType == HLSLNodeType_LiteralExpression) {
      return NeedsFlattening(expr->nextExpression, level);
    }
    else if (expr->nodeType == HLSLNodeType_IdentifierExpression) {
      return NeedsFlattening(expr->nextExpression, level);
    }
    else if (expr->nodeType == HLSLNodeType_ConstructorExpression) {
      HLSLConstructorExpression* constructorExpr = (HLSLConstructorExpression*)expr;
      return NeedsFlattening(constructorExpr->argument, level + 1) || NeedsFlattening(expr->nextExpression, level);
    }
    else if (expr->nodeType == HLSLNodeType_MemberAccess) {
      return NeedsFlattening(expr->nextExpression, level + 1);
    }
    else if (expr->nodeType == HLSLNodeType_ArrayAccess) {
      HLSLArrayAccess* arrayAccess = (HLSLArrayAccess*)expr;
      return NeedsFlattening(arrayAccess->array, level + 1) || NeedsFlattening(arrayAccess->index, level + 1) || NeedsFlattening(expr->nextExpression, level);
    }
    else if (expr->nodeType == HLSLNodeType_FunctionCall) {
      HLSLFunctionCall* functionCall = (HLSLFunctionCall*)expr;
      if (functionCall->function->numOutputArguments > 0) {
        if (level > 0) {
          return true;
        }
      }
      return NeedsFlattening(functionCall->argument, level + 1) || NeedsFlattening(expr->nextExpression, level);
    }
    else {
      //assert(false);
      return false;
    }
  }


  struct StatementList {
    HLSLStatement* head = nullptr;
    HLSLStatement* tail = nullptr;
    void append(HLSLStatement* st) {
      if (head == nullptr) {
        tail = head = st;
      }
      tail->nextStatement = st;
      tail = st;
    }
  };


  class ExpressionFlattener : public HLSLTreeVisitor
  {
  public:
    HLSLTree* m_tree;
    int tmp_index;
    HLSLStatement** statement_pointer;
    HLSLFunction* current_function;

    ExpressionFlattener()
    {
      m_tree = nullptr;
      tmp_index = 0;
      statement_pointer = nullptr;
      current_function = nullptr;
    }

    void FlattenExpressions(HLSLTree* tree)
    {
      m_tree = tree;
      VisitRoot(tree->GetRoot());
    }

    // Visit all statements updating the statement_pointer so that we can insert and replace statements. @@ Add this to the default visitor?
    virtual void VisitFunction(HLSLFunction* node) override
    {
      current_function = node;
      statement_pointer = &node->statement;
      VisitStatements(node->statement);
      statement_pointer = nullptr;
      current_function = nullptr;
    }

    virtual void VisitIfStatement(HLSLIfStatement* node) override
    {
      if (NeedsFlattening(node->condition, 1)) {
        assert(false);  // @@ Add statements before if statement.
      }

      statement_pointer = &node->statement;
      VisitStatements(node->statement);
      if (node->elseStatement) {
        statement_pointer = &node->elseStatement;
        VisitStatements(node->elseStatement);
      }
    }

    virtual void VisitForStatement(HLSLForStatement* node) override
    {
      if (NeedsFlattening(node->initialization->assignment, 1)) {
        assert(false);  // @@ Add statements before for statement.
      }
      if (NeedsFlattening(node->condition, 1) || NeedsFlattening(node->increment, 1)) {
        assert(false);  // @@ These are tricky to implement. Need to handle all loop exits.
      }

      statement_pointer = &node->statement;
      VisitStatements(node->statement);
    }

    virtual void VisitBlockStatement(HLSLBlockStatement* node) override
    {
      statement_pointer = &node->statement;
      VisitStatements(node->statement);
    }

    virtual void VisitStatements(HLSLStatement* statement) override
    {
      while (statement != nullptr) {
        VisitStatement(statement);
        statement_pointer = &statement->nextStatement;
        statement = statement->nextStatement;
      }
    }

    // This is usually a function call or assignment.
    virtual void VisitExpressionStatement(HLSLExpressionStatement* node) override
    {
      if (NeedsFlattening(node->expression, 0))
      {
        StatementList statements;
        Flatten(node->expression, statements, false);

        // Link beginning of statement list.
        *statement_pointer = statements.head;

        // Link end of statement list.
        HLSLStatement* tail = statements.tail;
        tail->nextStatement = node->nextStatement;

        // Update statement pointer.
        statement_pointer = &tail->nextStatement;

        // @@ Delete node?
      }
    }

    virtual void VisitDeclaration(HLSLDeclaration* node) override
    {
      // Skip global declarations.
      if (statement_pointer == nullptr) return;

      if (NeedsFlattening(node->assignment, 1))
      {
        StatementList statements;
        HLSLIdentifierExpression* ident = Flatten(node->assignment, statements, true);

        // @@ Delete node->assignment?

        node->assignment = ident;
        statements.append(node);

        // Link beginning of statement list.
        *statement_pointer = statements.head;

        // Link end of statement list.
        HLSLStatement* tail = statements.tail;
        tail->nextStatement = node->nextStatement;

        // Update statement pointer.
        statement_pointer = &tail->nextStatement;
      }
    }

    virtual void VisitReturnStatement(HLSLReturnStatement* node) override
    {
      if (NeedsFlattening(node->expression, 1))
      {
        StatementList statements;
        HLSLIdentifierExpression* ident = Flatten(node->expression, statements, true);

        // @@ Delete node->expression?

        node->expression = ident;
        statements.append(node);

        // Link beginning of statement list.
        *statement_pointer = statements.head;

        // Link end of statement list.
        HLSLStatement* tail = statements.tail;
        tail->nextStatement = node->nextStatement;

        // Update statement pointer.
        statement_pointer = &tail->nextStatement;
      }
    }


    HLSLDeclaration* BuildTemporaryDeclaration(HLSLExpression* expr)
    {
      assert(expr->expressionType.baseType != HLSLBaseType_Void);

      HLSLDeclaration* declaration = m_tree->AddNode<HLSLDeclaration>(expr->fileName, expr->line);
      declaration->name = m_tree->AddStringFormat("tmp%d", tmp_index++);
      declaration->type = expr->expressionType;
      declaration->assignment = expr;

      return declaration;
    }

    HLSLExpressionStatement* BuildExpressionStatement(HLSLExpression* expr)
    {
      HLSLExpressionStatement* statement = m_tree->AddNode<HLSLExpressionStatement>(expr->fileName, expr->line);
      statement->expression = expr;
      return statement;
    }

    HLSLIdentifierExpression* AddExpressionStatement(HLSLExpression* expr, StatementList& statements, bool wantIdent)
    {
      if (wantIdent) {
        HLSLDeclaration* declaration = BuildTemporaryDeclaration(expr);
        statements.append(declaration);

        HLSLIdentifierExpression* ident = m_tree->AddNode<HLSLIdentifierExpression>(expr->fileName, expr->line);
        ident->name = declaration->name;
        ident->expressionType = declaration->type;
        return ident;
      }
      else {
        HLSLExpressionStatement* statement = BuildExpressionStatement(expr);
        statements.append(statement);
        return nullptr;
      }
    }

    HLSLIdentifierExpression* Flatten(HLSLExpression* expr, StatementList& statements, bool wantIdent = true)
    {
      if (!NeedsFlattening(expr, wantIdent)) {
        return AddExpressionStatement(expr, statements, wantIdent);
      }

      if (expr->nodeType == HLSLNodeType_UnaryExpression) {
        assert(expr->nextExpression == nullptr);

        HLSLUnaryExpression* unaryExpr = (HLSLUnaryExpression*)expr;

        HLSLIdentifierExpression* tmp = Flatten(unaryExpr->expression, statements, true);

        HLSLUnaryExpression* newUnaryExpr = m_tree->AddNode<HLSLUnaryExpression>(unaryExpr->fileName, unaryExpr->line);
        newUnaryExpr->unaryOp = unaryExpr->unaryOp;
        newUnaryExpr->expression = tmp;
        newUnaryExpr->expressionType = unaryExpr->expressionType;

        return AddExpressionStatement(newUnaryExpr, statements, wantIdent);
      }
      else if (expr->nodeType == HLSLNodeType_BinaryExpression) {
        assert(expr->nextExpression == nullptr);

        HLSLBinaryExpression* binaryExpr = (HLSLBinaryExpression*)expr;

        if (IsAssignOp(binaryExpr->binaryOp)) {
          // Flatten right hand side only.
          HLSLIdentifierExpression* tmp2 = Flatten(binaryExpr->expression2, statements, true);

          HLSLBinaryExpression* newBinaryExpr = m_tree->AddNode<HLSLBinaryExpression>(binaryExpr->fileName, binaryExpr->line);
          newBinaryExpr->binaryOp = binaryExpr->binaryOp;
          newBinaryExpr->expression1 = binaryExpr->expression1;
          newBinaryExpr->expression2 = tmp2;
          newBinaryExpr->expressionType = binaryExpr->expressionType;

          return AddExpressionStatement(newBinaryExpr, statements, wantIdent);
        }
        else {
          HLSLIdentifierExpression* tmp1 = Flatten(binaryExpr->expression1, statements, true);
          HLSLIdentifierExpression* tmp2 = Flatten(binaryExpr->expression2, statements, true);

          HLSLBinaryExpression* newBinaryExpr = m_tree->AddNode<HLSLBinaryExpression>(binaryExpr->fileName, binaryExpr->line);
          newBinaryExpr->binaryOp = binaryExpr->binaryOp;
          newBinaryExpr->expression1 = tmp1;
          newBinaryExpr->expression2 = tmp2;
          newBinaryExpr->expressionType = binaryExpr->expressionType;

          return AddExpressionStatement(newBinaryExpr, statements, wantIdent);
        }
      }
      else if (expr->nodeType == HLSLNodeType_ConditionalExpression) {
        assert(false);
      }
      else if (expr->nodeType == HLSLNodeType_CastingExpression) {
        assert(false);
      }
      else if (expr->nodeType == HLSLNodeType_LiteralExpression) {
        assert(false);
      }
      else if (expr->nodeType == HLSLNodeType_IdentifierExpression) {
        assert(false);
      }
      else if (expr->nodeType == HLSLNodeType_ConstructorExpression) {
        assert(false);
      }
      else if (expr->nodeType == HLSLNodeType_MemberAccess) {
        assert(false);
      }
      else if (expr->nodeType == HLSLNodeType_ArrayAccess) {
        assert(false);
      }
      else if (expr->nodeType == HLSLNodeType_FunctionCall) {
        HLSLFunctionCall* functionCall = (HLSLFunctionCall*)expr;

        // @@ Output function as is?
        // @@ We have to flatten function arguments! This is tricky, need to handle input/output arguments.
        assert(!NeedsFlattening(functionCall->argument));

        return AddExpressionStatement(expr, statements, wantIdent);
      }
      else {
        assert(false);
      }
      return nullptr;
    }
  };


  void FlattenExpressions(HLSLTree* tree) {
    ExpressionFlattener flattener;
    flattener.FlattenExpressions(tree);
  }

} // M4

namespace M4
{

  /** In addition to the values in this enum, all of the ASCII characters are
  valid tokens. */
  enum HLSLToken
  {
    // Built-in types.
    HLSLToken_Float = 256,
    HLSLToken_Float1,
    HLSLToken_Float1x1,
    HLSLToken_Float2,
    HLSLToken_Float2x1,
    HLSLToken_Float3,
    HLSLToken_Float3x1,
    HLSLToken_Float4,
    HLSLToken_Float4x1,
    HLSLToken_Float2x4,
    HLSLToken_Float2x3,
    HLSLToken_Float2x2,
    HLSLToken_Float3x4,
    HLSLToken_Float3x3,
    HLSLToken_Float3x2,
    HLSLToken_Float4x4,
    HLSLToken_Float4x3,
    HLSLToken_Float4x2,

    HLSLToken_Half,
    HLSLToken_Half1,
    HLSLToken_Half1x1,
    HLSLToken_Half2,
    HLSLToken_Half2x1,
    HLSLToken_Half3,
    HLSLToken_Half3x1,
    HLSLToken_Half4,
    HLSLToken_Half4x1,
    HLSLToken_Half2x4,
    HLSLToken_Half2x3,
    HLSLToken_Half2x2,
    HLSLToken_Half3x4,
    HLSLToken_Half3x3,
    HLSLToken_Half3x2,
    HLSLToken_Half4x4,
    HLSLToken_Half4x3,
    HLSLToken_Half4x2,

    HLSLToken_Double,
    HLSLToken_Double1,
    HLSLToken_Double1x1,
    HLSLToken_Double2,
    HLSLToken_Double2x1,
    HLSLToken_Double3,
    HLSLToken_Double3x1,
    HLSLToken_Double4,
    HLSLToken_Double4x1,
    HLSLToken_Double2x4,
    HLSLToken_Double2x3,
    HLSLToken_Double2x2,
    HLSLToken_Double3x4,
    HLSLToken_Double3x3,
    HLSLToken_Double3x2,
    HLSLToken_Double4x4,
    HLSLToken_Double4x3,
    HLSLToken_Double4x2,

    HLSLToken_Bool,
    HLSLToken_Bool2,
    HLSLToken_Bool3,
    HLSLToken_Bool4,
    HLSLToken_Int,
    HLSLToken_Int2,
    HLSLToken_Int3,
    HLSLToken_Int4,
    HLSLToken_Uint,
    HLSLToken_Uint2,
    HLSLToken_Uint3,
    HLSLToken_Uint4,
    HLSLToken_Texture,
    HLSLToken_Sampler,
    HLSLToken_Sampler2D,
    HLSLToken_Sampler3D,
    HLSLToken_SamplerCube,
    HLSLToken_Sampler2DShadow,
    HLSLToken_Sampler2DMS,
    HLSLToken_Sampler2DArray,

    // Reserved words.
    HLSLToken_If,
    HLSLToken_Else,
    HLSLToken_For,
    HLSLToken_While,
    HLSLToken_Break,
    HLSLToken_True,
    HLSLToken_False,
    HLSLToken_Void,
    HLSLToken_Struct,
    HLSLToken_CBuffer,
    HLSLToken_TBuffer,
    HLSLToken_Register,
    HLSLToken_Return,
    HLSLToken_Continue,
    HLSLToken_Discard,
    HLSLToken_Const,
    HLSLToken_Static,
    HLSLToken_Inline,
    HLSLToken_PreprocessorDefine,
    HLSLToken_PreprocessorIf,
    HLSLToken_PreprocessorElse,
    HLSLToken_PreprocessorEndif,

    // Input modifiers.
    HLSLToken_Uniform,
    HLSLToken_In,
    HLSLToken_Out,
    HLSLToken_InOut,

    // Effect keywords.
    HLSLToken_SamplerState,
    HLSLToken_Technique,
    HLSLToken_Pass,

    // Multi-character symbols.
    HLSLToken_LessEqual,
    HLSLToken_GreaterEqual,
    HLSLToken_EqualEqual,
    HLSLToken_NotEqual,
    HLSLToken_PlusPlus,
    HLSLToken_MinusMinus,
    HLSLToken_PlusEqual,
    HLSLToken_MinusEqual,
    HLSLToken_TimesEqual,
    HLSLToken_DivideEqual,
    HLSLToken_AndAnd,       // &&
    HLSLToken_BarBar,       // ||

    // Other token types.
    HLSLToken_FloatLiteral,
    HLSLToken_IntLiteral,
    HLSLToken_Identifier,

    HLSLToken_EndOfLine,

    HLSLToken_EndOfStream,
  };


  /// Maximum string length of an identifier.
#define s_maxIdentifier 256

  class HLSLTokenizer
  {

  public:
    HLSLTokenizer() { }

    /** The file name is only used for error reporting. */
    HLSLTokenizer(const char* fileName, const char* buffer, size_t length);

    /** Advances to the next token in the stream. */
    void Next(const bool EOLSkipping = true);

    /** Returns the current token in the stream. */
    int GetToken() const;

    /** Returns the number of the current token. */
    float GetFloat() const;
    int   GetInt() const;

    /** Returns the identifier for the current token. */
    const char* GetIdentifier() const;

    /** Returns the line number where the current token began. */
    int GetLineNumber() const;

    /** Returns the file name where the current token began. */
    const char* GetFileName() const;

    /** Gets a human readable text description of the current token. */
    void GetTokenName(char buffer[s_maxIdentifier]) const;

    /** Reports an error using printf style formatting. The current line number
    is included. Only the first error reported will be output. */
    void Error(const char* format, ...);

    /** Gets a human readable text description of the specified token. */
    static void GetTokenName(int token, char buffer[s_maxIdentifier]);

    /** Returns true if the next caracterer is a whitespace. */
    bool NextIsWhitespace();

    const char* getLastPos(const bool trimmed);
    const char* getCurrentPos() { return m_buffer; }

    void ReturnToPos(const char* pos);

  private:

    bool SkipWhitespace(const bool EOLSkipping);
    bool SkipComment(const char** buffer, const bool EOLSkipping);
    bool SkipPragmaDirective();
    bool ScanNumber();
    bool ScanLineDirective();

  private:

    const char* m_fileName;
    const char* m_buffer;
    const char* m_bufferPrevious;
    const char* m_bufferEnd;
    int                 m_lineNumber;
    bool                m_error;

    int                 m_token;
    float               m_fValue;
    int                 m_iValue;
    char                m_identifier[s_maxIdentifier];
    char                m_lineDirectiveFileName[s_maxIdentifier];
    int                 m_tokenLineNumber;

  };

}


namespace M4
{
  // The order here must match the order in the Token enum.
  static const char* _reservedWords[] =
  {
      "float",
      "float1",
      "float1x1",
      "float2",
      "float2x1",
      "float3",
      "float3x1",
      "float4",
      "float4x1",
      "float2x4",
      "float2x3",
  "float2x2",
      "float3x4",
      "float3x3",
      "float3x2",
      "float4x4",
      "float4x3",
      "float4x2",

      "half",
      "half1",
      "half1x1",
      "half2",
      "half2x1",
      "half3",
      "half3x1",
      "half4",
      "half4x1",
      "half2x4",
      "half2x3",
  "half2x2",
      "half3x4",
      "half3x3",
      "half3x2",
      "half4x4",
      "half4x3",
      "half4x2",

      "double",
      "double1",
      "double1x1",
      "double2",
      "double2x1",
      "double3",
      "double3x1",
      "double4",
      "double4x1",
      "double2x4",
      "double2x3",
      "double2x2",
      "double3x4",
      "double3x3",
      "double3x2",
      "double4x4",
      "double4x3",
      "double4x2",

      "bool",
  "bool2",
  "bool3",
  "bool4",
      "int",
      "int2",
      "int3",
      "int4",
      "uint",
      "uint2",
      "uint3",
      "uint4",
      "texture",
      "sampler",
      "sampler2D",
      "sampler3D",
      "samplerCUBE",
      "sampler2DShadow",
      "sampler2DMS",
      "sampler2DArray",
      "if",
      "else",
      "for",
      "while",
      "break",
      "true",
      "false",
      "void",
      "struct",
      "cbuffer",
      "tbuffer",
      "register",
      "return",
      "continue",
      "discard",
      "const",
      "static",
      "inline",
      "#define",
      "#if",
      "#else",
      "#endif",
      "uniform",
      "in",
      "out",
      "inout",
      "sampler_state",
      "technique",
      "pass",
  };

  static bool GetIsSymbol(char c)
  {
    switch (c)
    {
    case ';':
    case ':':
    case '(': case ')':
    case '[': case ']':
    case '{': case '}':
    case '-': case '+':
    case '*': case '/': case '%':
    case '?':
    case '!':
    case ',':
    case '=':
    case '.':
    case '<': case '>':
    case '|': case '&': case '^': case '~':
    case '@':
      return true;
    }
    return false;
  }

  /** Returns true if the character is a valid token separator at the end of a number type token */
  static bool GetIsNumberSeparator(char c)
  {
    return c == 0 || isspace(c) || GetIsSymbol(c);
  }

  HLSLTokenizer::HLSLTokenizer(const char* fileName, const char* buffer, size_t length)
  {
    m_buffer = buffer;
    m_bufferPrevious = m_buffer;
    m_bufferEnd = buffer + length;
    m_fileName = fileName;
    m_lineNumber = 1;
    m_tokenLineNumber = 1;
    m_error = false;
    Next();
  }

  void HLSLTokenizer::Next(const bool EOLSkipping)
  {
    m_bufferPrevious = m_buffer;

    while (SkipWhitespace(EOLSkipping) || SkipComment(&m_buffer, EOLSkipping) || ScanLineDirective() || SkipPragmaDirective())
    {
    }

    if (m_error)
    {
      m_token = HLSLToken_EndOfStream;
      return;
    }

    if (!EOLSkipping && m_buffer[0] == '\n')
    {
      m_token = HLSLToken_EndOfLine;
      return;
    }

    m_tokenLineNumber = m_lineNumber;

    if (m_buffer >= m_bufferEnd || *m_buffer == '\0')
    {
      m_token = HLSLToken_EndOfStream;
      return;
    }

    const char* start = m_buffer;

    // +=, -=, *=, /=, ==, <=, >=
    if (m_buffer[0] == '+' && m_buffer[1] == '=')
    {
      m_token = HLSLToken_PlusEqual;
      m_buffer += 2;
      return;
    }
    else if (m_buffer[0] == '-' && m_buffer[1] == '=')
    {
      m_token = HLSLToken_MinusEqual;
      m_buffer += 2;
      return;
    }
    else if (m_buffer[0] == '*' && m_buffer[1] == '=')
    {
      m_token = HLSLToken_TimesEqual;
      m_buffer += 2;
      return;
    }
    else if (m_buffer[0] == '/' && m_buffer[1] == '=')
    {
      m_token = HLSLToken_DivideEqual;
      m_buffer += 2;
      return;
    }
    else if (m_buffer[0] == '=' && m_buffer[1] == '=')
    {
      m_token = HLSLToken_EqualEqual;
      m_buffer += 2;
      return;
    }
    else if (m_buffer[0] == '!' && m_buffer[1] == '=')
    {
      m_token = HLSLToken_NotEqual;
      m_buffer += 2;
      return;
    }
    else if (m_buffer[0] == '<' && m_buffer[1] == '=')
    {
      m_token = HLSLToken_LessEqual;
      m_buffer += 2;
      return;
    }
    else if (m_buffer[0] == '>' && m_buffer[1] == '=')
    {
      m_token = HLSLToken_GreaterEqual;
      m_buffer += 2;
      return;
    }
    else if (m_buffer[0] == '&' && m_buffer[1] == '&')
    {
      m_token = HLSLToken_AndAnd;
      m_buffer += 2;
      return;
    }
    else if (m_buffer[0] == '|' && m_buffer[1] == '|')
    {
      m_token = HLSLToken_BarBar;
      m_buffer += 2;
      return;
    }

    // ++, --
    if ((m_buffer[0] == '-' || m_buffer[0] == '+') && (m_buffer[1] == m_buffer[0]))
    {
      m_token = (m_buffer[0] == '+') ? HLSLToken_PlusPlus : HLSLToken_MinusMinus;
      m_buffer += 2;
      return;
    }

    // Check for the start of a number.
    if (ScanNumber())
    {
      return;
    }

    if (GetIsSymbol(m_buffer[0]))
    {
      m_token = static_cast<unsigned char>(m_buffer[0]);
      ++m_buffer;
      return;
    }

    // Must be an identifier or a reserved word.
    while (m_buffer < m_bufferEnd && m_buffer[0] != 0 && !GetIsSymbol(m_buffer[0]) && !isspace(m_buffer[0]))
    {
      ++m_buffer;
    }

    size_t length = m_buffer - start;
    memcpy(m_identifier, start, length);
    m_identifier[length] = 0;

    const int numReservedWords = sizeof(_reservedWords) / sizeof(const char*);
    for (int i = 0; i < numReservedWords; ++i)
    {
      if (strcmp(_reservedWords[i], m_identifier) == 0)
      {
        m_token = 256 + i;
        return;
      }
    }

    m_token = HLSLToken_Identifier;

  }

  bool HLSLTokenizer::NextIsWhitespace()
  {
    return isspace(m_buffer[0]);
  }

  const char* HLSLTokenizer::getLastPos(const bool trimmed)
  {
    const char* start = m_bufferPrevious;

    if (trimmed)
    {
      // Skip white space
      while (isspace(start[0])) {
        start++;
      }
    }

    return start;
  }


  bool HLSLTokenizer::SkipWhitespace(const bool EOLSkipping)
  {
    bool result = false;
    while (m_buffer < m_bufferEnd && isspace(m_buffer[0]) &&
      (EOLSkipping || (!EOLSkipping && m_buffer[0] != '\n')))
    {
      result = true;
      if (m_buffer[0] == '\n')
      {
        ++m_lineNumber;
      }
      ++m_buffer;
    }
    return result;
  }

  bool HLSLTokenizer::SkipComment(const char** buffer, const bool EOLSkipping)
  {
    bool result = false;
    if ((*buffer)[0] == '/')
    {
      if ((*buffer)[1] == '/')
      {
        // Single line comment.
        result = true;
        *buffer += 2;
        while (*buffer < m_bufferEnd)
        {
          if ((*buffer)[0] == '\n')
          {
            ++m_lineNumber;
            if (EOLSkipping)
            {
              ++*buffer;
            }
            break;
          }
          ++ * buffer;
        }
      }
      else if ((*buffer)[1] == '*')
      {
        // Multi-line comment.
        result = true;
        *buffer += 2;
        while (*buffer < m_bufferEnd)
        {
          if ((*buffer)[0] == '\n')
          {
            ++m_lineNumber;
          }
          if ((*buffer)[0] == '*' && (*buffer)[1] == '/')
          {
            break;
          }
          ++ * buffer;
        }
        if (*buffer < m_bufferEnd)
        {
          *buffer += 2;
        }
      }
    }
    return result;
  }

  bool HLSLTokenizer::SkipPragmaDirective()
  {
    bool result = false;
    if (m_bufferEnd - m_buffer > 7 && *m_buffer == '#')
    {
      const char* ptr = m_buffer + 1;
      while (isspace(*ptr))
        ptr++;

      if (strncmp(ptr, "pragma", 6) == 0 && isspace(ptr[6]))
      {
        m_buffer = ptr + 6;
        result = true;
        while (m_buffer < m_bufferEnd)
        {
          if (*(m_buffer++) == '\n')
          {
            ++m_lineNumber;
            break;
          }
        }
      }
    }
    return result;
  }

  bool HLSLTokenizer::ScanNumber()
  {

    // Don't treat the + or - as part of the number.
    if (m_buffer[0] == '+' || m_buffer[0] == '-')
    {
      return false;
    }

    // Parse hex literals.
    if (m_bufferEnd - m_buffer > 2 && m_buffer[0] == '0' && m_buffer[1] == 'x')
    {
      char* hEnd = nullptr;
      int     iValue = strtol(m_buffer + 2, &hEnd, 16);
      if (GetIsNumberSeparator(hEnd[0]))
      {
        m_buffer = hEnd;
        m_token = HLSLToken_IntLiteral;
        m_iValue = iValue;
        return true;
      }
    }

    char* fEnd = nullptr;
    double fValue = String_ToDouble(m_buffer, &fEnd);

    if (fEnd == m_buffer)
    {
      return false;
    }

    char* iEnd = nullptr;
    int    iValue = String_ToInteger(m_buffer, &iEnd);

    // If the character after the number is an f then the f is treated as part
    // of the number (to handle 1.0f syntax).
    if ((fEnd[0] == 'f' || fEnd[0] == 'h') && fEnd < m_bufferEnd)
    {
      ++fEnd;
    }

    if (fEnd > iEnd && GetIsNumberSeparator(fEnd[0]))
    {
      m_buffer = fEnd;
      m_token = HLSLToken_FloatLiteral;
      m_fValue = static_cast<float>(fValue);
      return true;
    }
    else if (iEnd > m_buffer && GetIsNumberSeparator(iEnd[0]))
    {
      m_buffer = iEnd;
      m_token = HLSLToken_IntLiteral;
      m_iValue = iValue;
      return true;
    }

    return false;
  }

  bool HLSLTokenizer::ScanLineDirective()
  {

    if (m_bufferEnd - m_buffer > 5 && strncmp(m_buffer, "#line", 5) == 0 && isspace(m_buffer[5]))
    {

      m_buffer += 5;

      while (m_buffer < m_bufferEnd && isspace(m_buffer[0]))
      {
        if (m_buffer[0] == '\n')
        {
          Error("Syntax error: expected line number after #line");
          return false;
        }
        ++m_buffer;
      }

      char* iEnd = nullptr;
      int lineNumber = String_ToInteger(m_buffer, &iEnd);

      if (!isspace(*iEnd))
      {
        Error("Syntax error: expected line number after #line");
        return false;
      }

      m_buffer = iEnd;
      while (m_buffer < m_bufferEnd && isspace(m_buffer[0]))
      {
        char c = m_buffer[0];
        ++m_buffer;
        if (c == '\n')
        {
          m_lineNumber = lineNumber;
          return true;
        }
      }

      if (m_buffer >= m_bufferEnd)
      {
        m_lineNumber = lineNumber;
        return true;
      }

      if (m_buffer[0] != '"')
      {
        Error("Syntax error: expected '\"' after line number near #line");
        return false;
      }

      ++m_buffer;

      int i = 0;
      while (i + 1 < s_maxIdentifier && m_buffer < m_bufferEnd && m_buffer[0] != '"')
      {
        if (m_buffer[0] == '\n')
        {
          Error("Syntax error: expected '\"' before end of line near #line");
          return false;
        }

        m_lineDirectiveFileName[i] = *m_buffer;
        ++m_buffer;
        ++i;
      }

      m_lineDirectiveFileName[i] = 0;

      if (m_buffer >= m_bufferEnd)
      {
        Error("Syntax error: expected '\"' before end of file near #line");
        return false;
      }

      if (i + 1 >= s_maxIdentifier)
      {
        Error("Syntax error: file name too long near #line");
        return false;
      }

      // Skip the closing quote
      ++m_buffer;

      while (m_buffer < m_bufferEnd && m_buffer[0] != '\n')
      {
        if (!isspace(m_buffer[0]))
        {
          Error("Syntax error: unexpected input after file name near #line");
          return false;
        }
        ++m_buffer;
      }

      // Skip new line
      ++m_buffer;

      m_lineNumber = lineNumber;
      m_fileName = m_lineDirectiveFileName;

      return true;

    }

    return false;

  }

  int HLSLTokenizer::GetToken() const
  {
    return m_token;
  }

  float HLSLTokenizer::GetFloat() const
  {
    return m_fValue;
  }

  int HLSLTokenizer::GetInt() const
  {
    return m_iValue;
  }

  const char* HLSLTokenizer::GetIdentifier() const
  {
    return m_identifier;
  }

  int HLSLTokenizer::GetLineNumber() const
  {
    return m_tokenLineNumber;
  }

  const char* HLSLTokenizer::GetFileName() const
  {
    return m_fileName;
  }

  void HLSLTokenizer::Error(const char* format, ...)
  {
    // It's not always convenient to stop executing when an error occurs,
    // so just track once we've hit an error and stop reporting them until
    // we successfully bail out of execution.
    if (m_error)
    {
      return;
    }
    m_error = true;


    char buffer[1024];
    va_list args;
    va_start(args, format);
    vsnprintf(buffer, sizeof(buffer) - 1, format, args);
    va_end(args);

    Log_Error("%s(%d) : %s\n", m_fileName, m_lineNumber, buffer);
  }

  void HLSLTokenizer::GetTokenName(char buffer[s_maxIdentifier]) const
  {
    if (m_token == HLSLToken_FloatLiteral)
    {
      sprintf(buffer, "%f", m_fValue);
    }
    else if (m_token == HLSLToken_IntLiteral)
    {
      sprintf(buffer, "%d", m_iValue);
    }
    else if (m_token == HLSLToken_Identifier)
    {
      strcpy(buffer, m_identifier);
    }
    else
    {
      GetTokenName(m_token, buffer);
    }
  }

  void HLSLTokenizer::GetTokenName(int token, char buffer[s_maxIdentifier])
  {
    if (token < 256)
    {
      buffer[0] = (char)token;
      buffer[1] = 0;
    }
    else if (token < HLSLToken_LessEqual)
    {
      strcpy(buffer, _reservedWords[token - 256]);
    }
    else
    {
      switch (token)
      {
      case HLSLToken_PlusPlus:
        strcpy(buffer, "++");
        break;
      case HLSLToken_MinusMinus:
        strcpy(buffer, "--");
        break;
      case HLSLToken_PlusEqual:
        strcpy(buffer, "+=");
        break;
      case HLSLToken_MinusEqual:
        strcpy(buffer, "-=");
        break;
      case HLSLToken_TimesEqual:
        strcpy(buffer, "*=");
        break;
      case HLSLToken_DivideEqual:
        strcpy(buffer, "/=");
        break;
      case HLSLToken_FloatLiteral:
        strcpy(buffer, "float");
        break;
      case HLSLToken_IntLiteral:
        strcpy(buffer, "int");
        break;
      case HLSLToken_Identifier:
        strcpy(buffer, "identifier");
        break;
      case HLSLToken_EndOfStream:
        strcpy(buffer, "<eof>");
        break;
      default:
        strcpy(buffer, "unknown");
        break;
      }
    }

  }

  void HLSLTokenizer::ReturnToPos(const char* pos)
  {
    m_buffer = pos;
  }


}

namespace M4
{

  struct EffectState;

  class HLSLParser
  {

  public:

    HLSLParser(Allocator* allocator, HLSLTree* tree);

    bool Parse(const char* fileName, const char* buffer, size_t length);
    bool ApplyPreprocessor(const char* fileName, const char* buffer, size_t length, std::string& sourcePreprocessed);

  private:

    bool Accept(int token);
    bool Expect(int token);

    /**
     * Special form of Accept for accepting a word that is not actually a token
     * but will be treated like one. This is useful for HLSL keywords that are
     * only tokens in specific contexts (like in/inout in parameter lists).
     */
    bool Accept(const char* token);
    bool Expect(const char* token);

    bool AcceptIdentifier(const char*& identifier);
    bool ExpectIdentifier(const char*& identifier);
    bool AcceptFloat(float& value);
    bool AcceptInt(int& value);
    bool AcceptType(bool allowVoid, HLSLType& type);
    bool ExpectType(bool allowVoid, HLSLType& type);
    bool AcceptBinaryOperator(int priority, HLSLBinaryOp& binaryOp);
    bool AcceptUnaryOperator(bool pre, HLSLUnaryOp& unaryOp);
    bool AcceptAssign(HLSLBinaryOp& binaryOp);
    bool AcceptTypeModifier(int& typeFlags);
    bool AcceptInterpolationModifier(int& flags);

    /**
     * Handles a declaration like: "float2 name[5]". If allowUnsizedArray is true, it is
     * is acceptable for the declaration to not specify the bounds of the array (i.e. name[]).
     */
    bool AcceptDeclaration(bool allowUnsizedArray, HLSLType& type, const char*& name);
    bool ExpectDeclaration(bool allowUnsizedArray, HLSLType& type, const char*& name);

    bool ParseTopLevel(HLSLStatement*& statement);
    bool ParseBlock(HLSLStatement*& firstStatement, const HLSLType& returnType);
    bool ParseStatementOrBlock(HLSLStatement*& firstStatement, const HLSLType& returnType, bool scoped = true);
    bool ParseStatement(HLSLStatement*& statement, const HLSLType& returnType);
    bool ParseDeclaration(HLSLDeclaration*& declaration);
    bool ParseFieldDeclaration(HLSLStructField*& field);
    //bool ParseBufferFieldDeclaration(HLSLBufferField*& field);
    bool ParseExpression(HLSLExpression*& expression);
    bool ParseBinaryExpression(int priority, HLSLExpression*& expression);
    bool ParseTerminalExpression(HLSLExpression*& expression, char& needsExpressionEndChar);
    bool ParseExpressionList(int endToken, bool allowEmptyEnd, HLSLExpression*& firstExpression, int& numExpressions);
    bool ParseArgumentList(HLSLArgument*& firstArgument, int& numArguments, int& numOutputArguments);
    bool ParseDeclarationAssignment(HLSLDeclaration* declaration);
    bool ParsePartialConstructor(HLSLExpression*& expression, HLSLBaseType type, const char* typeName);

    bool ParseStateName(bool isSamplerState, bool isPipelineState, const char*& name, const EffectState*& state);
    bool ParseColorMask(int& mask);
    bool ParseStateValue(const EffectState* state, HLSLStateAssignment* stateAssignment);
    bool ParseStateAssignment(HLSLStateAssignment*& stateAssignment, bool isSamplerState, bool isPipelineState);
    bool ParseSamplerState(HLSLExpression*& expression);
    bool ParseTechnique(HLSLStatement*& statement);
    bool ParsePass(HLSLPass*& pass);
    bool ParsePipeline(HLSLStatement*& pipeline);
    bool ParseStage(HLSLStatement*& stage);
    bool ParsePreprocessorDefine();

    bool ParseAttributeList(HLSLAttribute*& attribute);
    bool ParseAttributeBlock(HLSLAttribute*& attribute);

    bool CheckForUnexpectedEndOfStream(int endToken);

    const HLSLStruct* FindUserDefinedType(const char* name) const;

    void BeginScope();
    void EndScope();

    void DeclareVariable(const char* name, const HLSLType& type);

    /** Returned pointer is only valid until Declare or Begin/EndScope is called. */
    const HLSLType* FindVariable(const char* name, bool& global) const;

    const HLSLFunction* FindFunction(const char* name) const;
    const HLSLFunction* FindFunction(const HLSLFunction* fun) const;

    bool GetIsFunction(const char* name) const;

    /** Finds the overloaded function that matches the specified call. */
    const HLSLFunction* MatchFunctionCall(const HLSLFunctionCall* functionCall, const char* name);

    /** Gets the type of the named field on the specified object type (fieldName can also specify a swizzle. ) */
    bool GetMemberType(const HLSLType& objectType, HLSLMemberAccess* memberAccess);

    bool CheckTypeCast(const HLSLType& srcType, const HLSLType& dstType);

    const char* GetFileName();
    int GetLineNumber() const;

    bool ProcessMacroArguments(HLSLMacro* macro, std::string& sourcePreprocessed);
    HLSLMacro* ProcessMacroFromIdentifier(std::string& sourcePreprocessed, bool& addOriginalSource);

  private:

    struct Variable
    {
      const char* name;
      HLSLType        type;
    };

    HLSLTokenizer           m_tokenizer;
    Array<HLSLStruct*>      m_userTypes;
    Array<Variable>         m_variables;
    Array<HLSLFunction*>    m_functions;
    Array<HLSLMacro*>       m_macros;
    int                     m_numGlobals;

    HLSLTree* m_tree;

    bool                    m_allowUndeclaredIdentifiers = false;
    // not used bool                    m_disableSemanticValidation = false;
  };
}

namespace M4
{

  enum CompareFunctionsResult
  {
    FunctionsEqual,
    Function1Better,
    Function2Better
  };


  /** This structure stores a HLSLFunction-like declaration for an intrinsic function */
  struct Intrinsic
  {
    explicit Intrinsic(const char* name, HLSLBaseType returnType)
    {
      function.name = name;
      function.returnType.baseType = returnType;
      function.numArguments = 0;
    }
    explicit Intrinsic(const char* name, HLSLBaseType returnType, HLSLBaseType arg1)
    {
      function.name = name;
      function.returnType.baseType = returnType;
      function.numArguments = 1;
      function.argument = argument + 0;
      argument[0].type.baseType = arg1;
      argument[0].type.flags = HLSLTypeFlag_Const;
    }
    explicit Intrinsic(const char* name, HLSLBaseType returnType, HLSLBaseType arg1, HLSLBaseType arg2)
    {
      function.name = name;
      function.returnType.baseType = returnType;
      function.argument = argument + 0;
      function.numArguments = 2;
      argument[0].type.baseType = arg1;
      argument[0].type.flags = HLSLTypeFlag_Const;
      argument[0].nextArgument = argument + 1;
      argument[1].type.baseType = arg2;
      argument[1].type.flags = HLSLTypeFlag_Const;
    }
    explicit Intrinsic(const char* name, HLSLBaseType returnType, HLSLBaseType arg1, HLSLBaseType arg2, HLSLBaseType arg3)
    {
      function.name = name;
      function.returnType.baseType = returnType;
      function.argument = argument + 0;
      function.numArguments = 3;
      argument[0].type.baseType = arg1;
      argument[0].type.flags = HLSLTypeFlag_Const;
      argument[0].nextArgument = argument + 1;
      argument[1].type.baseType = arg2;
      argument[1].type.flags = HLSLTypeFlag_Const;
      argument[1].nextArgument = argument + 2;
      argument[2].type.baseType = arg3;
      argument[2].type.flags = HLSLTypeFlag_Const;
    }
    explicit Intrinsic(const char* name, HLSLBaseType returnType, HLSLBaseType arg1, HLSLBaseType arg2, HLSLBaseType arg3, HLSLBaseType arg4)
    {
      function.name = name;
      function.returnType.baseType = returnType;
      function.argument = argument + 0;
      function.numArguments = 4;
      argument[0].type.baseType = arg1;
      argument[0].type.flags = HLSLTypeFlag_Const;
      argument[0].nextArgument = argument + 1;
      argument[1].type.baseType = arg2;
      argument[1].type.flags = HLSLTypeFlag_Const;
      argument[1].nextArgument = argument + 2;
      argument[2].type.baseType = arg3;
      argument[2].type.flags = HLSLTypeFlag_Const;
      argument[2].nextArgument = argument + 3;
      argument[3].type.baseType = arg4;
      argument[3].type.flags = HLSLTypeFlag_Const;
    }
    HLSLFunction    function;
    HLSLArgument    argument[4];
  };

  Intrinsic SamplerIntrinsic(const char* name, HLSLBaseType returnType, HLSLBaseType arg1, HLSLBaseType samplerType, HLSLBaseType arg2)
  {
    Intrinsic i(name, returnType, arg1, arg2);
    i.argument[0].type.samplerType = samplerType;
    return i;
  }



  static const int _numberTypeRank[NumericType_Count][NumericType_Count] =
  {
    //F  B  I  U
    { 0, 4, 4, 4 },  // NumericType_Float
    { 5, 0, 5, 5 },  // NumericType_Bool
    { 2, 4, 0, 1 },  // NumericType_Int
    { 2, 4, 1, 0 }   // NumericType_Uint
  };


  struct EffectStateValue
  {
    const char* name;
    int value;
  };

  static const EffectStateValue textureFilteringValues[] = {
      {"None", 0},
      {"Point", 1},
      {"Linear", 2},
      {"Anisotropic", 3},
      {nullptr, 0}
  };

  static const EffectStateValue textureAddressingValues[] = {
      {"Wrap", 1},
      {"Mirror", 2},
      {"Clamp", 3},
      {"Border", 4},
      {"MirrorOnce", 5},
      {nullptr, 0}
  };

  static const EffectStateValue booleanValues[] = {
      {"False", 0},
      {"True", 1},
      {nullptr, 0}
  };

  static const EffectStateValue cullValues[] = {
      {"None", 1},
      {"CW", 2},
      {"CCW", 3},
      {nullptr, 0}
  };

  static const EffectStateValue cmpValues[] = {
      {"Never", 1},
      {"Less", 2},
      {"Equal", 3},
      {"LessEqual", 4},
      {"Greater", 5},
      {"NotEqual", 6},
      {"GreaterEqual", 7},
      {"Always", 8},
      {nullptr, 0}
  };

  static const EffectStateValue blendValues[] = {
      {"Zero", 1},
      {"One", 2},
      {"SrcColor", 3},
      {"InvSrcColor", 4},
      {"SrcAlpha", 5},
      {"InvSrcAlpha", 6},
      {"DestAlpha", 7},
      {"InvDestAlpha", 8},
      {"DestColor", 9},
      {"InvDestColor", 10},
      {"SrcAlphaSat", 11},
      {"BothSrcAlpha", 12},
      {"BothInvSrcAlpha", 13},
      {"BlendFactor", 14},
      {"InvBlendFactor", 15},
      {"SrcColor2", 16},          // Dual source blending. D3D9Ex only.
      {"InvSrcColor2", 17},
      {nullptr, 0}
  };

  static const EffectStateValue blendOpValues[] = {
      {"Add", 1},
      {"Subtract", 2},
      {"RevSubtract", 3},
      {"Min", 4},
      {"Max", 5},
      {nullptr, 0}
  };

  static const EffectStateValue fillModeValues[] = {
      {"Point", 1},
      {"Wireframe", 2},
      {"Solid", 3},
      {nullptr, 0}
  };

  static const EffectStateValue stencilOpValues[] = {
      {"Keep", 1},
      {"Zero", 2},
      {"Replace", 3},
      {"IncrSat", 4},
      {"DecrSat", 5},
      {"Invert", 6},
      {"Incr", 7},
      {"Decr", 8},
      {nullptr, 0}
  };

  // These are flags.
  static const EffectStateValue colorMaskValues[] = {
      {"False", 0},
      {"Red",   1 << 0},
      {"Green", 1 << 1},
      {"Blue",  1 << 2},
      {"Alpha", 1 << 3},
      {"X", 1 << 0},
      {"Y", 1 << 1},
      {"Z", 1 << 2},
      {"W", 1 << 3},
      {nullptr, 0}
  };

  static const EffectStateValue integerValues[] = {
      {nullptr, 0}
  };

  static const EffectStateValue floatValues[] = {
      {nullptr, 0}
  };


  struct EffectState
  {
    const char* name;
    int d3drs;
    const EffectStateValue* values;
  };

  static const EffectState samplerStates[] = {
      {"AddressU", 1, textureAddressingValues},
      {"AddressV", 2, textureAddressingValues},
      {"AddressW", 3, textureAddressingValues},
      // "BorderColor", 4, D3DCOLOR
      {"MagFilter", 5, textureFilteringValues},
      {"MinFilter", 6, textureFilteringValues},
      {"MipFilter", 7, textureFilteringValues},
      {"MipMapLodBias", 8, floatValues},
      {"MaxMipLevel", 9, integerValues},
      {"MaxAnisotropy", 10, integerValues},
      {"sRGBTexture", 11, booleanValues},
  };

  static const EffectState effectStates[] = {
      {"VertexShader", 0, nullptr},
      {"PixelShader", 0, nullptr},
      {"AlphaBlendEnable", 27, booleanValues},
      {"SrcBlend", 19, blendValues},
      {"DestBlend", 20, blendValues},
      {"BlendOp", 171, blendOpValues},
      {"SeparateAlphaBlendEanble", 206, booleanValues},
      {"SrcBlendAlpha", 207, blendValues},
      {"DestBlendAlpha", 208, blendValues},
      {"BlendOpAlpha", 209, blendOpValues},
      {"AlphaTestEnable", 15, booleanValues},
      {"AlphaRef", 24, integerValues},
      {"AlphaFunc", 25, cmpValues},
      {"CullMode", 22, cullValues},
      {"ZEnable", 7, booleanValues},
      {"ZWriteEnable", 14, booleanValues},
      {"ZFunc", 23, cmpValues},
      {"StencilEnable", 52, booleanValues},
      {"StencilFail", 53, stencilOpValues},
      {"StencilZFail", 54, stencilOpValues},
      {"StencilPass", 55, stencilOpValues},
      {"StencilFunc", 56, cmpValues},
      {"StencilRef", 57, integerValues},
      {"StencilMask", 58, integerValues},
      {"StencilWriteMask", 59, integerValues},
      {"TwoSidedStencilMode", 185, booleanValues},
      {"CCW_StencilFail", 186, stencilOpValues},
      {"CCW_StencilZFail", 187, stencilOpValues},
      {"CCW_StencilPass", 188, stencilOpValues},
      {"CCW_StencilFunc", 189, cmpValues},
      {"ColorWriteEnable", 168, colorMaskValues},
      {"FillMode", 8, fillModeValues},
      {"MultisampleAlias", 161, booleanValues},
      {"MultisampleMask", 162, integerValues},
      {"ScissorTestEnable", 174, booleanValues},
      {"SlopeScaleDepthBias", 175, floatValues},
      {"DepthBias", 195, floatValues}
  };


  static const EffectStateValue witnessCullModeValues[] = {
      {"None", 0},
      {"Back", 1},
      {"Front", 2},
      {nullptr, 0}
  };

  static const EffectStateValue witnessFillModeValues[] = {
      {"Solid", 0},
      {"Wireframe", 1},
      {nullptr, 0}
  };

  static const EffectStateValue witnessBlendModeValues[] = {
      {"Disabled", 0},
      {"AlphaBlend", 1},          // src * a + dst * (1-a)
      {"Add", 2},                 // src + dst
      {"Mixed", 3},               // src + dst * (1-a)
      {"Multiply", 4},            // src * dst
      {"Multiply2", 5},           // 2 * src * dst
      {nullptr, 0}
  };

  static const EffectStateValue witnessDepthFuncValues[] = {
      {"LessEqual", 0},
      {"Less", 1},
      {"Equal", 2},
      {"Greater", 3},
      {"Always", 4},
      {nullptr, 0}
  };

  static const EffectStateValue witnessStencilModeValues[] = {
      {"Disabled", 0},
      {"Set", 1},
      {"Test", 2},
      {nullptr, 0}
  };

  /* not used
  static const EffectStateValue witnessFilterModeValues[] = {
      {"Point", 0},
      {"Linear", 1},
      {"Mipmap_Nearest", 2},
      {"Mipmap_Best", 3},     // Quality of mipmap filtering depends on render settings.
      {"Anisotropic", 4},     // Aniso without mipmaps for reflection maps.
      {nullptr, 0}
  };

  static const EffectStateValue witnessWrapModeValues[] = {
      {"Repeat", 0},
      {"Clamp", 1},
      {"ClampToBorder", 2},
      {nullptr, 0}
  };
  */

  static const EffectState pipelineStates[] = {
      {"VertexShader", 0, nullptr},
      {"PixelShader", 0, nullptr},

      // Depth_Stencil_State
      {"DepthWrite", 0, booleanValues},
      {"DepthEnable", 0, booleanValues},
      {"DepthFunc", 0, witnessDepthFuncValues},
      {"StencilMode", 0, witnessStencilModeValues},

      // Raster_State
      {"CullMode", 0, witnessCullModeValues},
      {"FillMode", 0, witnessFillModeValues},
      {"MultisampleEnable", 0, booleanValues},
      {"PolygonOffset", 0, booleanValues},

      // Blend_State
      {"BlendMode", 0, witnessBlendModeValues},
      {"ColorWrite", 0, booleanValues},
      {"AlphaWrite", 0, booleanValues},
      {"AlphaTest", 0, booleanValues},       // This is really alpha to coverage.
  };





#define INTRINSIC_FLOAT1_FUNCTION(name) \
        Intrinsic( name, HLSLBaseType_Float,   HLSLBaseType_Float  ),   \
        Intrinsic( name, HLSLBaseType_Float2,  HLSLBaseType_Float2 ),   \
        Intrinsic( name, HLSLBaseType_Float3,  HLSLBaseType_Float3 ),   \
        Intrinsic( name, HLSLBaseType_Float4,  HLSLBaseType_Float4 )

#define INTRINSIC_FLOAT2_FUNCTION(name) \
        Intrinsic( name, HLSLBaseType_Float,   HLSLBaseType_Float,   HLSLBaseType_Float  ),   \
        Intrinsic( name, HLSLBaseType_Float2,  HLSLBaseType_Float2,  HLSLBaseType_Float2 ),   \
        Intrinsic( name, HLSLBaseType_Float3,  HLSLBaseType_Float3,  HLSLBaseType_Float3 ),   \
        Intrinsic( name, HLSLBaseType_Float4,  HLSLBaseType_Float4,  HLSLBaseType_Float4 )

#define INTRINSIC_FLOAT3_FUNCTION(name) \
        Intrinsic( name, HLSLBaseType_Float,   HLSLBaseType_Float,   HLSLBaseType_Float,  HLSLBaseType_Float ),   \
        Intrinsic( name, HLSLBaseType_Float2,  HLSLBaseType_Float2,  HLSLBaseType_Float2,  HLSLBaseType_Float2 ),  \
        Intrinsic( name, HLSLBaseType_Float3,  HLSLBaseType_Float3,  HLSLBaseType_Float3,  HLSLBaseType_Float3 ),  \
        Intrinsic( name, HLSLBaseType_Float4,  HLSLBaseType_Float4,  HLSLBaseType_Float4,  HLSLBaseType_Float4 )

#if 0
  // @@ IC: For some reason this is not working with the Visual Studio compiler:
#define SAMPLER_INTRINSIC_FUNCTION(name, sampler, arg1) \
        SamplerIntrinsic( name, HLSLBaseType_Float4, sampler, HLSLBaseType_Float, arg1)
#else
#define SAMPLER_INTRINSIC_FUNCTION(name, sampler, arg1) \
        Intrinsic( name, HLSLBaseType_Float4, sampler, arg1)
#endif

  const Intrinsic _intrinsic[] =
  {
      INTRINSIC_FLOAT1_FUNCTION("abs"),
      INTRINSIC_FLOAT1_FUNCTION("acos"),

      Intrinsic("any", HLSLBaseType_Bool, HLSLBaseType_Float),
      Intrinsic("any", HLSLBaseType_Bool, HLSLBaseType_Float2),
      Intrinsic("any", HLSLBaseType_Bool, HLSLBaseType_Float3),
      Intrinsic("any", HLSLBaseType_Bool, HLSLBaseType_Float4),
      Intrinsic("any", HLSLBaseType_Bool, HLSLBaseType_Float2x4),
      Intrinsic("any", HLSLBaseType_Bool, HLSLBaseType_Float2x3),
  Intrinsic("any", HLSLBaseType_Bool, HLSLBaseType_Float2x2),
      Intrinsic("any", HLSLBaseType_Bool, HLSLBaseType_Float3x4),
      Intrinsic("any", HLSLBaseType_Bool, HLSLBaseType_Float3x3),
      Intrinsic("any", HLSLBaseType_Bool, HLSLBaseType_Float3x2),
      Intrinsic("any", HLSLBaseType_Bool, HLSLBaseType_Float4x4),
      Intrinsic("any", HLSLBaseType_Bool, HLSLBaseType_Float4x3),
      Intrinsic("any", HLSLBaseType_Bool, HLSLBaseType_Float4x2),
      Intrinsic("any", HLSLBaseType_Bool, HLSLBaseType_Bool),
      Intrinsic("any", HLSLBaseType_Bool, HLSLBaseType_Int),
      Intrinsic("any", HLSLBaseType_Bool, HLSLBaseType_Int2),
      Intrinsic("any", HLSLBaseType_Bool, HLSLBaseType_Int3),
      Intrinsic("any", HLSLBaseType_Bool, HLSLBaseType_Int4),
      Intrinsic("any", HLSLBaseType_Bool, HLSLBaseType_Uint),
      Intrinsic("any", HLSLBaseType_Bool, HLSLBaseType_Uint2),
      Intrinsic("any", HLSLBaseType_Bool, HLSLBaseType_Uint3),
      Intrinsic("any", HLSLBaseType_Bool, HLSLBaseType_Uint4),

      INTRINSIC_FLOAT1_FUNCTION("asin"),
      INTRINSIC_FLOAT1_FUNCTION("atan"),
      INTRINSIC_FLOAT2_FUNCTION("atan2"),
      INTRINSIC_FLOAT3_FUNCTION("clamp"),
      INTRINSIC_FLOAT1_FUNCTION("cos"),
      INTRINSIC_FLOAT1_FUNCTION("tan"),

      INTRINSIC_FLOAT3_FUNCTION("lerp"),
      INTRINSIC_FLOAT3_FUNCTION("smoothstep"),

      INTRINSIC_FLOAT1_FUNCTION("floor"),
      INTRINSIC_FLOAT1_FUNCTION("ceil"),
      INTRINSIC_FLOAT1_FUNCTION("frac"),

      INTRINSIC_FLOAT2_FUNCTION("fmod"),

      Intrinsic("clip", HLSLBaseType_Void,  HLSLBaseType_Float),
      Intrinsic("clip", HLSLBaseType_Void,  HLSLBaseType_Float2),
      Intrinsic("clip", HLSLBaseType_Void,  HLSLBaseType_Float3),
      Intrinsic("clip", HLSLBaseType_Void,  HLSLBaseType_Float4),

      Intrinsic("dot", HLSLBaseType_Float,  HLSLBaseType_Float,   HLSLBaseType_Float),
      Intrinsic("dot", HLSLBaseType_Float,  HLSLBaseType_Float2,  HLSLBaseType_Float2),
      Intrinsic("dot", HLSLBaseType_Float,  HLSLBaseType_Float3,  HLSLBaseType_Float3),
      Intrinsic("dot", HLSLBaseType_Float,  HLSLBaseType_Float4,  HLSLBaseType_Float4),

      Intrinsic("cross", HLSLBaseType_Float3,  HLSLBaseType_Float3,  HLSLBaseType_Float3),

      Intrinsic("length", HLSLBaseType_Float,  HLSLBaseType_Float),
      Intrinsic("length", HLSLBaseType_Float,  HLSLBaseType_Float2),
      Intrinsic("length", HLSLBaseType_Float,  HLSLBaseType_Float3),
      Intrinsic("length", HLSLBaseType_Float,  HLSLBaseType_Float4),

      INTRINSIC_FLOAT2_FUNCTION("max"),
      INTRINSIC_FLOAT2_FUNCTION("min"),

      // @@ Add all combinations.
      // https://docs.microsoft.com/en-us/windows/desktop/direct3dhlsl/dx-graphics-hlsl-mul

      // scalar = mul(scalar, scalar)
      Intrinsic("mul", HLSLBaseType_Float, HLSLBaseType_Float, HLSLBaseType_Float),

      // vector<N> = mul(scalar, vector<N>)
      Intrinsic("mul", HLSLBaseType_Float4, HLSLBaseType_Float, HLSLBaseType_Float4),
      Intrinsic("mul", HLSLBaseType_Float3, HLSLBaseType_Float, HLSLBaseType_Float3),
      Intrinsic("mul", HLSLBaseType_Float2, HLSLBaseType_Float, HLSLBaseType_Float2),

      // matrix<N,M> = mul(scalar, matrix<M,N>)
      Intrinsic("mul", HLSLBaseType_Float4x4, HLSLBaseType_Float, HLSLBaseType_Float4x4),
      Intrinsic("mul", HLSLBaseType_Float4x3, HLSLBaseType_Float, HLSLBaseType_Float4x3),
      Intrinsic("mul", HLSLBaseType_Float4x2, HLSLBaseType_Float, HLSLBaseType_Float4x2),

      Intrinsic("mul", HLSLBaseType_Float3x4, HLSLBaseType_Float, HLSLBaseType_Float3x4),
      Intrinsic("mul", HLSLBaseType_Float3x3, HLSLBaseType_Float, HLSLBaseType_Float3x3),
      Intrinsic("mul", HLSLBaseType_Float3x2, HLSLBaseType_Float, HLSLBaseType_Float3x2),

      Intrinsic("mul", HLSLBaseType_Float2x4, HLSLBaseType_Float, HLSLBaseType_Float2x4),
      Intrinsic("mul", HLSLBaseType_Float2x3, HLSLBaseType_Float, HLSLBaseType_Float2x3),
      Intrinsic("mul", HLSLBaseType_Float2x2, HLSLBaseType_Float, HLSLBaseType_Float2x2),

      // vector<N> = mul(vector<N>, scalar)
      Intrinsic("mul", HLSLBaseType_Float4, HLSLBaseType_Float4, HLSLBaseType_Float),
      Intrinsic("mul", HLSLBaseType_Float3, HLSLBaseType_Float3, HLSLBaseType_Float),
      Intrinsic("mul", HLSLBaseType_Float2, HLSLBaseType_Float2, HLSLBaseType_Float),

      // scalar = mul(vector<N>, vector<N>) it's a dot product !
      Intrinsic("mul", HLSLBaseType_Float, HLSLBaseType_Float4, HLSLBaseType_Float4),
      Intrinsic("mul", HLSLBaseType_Float, HLSLBaseType_Float3, HLSLBaseType_Float3),
      Intrinsic("mul", HLSLBaseType_Float, HLSLBaseType_Float2, HLSLBaseType_Float2),

      // vector<M> = mul(vector<N>, matrix<N,M>)
      Intrinsic("mul", HLSLBaseType_Float4, HLSLBaseType_Float4, HLSLBaseType_Float4x4),
      Intrinsic("mul", HLSLBaseType_Float3, HLSLBaseType_Float4, HLSLBaseType_Float4x3),
      Intrinsic("mul", HLSLBaseType_Float2, HLSLBaseType_Float4, HLSLBaseType_Float4x2),

      Intrinsic("mul", HLSLBaseType_Float4, HLSLBaseType_Float3, HLSLBaseType_Float3x4),
      Intrinsic("mul", HLSLBaseType_Float3, HLSLBaseType_Float3, HLSLBaseType_Float3x3),
      Intrinsic("mul", HLSLBaseType_Float2, HLSLBaseType_Float3, HLSLBaseType_Float3x2),

      Intrinsic("mul", HLSLBaseType_Float4, HLSLBaseType_Float2, HLSLBaseType_Float2x4),
      Intrinsic("mul", HLSLBaseType_Float3, HLSLBaseType_Float2, HLSLBaseType_Float2x3),
      Intrinsic("mul", HLSLBaseType_Float2, HLSLBaseType_Float2, HLSLBaseType_Float2x2),

      // matrix<N,M> = mul(matrix<N,M>,scalar)
      Intrinsic("mul", HLSLBaseType_Float4x4, HLSLBaseType_Float4x4, HLSLBaseType_Float),
      Intrinsic("mul", HLSLBaseType_Float3x4, HLSLBaseType_Float3x4, HLSLBaseType_Float),
      Intrinsic("mul", HLSLBaseType_Float2x4, HLSLBaseType_Float2x4, HLSLBaseType_Float),

      Intrinsic("mul", HLSLBaseType_Float4x3, HLSLBaseType_Float4x3, HLSLBaseType_Float),
      Intrinsic("mul", HLSLBaseType_Float3x3, HLSLBaseType_Float3x3, HLSLBaseType_Float),
      Intrinsic("mul", HLSLBaseType_Float2x3, HLSLBaseType_Float2x3, HLSLBaseType_Float),

      Intrinsic("mul", HLSLBaseType_Float4x2, HLSLBaseType_Float4x2, HLSLBaseType_Float),
      Intrinsic("mul", HLSLBaseType_Float3x2, HLSLBaseType_Float3x2, HLSLBaseType_Float),
      Intrinsic("mul", HLSLBaseType_Float2x2, HLSLBaseType_Float2x2, HLSLBaseType_Float),

      // vector<N> = mul(matrix<N,M>, vector<M>)
      Intrinsic("mul", HLSLBaseType_Float4, HLSLBaseType_Float4x4, HLSLBaseType_Float4),
      Intrinsic("mul", HLSLBaseType_Float3, HLSLBaseType_Float3x4, HLSLBaseType_Float4),
      Intrinsic("mul", HLSLBaseType_Float2, HLSLBaseType_Float2x4, HLSLBaseType_Float4),

      Intrinsic("mul", HLSLBaseType_Float4, HLSLBaseType_Float4x3, HLSLBaseType_Float3),
      Intrinsic("mul", HLSLBaseType_Float3, HLSLBaseType_Float3x3, HLSLBaseType_Float3),
      Intrinsic("mul", HLSLBaseType_Float2, HLSLBaseType_Float2x3, HLSLBaseType_Float3),

      Intrinsic("mul", HLSLBaseType_Float4, HLSLBaseType_Float4x2, HLSLBaseType_Float2),
      Intrinsic("mul", HLSLBaseType_Float3, HLSLBaseType_Float3x2, HLSLBaseType_Float2),
      Intrinsic("mul", HLSLBaseType_Float2, HLSLBaseType_Float2x2, HLSLBaseType_Float2),

      // matrix<N,M> = mul(matrix<N,4>, matrix<4,M)
      Intrinsic("mul", HLSLBaseType_Float4x4, HLSLBaseType_Float4x4, HLSLBaseType_Float4x4),
      Intrinsic("mul", HLSLBaseType_Float4x3, HLSLBaseType_Float4x4, HLSLBaseType_Float4x3),
      Intrinsic("mul", HLSLBaseType_Float4x2, HLSLBaseType_Float4x4, HLSLBaseType_Float4x2),

      Intrinsic("mul", HLSLBaseType_Float3x4, HLSLBaseType_Float3x4, HLSLBaseType_Float4x4),
      Intrinsic("mul", HLSLBaseType_Float3x3, HLSLBaseType_Float3x4, HLSLBaseType_Float4x3),
      Intrinsic("mul", HLSLBaseType_Float3x2, HLSLBaseType_Float3x4, HLSLBaseType_Float4x2),

      Intrinsic("mul", HLSLBaseType_Float2x4, HLSLBaseType_Float2x4, HLSLBaseType_Float4x4),
      Intrinsic("mul", HLSLBaseType_Float2x3, HLSLBaseType_Float2x4, HLSLBaseType_Float4x3),
      Intrinsic("mul", HLSLBaseType_Float2x2, HLSLBaseType_Float2x4, HLSLBaseType_Float4x2),

      // matrix<N,M> = mul(matrix<N,3>, matrix<3,M>)
      Intrinsic("mul", HLSLBaseType_Float4x4, HLSLBaseType_Float4x3, HLSLBaseType_Float3x4),
      Intrinsic("mul", HLSLBaseType_Float4x3, HLSLBaseType_Float4x3, HLSLBaseType_Float3x3),
      Intrinsic("mul", HLSLBaseType_Float4x2, HLSLBaseType_Float4x3, HLSLBaseType_Float3x2),

      Intrinsic("mul", HLSLBaseType_Float3x4, HLSLBaseType_Float3x3, HLSLBaseType_Float3x4),
      Intrinsic("mul", HLSLBaseType_Float3x3, HLSLBaseType_Float3x3, HLSLBaseType_Float3x3),
      Intrinsic("mul", HLSLBaseType_Float3x2, HLSLBaseType_Float3x3, HLSLBaseType_Float3x2),

      Intrinsic("mul", HLSLBaseType_Float2x4, HLSLBaseType_Float2x3, HLSLBaseType_Float3x4),
      Intrinsic("mul", HLSLBaseType_Float2x3, HLSLBaseType_Float2x3, HLSLBaseType_Float3x3),
      Intrinsic("mul", HLSLBaseType_Float2x2, HLSLBaseType_Float2x3, HLSLBaseType_Float3x2),

      // matrix<N,M> = mul(matrix<N,2>, matrix<2,M>)
      Intrinsic("mul", HLSLBaseType_Float4x4, HLSLBaseType_Float4x2, HLSLBaseType_Float2x4),
      Intrinsic("mul", HLSLBaseType_Float4x3, HLSLBaseType_Float4x2, HLSLBaseType_Float2x3),
      Intrinsic("mul", HLSLBaseType_Float4x2, HLSLBaseType_Float4x2, HLSLBaseType_Float2x2),

      Intrinsic("mul", HLSLBaseType_Float3x4, HLSLBaseType_Float3x2, HLSLBaseType_Float2x4),
      Intrinsic("mul", HLSLBaseType_Float3x3, HLSLBaseType_Float3x2, HLSLBaseType_Float2x3),
      Intrinsic("mul", HLSLBaseType_Float3x2, HLSLBaseType_Float3x2, HLSLBaseType_Float2x2),

      Intrinsic("mul", HLSLBaseType_Float2x4, HLSLBaseType_Float2x2, HLSLBaseType_Float2x4),
      Intrinsic("mul", HLSLBaseType_Float2x3, HLSLBaseType_Float2x2, HLSLBaseType_Float2x3),
      Intrinsic("mul", HLSLBaseType_Float2x2, HLSLBaseType_Float2x2, HLSLBaseType_Float2x2),


  Intrinsic("transpose", HLSLBaseType_Float2x2, HLSLBaseType_Float2x2),
      Intrinsic("transpose", HLSLBaseType_Float3x3, HLSLBaseType_Float3x3),
      Intrinsic("transpose", HLSLBaseType_Float4x4, HLSLBaseType_Float4x4),

      INTRINSIC_FLOAT2_FUNCTION("modf"),

      INTRINSIC_FLOAT1_FUNCTION("normalize"),
      INTRINSIC_FLOAT2_FUNCTION("pow"),
      INTRINSIC_FLOAT1_FUNCTION("saturate"),
      INTRINSIC_FLOAT1_FUNCTION("sin"),
      INTRINSIC_FLOAT1_FUNCTION("sqrt"),
      INTRINSIC_FLOAT1_FUNCTION("rsqrt"),
      INTRINSIC_FLOAT1_FUNCTION("rcp"),
      INTRINSIC_FLOAT1_FUNCTION("exp"),
      INTRINSIC_FLOAT1_FUNCTION("exp2"),
      INTRINSIC_FLOAT1_FUNCTION("log"),
      INTRINSIC_FLOAT1_FUNCTION("log2"),

      INTRINSIC_FLOAT1_FUNCTION("ddx"),
      INTRINSIC_FLOAT1_FUNCTION("ddy"),

      INTRINSIC_FLOAT1_FUNCTION("sign"),
      INTRINSIC_FLOAT2_FUNCTION("step"),
      INTRINSIC_FLOAT2_FUNCTION("reflect"),

  INTRINSIC_FLOAT1_FUNCTION("isnan"),
  INTRINSIC_FLOAT1_FUNCTION("isinf"),

  Intrinsic("asuint",    HLSLBaseType_Uint, HLSLBaseType_Float),

      SAMPLER_INTRINSIC_FUNCTION("tex2D", HLSLBaseType_Sampler2D, HLSLBaseType_Float2),

      Intrinsic("tex2Dproj", HLSLBaseType_Float4, HLSLBaseType_Sampler2D, HLSLBaseType_Float4),

      SAMPLER_INTRINSIC_FUNCTION("tex2Dlod", HLSLBaseType_Sampler2D, HLSLBaseType_Float4),

      Intrinsic("tex2Dlod",  HLSLBaseType_Float4, HLSLBaseType_Sampler2D, HLSLBaseType_Float4, HLSLBaseType_Int2),   // With offset.

      SAMPLER_INTRINSIC_FUNCTION("tex2Dbias", HLSLBaseType_Sampler2D, HLSLBaseType_Float4),

      Intrinsic("tex2Dgrad", HLSLBaseType_Float4, HLSLBaseType_Sampler2D, HLSLBaseType_Float2, HLSLBaseType_Float2, HLSLBaseType_Float2),
      Intrinsic("tex2Dgather", HLSLBaseType_Float4, HLSLBaseType_Sampler2D, HLSLBaseType_Float2, HLSLBaseType_Int),
      Intrinsic("tex2Dgather", HLSLBaseType_Float4, HLSLBaseType_Sampler2D, HLSLBaseType_Float2, HLSLBaseType_Int2, HLSLBaseType_Int),    // With offset.
      Intrinsic("tex2Dsize", HLSLBaseType_Int2, HLSLBaseType_Sampler2D),
      Intrinsic("tex2Dfetch", HLSLBaseType_Float4, HLSLBaseType_Sampler2D, HLSLBaseType_Int3),    // u,v,mipmap

      Intrinsic("tex2Dcmp", HLSLBaseType_Float4, HLSLBaseType_Sampler2DShadow, HLSLBaseType_Float4),                // @@ IC: This really takes a float3 (uvz) and returns a float.

      Intrinsic("tex2DMSfetch", HLSLBaseType_Float4, HLSLBaseType_Sampler2DMS, HLSLBaseType_Int2, HLSLBaseType_Int),
      Intrinsic("tex2DMSsize", HLSLBaseType_Int3, HLSLBaseType_Sampler2DMS),

      Intrinsic("tex2DArray", HLSLBaseType_Float4, HLSLBaseType_Sampler2DArray, HLSLBaseType_Float3),

      Intrinsic("tex3D",     HLSLBaseType_Float4, HLSLBaseType_Sampler3D, HLSLBaseType_Float3),
      Intrinsic("tex3Dlod",  HLSLBaseType_Float4, HLSLBaseType_Sampler3D, HLSLBaseType_Float4),
      Intrinsic("tex3Dbias", HLSLBaseType_Float4, HLSLBaseType_Sampler3D, HLSLBaseType_Float4),
      Intrinsic("tex3Dsize", HLSLBaseType_Int3, HLSLBaseType_Sampler3D),

      Intrinsic("texCUBE",       HLSLBaseType_Float4, HLSLBaseType_SamplerCube, HLSLBaseType_Float3),
      Intrinsic("texCUBElod", HLSLBaseType_Float4, HLSLBaseType_SamplerCube, HLSLBaseType_Float4),
      Intrinsic("texCUBEbias", HLSLBaseType_Float4, HLSLBaseType_SamplerCube, HLSLBaseType_Float4),
      Intrinsic("texCUBEsize", HLSLBaseType_Int, HLSLBaseType_SamplerCube),

      Intrinsic("sincos", HLSLBaseType_Void,  HLSLBaseType_Float,   HLSLBaseType_Float,  HLSLBaseType_Float),
      Intrinsic("sincos", HLSLBaseType_Void,  HLSLBaseType_Float2,  HLSLBaseType_Float,  HLSLBaseType_Float2),
      Intrinsic("sincos", HLSLBaseType_Void,  HLSLBaseType_Float3,  HLSLBaseType_Float,  HLSLBaseType_Float3),
      Intrinsic("sincos", HLSLBaseType_Void,  HLSLBaseType_Float4,  HLSLBaseType_Float,  HLSLBaseType_Float4),

      Intrinsic("mad", HLSLBaseType_Float, HLSLBaseType_Float, HLSLBaseType_Float, HLSLBaseType_Float),
      Intrinsic("mad", HLSLBaseType_Float2, HLSLBaseType_Float2, HLSLBaseType_Float2, HLSLBaseType_Float2),
      Intrinsic("mad", HLSLBaseType_Float3, HLSLBaseType_Float3, HLSLBaseType_Float3, HLSLBaseType_Float3),
      Intrinsic("mad", HLSLBaseType_Float4, HLSLBaseType_Float4, HLSLBaseType_Float4, HLSLBaseType_Float4),
  };

  const int _numIntrinsics = sizeof(_intrinsic) / sizeof(Intrinsic);

  // The order in this array must match up with HLSLBinaryOp
  const int _binaryOpPriority[] =
  {
      2, 1, //  &&, ||
      8, 8, //  +,  -
      9, 9, //  *,  /
      7, 7, //  <,  >,
      7, 7, //  <=, >=,
      6, 6, //  ==, !=
      5, 3, 4, // &, |, ^
  };


  // IC: I'm not sure this table is right, but any errors should be caught by the backend compiler.
  // Also, this is operator dependent. The type resulting from (float4 * float4x4) is not the same as (float4 + float4x4).
  // We should probably distinguish between component-wise operator and only allow same dimensions
  HLSLBaseType _binaryOpTypeLookup[HLSLBaseType_NumericCount][HLSLBaseType_NumericCount] =
  {
      {   // float
          HLSLBaseType_Float, HLSLBaseType_Float2, HLSLBaseType_Float3, HLSLBaseType_Float4,
          HLSLBaseType_Float2x4, HLSLBaseType_Float2x3, HLSLBaseType_Float2x2,
          HLSLBaseType_Float3x4, HLSLBaseType_Float3x3, HLSLBaseType_Float3x2,
          HLSLBaseType_Float4x4, HLSLBaseType_Float4x3, HLSLBaseType_Float4x2,
          HLSLBaseType_Float, HLSLBaseType_Float2, HLSLBaseType_Float3, HLSLBaseType_Float4,
          HLSLBaseType_Float, HLSLBaseType_Float2, HLSLBaseType_Float3, HLSLBaseType_Float4,
          HLSLBaseType_Float, HLSLBaseType_Float2, HLSLBaseType_Float3, HLSLBaseType_Float4
      },
      {   // float2
          HLSLBaseType_Float2, HLSLBaseType_Float2, HLSLBaseType_Float2, HLSLBaseType_Float2,
          HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown,
          HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown,
          HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown,
          HLSLBaseType_Float2, HLSLBaseType_Float2, HLSLBaseType_Float2, HLSLBaseType_Float2,
          HLSLBaseType_Float2, HLSLBaseType_Float2, HLSLBaseType_Float2, HLSLBaseType_Float2,
          HLSLBaseType_Float2, HLSLBaseType_Float2, HLSLBaseType_Float2, HLSLBaseType_Float2
      },
      {   // float3
          HLSLBaseType_Float3, HLSLBaseType_Float2, HLSLBaseType_Float3, HLSLBaseType_Float3,
          HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown,
          HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown,
          HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown,
          HLSLBaseType_Float3, HLSLBaseType_Float2, HLSLBaseType_Float3, HLSLBaseType_Float3,
          HLSLBaseType_Float3, HLSLBaseType_Float2, HLSLBaseType_Float3, HLSLBaseType_Float3,
          HLSLBaseType_Float3, HLSLBaseType_Float2, HLSLBaseType_Float3, HLSLBaseType_Float3
      },
      {   // float4
          HLSLBaseType_Float4, HLSLBaseType_Float2, HLSLBaseType_Float3, HLSLBaseType_Float4,
          HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown,
          HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown,
          HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown,
          HLSLBaseType_Float4, HLSLBaseType_Float2, HLSLBaseType_Float3, HLSLBaseType_Float4,
          HLSLBaseType_Float4, HLSLBaseType_Float2, HLSLBaseType_Float3, HLSLBaseType_Float4,
          HLSLBaseType_Float4, HLSLBaseType_Float2, HLSLBaseType_Float3, HLSLBaseType_Float4
      },
      {   // float2x4
          HLSLBaseType_Float2x4, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown,
          HLSLBaseType_Float2x4, HLSLBaseType_Unknown, HLSLBaseType_Unknown,
          HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown,
          HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown,
          HLSLBaseType_Float2x4, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown,
          HLSLBaseType_Float2x4, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown,
          HLSLBaseType_Float2x4, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown
      },
      {   // float2x3
          HLSLBaseType_Float2x3, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown,
          HLSLBaseType_Unknown, HLSLBaseType_Float2x3, HLSLBaseType_Unknown,
          HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown,
          HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown,
          HLSLBaseType_Float2x3, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown,
          HLSLBaseType_Float2x3, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown,
          HLSLBaseType_Float2x3, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown
      },
      {   // float2x2
          HLSLBaseType_Float2x2, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown,
          HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Float2x2,
          HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown,
          HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown,
          HLSLBaseType_Float2x2, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown,
          HLSLBaseType_Float2x2, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown,
          HLSLBaseType_Float2x2, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown
      },
      {   // float3x4
          HLSLBaseType_Float3x4, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown,
          HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown,
          HLSLBaseType_Float3x4, HLSLBaseType_Unknown, HLSLBaseType_Unknown,
          HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown,
          HLSLBaseType_Float3x4, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown,
          HLSLBaseType_Float3x4, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown,
          HLSLBaseType_Float3x4, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown
      },
      {   // float3x3
          HLSLBaseType_Float3x3, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown,
          HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown,
          HLSLBaseType_Unknown, HLSLBaseType_Float3x3, HLSLBaseType_Unknown,
          HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown,
          HLSLBaseType_Float3x3, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown,
          HLSLBaseType_Float3x3, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown,
          HLSLBaseType_Float3x3, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown
      },
      {   // float3x2
          HLSLBaseType_Float3x2, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown,
          HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown,
          HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Float3x2,
          HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown,
          HLSLBaseType_Float3x2, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown,
          HLSLBaseType_Float3x2, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown,
          HLSLBaseType_Float3x2, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown
      },
      {   // float4x4
          HLSLBaseType_Float4x4, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown,
          HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown,
          HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown,
          HLSLBaseType_Float4x4, HLSLBaseType_Unknown, HLSLBaseType_Unknown,
          HLSLBaseType_Float4x4, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown,
          HLSLBaseType_Float4x4, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown,
          HLSLBaseType_Float4x4, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown
      },
      {   // float4x3
          HLSLBaseType_Float4x3, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown,
          HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown,
          HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown,
          HLSLBaseType_Unknown, HLSLBaseType_Float4x3, HLSLBaseType_Unknown,
          HLSLBaseType_Float4x3, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown,
          HLSLBaseType_Float4x3, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown,
          HLSLBaseType_Float4x3, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown
      },
      {   // float4x2
          HLSLBaseType_Float4x2, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown,
          HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown,
          HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown,
          HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Float4x2,
          HLSLBaseType_Float4x2, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown,
          HLSLBaseType_Float4x2, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown,
          HLSLBaseType_Float4x2, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown
      },
       {   // bool
          HLSLBaseType_Float, HLSLBaseType_Float2, HLSLBaseType_Float3, HLSLBaseType_Float4,
          HLSLBaseType_Float2x4, HLSLBaseType_Float2x3, HLSLBaseType_Float2x2,
          HLSLBaseType_Float3x4, HLSLBaseType_Float3x3, HLSLBaseType_Float3x2,
          HLSLBaseType_Float4x4, HLSLBaseType_Float4x3, HLSLBaseType_Float4x2,
          HLSLBaseType_Int, HLSLBaseType_Int2, HLSLBaseType_Int3, HLSLBaseType_Int4,
          HLSLBaseType_Int, HLSLBaseType_Int2, HLSLBaseType_Int3, HLSLBaseType_Int4,
          HLSLBaseType_Uint, HLSLBaseType_Uint2, HLSLBaseType_Uint3, HLSLBaseType_Uint4
      },
      {   // bool2
          HLSLBaseType_Float2, HLSLBaseType_Float2, HLSLBaseType_Float3, HLSLBaseType_Float4,
          HLSLBaseType_Float2x4, HLSLBaseType_Float2x3, HLSLBaseType_Float2x2,
          HLSLBaseType_Float3x4, HLSLBaseType_Float3x3, HLSLBaseType_Float3x2,
          HLSLBaseType_Float4x4, HLSLBaseType_Float4x3, HLSLBaseType_Float4x2,
          HLSLBaseType_Int2, HLSLBaseType_Int2, HLSLBaseType_Int3, HLSLBaseType_Int4,
          HLSLBaseType_Int2, HLSLBaseType_Int2, HLSLBaseType_Int3, HLSLBaseType_Int4,
          HLSLBaseType_Uint2, HLSLBaseType_Uint2, HLSLBaseType_Uint3, HLSLBaseType_Uint4
      },
      {   // bool3
          HLSLBaseType_Float3, HLSLBaseType_Float3, HLSLBaseType_Float3, HLSLBaseType_Float4,
          HLSLBaseType_Float2x4, HLSLBaseType_Float2x3, HLSLBaseType_Float2x2,
          HLSLBaseType_Float3x4, HLSLBaseType_Float3x3, HLSLBaseType_Float3x2,
          HLSLBaseType_Float4x4, HLSLBaseType_Float4x3, HLSLBaseType_Float4x2,
          HLSLBaseType_Int3, HLSLBaseType_Int2, HLSLBaseType_Int3, HLSLBaseType_Int4,
          HLSLBaseType_Int3, HLSLBaseType_Int2, HLSLBaseType_Int3, HLSLBaseType_Int4,
          HLSLBaseType_Uint3, HLSLBaseType_Uint2, HLSLBaseType_Uint3, HLSLBaseType_Uint4
      },
      {   // bool4
          HLSLBaseType_Float, HLSLBaseType_Float2, HLSLBaseType_Float3, HLSLBaseType_Float4,
          HLSLBaseType_Float2x4, HLSLBaseType_Float2x3, HLSLBaseType_Float2x2,
          HLSLBaseType_Float3x4, HLSLBaseType_Float3x3, HLSLBaseType_Float3x2,
          HLSLBaseType_Float4x4, HLSLBaseType_Float4x3, HLSLBaseType_Float4x2,
          HLSLBaseType_Int, HLSLBaseType_Int2, HLSLBaseType_Int3, HLSLBaseType_Int4,
          HLSLBaseType_Int, HLSLBaseType_Int2, HLSLBaseType_Int3, HLSLBaseType_Int4,
          HLSLBaseType_Uint, HLSLBaseType_Uint2, HLSLBaseType_Uint3, HLSLBaseType_Uint4
      },
      {   // int
          HLSLBaseType_Float, HLSLBaseType_Float2, HLSLBaseType_Float3, HLSLBaseType_Float4,
          HLSLBaseType_Float2x4, HLSLBaseType_Float2x3, HLSLBaseType_Float2x2,
          HLSLBaseType_Float3x4, HLSLBaseType_Float3x3, HLSLBaseType_Float3x2,
          HLSLBaseType_Float4x4, HLSLBaseType_Float4x3, HLSLBaseType_Float4x2,
          HLSLBaseType_Int, HLSLBaseType_Int2, HLSLBaseType_Int2, HLSLBaseType_Int2,
          HLSLBaseType_Int, HLSLBaseType_Int2, HLSLBaseType_Int3, HLSLBaseType_Int4,
          HLSLBaseType_Uint, HLSLBaseType_Uint2, HLSLBaseType_Uint3, HLSLBaseType_Uint4
      },
      {   // int2
          HLSLBaseType_Float2, HLSLBaseType_Float2, HLSLBaseType_Float2, HLSLBaseType_Float2,
          HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown,
          HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown,
          HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown,
          HLSLBaseType_Int2, HLSLBaseType_Int2, HLSLBaseType_Int2, HLSLBaseType_Int2,
          HLSLBaseType_Int2, HLSLBaseType_Int2, HLSLBaseType_Int2, HLSLBaseType_Int2,
          HLSLBaseType_Uint2, HLSLBaseType_Uint2, HLSLBaseType_Uint2, HLSLBaseType_Uint2
      },
      {   // int3
          HLSLBaseType_Float3, HLSLBaseType_Float2, HLSLBaseType_Float3, HLSLBaseType_Float3,
          HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown,
          HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown,
          HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown,
          HLSLBaseType_Int3, HLSLBaseType_Int2, HLSLBaseType_Int3, HLSLBaseType_Int3,
          HLSLBaseType_Int3, HLSLBaseType_Int2, HLSLBaseType_Int3, HLSLBaseType_Int3,
          HLSLBaseType_Uint3, HLSLBaseType_Uint2, HLSLBaseType_Uint3, HLSLBaseType_Uint3
      },
      {   // int4
          HLSLBaseType_Float4, HLSLBaseType_Float2, HLSLBaseType_Float3, HLSLBaseType_Float4,
          HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown,
          HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown,
          HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown,
          HLSLBaseType_Int4, HLSLBaseType_Int2, HLSLBaseType_Int3, HLSLBaseType_Int4,
          HLSLBaseType_Int4, HLSLBaseType_Int2, HLSLBaseType_Int3, HLSLBaseType_Int4,
          HLSLBaseType_Uint4, HLSLBaseType_Uint2, HLSLBaseType_Uint3, HLSLBaseType_Uint4
      },
      {   // uint
          HLSLBaseType_Float, HLSLBaseType_Float2, HLSLBaseType_Float3, HLSLBaseType_Float4,
          HLSLBaseType_Float2x4, HLSLBaseType_Float2x3, HLSLBaseType_Float2x2,
          HLSLBaseType_Float3x4, HLSLBaseType_Float3x3, HLSLBaseType_Float3x2,
          HLSLBaseType_Float4x4, HLSLBaseType_Float4x3, HLSLBaseType_Float4x2,
          HLSLBaseType_Uint, HLSLBaseType_Uint2, HLSLBaseType_Uint3, HLSLBaseType_Uint4,
          HLSLBaseType_Uint, HLSLBaseType_Uint2, HLSLBaseType_Uint3, HLSLBaseType_Uint4,
          HLSLBaseType_Uint, HLSLBaseType_Uint2, HLSLBaseType_Uint3, HLSLBaseType_Uint4
      },
      {   // uint2
          HLSLBaseType_Float2, HLSLBaseType_Float2, HLSLBaseType_Float2, HLSLBaseType_Float2,
          HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown,
          HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown,
          HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown,
          HLSLBaseType_Uint2, HLSLBaseType_Uint2, HLSLBaseType_Uint2, HLSLBaseType_Uint2,
          HLSLBaseType_Uint2, HLSLBaseType_Uint2, HLSLBaseType_Uint2, HLSLBaseType_Uint2,
          HLSLBaseType_Uint2, HLSLBaseType_Uint2, HLSLBaseType_Uint2, HLSLBaseType_Uint2
      },
      {   // uint3
          HLSLBaseType_Float3, HLSLBaseType_Float2, HLSLBaseType_Float3, HLSLBaseType_Float3,
          HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown,
          HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown,
          HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown,
          HLSLBaseType_Uint3, HLSLBaseType_Uint2, HLSLBaseType_Uint3, HLSLBaseType_Uint3,
          HLSLBaseType_Uint3, HLSLBaseType_Uint2, HLSLBaseType_Uint3, HLSLBaseType_Uint3,
          HLSLBaseType_Uint3, HLSLBaseType_Uint2, HLSLBaseType_Uint3, HLSLBaseType_Uint3
      },
      {   // uint4
          HLSLBaseType_Float4, HLSLBaseType_Float2, HLSLBaseType_Float3, HLSLBaseType_Float4,
          HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown,
          HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown,
          HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown,
          HLSLBaseType_Uint4, HLSLBaseType_Uint2, HLSLBaseType_Uint3, HLSLBaseType_Uint4,
          HLSLBaseType_Uint4, HLSLBaseType_Uint2, HLSLBaseType_Uint3, HLSLBaseType_Uint4,
          HLSLBaseType_Uint4, HLSLBaseType_Uint2, HLSLBaseType_Uint3, HLSLBaseType_Uint4
      },
  };

  // Priority of the ? : operator.
  const int _conditionalOpPriority = 1;

  static const char* GetTypeName1(const HLSLType& type)
  {
    if (type.baseType == HLSLBaseType_UserDefined)
    {
      return type.typeName;
    }
    else
    {
      return baseTypeDescriptions[type.baseType].typeName;
    }
  }

  static const char* GetBinaryOpName(HLSLBinaryOp binaryOp)
  {
    switch (binaryOp)
    {
    case HLSLBinaryOp_And:          return "&&";
    case HLSLBinaryOp_Or:           return "||";
    case HLSLBinaryOp_Add:          return "+";
    case HLSLBinaryOp_Sub:          return "-";
    case HLSLBinaryOp_Mul:          return "*";
    case HLSLBinaryOp_Div:          return "/";
    case HLSLBinaryOp_Mod:          return "%";
    case HLSLBinaryOp_Less:         return "<";
    case HLSLBinaryOp_Greater:      return ">";
    case HLSLBinaryOp_LessEqual:    return "<=";
    case HLSLBinaryOp_GreaterEqual: return ">=";
    case HLSLBinaryOp_Equal:        return "==";
    case HLSLBinaryOp_NotEqual:     return "!=";
    case HLSLBinaryOp_BitAnd:       return "&";
    case HLSLBinaryOp_BitOr:        return "|";
    case HLSLBinaryOp_BitXor:       return "^";
    case HLSLBinaryOp_Assign:       return "=";
    case HLSLBinaryOp_AddAssign:    return "+=";
    case HLSLBinaryOp_SubAssign:    return "-=";
    case HLSLBinaryOp_MulAssign:    return "*=";
    case HLSLBinaryOp_DivAssign:    return "/=";
    default:
      ASSERT(0);
      return "???";
    }
  }


  /*
   * 1.) Match
   * 2.) Scalar dimension promotion (scalar -> vector/matrix)
   * 3.) Conversion
   * 4.) Conversion + scalar dimension promotion
   * 5.) Truncation (vector -> scalar or lower component vector, matrix -> scalar or lower component matrix)
   * 6.) Conversion + truncation
   */
  static int GetTypeCastRank(HLSLTree* tree, const HLSLType& srcType, const HLSLType& dstType)
  {
    /*if (srcType.array != dstType.array || srcType.arraySize != dstType.arraySize)
    {
        return -1;
    }*/

    if (srcType.array != dstType.array)
    {
      return -1;
    }

    if (srcType.array == true)
    {
      ASSERT(dstType.array == true);
      int srcArraySize = -1;
      int dstArraySize = -1;

      tree->GetExpressionValue(srcType.arraySize, srcArraySize);
      tree->GetExpressionValue(dstType.arraySize, dstArraySize);

      if (srcArraySize != dstArraySize) {
        return -1;
      }
    }

    if (srcType.baseType == HLSLBaseType_UserDefined && dstType.baseType == HLSLBaseType_UserDefined)
    {
      return strcmp(srcType.typeName, dstType.typeName) == 0 ? 0 : -1;
    }

    if (srcType.baseType == dstType.baseType)
    {
      if (IsSamplerType(srcType.baseType))
      {
        return srcType.samplerType == dstType.samplerType ? 0 : -1;
      }

      return 0;
    }

    const BaseTypeDescription& srcDesc = baseTypeDescriptions[srcType.baseType];
    const BaseTypeDescription& dstDesc = baseTypeDescriptions[dstType.baseType];
    if (srcDesc.numericType == NumericType_NaN || dstDesc.numericType == NumericType_NaN)
    {
      return -1;
    }

    // Result bits: T R R R P H C (T = truncation, R = conversion rank, P = dimension promotion, H = height promotion, C = component promotion)
    int result = _numberTypeRank[srcDesc.numericType][dstDesc.numericType] << 3;

    if (srcDesc.numDimensions == 0 && dstDesc.numDimensions > 0)
    {
      // Scalar dimension promotion
      result |= (1 << 2);
    }
    else if ((srcDesc.numDimensions == dstDesc.numDimensions && (srcDesc.numComponents > dstDesc.numComponents || srcDesc.height > dstDesc.height)) ||
      (srcDesc.numDimensions > 0 && dstDesc.numDimensions == 0))
    {
      // Truncation
      result |= (1 << 6);
    }
    else if (srcDesc.numDimensions != dstDesc.numDimensions)
    {
      // Can't convert
      return -1;
    }
    else if (srcDesc.height != dstDesc.height)
    {
      // Scalar height promotion
      result |= (1 << 1);
    }
    else if (srcDesc.numComponents != dstDesc.numComponents)
    {
      // Scalar components promotion
      result |= (1 << 0);
    }

    return result;

  }

  static bool GetFunctionCallCastRanks(HLSLTree* tree, const HLSLFunctionCall* call, const HLSLFunction* function, int* rankBuffer)
  {

    if (function == nullptr || function->numArguments < call->numArguments)
    {
      // Function not viable
      return false;
    }

    const HLSLExpression* expression = call->argument;
    const HLSLArgument* argument = function->argument;

    for (int i = 0; i < call->numArguments; ++i)
    {
      int rank = GetTypeCastRank(tree, expression->expressionType, argument->type);
      if (rank == -1)
      {
        return false;
      }

      rankBuffer[i] = rank;

      argument = argument->nextArgument;
      expression = expression->nextExpression;
    }

    for (int i = call->numArguments; i < function->numArguments; ++i)
    {
      if (argument->defaultValue == nullptr)
      {
        // Function not viable.
        return false;
      }
    }

    return true;

  }

  struct CompareRanks
  {
    bool operator() (const int& rank1, const int& rank2) { return rank1 > rank2; }
  };

  static CompareFunctionsResult CompareFunctions(HLSLTree* tree, const HLSLFunctionCall* call, const HLSLFunction* function1, const HLSLFunction* function2)
  {

#if defined WIN32 && !defined alloca
    int* function1Ranks = static_cast<int*>(_alloca(sizeof(int) * call->numArguments));
    int* function2Ranks = static_cast<int*>(_alloca(sizeof(int) * call->numArguments));
#else
    int* function1Ranks = static_cast<int*>(alloca(sizeof(int) * call->numArguments));
    int* function2Ranks = static_cast<int*>(alloca(sizeof(int) * call->numArguments));
#endif /** WIN32 */

    const bool function1Viable = GetFunctionCallCastRanks(tree, call, function1, function1Ranks);
    const bool function2Viable = GetFunctionCallCastRanks(tree, call, function2, function2Ranks);

    // Both functions have to be viable to be able to compare them
    if (!(function1Viable && function2Viable))
    {
      if (function1Viable)
      {
        return Function1Better;
      }
      else if (function2Viable)
      {
        return Function2Better;
      }
      else
      {
        return FunctionsEqual;
      }
    }

    std::sort(function1Ranks, function1Ranks + call->numArguments, CompareRanks());
    std::sort(function2Ranks, function2Ranks + call->numArguments, CompareRanks());

    for (int i = 0; i < call->numArguments; ++i)
    {
      if (function1Ranks[i] < function2Ranks[i])
      {
        return Function1Better;
      }
      else if (function2Ranks[i] < function1Ranks[i])
      {
        return Function2Better;
      }
    }

    //    // Dump matched function argument types
    //    std::string arg1;
    //    std::string arg2;
    //    const HLSLArgument* argument1 = function1->argument;
    //    const HLSLArgument* argument2 = function2->argument;
    //    for (int i = 0; i < call->numArguments; ++i)
    //    {
    //        arg1 += GetTypeName1(argument1->type);
    //        arg1 += " ";
    //        arg2 += GetTypeName1(argument2->type);
    //        arg2 += " ";

    //        argument1 = argument1->nextArgument;
    //        argument2 = argument2->nextArgument;
    //    }

    //    std::cout << "(" << arg1 << ") vs (" << arg2 << ")" << std::endl;


    return FunctionsEqual;

  }

  static bool GetBinaryOpResultType(HLSLBinaryOp binaryOp, const HLSLType& type1, const HLSLType& type2, HLSLType& result)
  {

    if (type1.baseType < HLSLBaseType_FirstNumeric || type1.baseType > HLSLBaseType_LastNumeric || type1.array ||
      type2.baseType < HLSLBaseType_FirstNumeric || type2.baseType > HLSLBaseType_LastNumeric || type2.array)
    {
      return false;
    }

    if (binaryOp == HLSLBinaryOp_BitAnd || binaryOp == HLSLBinaryOp_BitOr || binaryOp == HLSLBinaryOp_BitXor)
    {
      if (type1.baseType < HLSLBaseType_FirstInteger || type1.baseType > HLSLBaseType_LastInteger)
      {
        return false;
      }
    }

    switch (binaryOp)
    {
    case HLSLBinaryOp_And:
    case HLSLBinaryOp_Or:
    case HLSLBinaryOp_Less:
    case HLSLBinaryOp_Greater:
    case HLSLBinaryOp_LessEqual:
    case HLSLBinaryOp_GreaterEqual:
    case HLSLBinaryOp_Equal:
    case HLSLBinaryOp_NotEqual:
    {
      int numComponents = std::max(baseTypeDescriptions[type1.baseType].numComponents, baseTypeDescriptions[type2.baseType].numComponents);
      result.baseType = HLSLBaseType(HLSLBaseType_Bool + numComponents - 1);
      break;
    }
    case HLSLBinaryOp_Mod:
      result.baseType = HLSLBaseType_Int;
      break;

    default:
      result.baseType = _binaryOpTypeLookup[type1.baseType - HLSLBaseType_FirstNumeric][type2.baseType - HLSLBaseType_FirstNumeric];
      break;
    }

    result.typeName = nullptr;
    result.array = false;
    result.arraySize = nullptr;
    result.flags = (type1.flags & type2.flags) & HLSLTypeFlag_Const; // Propagate constness.

    return result.baseType != HLSLBaseType_Unknown;

  }

  HLSLParser::HLSLParser(Allocator* allocator, HLSLTree* tree) :
    m_userTypes(allocator),
    m_variables(allocator),
    m_functions(allocator),
    m_macros(allocator)
  {
    m_numGlobals = 0;
    m_tree = tree;
  }

  bool HLSLParser::Accept(int token)
  {
    if (m_tokenizer.GetToken() == token)
    {
      m_tokenizer.Next();
      return true;
    }
    return false;
  }

  bool HLSLParser::Accept(const char* token)
  {
    if (m_tokenizer.GetToken() == HLSLToken_Identifier && String_Equal(token, m_tokenizer.GetIdentifier()))
    {
      m_tokenizer.Next();
      return true;
    }
    return false;
  }

  bool HLSLParser::Expect(int token)
  {
    if (!Accept(token))
    {
      char want[s_maxIdentifier];
      m_tokenizer.GetTokenName(token, want);
      char near_[s_maxIdentifier];
      m_tokenizer.GetTokenName(near_);
      m_tokenizer.Error("Syntax error: expected '%s' near '%s'", want, near_);
      return false;
    }
    return true;
  }

  bool HLSLParser::Expect(const char* token)
  {
    if (!Accept(token))
    {
      const char* want = token;
      char near_[s_maxIdentifier];
      m_tokenizer.GetTokenName(near_);
      m_tokenizer.Error("Syntax error: expected '%s' near '%s'", want, near_);
      return false;
    }
    return true;
  }


  bool HLSLParser::AcceptIdentifier(const char*& identifier)
  {
    if (m_tokenizer.GetToken() == HLSLToken_Identifier)
    {
      identifier = m_tree->AddString(m_tokenizer.GetIdentifier());
      m_tokenizer.Next();
      return true;
    }
    return false;
  }

  bool HLSLParser::ExpectIdentifier(const char*& identifier)
  {
    if (!AcceptIdentifier(identifier))
    {
      char near_[s_maxIdentifier];
      m_tokenizer.GetTokenName(near_);
      m_tokenizer.Error("Syntax error: expected identifier near '%s'", near_);
      identifier = "";
      return false;
    }
    return true;
  }

  bool HLSLParser::AcceptFloat(float& value)
  {
    if (m_tokenizer.GetToken() == HLSLToken_FloatLiteral)
    {
      value = m_tokenizer.GetFloat();
      m_tokenizer.Next();
      return true;
    }
    return false;
  }

  bool HLSLParser::AcceptInt(int& value)
  {
    if (m_tokenizer.GetToken() == HLSLToken_IntLiteral)
    {
      value = m_tokenizer.GetInt();
      m_tokenizer.Next();
      return true;
    }
    return false;
  }

  bool HLSLParser::ParseTopLevel(HLSLStatement*& statement)
  {
    HLSLAttribute* attributes = nullptr;
    ParseAttributeBlock(attributes);

    int line = GetLineNumber();
    const char* fileName = GetFileName();

    HLSLType type;
    //HLSLBaseType type;
    //const char*  typeName = nullptr;
    //int          typeFlags = false;

    bool doesNotExpectSemicolon = false;

    if (Accept(HLSLToken_Struct))
    {
      // Struct declaration.

      const char* structName = nullptr;
      if (!ExpectIdentifier(structName))
      {
        return false;
      }
      if (FindUserDefinedType(structName) != nullptr)
      {
        m_tokenizer.Error("struct %s already defined", structName);
        return false;
      }

      if (!Expect('{'))
      {
        return false;
      }

      HLSLStruct* structure = m_tree->AddNode<HLSLStruct>(fileName, line);
      structure->name = structName;

      m_userTypes.PushBack(structure);

      HLSLStructField* lastField = nullptr;

      // Add the struct to our list of user defined types.
      while (!Accept('}'))
      {
        if (CheckForUnexpectedEndOfStream('}'))
        {
          return false;
        }
        HLSLStructField* field = nullptr;
        if (!ParseFieldDeclaration(field))
        {
          return false;
        }
        ASSERT(field != nullptr);
        if (lastField == nullptr)
        {
          structure->field = field;
        }
        else
        {
          lastField->nextField = field;
        }
        lastField = field;
      }

      statement = structure;
    }
    else if (Accept(HLSLToken_CBuffer) || Accept(HLSLToken_TBuffer))
    {
      // cbuffer/tbuffer declaration.

      HLSLBuffer* buffer = m_tree->AddNode<HLSLBuffer>(fileName, line);
      AcceptIdentifier(buffer->name);

      // Optional register assignment.
      if (Accept(':'))
      {
        if (!Expect(HLSLToken_Register) || !Expect('(') || !ExpectIdentifier(buffer->registerName) || !Expect(')'))
        {
          return false;
        }
        // TODO: Check that we aren't re-using a register.
      }

      // Fields.
      if (!Expect('{'))
      {
        return false;
      }
      HLSLDeclaration* lastField = nullptr;
      while (!Accept('}'))
      {
        if (CheckForUnexpectedEndOfStream('}'))
        {
          return false;
        }
        HLSLDeclaration* field = nullptr;
        if (!ParseDeclaration(field))
        {
          m_tokenizer.Error("Expected variable declaration");
          return false;
        }
        DeclareVariable(field->name, field->type);
        field->buffer = buffer;
        if (buffer->field == nullptr)
        {
          buffer->field = field;
        }
        else
        {
          lastField->nextStatement = field;
        }
        lastField = field;

        if (!Expect(';')) {
          return false;
        }
      }

      statement = buffer;
    }
    else if (AcceptType(true, type))
    {
      // Global declaration (uniform or function).
      const char* globalName = nullptr;
      if (!ExpectIdentifier(globalName))
      {
        return false;
      }

      if (Accept('('))
      {
        // Function declaration.

        HLSLFunction* function = m_tree->AddNode<HLSLFunction>(fileName, line);
        function->name = globalName;
        function->returnType.baseType = type.baseType;
        function->returnType.typeName = type.typeName;
        function->attributes = attributes;

        BeginScope();

        if (!ParseArgumentList(function->argument, function->numArguments, function->numOutputArguments))
        {
          return false;
        }

        const HLSLFunction* declaration = FindFunction(function);

        // Forward declaration
        if (Accept(';'))
        {
          // Add a function entry so that calls can refer to it
          if (!declaration)
          {
            m_functions.PushBack(function);
            statement = function;
          }
          EndScope();
          return true;
        }

        // Optional semantic.
        if (Accept(':') && !ExpectIdentifier(function->semantic))
        {
          return false;
        }

        if (declaration)
        {
          if (declaration->forward || declaration->statement)
          {
            m_tokenizer.Error("Duplicate function definition");
            return false;
          }

          const_cast<HLSLFunction*>(declaration)->forward = function;
        }
        else
        {
          m_functions.PushBack(function);
        }

        if (!Expect('{') || !ParseBlock(function->statement, function->returnType))
        {
          return false;
        }

        EndScope();

        // Note, no semi-colon at the end of a function declaration.
        statement = function;

        return true;
      }
      else
      {
        HLSLDeclaration* firstDeclaration = nullptr;
        HLSLDeclaration* lastDeclaration = nullptr;

        do {
          if (firstDeclaration != nullptr) {
            if (!ExpectIdentifier(globalName))
            {
              return false;
            }
          }

          // Uniform declaration.
          HLSLDeclaration* declaration = m_tree->AddNode<HLSLDeclaration>(fileName, line);
          declaration->name = globalName;
          declaration->type = type;

          // Handle array syntax.
          if (Accept('['))
          {
            if (!Accept(']'))
            {
              if (!ParseExpression(declaration->type.arraySize) || !Expect(']'))
              {
                return false;
              }
            }
            declaration->type.array = true;
          }

          // Handle optional register.
          if (Accept(':'))
          {
            // @@ Currently we support either a semantic or a register, but not both.
            if (AcceptIdentifier(declaration->semantic)) {
              // nothing
            }
            else if (!Expect(HLSLToken_Register) || !Expect('(') || !ExpectIdentifier(declaration->registerName) || !Expect(')'))
            {
              return false;
            }
          }

          DeclareVariable(globalName, declaration->type);

          if (!ParseDeclarationAssignment(declaration))
          {
            return false;
          }
          if (firstDeclaration == nullptr) firstDeclaration = declaration;
          if (lastDeclaration != nullptr) lastDeclaration->nextDeclaration = declaration;
          lastDeclaration = declaration;

        } while (Accept(','));

        statement = firstDeclaration;
      }
    }
    else if (ParseTechnique(statement)) {
      doesNotExpectSemicolon = true;
    }
    else if (ParsePipeline(statement)) {
      doesNotExpectSemicolon = true;
    }
    else if (ParseStage(statement)) {
      doesNotExpectSemicolon = true;
    }

    if (statement != nullptr) {
      statement->attributes = attributes;
    }

    return doesNotExpectSemicolon || Expect(';');
  }

  bool HLSLParser::ParseStatementOrBlock(HLSLStatement*& firstStatement, const HLSLType& returnType, bool scoped/*=true*/)
  {
    if (scoped)
    {
      BeginScope();
    }
    if (Accept('{'))
    {
      if (!ParseBlock(firstStatement, returnType))
      {
        return false;
      }
    }
    else
    {
      if (!ParseStatement(firstStatement, returnType))
      {
        return false;
      }
    }
    if (scoped)
    {
      EndScope();
    }
    return true;
  }

  bool HLSLParser::ParseBlock(HLSLStatement*& firstStatement, const HLSLType& returnType)
  {
    HLSLStatement* lastStatement = nullptr;
    while (!Accept('}'))
    {
      if (CheckForUnexpectedEndOfStream('}'))
      {
        return false;
      }
      HLSLStatement* statement = nullptr;
      if (!ParseStatement(statement, returnType))
      {
        return false;
      }
      if (statement != nullptr)
      {
        if (firstStatement == nullptr)
        {
          firstStatement = statement;
        }
        else
        {
          lastStatement->nextStatement = statement;
        }
        lastStatement = statement;
        while (lastStatement->nextStatement) lastStatement = lastStatement->nextStatement;
      }
    }
    return true;
  }

  bool HLSLParser::ParseStatement(HLSLStatement*& statement, const HLSLType& returnType)
  {
    const char* fileName = GetFileName();
    int         line = GetLineNumber();

    // Empty statements.
    if (Accept(';'))
    {
      return true;
    }

    HLSLAttribute* attributes = nullptr;
    ParseAttributeBlock(attributes);    // @@ Leak if not assigned to node? 

#if 0 // @@ Work in progress.
    // Static statements: @if only for now.
    if (Accept('@'))
    {
      if (Accept(HLSLToken_If))
      {
        //HLSLIfStatement* ifStatement = m_tree->AddNode<HLSLIfStatement>(fileName, line);
        //ifStatement->isStatic = true;
        //ifStatement->attributes = attributes;

        HLSLExpression* condition = nullptr;

        m_allowUndeclaredIdentifiers = true;    // Not really correct... better to push to stack?
        if (!Expect('(') || !ParseExpression(condition) || !Expect(')'))
        {
          m_allowUndeclaredIdentifiers = false;
          return false;
        }
        m_allowUndeclaredIdentifiers = false;

        if ((condition->expressionType.flags & HLSLTypeFlag_Const) == 0)
        {
          m_tokenizer.Error("Syntax error: @if condition is not constant");
          return false;
        }

        int conditionValue;
        if (!m_tree->GetExpressionValue(condition, conditionValue))
        {
          m_tokenizer.Error("Syntax error: Cannot evaluate @if condition");
          return false;
        }

        if (!conditionValue) m_disableSemanticValidation = true;

        HLSLStatement* ifStatements = nullptr;
        HLSLStatement* elseStatements = nullptr;

        if (!ParseStatementOrBlock(ifStatements, returnType, /*scoped=*/false))
        {
          m_disableSemanticValidation = false;
          return false;
        }
        if (Accept(HLSLToken_Else))
        {
          if (conditionValue) m_disableSemanticValidation = true;

          if (!ParseStatementOrBlock(elseStatements, returnType, /*scoped=*/false))
          {
            m_disableSemanticValidation = false;
            return false;
          }
        }
        m_disableSemanticValidation = false;

        if (conditionValue) statement = ifStatements;
        else statement = elseStatements;

        // @@ Free the pruned statements?

        return true;
      }
      else {
        m_tokenizer.Error("Syntax error: unexpected token '@'");
      }
    }
#endif

    // If statement.
    if (Accept(HLSLToken_If))
    {
      HLSLIfStatement* ifStatement = m_tree->AddNode<HLSLIfStatement>(fileName, line);
      ifStatement->attributes = attributes;
      if (!Expect('(') || !ParseExpression(ifStatement->condition) || !Expect(')'))
      {
        return false;
      }
      statement = ifStatement;
      if (!ParseStatementOrBlock(ifStatement->statement, returnType))
      {
        return false;
      }
      if (Accept(HLSLToken_Else))
      {
        return ParseStatementOrBlock(ifStatement->elseStatement, returnType);
      }
      return true;
    }

    // For statement.
    if (Accept(HLSLToken_For))
    {
      HLSLForStatement* forStatement = m_tree->AddNode<HLSLForStatement>(fileName, line);
      forStatement->attributes = attributes;
      if (!Expect('('))
      {
        return false;
      }
      BeginScope();
      if (!ParseDeclaration(forStatement->initialization) && !ParseExpression(forStatement->initializationWithoutType))
      {
        return false;
      }
      if (!Expect(';'))
      {
        return false;
      }
      ParseExpression(forStatement->condition);
      if (!Expect(';'))
      {
        return false;
      }
      ParseExpression(forStatement->increment);
      if (!Expect(')'))
      {
        return false;
      }
      statement = forStatement;
      if (!ParseStatementOrBlock(forStatement->statement, returnType))
      {
        return false;
      }
      EndScope();
      return true;
    }

    // While statement.
    if (Accept(HLSLToken_While))
    {
      HLSLWhileStatement* whileStatement = m_tree->AddNode<HLSLWhileStatement>(fileName, line);
      whileStatement->attributes = attributes;
      if (!Expect('(') || !ParseExpression(whileStatement->condition) || !Expect(')'))
      {
        return false;
      }
      statement = whileStatement;
      if (!ParseStatementOrBlock(whileStatement->statement, returnType))
      {
        return false;
      }
      return true;
    }


    if (attributes != nullptr)
    {
      // @@ Error. Unexpected attribute. We only support attributes associated to if and for statements.
    }

    // Block statement.
    if (Accept('{'))
    {
      HLSLBlockStatement* blockStatement = m_tree->AddNode<HLSLBlockStatement>(fileName, line);
      statement = blockStatement;
      BeginScope();
      bool success = ParseBlock(blockStatement->statement, returnType);
      EndScope();
      return success;
    }

    // Discard statement.
    if (Accept(HLSLToken_Discard))
    {
      HLSLDiscardStatement* discardStatement = m_tree->AddNode<HLSLDiscardStatement>(fileName, line);
      statement = discardStatement;
      return Expect(';');
    }

    // Break statement.
    if (Accept(HLSLToken_Break))
    {
      HLSLBreakStatement* breakStatement = m_tree->AddNode<HLSLBreakStatement>(fileName, line);
      statement = breakStatement;
      return Expect(';');
    }

    // Continue statement.
    if (Accept(HLSLToken_Continue))
    {
      HLSLContinueStatement* continueStatement = m_tree->AddNode<HLSLContinueStatement>(fileName, line);
      statement = continueStatement;
      return Expect(';');
    }

    // Return statement
    if (Accept(HLSLToken_Return))
    {
      HLSLReturnStatement* returnStatement = m_tree->AddNode<HLSLReturnStatement>(fileName, line);
      if (!Accept(';') && !ParseExpression(returnStatement->expression))
      {
        return false;
      }
      // Check that the return expression can be cast to the return type of the function.
      HLSLType voidType(HLSLBaseType_Void);
      if (!CheckTypeCast(returnStatement->expression ? returnStatement->expression->expressionType : voidType, returnType))
      {
        return false;
      }

      statement = returnStatement;
      return Expect(';');
    }

    HLSLDeclaration* declaration = nullptr;
    HLSLExpression* expression = nullptr;

    if (ParseDeclaration(declaration))
    {
      statement = declaration;
    }
    else if (ParseExpression(expression))
    {
      HLSLExpressionStatement* expressionStatement;
      expressionStatement = m_tree->AddNode<HLSLExpressionStatement>(fileName, line);
      expressionStatement->expression = expression;
      statement = expressionStatement;
    }

    // Some statements are separated by a colon instead of a semicolon
    if (Accept(',')) {
      return true;
    }

    return Expect(';');
  }


  // IC: This is only used in block statements, or within control flow statements. So, it doesn't support semantics or layout modifiers.
  // @@ We should add suport for semantics for inline input/output declarations.
  bool HLSLParser::ParseDeclaration(HLSLDeclaration*& declaration)
  {
    const char* fileName = GetFileName();
    int         line = GetLineNumber();

    HLSLType type;
    if (!AcceptType(/*allowVoid=*/false, type))
    {
      return false;
    }

    bool allowUnsizedArray = true;  // @@ Really?

    HLSLDeclaration* firstDeclaration = nullptr;
    HLSLDeclaration* lastDeclaration = nullptr;

    do {
      const char* name;
      if (!ExpectIdentifier(name))
      {
        // TODO: false means we didn't accept a declaration and we had an error!
        return false;
      }
      // Handle array syntax.
      if (Accept('['))
      {
        type.array = true;
        // Optionally allow no size to the specified for the array.
        if (Accept(']') && allowUnsizedArray)
        {
          return true;
        }
        if (!ParseExpression(type.arraySize) || !Expect(']'))
        {
          return false;
        }
      }

      HLSLDeclaration* declaration2 = m_tree->AddNode<HLSLDeclaration>(fileName, line);
      declaration2->type = type;
      declaration2->name = name;

      DeclareVariable(declaration2->name, declaration2->type);

      // Handle option assignment of the declared variables(s).
      if (!ParseDeclarationAssignment(declaration2)) {
        return false;
      }

      if (firstDeclaration == nullptr) firstDeclaration = declaration2;
      if (lastDeclaration != nullptr) lastDeclaration->nextDeclaration = declaration2;
      lastDeclaration = declaration2;

    } while (Accept(','));

    declaration = firstDeclaration;

    return true;
  }

  bool HLSLParser::ParseDeclarationAssignment(HLSLDeclaration* declaration)
  {
    if (Accept('='))
    {
      // Handle array initialization syntax.
      if (declaration->type.array)
      {
        int numValues = 0;
        if (!Expect('{') || !ParseExpressionList('}', true, declaration->assignment, numValues))
        {
          return false;
        }
      }
      else if (IsSamplerType(declaration->type.baseType))
      {
        if (!ParseSamplerState(declaration->assignment))
        {
          return false;
        }
      }
      else if (!ParseExpression(declaration->assignment))
      {
        return false;
      }
    }
    return true;
  }

  bool HLSLParser::ParseFieldDeclaration(HLSLStructField*& field)
  {
    field = m_tree->AddNode<HLSLStructField>(GetFileName(), GetLineNumber());
    if (!ExpectDeclaration(false, field->type, field->name))
    {
      return false;
    }
    // Handle optional semantics.
    if (Accept(':'))
    {
      if (!ExpectIdentifier(field->semantic))
      {
        return false;
      }
    }
    return Expect(';');
  }

  // @@ Add support for packoffset to general declarations.
  /*bool HLSLParser::ParseBufferFieldDeclaration(HLSLBufferField*& field)
  {
      field = m_tree->AddNode<HLSLBufferField>( GetFileName(), GetLineNumber() );
      if (AcceptDeclaration(false, field->type, field->name))
      {
          // Handle optional packoffset.
          if (Accept(':'))
          {
              if (!Expect("packoffset"))
              {
                  return false;
              }
              const char* constantName = nullptr;
              const char* swizzleMask  = nullptr;
              if (!Expect('(') || !ExpectIdentifier(constantName) || !Expect('.') || !ExpectIdentifier(swizzleMask) || !Expect(')'))
              {
                  return false;
              }
          }
          return Expect(';');
      }
      return false;
  }*/

  bool HLSLParser::CheckTypeCast(const HLSLType& srcType, const HLSLType& dstType)
  {
    if (GetTypeCastRank(m_tree, srcType, dstType) == -1)
    {
      const char* srcTypeName = GetTypeName1(srcType);
      const char* dstTypeName = GetTypeName1(dstType);
      m_tokenizer.Error("Cannot implicitly convert from '%s' to '%s'", srcTypeName, dstTypeName);
      return false;
    }
    return true;
  }

  bool HLSLParser::ParseExpression(HLSLExpression*& expression)
  {
    if (!ParseBinaryExpression(0, expression))
    {
      return false;
    }

    HLSLBinaryOp assignOp;
    if (AcceptAssign(assignOp))
    {
      HLSLExpression* expression2 = nullptr;
      if (!ParseExpression(expression2))
      {
        return false;
      }
      HLSLBinaryExpression* binaryExpression = m_tree->AddNode<HLSLBinaryExpression>(expression->fileName, expression->line);
      binaryExpression->binaryOp = assignOp;
      binaryExpression->expression1 = expression;
      binaryExpression->expression2 = expression2;
      // This type is not strictly correct, since the type should be a reference.
      // However, for our usage of the types it should be sufficient.
      binaryExpression->expressionType = expression->expressionType;

      if (!CheckTypeCast(expression2->expressionType, expression->expressionType))
      {
        const char* srcTypeName = GetTypeName1(expression2->expressionType);
        const char* dstTypeName = GetTypeName1(expression->expressionType);
        m_tokenizer.Error("Cannot implicitly convert from '%s' to '%s'", srcTypeName, dstTypeName);
        return false;
      }

      expression = binaryExpression;
    }

    return true;
  }

  bool HLSLParser::AcceptBinaryOperator(int priority, HLSLBinaryOp& binaryOp)
  {
    int token = m_tokenizer.GetToken();
    switch (token)
    {
    case HLSLToken_AndAnd:          binaryOp = HLSLBinaryOp_And;          break;
    case HLSLToken_BarBar:          binaryOp = HLSLBinaryOp_Or;           break;
    case '+':                       binaryOp = HLSLBinaryOp_Add;          break;
    case '-':                       binaryOp = HLSLBinaryOp_Sub;          break;
    case '*':                       binaryOp = HLSLBinaryOp_Mul;          break;
    case '/':                       binaryOp = HLSLBinaryOp_Div;          break;
    case '%':                       binaryOp = HLSLBinaryOp_Mod;          break;
    case '<':                       binaryOp = HLSLBinaryOp_Less;         break;
    case '>':                       binaryOp = HLSLBinaryOp_Greater;      break;
    case HLSLToken_LessEqual:       binaryOp = HLSLBinaryOp_LessEqual;    break;
    case HLSLToken_GreaterEqual:    binaryOp = HLSLBinaryOp_GreaterEqual; break;
    case HLSLToken_EqualEqual:      binaryOp = HLSLBinaryOp_Equal;        break;
    case HLSLToken_NotEqual:        binaryOp = HLSLBinaryOp_NotEqual;     break;
    case '&':                       binaryOp = HLSLBinaryOp_BitAnd;       break;
    case '|':                       binaryOp = HLSLBinaryOp_BitOr;        break;
    case '^':                       binaryOp = HLSLBinaryOp_BitXor;       break;
    default:
      return false;
    }
    if (_binaryOpPriority[binaryOp] > priority)
    {
      m_tokenizer.Next();
      return true;
    }
    return false;
  }

  bool HLSLParser::AcceptUnaryOperator(bool pre, HLSLUnaryOp& unaryOp)
  {
    int token = m_tokenizer.GetToken();
    if (token == HLSLToken_PlusPlus)
    {
      unaryOp = pre ? HLSLUnaryOp_PreIncrement : HLSLUnaryOp_PostIncrement;
    }
    else if (token == HLSLToken_MinusMinus)
    {
      unaryOp = pre ? HLSLUnaryOp_PreDecrement : HLSLUnaryOp_PostDecrement;
    }
    else if (pre && token == '-')
    {
      unaryOp = HLSLUnaryOp_Negative;
    }
    else if (pre && token == '+')
    {
      unaryOp = HLSLUnaryOp_Positive;
    }
    else if (pre && token == '!')
    {
      unaryOp = HLSLUnaryOp_Not;
    }
    else if (pre && token == '~')
    {
      unaryOp = HLSLUnaryOp_Not;
    }
    else
    {
      return false;
    }
    m_tokenizer.Next();
    return true;
  }

  bool HLSLParser::AcceptAssign(HLSLBinaryOp& binaryOp)
  {
    if (Accept('='))
    {
      binaryOp = HLSLBinaryOp_Assign;
    }
    else if (Accept(HLSLToken_PlusEqual))
    {
      binaryOp = HLSLBinaryOp_AddAssign;
    }
    else if (Accept(HLSLToken_MinusEqual))
    {
      binaryOp = HLSLBinaryOp_SubAssign;
    }
    else if (Accept(HLSLToken_TimesEqual))
    {
      binaryOp = HLSLBinaryOp_MulAssign;
    }
    else if (Accept(HLSLToken_DivideEqual))
    {
      binaryOp = HLSLBinaryOp_DivAssign;
    }
    else
    {
      return false;
    }
    return true;
  }

  bool HLSLParser::ParseBinaryExpression(int priority, HLSLExpression*& expression)
  {
    const char* fileName = GetFileName();
    int         line = GetLineNumber();

    char needsExpressionEndChar;

    if (!ParseTerminalExpression(expression, needsExpressionEndChar))
    {
      return false;
    }

    // reset priority cause openned parenthesis
    if (needsExpressionEndChar != 0)
      priority = 0;

    bool acceptBinaryOp = false;
    HLSLBinaryOp binaryOp;
    while (1)
    {
      if (acceptBinaryOp || AcceptBinaryOperator(priority, binaryOp))
      {
        acceptBinaryOp = false;
        HLSLExpression* expression2 = nullptr;
        ASSERT(binaryOp < sizeof(_binaryOpPriority) / sizeof(int));
        if (!ParseBinaryExpression(_binaryOpPriority[binaryOp], expression2))
        {
          return false;
        }
        HLSLBinaryExpression* binaryExpression = m_tree->AddNode<HLSLBinaryExpression>(fileName, line);
        binaryExpression->binaryOp = binaryOp;
        binaryExpression->expression1 = expression;
        binaryExpression->expression2 = expression2;
        if (!GetBinaryOpResultType(binaryOp, expression->expressionType, expression2->expressionType, binaryExpression->expressionType))
        {
          const char* typeName1 = GetTypeName1(binaryExpression->expression1->expressionType);
          const char* typeName2 = GetTypeName1(binaryExpression->expression2->expressionType);
          m_tokenizer.Error("binary '%s' : no global operator found which takes types '%s' and '%s' (or there is no acceptable conversion)",
            GetBinaryOpName(binaryOp), typeName1, typeName2);

          return false;
        }

        // Propagate constness.
        binaryExpression->expressionType.flags = (expression->expressionType.flags | expression2->expressionType.flags) & HLSLTypeFlag_Const;

        expression = binaryExpression;
      }
      else if (_conditionalOpPriority > priority && Accept('?'))
      {

        HLSLConditionalExpression* conditionalExpression = m_tree->AddNode<HLSLConditionalExpression>(fileName, line);
        conditionalExpression->condition = expression;

        HLSLExpression* expression1 = nullptr;
        HLSLExpression* expression2 = nullptr;
        if (!ParseBinaryExpression(_conditionalOpPriority, expression1) || !Expect(':') || !ParseBinaryExpression(_conditionalOpPriority, expression2))
        {
          return false;
        }

        // Make sure both cases have compatible types.
        if (GetTypeCastRank(m_tree, expression1->expressionType, expression2->expressionType) == -1)
        {
          const char* srcTypeName = GetTypeName1(expression2->expressionType);
          const char* dstTypeName = GetTypeName1(expression1->expressionType);
          m_tokenizer.Error("':' no possible conversion from '%s' to '%s'", srcTypeName, dstTypeName);
          return false;
        }

        conditionalExpression->trueExpression = expression1;
        conditionalExpression->falseExpression = expression2;
        conditionalExpression->expressionType = expression1->expressionType;

        expression = conditionalExpression;
      }
      else
      {
        break;
      }

      //  First: try if next is a binary op before exiting the loop
      if (AcceptBinaryOperator(priority, binaryOp))
      {
        acceptBinaryOp = true;
        continue;
      }

      if (needsExpressionEndChar != 0)
      {
        if (!Expect(needsExpressionEndChar))
          return false;
        needsExpressionEndChar = 0;
      }
    }

    return needsExpressionEndChar == 0 || Expect(needsExpressionEndChar);
  }

  bool HLSLParser::ParsePartialConstructor(HLSLExpression*& expression, HLSLBaseType type, const char* typeName)
  {
    const char* fileName = GetFileName();
    int         line = GetLineNumber();

    HLSLConstructorExpression* constructorExpression = m_tree->AddNode<HLSLConstructorExpression>(fileName, line);
    constructorExpression->type.baseType = type;
    constructorExpression->type.typeName = typeName;
    int numArguments = 0;
    if (!ParseExpressionList(')', false, constructorExpression->argument, numArguments))
    {
      return false;
    }
    constructorExpression->expressionType = constructorExpression->type;
    constructorExpression->expressionType.flags = HLSLTypeFlag_Const;
    expression = constructorExpression;
    return true;
  }

  bool HLSLParser::ParseTerminalExpression(HLSLExpression*& expression, char& needsExpressionEndChar)
  {
    const char* fileName = GetFileName();
    int         line = GetLineNumber();

    needsExpressionEndChar = 0;

    HLSLUnaryOp unaryOp;
    if (AcceptUnaryOperator(true, unaryOp))
    {
      HLSLUnaryExpression* unaryExpression = m_tree->AddNode<HLSLUnaryExpression>(fileName, line);
      unaryExpression->unaryOp = unaryOp;
      if (!ParseTerminalExpression(unaryExpression->expression, needsExpressionEndChar))
      {
        return false;
      }
      if (unaryOp == HLSLUnaryOp_BitNot)
      {
        if (unaryExpression->expression->expressionType.baseType < HLSLBaseType_FirstInteger ||
          unaryExpression->expression->expressionType.baseType > HLSLBaseType_LastInteger)
        {
          const char* typeName = GetTypeName1(unaryExpression->expression->expressionType);
          m_tokenizer.Error("unary '~' : no global operator found which takes type '%s' (or there is no acceptable conversion)", typeName);
          return false;
        }
      }
      if (unaryOp == HLSLUnaryOp_Not)
      {
        if (unaryExpression->expression->expressionType.baseType != HLSLBaseType_Bool)
        {
          // Insert a cast to bool
          HLSLCastingExpression* castingExpression = m_tree->AddNode<HLSLCastingExpression>(fileName, line);
          castingExpression->type.baseType = HLSLBaseType_Bool;
          castingExpression->expression = unaryExpression->expression;
          castingExpression->expressionType.baseType = HLSLBaseType_Bool;

          unaryExpression->expression = castingExpression;
          expression = unaryExpression;
        }

        unaryExpression->expressionType = HLSLType(HLSLBaseType_Bool);

        // Propagate constness.
        unaryExpression->expressionType.flags = unaryExpression->expression->expressionType.flags & HLSLTypeFlag_Const;
      }

      if (unaryOp == HLSLUnaryOp_Negative || unaryOp == HLSLUnaryOp_Positive)
      {
        if (unaryExpression->expression->expressionType.baseType <= HLSLBaseType_Bool4 &&
          unaryExpression->expression->expressionType.baseType >= HLSLBaseType_Bool)
        {
          // Insert a cast bool to int
          int numComponents = baseTypeDescriptions[unaryExpression->expression->expressionType.baseType].numComponents;
          HLSLBaseType baseType = HLSLBaseType(HLSLBaseType_Int + numComponents - 1);

          unaryExpression->expressionType = unaryExpression->expression->expressionType;

          HLSLCastingExpression* castingExpression = m_tree->AddNode<HLSLCastingExpression>(fileName, line);
          castingExpression->type.baseType = baseType;
          castingExpression->expression = unaryExpression->expression;
          castingExpression->expressionType.baseType = baseType;

          unaryExpression->expression = castingExpression;
          expression = unaryExpression;
        }
        else
        {
          unaryExpression->expressionType = unaryExpression->expression->expressionType;
        }
      }
      else
      {
        unaryExpression->expressionType = unaryExpression->expression->expressionType;
      }
      expression = unaryExpression;
      return true;
    }

    // Expressions inside parenthesis or casts.
    char expressionEndChar = 0;
    if (Accept('('))
    {
      expressionEndChar = ')';
    }
    else if (Accept('{'))
    {
      expressionEndChar = '}';
    }


    if (expressionEndChar != 0)
    {
      // Check for a casting operator.
      HLSLType type;
      if (AcceptType(false, type))
      {
        // This is actually a type constructor like (float2(...
        if (Accept('('))
        {
          needsExpressionEndChar = expressionEndChar;
          return ParsePartialConstructor(expression, type.baseType, type.typeName);
        }
        HLSLCastingExpression* castingExpression = m_tree->AddNode<HLSLCastingExpression>(fileName, line);
        castingExpression->type = type;
        expression = castingExpression;
        castingExpression->expressionType = type;
        return Expect(')') && ParseExpression(castingExpression->expression);
      }

      int numArguments = 0;
      if (!ParseExpressionList(expressionEndChar, false, expression, numArguments))
      {
        return false;
      }

      if (expressionEndChar == ')')
      {
        // Allows comma separated expression lists, HLSL compiler keeps the last one
        HLSLExpression* lastExpression = expression;
        while (lastExpression->nextExpression != nullptr) {
          lastExpression = lastExpression->nextExpression;
        }
      }
    }
    else
    {
      // Terminal values.
      float fValue = 0.0f;
      int   iValue = 0;

      if (AcceptFloat(fValue))
      {
        HLSLLiteralExpression* literalExpression = m_tree->AddNode<HLSLLiteralExpression>(fileName, line);
        literalExpression->type = HLSLBaseType_Float;
        literalExpression->fValue = fValue;
        literalExpression->expressionType.baseType = literalExpression->type;
        literalExpression->expressionType.flags = HLSLTypeFlag_Const;
        expression = literalExpression;
        return true;
      }
      else if (AcceptInt(iValue))
      {
        HLSLLiteralExpression* literalExpression = m_tree->AddNode<HLSLLiteralExpression>(fileName, line);
        literalExpression->type = HLSLBaseType_Int;
        literalExpression->iValue = iValue;
        literalExpression->expressionType.baseType = literalExpression->type;
        literalExpression->expressionType.flags = HLSLTypeFlag_Const;
        expression = literalExpression;
        return true;
      }
      else if (Accept(HLSLToken_True))
      {
        HLSLLiteralExpression* literalExpression = m_tree->AddNode<HLSLLiteralExpression>(fileName, line);
        literalExpression->type = HLSLBaseType_Bool;
        literalExpression->bValue = true;
        literalExpression->expressionType.baseType = literalExpression->type;
        literalExpression->expressionType.flags = HLSLTypeFlag_Const;
        expression = literalExpression;
        return true;
      }
      else if (Accept(HLSLToken_False))
      {
        HLSLLiteralExpression* literalExpression = m_tree->AddNode<HLSLLiteralExpression>(fileName, line);
        literalExpression->type = HLSLBaseType_Bool;
        literalExpression->bValue = false;
        literalExpression->expressionType.baseType = literalExpression->type;
        literalExpression->expressionType.flags = HLSLTypeFlag_Const;
        expression = literalExpression;
        return true;
      }

      // Type constructor.
      HLSLType type;
      if (AcceptType(/*allowVoid=*/false, type))
      {
        Expect('(');
        if (!ParsePartialConstructor(expression, type.baseType, type.typeName))
        {
          return false;
        }
      }
      else
      {
        HLSLIdentifierExpression* identifierExpression = m_tree->AddNode<HLSLIdentifierExpression>(fileName, line);
        if (!ExpectIdentifier(identifierExpression->name))
        {
          return false;
        }

        bool undeclaredIdentifier = false;

        const HLSLType* identifierType = FindVariable(identifierExpression->name, identifierExpression->global);
        if (identifierType != nullptr)
        {
          identifierExpression->expressionType = *identifierType;
        }
        else
        {
          if (GetIsFunction(identifierExpression->name))
          {
            // Functions are always global scope.
            identifierExpression->global = true;
          }
          else
          {
            undeclaredIdentifier = true;
          }
        }

        if (undeclaredIdentifier)
        {
          if (m_allowUndeclaredIdentifiers)
          {
            HLSLLiteralExpression* literalExpression = m_tree->AddNode<HLSLLiteralExpression>(fileName, line);
            literalExpression->bValue = false;
            literalExpression->type = HLSLBaseType_Bool;
            literalExpression->expressionType.baseType = literalExpression->type;
            literalExpression->expressionType.flags = HLSLTypeFlag_Const;
            expression = literalExpression;
          }
          else
          {
            m_tokenizer.Error("Undeclared identifier '%s'", identifierExpression->name);
            return false;
          }
        }
        else {
          expression = identifierExpression;
        }
      }
    }

    bool done = false;
    while (!done)
    {
      done = true;

      // Post fix unary operator
      HLSLUnaryOp unaryOp2;
      while (AcceptUnaryOperator(false, unaryOp2))
      {
        HLSLUnaryExpression* unaryExpression = m_tree->AddNode<HLSLUnaryExpression>(fileName, line);
        unaryExpression->unaryOp = unaryOp2;
        unaryExpression->expression = expression;
        unaryExpression->expressionType = unaryExpression->expression->expressionType;
        expression = unaryExpression;
        done = false;
      }

      // Member access operator.
      while (Accept('.'))
      {
        HLSLMemberAccess* memberAccess = m_tree->AddNode<HLSLMemberAccess>(fileName, line);
        memberAccess->object = expression;
        if (!ExpectIdentifier(memberAccess->field))
        {
          return false;
        }
        if (!GetMemberType(expression->expressionType, memberAccess))
        {
          m_tokenizer.Error("Couldn't access '%s'", memberAccess->field);
          return false;
        }
        expression = memberAccess;
        done = false;
      }

      // Handle array access.
      while (Accept('['))
      {
        HLSLArrayAccess* arrayAccess = m_tree->AddNode<HLSLArrayAccess>(fileName, line);
        arrayAccess->array = expression;
        if (!ParseExpression(arrayAccess->index) || !Expect(']'))
        {
          return false;
        }

        if (expression->expressionType.array)
        {
          arrayAccess->expressionType = expression->expressionType;
          arrayAccess->expressionType.array = false;
          arrayAccess->expressionType.arraySize = nullptr;
        }
        else
        {
          switch (expression->expressionType.baseType)
          {
          case HLSLBaseType_Float2:
          case HLSLBaseType_Float3:
          case HLSLBaseType_Float4:
            arrayAccess->expressionType.baseType = HLSLBaseType_Float;
            break;
          case HLSLBaseType_Float4x4:
          case HLSLBaseType_Float3x4:
          case HLSLBaseType_Float2x4:
            arrayAccess->expressionType.baseType = HLSLBaseType_Float4;
            break;
          case HLSLBaseType_Float4x3:
          case HLSLBaseType_Float3x3:
          case HLSLBaseType_Float2x3:
            arrayAccess->expressionType.baseType = HLSLBaseType_Float3;
            break;
          case HLSLBaseType_Float4x2:
          case HLSLBaseType_Float3x2:
          case HLSLBaseType_Float2x2:
            arrayAccess->expressionType.baseType = HLSLBaseType_Float2;
            break;
          case HLSLBaseType_Bool2:
          case HLSLBaseType_Bool3:
          case HLSLBaseType_Bool4:
            arrayAccess->expressionType.baseType = HLSLBaseType_Bool;
            break;
          case HLSLBaseType_Int2:
          case HLSLBaseType_Int3:
          case HLSLBaseType_Int4:
            arrayAccess->expressionType.baseType = HLSLBaseType_Int;
            break;
          case HLSLBaseType_Uint2:
          case HLSLBaseType_Uint3:
          case HLSLBaseType_Uint4:
            arrayAccess->expressionType.baseType = HLSLBaseType_Uint;
            break;
          default:
            m_tokenizer.Error("array, matrix, vector, or indexable object type expected in index expression");
            return false;
          }
        }

        expression = arrayAccess;
        done = false;
      }

      // Handle function calls. Note, HLSL functions aren't like C function
      // pointers -- we can only directly call on an identifier, not on an
      // expression.
      if (Accept('('))
      {
        HLSLFunctionCall* functionCall = m_tree->AddNode<HLSLFunctionCall>(fileName, line);
        done = false;
        if (!ParseExpressionList(')', false, functionCall->argument, functionCall->numArguments))
        {
          return false;
        }

        if (expression->nodeType != HLSLNodeType_IdentifierExpression)
        {
          m_tokenizer.Error("Expected function identifier");
          return false;
        }

        const HLSLIdentifierExpression* identifierExpression = static_cast<const HLSLIdentifierExpression*>(expression);
        const HLSLFunction* function = MatchFunctionCall(functionCall, identifierExpression->name);
        if (function == nullptr)
        {
          return false;
        }

        functionCall->function = function;
        functionCall->expressionType = function->returnType;
        expression = functionCall;
      }

    }
    return true;

  }

  bool HLSLParser::ParseExpressionList(int endToken, bool allowEmptyEnd, HLSLExpression*& firstExpression, int& numExpressions)
  {
    numExpressions = 0;
    HLSLExpression* lastExpression = nullptr;
    while (!Accept(endToken))
    {
      if (CheckForUnexpectedEndOfStream(endToken))
      {
        return false;
      }
      if (numExpressions > 0 && !Expect(','))
      {
        return false;
      }
      // It is acceptable for the final element in the initialization list to
      // have a trailing comma in some cases, like array initialization such as {1, 2, 3,}
      if (allowEmptyEnd && Accept(endToken))
      {
        break;
      }
      HLSLExpression* expression = nullptr;
      if (!ParseExpression(expression))
      {
        return false;
      }
      if (firstExpression == nullptr)
      {
        firstExpression = expression;
      }
      else
      {
        lastExpression->nextExpression = expression;
      }
      lastExpression = expression;
      ++numExpressions;
    }
    return true;
  }

  bool HLSLParser::ParseArgumentList(HLSLArgument*& firstArgument, int& numArguments, int& numOutputArguments)
  {
    const char* fileName = GetFileName();
    int         line = GetLineNumber();

    HLSLArgument* lastArgument = nullptr;
    numArguments = 0;

    while (!Accept(')'))
    {
      if (CheckForUnexpectedEndOfStream(')'))
      {
        return false;
      }
      if (numArguments > 0 && !Expect(','))
      {
        return false;
      }

      HLSLArgument* argument = m_tree->AddNode<HLSLArgument>(fileName, line);

      if (Accept(HLSLToken_Uniform)) { argument->modifier = HLSLArgumentModifier_Uniform; }
      else if (Accept(HLSLToken_In)) { argument->modifier = HLSLArgumentModifier_In; }
      else if (Accept(HLSLToken_Out)) { argument->modifier = HLSLArgumentModifier_Out; }
      else if (Accept(HLSLToken_InOut)) { argument->modifier = HLSLArgumentModifier_Inout; }
      else if (Accept(HLSLToken_Const)) { argument->modifier = HLSLArgumentModifier_Const; }

      if (!ExpectDeclaration(/*allowUnsizedArray=*/true, argument->type, argument->name))
      {
        return false;
      }

      DeclareVariable(argument->name, argument->type);

      // Optional semantic.
      if (Accept(':') && !ExpectIdentifier(argument->semantic))
      {
        return false;
      }

      if (Accept('=') && !ParseExpression(argument->defaultValue))
      {
        // @@ Print error!
        return false;
      }

      if (lastArgument != nullptr)
      {
        lastArgument->nextArgument = argument;
      }
      else
      {
        firstArgument = argument;
      }
      lastArgument = argument;

      ++numArguments;
      if (argument->modifier == HLSLArgumentModifier_Out || argument->modifier == HLSLArgumentModifier_Inout)
      {
        ++numOutputArguments;
      }
    }
    return true;
  }


  bool HLSLParser::ParseSamplerState(HLSLExpression*& expression)
  {
    if (!Expect(HLSLToken_SamplerState))
    {
      return false;
    }

    const char* fileName = GetFileName();
    int         line = GetLineNumber();

    HLSLSamplerState* samplerState = m_tree->AddNode<HLSLSamplerState>(fileName, line);

    if (!Expect('{'))
    {
      return false;
    }

    HLSLStateAssignment* lastStateAssignment = nullptr;

    // Parse state assignments.
    while (!Accept('}'))
    {
      if (CheckForUnexpectedEndOfStream('}'))
      {
        return false;
      }

      HLSLStateAssignment* stateAssignment = nullptr;
      if (!ParseStateAssignment(stateAssignment, /*isSamplerState=*/true, /*isPipeline=*/false))
      {
        return false;
      }
      ASSERT(stateAssignment != nullptr);
      if (lastStateAssignment == nullptr)
      {
        samplerState->stateAssignments = stateAssignment;
      }
      else
      {
        lastStateAssignment->nextStateAssignment = stateAssignment;
      }
      lastStateAssignment = stateAssignment;
      samplerState->numStateAssignments++;
    }

    expression = samplerState;
    return true;
  }

  bool HLSLParser::ParseTechnique(HLSLStatement*& statement)
  {
    if (!Accept(HLSLToken_Technique)) {
      return false;
    }

    const char* techniqueName = nullptr;
    if (!ExpectIdentifier(techniqueName))
    {
      return false;
    }

    if (!Expect('{'))
    {
      return false;
    }

    HLSLTechnique* technique = m_tree->AddNode<HLSLTechnique>(GetFileName(), GetLineNumber());
    technique->name = techniqueName;

    //m_techniques.PushBack(technique);

    HLSLPass* lastPass = nullptr;

    // Parse state assignments.
    while (!Accept('}'))
    {
      if (CheckForUnexpectedEndOfStream('}'))
      {
        return false;
      }

      HLSLPass* pass = nullptr;
      if (!ParsePass(pass))
      {
        return false;
      }
      ASSERT(pass != nullptr);
      if (lastPass == nullptr)
      {
        technique->passes = pass;
      }
      else
      {
        lastPass->nextPass = pass;
      }
      lastPass = pass;
      technique->numPasses++;
    }

    statement = technique;
    return true;
  }

  bool HLSLParser::ParsePass(HLSLPass*& pass)
  {
    if (!Accept(HLSLToken_Pass)) {
      return false;
    }

    // Optional pass name.
    const char* passName = nullptr;
    AcceptIdentifier(passName);

    if (!Expect('{'))
    {
      return false;
    }

    const char* fileName = GetFileName();
    int         line = GetLineNumber();

    pass = m_tree->AddNode<HLSLPass>(fileName, line);
    pass->name = passName;

    HLSLStateAssignment* lastStateAssignment = nullptr;

    // Parse state assignments.
    while (!Accept('}'))
    {
      if (CheckForUnexpectedEndOfStream('}'))
      {
        return false;
      }

      HLSLStateAssignment* stateAssignment = nullptr;
      if (!ParseStateAssignment(stateAssignment, /*isSamplerState=*/false, /*isPipelineState=*/false))
      {
        return false;
      }
      ASSERT(stateAssignment != nullptr);
      if (lastStateAssignment == nullptr)
      {
        pass->stateAssignments = stateAssignment;
      }
      else
      {
        lastStateAssignment->nextStateAssignment = stateAssignment;
      }
      lastStateAssignment = stateAssignment;
      pass->numStateAssignments++;
    }
    return true;
  }


  bool HLSLParser::ParsePipeline(HLSLStatement*& statement)
  {
    if (!Accept("pipeline")) {
      return false;
    }

    // Optional pipeline name.
    const char* pipelineName = nullptr;
    AcceptIdentifier(pipelineName);

    if (!Expect('{'))
    {
      return false;
    }

    HLSLPipeline* pipeline = m_tree->AddNode<HLSLPipeline>(GetFileName(), GetLineNumber());
    pipeline->name = pipelineName;

    HLSLStateAssignment* lastStateAssignment = nullptr;

    // Parse state assignments.
    while (!Accept('}'))
    {
      if (CheckForUnexpectedEndOfStream('}'))
      {
        return false;
      }

      HLSLStateAssignment* stateAssignment = nullptr;
      if (!ParseStateAssignment(stateAssignment, /*isSamplerState=*/false, /*isPipeline=*/true))
      {
        return false;
      }
      ASSERT(stateAssignment != nullptr);
      if (lastStateAssignment == nullptr)
      {
        pipeline->stateAssignments = stateAssignment;
      }
      else
      {
        lastStateAssignment->nextStateAssignment = stateAssignment;
      }
      lastStateAssignment = stateAssignment;
      pipeline->numStateAssignments++;
    }

    statement = pipeline;
    return true;
  }


  const EffectState* GetEffectState(const char* name, bool isSamplerState, bool isPipeline)
  {
    const EffectState* validStates = effectStates;
    int count = sizeof(effectStates) / sizeof(effectStates[0]);

    if (isPipeline)
    {
      validStates = pipelineStates;
      count = sizeof(pipelineStates) / sizeof(pipelineStates[0]);
    }

    if (isSamplerState)
    {
      validStates = samplerStates;
      count = sizeof(samplerStates) / sizeof(samplerStates[0]);
    }

    // Case insensitive comparison.
    for (int i = 0; i < count; i++)
    {
      if (String_EqualNoCase(name, validStates[i].name))
      {
        return &validStates[i];
      }
    }

    return nullptr;
  }

  static const EffectStateValue* GetStateValue(const char* name, const EffectState* state)
  {
    // Case insensitive comparison.
    for (int i = 0; ; i++)
    {
      const EffectStateValue& value = state->values[i];
      if (value.name == nullptr) break;

      if (String_EqualNoCase(name, value.name))
      {
        return &value;
      }
    }

    return nullptr;
  }


  bool HLSLParser::ParseStateName(bool isSamplerState, bool isPipelineState, const char*& name, const EffectState*& state)
  {
    if (m_tokenizer.GetToken() != HLSLToken_Identifier)
    {
      char near_[s_maxIdentifier];
      m_tokenizer.GetTokenName(near_);
      m_tokenizer.Error("Syntax error: expected identifier near '%s'", near_);
      return false;
    }

    state = GetEffectState(m_tokenizer.GetIdentifier(), isSamplerState, isPipelineState);
    if (state == nullptr)
    {
      m_tokenizer.Error("Syntax error: unexpected identifier '%s'", m_tokenizer.GetIdentifier());
      return false;
    }

    m_tokenizer.Next();
    return true;
  }

  bool HLSLParser::ParseColorMask(int& mask)
  {
    mask = 0;

    do {
      if (m_tokenizer.GetToken() == HLSLToken_IntLiteral) {
        mask |= m_tokenizer.GetInt();
      }
      else if (m_tokenizer.GetToken() == HLSLToken_Identifier) {
        const char* ident = m_tokenizer.GetIdentifier();
        const EffectStateValue* stateValue = colorMaskValues;
        while (stateValue->name != nullptr) {
          if (String_EqualNoCase(stateValue->name, ident)) {
            mask |= stateValue->value;
            break;
          }
          ++stateValue;
        }
      }
      else {
        return false;
      }
      m_tokenizer.Next();
    } while (Accept('|'));

    return true;
  }

  bool HLSLParser::ParseStateValue(const EffectState* state, HLSLStateAssignment* stateAssignment)
  {
    const bool expectsExpression = state->values == colorMaskValues;
    const bool expectsInteger = state->values == integerValues;
    const bool expectsFloat = state->values == floatValues;
    const bool expectsBoolean = state->values == booleanValues;

    if (!expectsExpression && !expectsInteger && !expectsFloat && !expectsBoolean)
    {
      if (m_tokenizer.GetToken() != HLSLToken_Identifier)
      {
        char near_[s_maxIdentifier];
        m_tokenizer.GetTokenName(near_);
        m_tokenizer.Error("Syntax error: expected identifier near '%s'", near_);
        stateAssignment->iValue = 0;
        return false;
      }
    }

    if (state->values == nullptr)
    {
      if (strcmp(m_tokenizer.GetIdentifier(), "compile") != 0)
      {
        m_tokenizer.Error("Syntax error: unexpected identifier '%s' expected compile statement", m_tokenizer.GetIdentifier());
        stateAssignment->iValue = 0;
        return false;
      }

      // @@ Parse profile name, function name, argument expressions.

      // Skip the rest of the compile statement.
      while (m_tokenizer.GetToken() != ';')
      {
        m_tokenizer.Next();
      }
    }
    else {
      if (expectsInteger)
      {
        if (!AcceptInt(stateAssignment->iValue))
        {
          m_tokenizer.Error("Syntax error: expected integer near '%s'", m_tokenizer.GetIdentifier());
          stateAssignment->iValue = 0;
          return false;
        }
      }
      else if (expectsFloat)
      {
        if (!AcceptFloat(stateAssignment->fValue))
        {
          m_tokenizer.Error("Syntax error: expected float near '%s'", m_tokenizer.GetIdentifier());
          stateAssignment->iValue = 0;
          return false;
        }
      }
      else if (expectsBoolean)
      {
        const EffectStateValue* stateValue = GetStateValue(m_tokenizer.GetIdentifier(), state);

        if (stateValue != nullptr)
        {
          stateAssignment->iValue = stateValue->value;

          m_tokenizer.Next();
        }
        else if (AcceptInt(stateAssignment->iValue))
        {
          stateAssignment->iValue = (stateAssignment->iValue != 0);
        }
        else {
          m_tokenizer.Error("Syntax error: expected bool near '%s'", m_tokenizer.GetIdentifier());
          stateAssignment->iValue = 0;
          return false;
        }
      }
      else if (expectsExpression)
      {
        if (!ParseColorMask(stateAssignment->iValue))
        {
          m_tokenizer.Error("Syntax error: expected color mask near '%s'", m_tokenizer.GetIdentifier());
          stateAssignment->iValue = 0;
          return false;
        }
      }
      else
      {
        // Expect one of the allowed values.
        const EffectStateValue* stateValue = GetStateValue(m_tokenizer.GetIdentifier(), state);

        if (stateValue == nullptr)
        {
          m_tokenizer.Error("Syntax error: unexpected value '%s' for state '%s'", m_tokenizer.GetIdentifier(), state->name);
          stateAssignment->iValue = 0;
          return false;
        }

        stateAssignment->iValue = stateValue->value;

        m_tokenizer.Next();
      }
    }

    return true;
  }

  bool HLSLParser::ParseStateAssignment(HLSLStateAssignment*& stateAssignment, bool isSamplerState, bool isPipelineState)
  {
    const char* fileName = GetFileName();
    int         line = GetLineNumber();

    stateAssignment = m_tree->AddNode<HLSLStateAssignment>(fileName, line);

    const EffectState* state;
    if (!ParseStateName(isSamplerState, isPipelineState, stateAssignment->stateName, state)) {
      return false;
    }

    //stateAssignment->name = m_tree->AddString(m_tokenizer.GetIdentifier());
    stateAssignment->stateName = state->name;
    stateAssignment->d3dRenderState = state->d3drs;

    if (!Expect('=')) {
      return false;
    }

    if (!ParseStateValue(state, stateAssignment)) {
      return false;
    }

    if (!Expect(';')) {
      return false;
    }

    return true;
  }


  bool HLSLParser::ParseAttributeList(HLSLAttribute*& firstAttribute)
  {
    const char* fileName = GetFileName();
    int         line = GetLineNumber();

    HLSLAttribute* lastAttribute = firstAttribute;
    do {
      const char* identifier = nullptr;
      if (!ExpectIdentifier(identifier)) {
        return false;
      }

      HLSLAttribute* attribute = m_tree->AddNode<HLSLAttribute>(fileName, line);

      if (strcmp(identifier, "unroll") == 0) attribute->attributeType = HLSLAttributeType_Unroll;
      else if (strcmp(identifier, "flatten") == 0) attribute->attributeType = HLSLAttributeType_Flatten;
      else if (strcmp(identifier, "branch") == 0) attribute->attributeType = HLSLAttributeType_Branch;
      else if (strcmp(identifier, "nofastmath") == 0) attribute->attributeType = HLSLAttributeType_NoFastMath;

      // @@ parse arguments, () not required if attribute constructor has no arguments.

      if (firstAttribute == nullptr)
      {
        firstAttribute = attribute;
      }
      else
      {
        lastAttribute->nextAttribute = attribute;
      }
      lastAttribute = attribute;

    } while (Accept(','));

    return true;
  }

  // Attributes can have all these forms:
  //   [A] statement;
  //   [A,B] statement;
  //   [A][B] statement;
  // These are not supported yet:
  //   [A] statement [B];
  //   [A()] statement;
  //   [A(a)] statement;
  bool HLSLParser::ParseAttributeBlock(HLSLAttribute*& attribute)
  {
    HLSLAttribute** lastAttribute = &attribute;
    while (*lastAttribute != nullptr) { lastAttribute = &(*lastAttribute)->nextAttribute; }

    if (!Accept('['))
    {
      return false;
    }

    // Parse list of attribute constructors.
    ParseAttributeList(*lastAttribute);

    if (!Expect(']'))
    {
      return false;
    }

    // Parse additional [] blocks.
    ParseAttributeBlock(*lastAttribute);

    return true;
  }

  bool HLSLParser::ParseStage(HLSLStatement*& statement)
  {
    if (!Accept("stage"))
    {
      return false;
    }

    // Required stage name.
    const char* stageName = nullptr;
    if (!ExpectIdentifier(stageName))
    {
      return false;
    }

    if (!Expect('{'))
    {
      return false;
    }

    HLSLStage* stage = m_tree->AddNode<HLSLStage>(GetFileName(), GetLineNumber());
    stage->name = stageName;

    BeginScope();

    HLSLType voidType(HLSLBaseType_Void);
    if (!Expect('{') || !ParseBlock(stage->statement, voidType))
    {
      return false;
    }

    EndScope();

    // @@ To finish the stage definition we should traverse the statements recursively (including function calls) and find all the input/output declarations.

    statement = stage;
    return true;
  }




  bool HLSLParser::Parse(const char* fileName, const char* buffer, size_t length)
  {
    HLSLRoot* root = m_tree->GetRoot();
    HLSLStatement* lastStatement = nullptr;

    m_tokenizer = HLSLTokenizer(fileName, buffer, length);
    while (!Accept(HLSLToken_EndOfStream))
    {
      HLSLStatement* statement = nullptr;
      if (!ParseTopLevel(statement))
      {
        return false;
      }
      if (statement != nullptr)
      {
        if (lastStatement == nullptr)
        {
          root->statement = statement;
        }
        else
        {
          lastStatement->nextStatement = statement;
        }
        lastStatement = statement;
        while (lastStatement->nextStatement) lastStatement = lastStatement->nextStatement;
      }
    }
    return true;
  }


  bool HLSLParser::ParsePreprocessorDefine()
  {
    int line = GetLineNumber();
    const char* fileName = GetFileName();

    // Define identifier parsing:
    // Don't use AcceptIdentifier to be able to check for whitespace after define name
    // This is required to separate define name and value as both could contains parenthesis
    const char* macroName = nullptr;
    m_tokenizer.Next();
    if (m_tokenizer.GetToken() == HLSLToken_Identifier)
    {
      macroName = m_tree->AddString(m_tokenizer.GetIdentifier());
    }
    else
    {
      char near_[s_maxIdentifier];
      m_tokenizer.GetTokenName(near_);
      m_tokenizer.Error("Syntax error: expected identifier near '%s'", near_);
      return false;
    }

    bool macroWithArguments = !m_tokenizer.NextIsWhitespace();

    // Macro declaration
    HLSLMacro* macro = m_tree->AddNode<HLSLMacro>(fileName, line);
    macro->name = macroName;
    m_macros.PushBack(macro);

    // Prepare next token
    m_tokenizer.Next();

    std::string value;
    if (macroWithArguments)
    {
      // Macro with arguments
      if (Accept('('))
      {
        unsigned int numArguments = 0;
        HLSLArgument* lastArgument = nullptr;

        while (!Accept(')'))
        {
          if (CheckForUnexpectedEndOfStream(')'))
          {
            return false;
          }
          if (numArguments > 0 && !Expect(','))
          {
            return false;
          }

          HLSLArgument* argument = m_tree->AddNode<HLSLArgument>(fileName, line);
          if (!ExpectIdentifier(argument->name))
          {
            return false;
          }

          if (lastArgument != nullptr)
          {
            lastArgument->nextArgument = argument;
          }
          else
          {
            macro->argument = argument;
          }
          lastArgument = argument;

          ++numArguments;
        }

        macro->numArguments = numArguments;
      }

      // Macro value
      while (m_tokenizer.GetToken() != HLSLToken_EndOfLine)
      {
        bool addOriginalSource = true;

        if (m_tokenizer.GetToken() == HLSLToken_Identifier)
        {
          // Search for macro attributes
          HLSLArgument* argument = macro->argument;
          unsigned argNum = 0;
          while (argument != nullptr)
          {
            if (String_Equal(argument->name, m_tokenizer.GetIdentifier()))
            {
              value.append("#" + std::to_string(argNum) + "#");
              addOriginalSource = false;
              break;
            }

            argument = argument->nextArgument;
            argNum++;
          }
        }

        if (addOriginalSource)
        {
          value.append(m_tokenizer.getLastPos(true),
            m_tokenizer.getCurrentPos() - m_tokenizer.getLastPos(true));
        }

        m_tokenizer.Next(false);
      }


    }
    else
    {
      // Macro value
      while (m_tokenizer.GetToken() != HLSLToken_EndOfLine)
      {
        const char* start = m_tokenizer.getLastPos(true);

        std::string valueAdd(start, m_tokenizer.getCurrentPos() - start);
        value.append(valueAdd);

        m_tokenizer.Next(false);
      }
    }

    // Remove extra parenthesis
    if (value[0] == '(')
    {
      value.erase(value.length() - 1, 1);
      value.erase(0, 1);
    }

    macro->value = value;

    return true;
  }


  bool HLSLParser::ApplyPreprocessor(const char* fileName, const char* buffer, size_t length,
    std::string& sourcePreprocessed) {

    m_tokenizer = HLSLTokenizer(fileName, buffer, length);

    // Fisrt pass, grab #define
    while (m_tokenizer.GetToken() != HLSLToken_EndOfStream)
    {
      if (m_tokenizer.GetToken() == HLSLToken_PreprocessorDefine)
      {
        ParsePreprocessorDefine();
      }

      m_tokenizer.Next();
    }


    // Second pass, propagate macros definitions across macros values
    int index = 0;
    while (index < m_macros.GetSize())
    {
      HLSLMacro* macro = m_macros[index];
      m_tokenizer = HLSLTokenizer(fileName, macro->value.c_str(), macro->value.length());
      std::string valueProcessed;

      while (m_tokenizer.GetToken() != HLSLToken_EndOfStream)
      {
        bool addOriginalSource = true;
        if (m_tokenizer.GetToken() == HLSLToken_Identifier)
        {
          HLSLMacro* matchedMacro = ProcessMacroFromIdentifier(valueProcessed, addOriginalSource);
          if (matchedMacro != nullptr)
          {
            // Check if macro is just an alias
            // macros value must be equal to matched macro name
            if (macro->value == matchedMacro->name)
            {
              macro->macroAliased = matchedMacro;
            }
          }
        }

        if (addOriginalSource)
        {
          valueProcessed.append(m_tokenizer.getLastPos(true),
            m_tokenizer.getCurrentPos() - m_tokenizer.getLastPos(true));
        }

        m_tokenizer.Next();
      }

      if (valueProcessed == "main")
      {
        valueProcessed = "sampler_fw_main";
      }

      if (valueProcessed != macro->value)
      {
        // Define value referenced another define, it was replaced
        // try again until there is no replacement
        macro->value = valueProcessed;

      }
      else
      {
        index++;
      }
    }

    // Third pass, macro aliases get original value and arguments
    index = 0;
    while (index < m_macros.GetSize())
    {
      HLSLMacro* macro = m_macros[index];

      if (macro->macroAliased != nullptr)
      {
        macro->argument = macro->macroAliased->argument;
        macro->numArguments = macro->macroAliased->numArguments;
        macro->value = macro->macroAliased->value;
      }

      index++;
    }

    // Fouth pass, search and replace preprocessor directives
    std::stack<bool> isCodeActive;
    isCodeActive.push(true);
    m_tokenizer = HLSLTokenizer(fileName, buffer, length);
    sourcePreprocessed.clear();
    while (m_tokenizer.GetToken() != HLSLToken_EndOfStream)
    {
      bool addOriginalSource = true;

      if (m_tokenizer.GetToken() == HLSLToken_PreprocessorIf)
      {
        while (m_tokenizer.GetToken() != HLSLToken_IntLiteral && m_tokenizer.GetToken() != HLSLToken_EndOfLine)
        {
          m_tokenizer.Next(false);
        }

        if (m_tokenizer.GetToken() == HLSLToken_IntLiteral)
        {
          isCodeActive.push(m_tokenizer.GetInt() != 0);
        }
        else
        {
          m_tokenizer.Error("#if evaluation failed: not an integer");
          return false;
        }
        addOriginalSource = false;
      }
      else if (m_tokenizer.GetToken() == HLSLToken_PreprocessorElse)
      {
        // Invert stack state
        bool state = isCodeActive.top();
        isCodeActive.pop();
        isCodeActive.push(!state);
        addOriginalSource = false;
      }
      else if (m_tokenizer.GetToken() == HLSLToken_PreprocessorEndif)
      {
        isCodeActive.pop();
        addOriginalSource = false;
      }
      else if (m_tokenizer.GetToken() == HLSLToken_PreprocessorDefine)
      {
        // Skip macros definition
        while (m_tokenizer.GetToken() != HLSLToken_EndOfLine)
        {
          m_tokenizer.Next(false);
        }

        addOriginalSource = false;
      }
      else if (m_tokenizer.GetToken() == HLSLToken_Identifier && isCodeActive.top())
      {
        ProcessMacroFromIdentifier(sourcePreprocessed, addOriginalSource);
      }
      else if (!isCodeActive.top())
      {
        addOriginalSource = false;
      }

      if (addOriginalSource)
      {
        sourcePreprocessed.append(m_tokenizer.getLastPos(false),
          m_tokenizer.getCurrentPos() - m_tokenizer.getLastPos(false));
      }

      m_tokenizer.Next();
    }


    return isCodeActive.size() == 1;
  }


  HLSLMacro* HLSLParser::ProcessMacroFromIdentifier(std::string& sourcePreprocessed, bool& addOriginalSource)
  {
    // Search a define matching
    for (int i = m_macros.GetSize() - 1; i >= 0; --i)
    {
      if (String_Equal(m_macros[i]->name, m_tokenizer.GetIdentifier()))
      {
        if (m_macros[i]->argument == nullptr)
        {
          // Macro without arguments
          sourcePreprocessed.append("(");
          sourcePreprocessed.append(m_macros[i]->value);
          sourcePreprocessed.append(")");
          addOriginalSource = false;
        }
        else
        {
          // Macro with arguments
          const char* savePos = m_tokenizer.getLastPos(false);

          m_tokenizer.Next();

          if (ProcessMacroArguments(m_macros[i], sourcePreprocessed))
          {
            // Macro replaced
            addOriginalSource = false;
          }
          else
          {
            // Macro signatures does not match: writing back readed parts
            sourcePreprocessed.append(savePos,
              m_tokenizer.getLastPos(false) - savePos);

            m_tokenizer.ReturnToPos(m_tokenizer.getLastPos(false));

            addOriginalSource = true;
          }
        }

        return m_macros[i];
      }
    }

    return nullptr;
  }



  bool HLSLParser::ProcessMacroArguments(HLSLMacro* macro, std::string& sourcePreprocessed)
  {
    unsigned scopeLevel = 0;
    std::vector<std::string> argumentsValues;
    std::string argValue;
    bool firstToken = true;

    // Parse arguments values
    while (m_tokenizer.GetToken() != HLSLToken_EndOfStream) {
      bool addToValue = true;

      if (firstToken && m_tokenizer.GetToken() != '(')
      {
        break;
      }
      else if (m_tokenizer.GetToken() == '(')
      {
        scopeLevel++;
        if (scopeLevel == 1)
        {
          addToValue = false;
        }
      }
      else if (m_tokenizer.GetToken() == ')')
      {
        scopeLevel--;
        if (scopeLevel == 0)
        {
          argumentsValues.push_back(argValue);
          break;
        }
      }
      else if (m_tokenizer.GetToken() == ',')
      {
        if (scopeLevel == 1)
        {
          argumentsValues.push_back(argValue);
          argValue.clear();
          addToValue = false;
        }
      }
      else if (m_tokenizer.GetToken() == HLSLToken_Identifier)
      {
        ProcessMacroFromIdentifier(argValue, addToValue);
      }

      if (addToValue)
      {
        argValue.append(m_tokenizer.getLastPos(false),
          m_tokenizer.getCurrentPos() - m_tokenizer.getLastPos(false));
      }

      m_tokenizer.Next();
      firstToken = false;
    }

    // Arguments count does not match
    if (argumentsValues.size() != macro->numArguments) {
      return false;
    }

    // Surround macro definition with parenthesis
    sourcePreprocessed.append("(");

    // Write arguments value
    unsigned index = 0;
    std::string arg;
    bool argFound = false;
    while (index < macro->value.length())
    {
      if (macro->value[index] == '#' && !argFound)
      {
        argFound = true;
        arg.clear();
      }
      else if (macro->value[index] == '#' && argFound)
      {
        unsigned indexArg = std::stoi(arg);
        if (indexArg < argumentsValues.size())
        {
          sourcePreprocessed.append(argumentsValues[indexArg]);
        }
        argFound = false;
      }
      else if (argFound)
      {
        arg += macro->value[index];
      }
      else
      {
        sourcePreprocessed += macro->value[index];
      }

      index++;
    }

    // Surround macro definition with parenthesis
    sourcePreprocessed.append(")");

    return true;
  }


  bool HLSLParser::AcceptTypeModifier(int& flags)
  {
    if (Accept(HLSLToken_Const))
    {
      flags |= HLSLTypeFlag_Const;
      return true;
    }
    else if (Accept(HLSLToken_Static))
    {
      flags |= HLSLTypeFlag_Static;
      return true;
    }
    else if (Accept(HLSLToken_Uniform))
    {
      flags |= HLSLTypeFlag_Uniform;
      return true;
    }
    else if (Accept(HLSLToken_Inline))
    {
      //flags |= HLSLTypeFlag_Uniform;      // @@ Ignored. In HLSL all functions are inline.
      return true;
    }
    /*else if (Accept("in"))
    {
        flags |= HLSLTypeFlag_Input;
        return true;
    }
    else if (Accept("out"))
    {
        flags |= HLSLTypeFlag_Output;
        return true;
    }*/

    // Not an usage keyword.
    return false;
  }

  bool HLSLParser::AcceptInterpolationModifier(int& flags)
  {
    if (Accept("linear"))
    {
      flags |= HLSLTypeFlag_Linear;
      return true;
    }
    else if (Accept("centroid"))
    {
      flags |= HLSLTypeFlag_Centroid;
      return true;
    }
    else if (Accept("nointerpolation"))
    {
      flags |= HLSLTypeFlag_NoInterpolation;
      return true;
    }
    else if (Accept("noperspective"))
    {
      flags |= HLSLTypeFlag_NoPerspective;
      return true;
    }
    else if (Accept("sample"))
    {
      flags |= HLSLTypeFlag_Sample;
      return true;
    }

    return false;
  }


  bool HLSLParser::AcceptType(bool allowVoid, HLSLType& type/*, bool acceptFlags*/)
  {
    //if (type.flags != nullptr)
    {
      type.flags = 0;
      while (AcceptTypeModifier(type.flags) || AcceptInterpolationModifier(type.flags)) {}
    }

    int token = m_tokenizer.GetToken();

    // Check built-in types.
    type.baseType = HLSLBaseType_Void;
    switch (token)
    {
    case HLSLToken_Float:
    case HLSLToken_Float1:
    case HLSLToken_Float1x1:
    case HLSLToken_Half:
    case HLSLToken_Half1:
    case HLSLToken_Half1x1:
    case HLSLToken_Double:
    case HLSLToken_Double1:
    case HLSLToken_Double1x1:
      type.baseType = HLSLBaseType_Float;
      break;
    case HLSLToken_Float2:
    case HLSLToken_Float2x1:
    case HLSLToken_Half2:
    case HLSLToken_Half2x1:
    case HLSLToken_Double2:
    case HLSLToken_Double2x1:
      type.baseType = HLSLBaseType_Float2;
      break;
    case HLSLToken_Float3:
    case HLSLToken_Float3x1:
    case HLSLToken_Half3:
    case HLSLToken_Half3x1:
    case HLSLToken_Double3:
    case HLSLToken_Double3x1:
      type.baseType = HLSLBaseType_Float3;
      break;
    case HLSLToken_Float4:
    case HLSLToken_Float4x1:
    case HLSLToken_Half4:
    case HLSLToken_Half4x1:
    case HLSLToken_Double4:
    case HLSLToken_Double4x1:
      type.baseType = HLSLBaseType_Float4;
      break;
    case HLSLToken_Float2x4:
    case HLSLToken_Half2x4:
    case HLSLToken_Double2x4:
      type.baseType = HLSLBaseType_Float2x4;
      break;
    case HLSLToken_Float2x3:
    case HLSLToken_Half2x3:
    case HLSLToken_Double2x3:
      type.baseType = HLSLBaseType_Float2x3;
      break;
    case HLSLToken_Float2x2:
    case HLSLToken_Half2x2:
    case HLSLToken_Double2x2:
      type.baseType = HLSLBaseType_Float2x2;
      break;
    case HLSLToken_Float3x4:
    case HLSLToken_Half3x4:
    case HLSLToken_Double3x4:
      type.baseType = HLSLBaseType_Float3x4;
      break;
    case HLSLToken_Float3x3:
    case HLSLToken_Half3x3:
    case HLSLToken_Double3x3:
      type.baseType = HLSLBaseType_Float3x3;
      break;
    case HLSLToken_Float3x2:
    case HLSLToken_Half3x2:
    case HLSLToken_Double3x2:
      type.baseType = HLSLBaseType_Float3x2;
      break;
    case HLSLToken_Float4x4:
    case HLSLToken_Half4x4:
    case HLSLToken_Double4x4:
      type.baseType = HLSLBaseType_Float4x4;
      break;
    case HLSLToken_Float4x3:
    case HLSLToken_Half4x3:
    case HLSLToken_Double4x3:
      type.baseType = HLSLBaseType_Float4x3;
      break;
    case HLSLToken_Float4x2:
    case HLSLToken_Half4x2:
    case HLSLToken_Double4x2:
      type.baseType = HLSLBaseType_Float4x2;
      break;
    case HLSLToken_Bool:
      type.baseType = HLSLBaseType_Bool;
      break;
    case HLSLToken_Bool2:
      type.baseType = HLSLBaseType_Bool2;
      break;
    case HLSLToken_Bool3:
      type.baseType = HLSLBaseType_Bool3;
      break;
    case HLSLToken_Bool4:
      type.baseType = HLSLBaseType_Bool4;
      break;
    case HLSLToken_Int:
      type.baseType = HLSLBaseType_Int;
      break;
    case HLSLToken_Int2:
      type.baseType = HLSLBaseType_Int2;
      break;
    case HLSLToken_Int3:
      type.baseType = HLSLBaseType_Int3;
      break;
    case HLSLToken_Int4:
      type.baseType = HLSLBaseType_Int4;
      break;
    case HLSLToken_Uint:
      type.baseType = HLSLBaseType_Uint;
      break;
    case HLSLToken_Uint2:
      type.baseType = HLSLBaseType_Uint2;
      break;
    case HLSLToken_Uint3:
      type.baseType = HLSLBaseType_Uint3;
      break;
    case HLSLToken_Uint4:
      type.baseType = HLSLBaseType_Uint4;
      break;
    case HLSLToken_Texture:
      type.baseType = HLSLBaseType_Texture;
      break;
    case HLSLToken_Sampler:
      type.baseType = HLSLBaseType_Sampler2D;  // @@ IC: For now we assume that generic samplers are always sampler2D
      break;
    case HLSLToken_Sampler2D:
      type.baseType = HLSLBaseType_Sampler2D;
      break;
    case HLSLToken_Sampler3D:
      type.baseType = HLSLBaseType_Sampler3D;
      break;
    case HLSLToken_SamplerCube:
      type.baseType = HLSLBaseType_SamplerCube;
      break;
    case HLSLToken_Sampler2DShadow:
      type.baseType = HLSLBaseType_Sampler2DShadow;
      break;
    case HLSLToken_Sampler2DMS:
      type.baseType = HLSLBaseType_Sampler2DMS;
      break;
    case HLSLToken_Sampler2DArray:
      type.baseType = HLSLBaseType_Sampler2DArray;
      break;
    }
    if (type.baseType != HLSLBaseType_Void)
    {
      m_tokenizer.Next();

      if (IsSamplerType(type.baseType))
      {
        // Parse optional sampler type.
        if (Accept('<'))
        {
          int token2 = m_tokenizer.GetToken();
          if (token2 == HLSLToken_Float || token2 == HLSLToken_Float1)
          {
            type.samplerType = HLSLBaseType_Float;
          }
          else
          {
            m_tokenizer.Error("Expected half or float.");
            return false;
          }
          m_tokenizer.Next();

          if (!Expect('>'))
          {
            m_tokenizer.Error("Syntax error: '>' expected for sampler type");
            return false;
          }
        }
      }
      return true;
    }

    if (allowVoid && Accept(HLSLToken_Void))
    {
      type.baseType = HLSLBaseType_Void;
      return true;
    }
    if (token == HLSLToken_Identifier)
    {
      const char* identifier = m_tree->AddString(m_tokenizer.GetIdentifier());
      if (FindUserDefinedType(identifier) != nullptr)
      {
        m_tokenizer.Next();
        type.baseType = HLSLBaseType_UserDefined;
        type.typeName = identifier;
        return true;
      }
    }
    return false;
  }

  bool HLSLParser::ExpectType(bool allowVoid, HLSLType& type)
  {
    if (!AcceptType(allowVoid, type))
    {
      m_tokenizer.Error("Expected type");
      return false;
    }
    return true;
  }

  bool HLSLParser::AcceptDeclaration(bool allowUnsizedArray, HLSLType& type, const char*& name)
  {
    if (!AcceptType(/*allowVoid=*/false, type))
    {
      return false;
    }

    if (!ExpectIdentifier(name))
    {
      // TODO: false means we didn't accept a declaration and we had an error!
      return false;
    }
    // Handle array syntax.
    if (Accept('['))
    {
      type.array = true;
      // Optionally allow no size to the specified for the array.
      if (Accept(']') && allowUnsizedArray)
      {
        return true;
      }
      if (!ParseExpression(type.arraySize) || !Expect(']'))
      {
        return false;
      }
    }
    return true;
  }

  bool HLSLParser::ExpectDeclaration(bool allowUnsizedArray, HLSLType& type, const char*& name)
  {
    if (!AcceptDeclaration(allowUnsizedArray, type, name))
    {
      m_tokenizer.Error("Expected declaration");
      return false;
    }
    return true;
  }

  const HLSLStruct* HLSLParser::FindUserDefinedType(const char* name) const
  {
    // Pointer comparison is sufficient for strings since they exist in the
    // string pool.
    for (int i = 0; i < m_userTypes.GetSize(); ++i)
    {
      if (m_userTypes[i]->name == name)
      {
        return m_userTypes[i];
      }
    }
    return nullptr;
  }

  bool HLSLParser::CheckForUnexpectedEndOfStream(int endToken)
  {
    if (Accept(HLSLToken_EndOfStream))
    {
      char what[s_maxIdentifier];
      m_tokenizer.GetTokenName(endToken, what);
      m_tokenizer.Error("Unexpected end of file while looking for '%s'", what);
      return true;
    }
    return false;
  }

  int HLSLParser::GetLineNumber() const
  {
    return m_tokenizer.GetLineNumber();
  }

  const char* HLSLParser::GetFileName()
  {
    return m_tree->AddString(m_tokenizer.GetFileName());
  }

  void HLSLParser::BeginScope()
  {
    // Use nullptr as a sentinel that indices a new scope level.
    Variable& variable = m_variables.PushBackNew();
    variable.name = nullptr;
  }

  void HLSLParser::EndScope()
  {
    int numVariables = m_variables.GetSize() - 1;
    while (m_variables[numVariables].name != nullptr)
    {
      --numVariables;
      ASSERT(numVariables >= 0);
    }
    m_variables.Resize(numVariables);
  }

  const HLSLType* HLSLParser::FindVariable(const char* name, bool& global) const
  {
    for (int i = m_variables.GetSize() - 1; i >= 0; --i)
    {
      if (m_variables[i].name == name)
      {
        global = (i < m_numGlobals);
        return &m_variables[i].type;
      }
    }
    return nullptr;
  }

  const HLSLFunction* HLSLParser::FindFunction(const char* name) const
  {
    for (int i = 0; i < m_functions.GetSize(); ++i)
    {
      if (m_functions[i]->name == name)
      {
        return m_functions[i];
      }
    }
    return nullptr;
  }

  static bool AreTypesEqual(HLSLTree* tree, const HLSLType& lhs, const HLSLType& rhs)
  {
    return GetTypeCastRank(tree, lhs, rhs) == 0;
  }

  static bool AreArgumentListsEqual(HLSLTree* tree, HLSLArgument* lhs, HLSLArgument* rhs)
  {
    while (lhs && rhs)
    {
      if (!AreTypesEqual(tree, lhs->type, rhs->type))
        return false;

      if (lhs->modifier != rhs->modifier)
        return false;

      if (lhs->semantic != rhs->semantic || lhs->sv_semantic != rhs->sv_semantic)
        return false;

      lhs = lhs->nextArgument;
      rhs = rhs->nextArgument;
    }

    return lhs == nullptr && rhs == nullptr;
  }

  const HLSLFunction* HLSLParser::FindFunction(const HLSLFunction* fun) const
  {
    for (int i = 0; i < m_functions.GetSize(); ++i)
    {
      if (m_functions[i]->name == fun->name &&
        AreTypesEqual(m_tree, m_functions[i]->returnType, fun->returnType) &&
        AreArgumentListsEqual(m_tree, m_functions[i]->argument, fun->argument))
      {
        return m_functions[i];
      }
    }
    return nullptr;
  }

  void HLSLParser::DeclareVariable(const char* name, const HLSLType& type)
  {
    if (m_variables.GetSize() == m_numGlobals)
    {
      ++m_numGlobals;
    }
    Variable& variable = m_variables.PushBackNew();
    variable.name = name;
    variable.type = type;
  }

  bool HLSLParser::GetIsFunction(const char* name) const
  {
    for (int i = 0; i < m_functions.GetSize(); ++i)
    {
      // == is ok here because we're passed the strings through the string pool.
      if (m_functions[i]->name == name)
      {
        return true;
      }
    }
    for (int i = 0; i < _numIntrinsics; ++i)
    {
      // Intrinsic names are not in the string pool (since they are compile time
      // constants, so we need full string compare).
      if (String_Equal(name, _intrinsic[i].function.name))
      {
        return true;
      }
    }

    return false;
  }

  const HLSLFunction* HLSLParser::MatchFunctionCall(const HLSLFunctionCall* functionCall, const char* name)
  {
    const HLSLFunction* matchedFunction = nullptr;

    int  numMatchedOverloads = 0;
    bool nameMatches = false;

    // Get the user defined functions with the specified name.
    for (int i = 0; i < m_functions.GetSize(); ++i)
    {
      const HLSLFunction* function = m_functions[i];
      if (function->name == name)
      {
        nameMatches = true;

        CompareFunctionsResult result = CompareFunctions(m_tree, functionCall, function, matchedFunction);
        if (result == Function1Better)
        {
          matchedFunction = function;
          numMatchedOverloads = 1;
        }
        else if (result == FunctionsEqual)
        {
          ++numMatchedOverloads;
        }
      }
    }

    // Get the intrinsic functions with the specified name.
    for (int i = 0; i < _numIntrinsics; ++i)
    {
      const HLSLFunction* function = &_intrinsic[i].function;
      if (String_Equal(function->name, name))
      {
        nameMatches = true;

        CompareFunctionsResult result = CompareFunctions(m_tree, functionCall, function, matchedFunction);
        if (result == Function1Better)
        {
          matchedFunction = function;
          numMatchedOverloads = 1;
        }
        else if (result == FunctionsEqual)
        {
          ++numMatchedOverloads;
        }
      }
    }

    if (matchedFunction != nullptr && numMatchedOverloads > 1)
    {
      // Multiple overloads match.
      m_tokenizer.Error("'%s' %d overloads have similar conversions", name, numMatchedOverloads);
      return nullptr;
    }
    else if (matchedFunction == nullptr)
    {
      if (nameMatches)
      {
        m_tokenizer.Error("'%s' no overloaded function matched all of the arguments", name);
      }
      else
      {
        m_tokenizer.Error("Undeclared identifier '%s'", name);
      }
    }

    return matchedFunction;
  }

  bool HLSLParser::GetMemberType(const HLSLType& objectType, HLSLMemberAccess* memberAccess)
  {
    const char* fieldName = memberAccess->field;

    if (objectType.baseType == HLSLBaseType_UserDefined)
    {
      const HLSLStruct* structure = FindUserDefinedType(objectType.typeName);
      ASSERT(structure != nullptr);

      const HLSLStructField* field = structure->field;
      while (field != nullptr)
      {
        if (field->name == fieldName)
        {
          memberAccess->expressionType = field->type;
          return true;
        }
        field = field->nextField;
      }

      return false;
    }

    if (baseTypeDescriptions[objectType.baseType].numericType == NumericType_NaN)
    {
      // Currently we don't have an non-numeric types that allow member access.
      return false;
    }

    int swizzleLength = 0;

    if (baseTypeDescriptions[objectType.baseType].numDimensions <= 1)
    {
      // Check for a swizzle on the scalar/vector types.
      for (int i = 0; fieldName[i] != 0; ++i)
      {
        if (fieldName[i] != 'x' && fieldName[i] != 'y' && fieldName[i] != 'z' && fieldName[i] != 'w' &&
          fieldName[i] != 'r' && fieldName[i] != 'g' && fieldName[i] != 'b' && fieldName[i] != 'a')
        {
          m_tokenizer.Error("Invalid swizzle '%s'", fieldName);
          return false;
        }
        ++swizzleLength;
      }
      ASSERT(swizzleLength > 0);
    }
    else
    {

      // Check for a matrix element access (e.g. _m00 or _11)

      const char* n = fieldName;
      while (n[0] == '_')
      {
        ++n;
        int base = 1;
        if (n[0] == 'm')
        {
          base = 0;
          ++n;
        }
        if (!isdigit(n[0]) || !isdigit(n[1]))
        {
          return false;
        }

        int r = (n[0] - '0') - base;
        int c = (n[1] - '0') - base;
        if (r >= baseTypeDescriptions[objectType.baseType].height ||
          c >= baseTypeDescriptions[objectType.baseType].numComponents)
        {
          return false;
        }
        ++swizzleLength;
        n += 2;

      }

      if (n[0] != 0)
      {
        return false;
      }

    }

    if (swizzleLength > 4)
    {
      m_tokenizer.Error("Invalid swizzle '%s'", fieldName);
      return false;
    }

    static const HLSLBaseType floatType[] = { HLSLBaseType_Float, HLSLBaseType_Float2, HLSLBaseType_Float3, HLSLBaseType_Float4 };
    static const HLSLBaseType intType[] = { HLSLBaseType_Int,   HLSLBaseType_Int2,   HLSLBaseType_Int3,   HLSLBaseType_Int4 };
    static const HLSLBaseType uintType[] = { HLSLBaseType_Uint,  HLSLBaseType_Uint2,  HLSLBaseType_Uint3,  HLSLBaseType_Uint4 };
    static const HLSLBaseType boolType[] = { HLSLBaseType_Bool,  HLSLBaseType_Bool2,  HLSLBaseType_Bool3,  HLSLBaseType_Bool4 };

    switch (baseTypeDescriptions[objectType.baseType].numericType)
    {
    case NumericType_Float:
      memberAccess->expressionType.baseType = floatType[swizzleLength - 1];
      break;
    case NumericType_Int:
      memberAccess->expressionType.baseType = intType[swizzleLength - 1];
      break;
    case NumericType_Uint:
      memberAccess->expressionType.baseType = uintType[swizzleLength - 1];
      break;
    case NumericType_Bool:
      memberAccess->expressionType.baseType = boolType[swizzleLength - 1];
      break;
    default:
      ASSERT(0);
    }

    memberAccess->swizzle = true;

    return true;
  }

}


namespace M4
{

  class GLSLGenerator
  {

  public:
    enum Target
    {
      Target_VertexShader,
      Target_FragmentShader,
    };

    enum Version
    {
      Version_110, // OpenGL 2.0
      Version_120, // OpenGL 2.1
      Version_140, // OpenGL 3.1
      Version_150, // OpenGL 3.2
      Version_330, // OpenGL 3.3
      Version_100_ES, // OpenGL ES 2.0
      Version_300_ES, // OpenGL ES 3.0
    };

    enum Flags
    {
      Flag_FlipPositionOutput = 1 << 0,
      Flag_EmulateConstantBuffer = 1 << 1,
      Flag_PackMatrixRowMajor = 1 << 2,
      Flag_LowerMatrixMultiplication = 1 << 3,
    };

    struct Options
    {
      unsigned int flags;
      const char* constantBufferPrefix;

      Options()
      {
        flags = 0;
        constantBufferPrefix = "";
      }
    };

    GLSLGenerator();

    bool Generate(HLSLTree* tree, Target target, Version versiom, const char* entryName, const Options& options = Options());
    const char* GetResult() const;

  private:

    enum AttributeModifier
    {
      AttributeModifier_In,
      AttributeModifier_Out,
    };

    void OutputExpressionList(HLSLExpression* expression, HLSLArgument* argument = nullptr);
    void OutputExpression(HLSLExpression* expression, const HLSLType* dstType = nullptr);
    void OutputIdentifier(const char* name);
    void OutputArguments(HLSLArgument* argument);

    /**
     * If the statements are part of a function, then returnType can be used to specify the type
     * that a return statement is expected to produce so that correct casts will be generated.
     */
    void OutputStatements(int indent, HLSLStatement* statement, const HLSLType* returnType = nullptr);

    void OutputAttribute(const HLSLType& type, const char* semantic, AttributeModifier modifier);
    void OutputAttributes(HLSLFunction* entryFunction);
    void OutputEntryCaller(HLSLFunction* entryFunction);
    void OutputDeclaration(HLSLDeclaration* declaration, const bool skipAssignment);
    void OutputDeclarationType(const HLSLType& type);
    void OutputDeclarationBody(const HLSLType& type, const char* name);
    void OutputDeclaration(const HLSLType& type, const char* name);
    void OutputDeclarationAssignment(HLSLDeclaration* declaration);
    void OutputCast(const HLSLType& type);

    void OutputSetOutAttribute(const char* semantic, const char* resultName);

    void LayoutBuffer(HLSLBuffer* buffer, unsigned int& offset);
    void LayoutBuffer(const HLSLType& type, unsigned int& offset);
    void LayoutBufferElement(const HLSLType& type, unsigned int& offset);
    void LayoutBufferAlign(const HLSLType& type, unsigned int& offset);

    HLSLBuffer* GetBufferAccessExpression(HLSLExpression* expression);
    void OutputBufferAccessExpression(HLSLBuffer* buffer, HLSLExpression* expression, const HLSLType& type, unsigned int postOffset);
    unsigned int OutputBufferAccessIndex(HLSLExpression* expression, unsigned int postOffset);

    void OutputBuffer(int indent, HLSLBuffer* buffer);

    HLSLFunction* FindFunction(HLSLRoot* root, const char* name);
    HLSLStruct* FindStruct(HLSLRoot* root, const char* name);

    void Error(const char* format, ...);

    /** GLSL contains some reserved words that don't exist in HLSL. This function will
     * sanitize those names. */
    const char* GetSafeIdentifierName(const char* name) const;

    /** Generates a name of the format "base+n" where n is an integer such that the name
     * isn't used in the syntax tree. */
    bool ChooseUniqueName(const char* base, char* dst, int dstLength) const;

    const char* GetBuiltInSemantic(const char* semantic, AttributeModifier modifier, int* outputIndex = 0);
    const char* GetAttribQualifier(AttributeModifier modifier);
    void CompleteConstructorArguments(HLSLExpression* expression, HLSLBaseType dstType);
    void OutputMatrixCtors();

  private:

    static const int    s_numReservedWords = 9;
    static const char* s_reservedWord[s_numReservedWords];

    CodeWriter          m_writer;

    HLSLTree* m_tree;
    const char* m_entryName;
    Target              m_target;
    Version             m_version;
    bool                m_versionLegacy;
    Options             m_options;

    bool                m_outputPosition;
    int                 m_outputTargets;

    const char* m_outAttribPrefix;
    const char* m_inAttribPrefix;

    char                m_matrixRowFunction[64];
    char                m_matrixCtorFunction[64];
    char                m_matrixMulFunction[64];
    char                m_clipFunction[64];
    char                m_tex2DlodFunction[64];
    char                m_tex2DbiasFunction[64];
    char                m_tex2DgradFunction[64];
    char                m_tex3DlodFunction[64];
    char                m_texCUBEbiasFunction[64];
    char                m_texCUBElodFunction[64];
    char                m_scalarSwizzle2Function[64];
    char                m_scalarSwizzle3Function[64];
    char                m_scalarSwizzle4Function[64];
    char                m_sinCosFunction[64];
    char                m_bvecTernary[64];
    char                m_modfFunction[64];

    bool                m_error;

    char                m_reservedWord[s_numReservedWords][64];

    std::vector<matrixCtor> matrixCtors;
    std::map<matrixCtor, std::string> matrixCtorsId;
    std::vector<HLSLDeclaration*> globalVarsAssignments;

  };

}

namespace M4
{
  static const HLSLType kFloatType(HLSLBaseType_Float);
  static const HLSLType kUintType(HLSLBaseType_Uint);
  static const HLSLType kIntType(HLSLBaseType_Int);
  static const HLSLType kBoolType(HLSLBaseType_Bool);

  // These are reserved words in GLSL that aren't reserved in HLSL.
  const char* GLSLGenerator::s_reservedWord[] =
  {
      "output",
      "input",
      "mod",
      "mix",
      "fract",
      "dFdx",
      "dFdy",
      "filter",
  "main",
  };

  static const char* GetTypeName(const HLSLType& type)
  {
    switch (type.baseType)
    {
    case HLSLBaseType_Void:         return "void";
    case HLSLBaseType_Float:        return "float";
    case HLSLBaseType_Float2:       return "vec2";
    case HLSLBaseType_Float3:       return "vec3";
    case HLSLBaseType_Float4:       return "vec4";
    case HLSLBaseType_Float2x4:     return "mat4x2";
    case HLSLBaseType_Float2x3:     return "mat3x2";
    case HLSLBaseType_Float2x2:     return "mat2";
    case HLSLBaseType_Float3x4:     return "mat4x3";
    case HLSLBaseType_Float3x3:     return "mat3";
    case HLSLBaseType_Float3x2:     return "mat2x3";
    case HLSLBaseType_Float4x4:     return "mat4";
    case HLSLBaseType_Float4x3:     return "mat3x4";
    case HLSLBaseType_Float4x2:     return "mat2x4";
    case HLSLBaseType_Bool:         return "bool";
    case HLSLBaseType_Bool2:        return "bvec2";
    case HLSLBaseType_Bool3:        return "bvec3";
    case HLSLBaseType_Bool4:        return "bvec4";
    case HLSLBaseType_Int:          return "int";
    case HLSLBaseType_Int2:         return "ivec2";
    case HLSLBaseType_Int3:         return "ivec3";
    case HLSLBaseType_Int4:         return "ivec4";
    case HLSLBaseType_Uint:         return "uint";
    case HLSLBaseType_Uint2:        return "uvec2";
    case HLSLBaseType_Uint3:        return "uvec3";
    case HLSLBaseType_Uint4:        return "uvec4";
    case HLSLBaseType_Texture:      return "texture";
    case HLSLBaseType_Sampler:      return "sampler";
    case HLSLBaseType_Sampler2D:    return "sampler2D";
    case HLSLBaseType_Sampler3D:    return "sampler3D";
    case HLSLBaseType_SamplerCube:  return "samplerCube";
    case HLSLBaseType_Sampler2DMS:  return "sampler2DMS";
    case HLSLBaseType_Sampler2DArray:  return "sampler2DArray";
    case HLSLBaseType_UserDefined:  return type.typeName;
    default:
      ASSERT(0);
      return "?";
    }
  }

  static bool GetCanImplicitCast(const HLSLType& srcType, const HLSLType& dstType)
  {
    return srcType.baseType == dstType.baseType;
  }

  static int GetFunctionArguments(HLSLFunctionCall* functionCall, HLSLExpression* expression[], int maxArguments)
  {
    HLSLExpression* argument = functionCall->argument;
    int numArguments = 0;
    while (argument != nullptr)
    {
      if (numArguments < maxArguments)
      {
        expression[numArguments] = argument;
      }
      argument = argument->nextExpression;
      ++numArguments;
    }
    return numArguments;
  }

  GLSLGenerator::GLSLGenerator() :
    m_writer(/* writeFileNames= */ false)
  {
    m_tree = nullptr;
    m_entryName = nullptr;
    m_target = Target_VertexShader;
    m_version = Version_330;
    m_versionLegacy = false;
    m_inAttribPrefix = nullptr;
    m_outAttribPrefix = nullptr;
    m_error = false;
    m_matrixRowFunction[0] = 0;
    m_matrixCtorFunction[0] = 0;
    m_matrixMulFunction[0] = 0;
    m_clipFunction[0] = 0;
    m_tex2DlodFunction[0] = 0;
    m_tex2DbiasFunction[0] = 0;
    m_tex3DlodFunction[0] = 0;
    m_texCUBEbiasFunction[0] = 0;
    m_texCUBElodFunction[0] = 0;
    m_scalarSwizzle2Function[0] = 0;
    m_scalarSwizzle3Function[0] = 0;
    m_scalarSwizzle4Function[0] = 0;
    m_sinCosFunction[0] = 0;
    m_bvecTernary[0] = 0;
    m_modfFunction[0] = 0;
    m_outputPosition = false;
    m_outputTargets = 0;
  }

  bool GLSLGenerator::Generate(HLSLTree* tree, Target target, Version version, const char* entryName, const Options& options)
  {

    m_tree = tree;
    m_entryName = entryName;
    m_target = target;
    m_version = version;
    m_versionLegacy = (version == Version_110 || version == Version_120 || version == Version_100_ES);
    m_options = options;

    globalVarsAssignments.clear();

    ChooseUniqueName("matrix_row", m_matrixRowFunction, sizeof(m_matrixRowFunction));
    ChooseUniqueName("matrix_ctor", m_matrixCtorFunction, sizeof(m_matrixCtorFunction));
    ChooseUniqueName("matrix_mul", m_matrixMulFunction, sizeof(m_matrixMulFunction));
    ChooseUniqueName("clip", m_clipFunction, sizeof(m_clipFunction));
    ChooseUniqueName("tex2Dlod", m_tex2DlodFunction, sizeof(m_tex2DlodFunction));
    ChooseUniqueName("tex2Dbias", m_tex2DbiasFunction, sizeof(m_tex2DbiasFunction));
    ChooseUniqueName("tex2Dgrad", m_tex2DgradFunction, sizeof(m_tex2DgradFunction));
    ChooseUniqueName("tex3Dlod", m_tex3DlodFunction, sizeof(m_tex3DlodFunction));
    ChooseUniqueName("texCUBEbias", m_texCUBEbiasFunction, sizeof(m_texCUBEbiasFunction));
    ChooseUniqueName("texCUBElod", m_texCUBElodFunction, sizeof(m_texCUBElodFunction));
    ChooseUniqueName("modf", m_modfFunction, sizeof(m_modfFunction));

    for (int i = 0; i < s_numReservedWords; ++i)
    {
      ChooseUniqueName(s_reservedWord[i], m_reservedWord[i], sizeof(m_reservedWord[i]));
    }

    ChooseUniqueName("m_scalar_swizzle2", m_scalarSwizzle2Function, sizeof(m_scalarSwizzle2Function));
    ChooseUniqueName("m_scalar_swizzle3", m_scalarSwizzle3Function, sizeof(m_scalarSwizzle3Function));
    ChooseUniqueName("m_scalar_swizzle4", m_scalarSwizzle4Function, sizeof(m_scalarSwizzle4Function));

    ChooseUniqueName("sincos", m_sinCosFunction, sizeof(m_sinCosFunction));

    ChooseUniqueName("bvecTernary", m_bvecTernary, sizeof(m_bvecTernary));

    if (target == Target_VertexShader)
    {
      m_inAttribPrefix = "";
      m_outAttribPrefix = "frag_";
    }
    else
    {
      m_inAttribPrefix = "frag_";
      m_outAttribPrefix = "rast_";
    }

    m_tree->ReplaceUniformsAssignments();

    HLSLRoot* root = m_tree->GetRoot();
    HLSLStatement* statement = root->statement;

    // Find the entry point function.
    HLSLFunction* entryFunction = FindFunction(root, m_entryName);
    if (entryFunction == nullptr)
    {
      Error("Entry point '%s' doesn't exist", m_entryName);
      return false;
    }

    if (m_version == Version_110)
    {
      m_writer.WriteLine(0, "#version 110");
    }
    if (m_version == Version_120)
    {
      m_writer.WriteLine(0, "#version 120");
    }
    else if (m_version == Version_140)
    {
      m_writer.WriteLine(0, "#version 140");

      // Pragmas for NVIDIA.
      m_writer.WriteLine(0, "#pragma optionNV(fastmath on)");
      //m_writer.WriteLine(0, "#pragma optionNV(fastprecision on)");
      m_writer.WriteLine(0, "#pragma optionNV(ifcvt none)");
      m_writer.WriteLine(0, "#pragma optionNV(inline all)");
      m_writer.WriteLine(0, "#pragma optionNV(strict on)");
      m_writer.WriteLine(0, "#pragma optionNV(unroll all)");
    }
    else if (m_version == Version_150)
    {
      m_writer.WriteLine(0, "#version 150");
    }
    else if (m_version == Version_330)
    {
      m_writer.WriteLine(0, "#version 330");
    }
    else if (m_version == Version_100_ES)
    {
      m_writer.WriteLine(0, "#version 100");
      m_writer.WriteLine(0, "precision highp float;");
    }
    else if (m_version == Version_300_ES)
    {
      m_writer.WriteLine(0, "#version 300 es");
      m_writer.WriteLine(0, "precision highp float;");
      m_writer.WriteLine(0, "precision highp sampler3D;");
    }
    else
    {
      Error("Unrecognized target version");
      return false;
    }

    // Output the special function used to access rows in a matrix.
    m_writer.WriteLine(0, "vec2 %s(mat2 m, int i) { return vec2( m[0][i], m[1][i] ); }", m_matrixRowFunction);
    m_writer.WriteLine(0, "vec3 %s(mat3 m, int i) { return vec3( m[0][i], m[1][i], m[2][i] ); }", m_matrixRowFunction);
    m_writer.WriteLine(0, "vec4 %s(mat4 m, int i) { return vec4( m[0][i], m[1][i], m[2][i], m[3][i] ); }", m_matrixRowFunction);

    // Output the special function used to do matrix cast for OpenGL 2.0
    if (m_versionLegacy)
    {
      m_writer.WriteLine(0, "mat3 %s(mat4 m) { return mat3(m[0][0], m[0][1], m[0][2], m[1][0], m[1][1], m[1][2], m[2][0], m[2][1], m[2][2]); }", m_matrixCtorFunction);
    }

    // Output the special functions used for matrix multiplication lowering
    // They make sure glsl-optimizer can fold expressions better
    if (m_tree->NeedsFunction("mul") && (m_options.flags & Flag_LowerMatrixMultiplication))
    {
      m_writer.WriteLine(0, "vec2 %s(mat2 m, vec2 v) { return m[0] * v.x + m[1] * v.y; }", m_matrixMulFunction);
      m_writer.WriteLine(0, "vec2 %s(vec2 v, mat2 m) { return vec2(dot(m[0], v), dot(m[1], v)); }", m_matrixMulFunction);
      m_writer.WriteLine(0, "vec3 %s(mat3 m, vec3 v) { return m[0] * v.x + m[1] * v.y + m[2] * v.z; }", m_matrixMulFunction);
      m_writer.WriteLine(0, "vec3 %s(vec3 v, mat3 m) { return vec3(dot(m[0], v), dot(m[1], v), dot(m[2], v)); }", m_matrixMulFunction);
      m_writer.WriteLine(0, "vec4 %s(mat4 m, vec4 v) { return m[0] * v.x + m[1] * v.y + m[2] * v.z + m[3] * v.w; }", m_matrixMulFunction);
      m_writer.WriteLine(0, "vec4 %s(vec4 v, mat4 m) { return vec4(dot(m[0], v), dot(m[1], v), dot(m[2], v), dot(m[3], v)); }", m_matrixMulFunction);
    }

    // Output the special function used to emulate HLSL clip.
    if (m_tree->NeedsFunction("clip"))
    {
      const char* discard = m_target == Target_FragmentShader ? "discard" : "";
      m_writer.WriteLine(0, "void %s(float x) { if (x < 0.0) %s;  }", m_clipFunction, discard);
      m_writer.WriteLine(0, "void %s(vec2  x) { if (any(lessThan(x, vec2(0.0, 0.0)))) %s;  }", m_clipFunction, discard);
      m_writer.WriteLine(0, "void %s(vec3  x) { if (any(lessThan(x, vec3(0.0, 0.0, 0.0)))) %s;  }", m_clipFunction, discard);
      m_writer.WriteLine(0, "void %s(vec4  x) { if (any(lessThan(x, vec4(0.0, 0.0, 0.0, 0.0)))) %s;  }", m_clipFunction, discard);
    }

    // Output the special function used to emulate tex2Dlod.
    if (m_tree->NeedsFunction("tex2Dlod"))
    {
      const char* function = "textureLod";

      if (m_versionLegacy)
      {
        m_writer.WriteLine(0, "#extension GL_ARB_shader_texture_lod : require");
        function = "texture2DLod";
      }
      else if (m_version == Version_100_ES)
      {
        m_writer.WriteLine(0, "#extension GL_EXT_shader_texture_lod : require");
        function = "texture2DLodEXT";
      }

      m_writer.WriteLine(0, "vec4 %s(sampler2D samp, vec4 texCoord) { return %s(samp, texCoord.xy, texCoord.w);  }", m_tex2DlodFunction, function);
    }

    // Output the special function used to emulate tex2Dgrad.
    if (m_tree->NeedsFunction("tex2Dgrad"))
    {
      const char* function = "textureGrad";

      if (m_versionLegacy)
      {
        m_writer.WriteLine(0, "#extension GL_ARB_shader_texture_lod : require");
        function = "texture2DGradARB";
      }
      else if (m_version == Version_100_ES)
      {
        m_writer.WriteLine(0, "#extension GL_EXT_shader_texture_lod : require");
        function = "texture2DGradEXT";
      }

      m_writer.WriteLine(0, "vec4 %s(sampler2D samp, vec2 texCoord, vec2 dx, vec2 dy) { return %s(samp, texCoord, dx, dy);  }", m_tex2DgradFunction, function);
    }

    // Output the special function used to emulate tex2Dbias.
    if (m_tree->NeedsFunction("tex2Dbias"))
    {
      if (target == Target_FragmentShader)
      {
        m_writer.WriteLine(0, "vec4 %s(sampler2D samp, vec4 texCoord) { return %s(samp, texCoord.xy, texCoord.w);  }", m_tex2DbiasFunction, m_versionLegacy ? "texture2D" : "texture");
      }
      else
      {
        // Bias value is not supported in vertex shader.
        m_writer.WriteLine(0, "vec4 %s(sampler2D samp, vec4 texCoord) { return texture(samp, texCoord.xy);  }", m_tex2DbiasFunction);
      }
    }

    // Output the special function used to emulate tex2DMSfetch.
    if (m_tree->NeedsFunction("tex2DMSfetch"))
    {
      m_writer.WriteLine(0, "vec4 tex2DMSfetch(sampler2DMS samp, ivec2 texCoord, int sample) {");
      m_writer.WriteLine(1, "return texelFetch(samp, texCoord, sample);");
      m_writer.WriteLine(0, "}");
    }

    // Output the special function used to emulate tex3Dlod.
    if (m_tree->NeedsFunction("tex3Dlod"))
    {
      m_writer.WriteLine(0, "vec4 %s(sampler3D samp, vec4 texCoord) { return %s(samp, texCoord.xyz, texCoord.w);  }", m_tex3DlodFunction, m_versionLegacy ? "texture3D" : "texture");
    }

    // Output the special function used to emulate texCUBEbias.
    if (m_tree->NeedsFunction("texCUBEbias"))
    {
      if (target == Target_FragmentShader)
      {
        m_writer.WriteLine(0, "vec4 %s(samplerCube samp, vec4 texCoord) { return %s(samp, texCoord.xyz, texCoord.w);  }", m_texCUBEbiasFunction, m_versionLegacy ? "textureCube" : "texture");
      }
      else
      {
        // Bias value is not supported in vertex shader.
        m_writer.WriteLine(0, "vec4 %s(samplerCube samp, vec4 texCoord) { return texture(samp, texCoord.xyz);  }", m_texCUBEbiasFunction);
      }
    }

    // Output the special function used to emulate texCUBElod
    if (m_tree->NeedsFunction("texCUBElod"))
    {
      const char* function = "textureLod";

      if (m_version == Version_110 || m_version == Version_120)
      {
        m_writer.WriteLine(0, "#extension GL_ARB_shader_texture_lod : require");
        function = "textureCubeLod";
      }
      else if (m_version == Version_100_ES)
      {
        m_writer.WriteLine(0, "#extension GL_EXT_shader_texture_lod : require");
        function = "textureCubeLodEXT";
      }

      m_writer.WriteLine(0, "vec4 %s(samplerCube samp, vec4 texCoord) { return %s(samp, texCoord.xyz, texCoord.w);  }", m_texCUBElodFunction, function);
    }

    if (m_tree->NeedsFunction("modf"))
    {
      if (m_version == Version_110 || m_version == Version_120 || m_version == Version_100_ES)
      {
        m_writer.WriteLine(0, "float %s(float x, out int ip) { ip = int(x); return x - ip; }", m_modfFunction);
      }
      else {
        m_writer.WriteLine(0, "float %s(float x, out int ip) { return modf(x, ip); }", m_modfFunction);
      }
    }

    m_writer.WriteLine(0, "vec2  %s(float x) { return  vec2(x, x); }", m_scalarSwizzle2Function);
    m_writer.WriteLine(0, "ivec2 %s(int   x) { return ivec2(x, x); }", m_scalarSwizzle2Function);

    m_writer.WriteLine(0, "vec3  %s(float x) { return  vec3(x, x, x); }", m_scalarSwizzle3Function);
    m_writer.WriteLine(0, "ivec3 %s(int   x) { return ivec3(x, x, x); }", m_scalarSwizzle3Function);

    m_writer.WriteLine(0, "vec4  %s(float x) { return  vec4(x, x, x, x); }", m_scalarSwizzle4Function);
    m_writer.WriteLine(0, "ivec4 %s(int   x) { return ivec4(x, x, x, x); }", m_scalarSwizzle4Function);

    if (!m_versionLegacy)
    {
      m_writer.WriteLine(0, "uvec2 %s(uint  x) { return uvec2(x, x); }", m_scalarSwizzle2Function);
      m_writer.WriteLine(0, "uvec3 %s(uint  x) { return uvec3(x, x, x); }", m_scalarSwizzle3Function);
      m_writer.WriteLine(0, "uvec4 %s(uint  x) { return uvec4(x, x, x, x); }", m_scalarSwizzle4Function);
    }

    if (m_tree->NeedsFunction("sincos"))
    {
      const char* floatTypes[] = { "float", "vec2", "vec3", "vec4" };
      for (int i = 0; i < 4; ++i)
      {
        m_writer.WriteLine(0, "void %s(%s x, out %s s, out %s c) { s = sin(x); c = cos(x); }", m_sinCosFunction,
          floatTypes[i], floatTypes[i], floatTypes[i]);
      }
    }

    // special function to emulate ?: with bool{2,3,4} condition type
    m_writer.WriteLine(0, "vec2 %s(bvec2 cond, vec2 trueExpr, vec2 falseExpr) { vec2 ret; ret.x = cond.x ? trueExpr.x : falseExpr.x; ret.y = cond.y ? trueExpr.y : falseExpr.y; return ret; }", m_bvecTernary);
    m_writer.WriteLine(0, "vec3 %s(bvec3 cond, vec3 trueExpr, vec3 falseExpr) { vec3 ret; ret.x = cond.x ? trueExpr.x : falseExpr.x; ret.y = cond.y ? trueExpr.y : falseExpr.y; ret.z = cond.z ? trueExpr.z : falseExpr.z; return ret; }", m_bvecTernary);
    m_writer.WriteLine(0, "vec4 %s(bvec4 cond, vec4 trueExpr, vec4 falseExpr) { vec4 ret; ret.x = cond.x ? trueExpr.x : falseExpr.x; ret.y = cond.y ? trueExpr.y : falseExpr.y; ret.z = cond.z ? trueExpr.z : falseExpr.z; ret.w = cond.w ? trueExpr.w : falseExpr.w; return ret; }", m_bvecTernary);

    m_tree->EnumerateMatrixCtorsNeeded(matrixCtors);
    for (matrixCtor& ctor : matrixCtors)
    {
      std::string id = GetTypeName(HLSLType(ctor.matrixType));
      id += "_from";
      for (HLSLBaseType argType : ctor.argumentTypes)
      {
        id += "_";
        id += GetTypeName(HLSLType(argType));
      }
      matrixCtorsId[ctor] = id;
    }

    OutputMatrixCtors();

    // Output the extension used for dFdx/dFdy in GLES2
    if (m_version == Version_100_ES && (m_tree->NeedsFunction("ddx") || m_tree->NeedsFunction("ddy")))
    {
      m_writer.WriteLine(0, "#extension GL_OES_standard_derivatives : require");
    }

    OutputAttributes(entryFunction);

    if (m_target == Target_FragmentShader)
    {
      if (!m_outputTargets)
        Error("Fragment shader must output a color");

      if (!m_versionLegacy)
        m_writer.WriteLine(0, "out vec4 rast_FragData[%d];", m_outputTargets);
    }

    OutputStatements(0, statement);
    OutputEntryCaller(entryFunction);

    m_tree = nullptr;

    // The GLSL compilers don't check for this, so generate our own error message.
    if (target == Target_VertexShader && !m_outputPosition)
    {
      Error("Vertex shader must output a position");
    }

    return !m_error;

  }

  const char* GLSLGenerator::GetResult() const
  {
    return m_writer.GetResult();
  }

  void GLSLGenerator::OutputExpressionList(HLSLExpression* expression, HLSLArgument* argument)
  {
    int numExpressions = 0;
    while (expression != nullptr)
    {
      if (numExpressions > 0)
      {
        m_writer.Write(", ");
      }

      HLSLType* expectedType = nullptr;
      if (argument != nullptr)
      {
        expectedType = &argument->type;
        argument = argument->nextArgument;
      }

      OutputExpression(expression, expectedType);
      expression = expression->nextExpression;
      ++numExpressions;
    }
  }

  const HLSLType* commonScalarType(const HLSLType& lhs, const HLSLType& rhs)
  {
    if (!IsScalarType(lhs) || !IsScalarType(rhs))
      return nullptr;

    if (lhs.baseType == HLSLBaseType_Float || rhs.baseType == HLSLBaseType_Float)
      return &kFloatType;

    if (lhs.baseType == HLSLBaseType_Uint || rhs.baseType == HLSLBaseType_Uint)
      return &kUintType;

    if (lhs.baseType == HLSLBaseType_Int || rhs.baseType == HLSLBaseType_Int)
      return &kIntType;

    if (lhs.baseType == HLSLBaseType_Bool || rhs.baseType == HLSLBaseType_Bool)
      return &kBoolType;

    return nullptr;
  }

  void GLSLGenerator::OutputExpression(HLSLExpression* expression, const HLSLType* dstType)
  {

    bool cast = dstType != nullptr && !GetCanImplicitCast(expression->expressionType, *dstType);
    if (expression->nodeType == HLSLNodeType_CastingExpression)
    {
      // No need to include a cast if the expression is already doing it.
      cast = false;
    }

    if (cast)
    {
      OutputCast(*dstType);
      m_writer.Write("(");
    }

    HLSLBuffer* bufferAccess = (m_options.flags & Flag_EmulateConstantBuffer) ? GetBufferAccessExpression(expression) : 0;

    if (bufferAccess)
    {
      OutputBufferAccessExpression(bufferAccess, expression, expression->expressionType, 0);
    }
    else if (expression->nodeType == HLSLNodeType_IdentifierExpression)
    {
      HLSLIdentifierExpression* identifierExpression = static_cast<HLSLIdentifierExpression*>(expression);
      OutputIdentifier(identifierExpression->name);
    }
    else if (expression->nodeType == HLSLNodeType_ConstructorExpression)
    {
      HLSLConstructorExpression* constructorExpression = static_cast<HLSLConstructorExpression*>(expression);

      bool matrixCtorNeeded = false;
      if (IsMatrixType(constructorExpression->type.baseType))
      {
        matrixCtor ctor = matrixCtorBuilder(constructorExpression->type, constructorExpression->argument);
        if (std::find(matrixCtors.cbegin(), matrixCtors.cend(), ctor) != matrixCtors.cend())
        {
          matrixCtorNeeded = true;
        }
      }

      if (matrixCtorNeeded)
      {
        // Matrix contructors needs to be adapted since GLSL access a matrix as m[c][r] while HLSL is m[r][c]
        matrixCtor ctor = matrixCtorBuilder(constructorExpression->type, constructorExpression->argument);
        m_writer.Write("%s(", matrixCtorsId[ctor].c_str());
        OutputExpressionList(constructorExpression->argument);
        m_writer.Write(")");
      }
      else
      {
        m_writer.Write("%s(", GetTypeName(constructorExpression->type));
        OutputExpressionList(constructorExpression->argument);
        m_writer.Write(")");
      }
    }
    else if (expression->nodeType == HLSLNodeType_CastingExpression)
    {
      HLSLCastingExpression* castingExpression = static_cast<HLSLCastingExpression*>(expression);
      OutputCast(castingExpression->type);
      m_writer.Write("(");
      OutputExpression(castingExpression->expression);
      m_writer.Write(")");
    }
    else if (expression->nodeType == HLSLNodeType_LiteralExpression)
    {
      HLSLLiteralExpression* literalExpression = static_cast<HLSLLiteralExpression*>(expression);
      switch (literalExpression->type)
      {
      case HLSLBaseType_Float:
      {
        // Don't use printf directly so that we don't use the system locale.
        char buffer[64];
        String_FormatFloat(buffer, sizeof(buffer), literalExpression->fValue);
        m_writer.Write("%s", buffer);
      }
      break;
      case HLSLBaseType_Int:
      case HLSLBaseType_Uint:
        m_writer.Write("%d", literalExpression->iValue);
        break;
      case HLSLBaseType_Bool:
        m_writer.Write("%s", literalExpression->bValue ? "true" : "false");
        break;
      default:
        ASSERT(0);
      }
    }
    else if (expression->nodeType == HLSLNodeType_UnaryExpression)
    {
      HLSLUnaryExpression* unaryExpression = static_cast<HLSLUnaryExpression*>(expression);
      const char* op = "?";
      bool pre = true;
      const HLSLType* dstType2 = nullptr;
      switch (unaryExpression->unaryOp)
      {
      case HLSLUnaryOp_Negative:      op = "-";  break;
      case HLSLUnaryOp_Positive:      op = "+";  break;
      case HLSLUnaryOp_Not:           op = "!";  dstType2 = &unaryExpression->expressionType; break;
      case HLSLUnaryOp_PreIncrement:  op = "++"; break;
      case HLSLUnaryOp_PreDecrement:  op = "--"; break;
      case HLSLUnaryOp_PostIncrement: op = "++"; pre = false; break;
      case HLSLUnaryOp_PostDecrement: op = "--"; pre = false; break;
      case HLSLUnaryOp_BitNot:        op = "~";  break;
      }
      m_writer.Write("(");
      if (pre)
      {
        m_writer.Write("%s", op);
        OutputExpression(unaryExpression->expression, dstType2);
      }
      else
      {
        OutputExpression(unaryExpression->expression, dstType2);
        m_writer.Write("%s", op);
      }
      m_writer.Write(")");
    }
    else if (expression->nodeType == HLSLNodeType_BinaryExpression)
    {
      HLSLBinaryExpression* binaryExpression = static_cast<HLSLBinaryExpression*>(expression);
      const char* op = "?";
      const HLSLType* dstType1 = nullptr;
      const HLSLType* dstType2 = nullptr;

      //
      bool vectorExpression = IsVectorType(binaryExpression->expression1->expressionType) || IsVectorType(binaryExpression->expression2->expressionType);
      if (vectorExpression && IsCompareOp(binaryExpression->binaryOp))
      {
        switch (binaryExpression->binaryOp)
        {
        case HLSLBinaryOp_Less:         m_writer.Write("lessThan(");			break;
        case HLSLBinaryOp_Greater:      m_writer.Write("greaterThan(");			break;
        case HLSLBinaryOp_LessEqual:    m_writer.Write("lessThanEqual(");		break;
        case HLSLBinaryOp_GreaterEqual: m_writer.Write("greaterThanEqual(");	break;
        case HLSLBinaryOp_Equal:        m_writer.Write("equal(");				break;
        case HLSLBinaryOp_NotEqual:     m_writer.Write("notEqual(");			break;
        default:
          ASSERT(0); // is so, check isCompareOp
        }

        if (IsVectorType(binaryExpression->expression1->expressionType) && IsScalarType(binaryExpression->expression2->expressionType))
          dstType2 = &binaryExpression->expression1->expressionType;
        else if (IsScalarType(binaryExpression->expression1->expressionType) && IsVectorType(binaryExpression->expression2->expressionType))
          dstType1 = &binaryExpression->expression2->expressionType;
        // TODO if both expressions are vector but with different dimension handle it here or in parser?

        OutputExpression(binaryExpression->expression1, dstType1);
        m_writer.Write(", ");
        OutputExpression(binaryExpression->expression2, dstType2);
        m_writer.Write(")");
      }
      else
      {
        switch (binaryExpression->binaryOp)
        {
        case HLSLBinaryOp_Add:          op = " + "; dstType1 = dstType2 = &binaryExpression->expressionType; break;
        case HLSLBinaryOp_Sub:          op = " - "; dstType1 = dstType2 = &binaryExpression->expressionType; break;
        case HLSLBinaryOp_Mul:          op = " * "; dstType1 = dstType2 = &binaryExpression->expressionType; break;
        case HLSLBinaryOp_Div:          op = " / "; dstType1 = dstType2 = &binaryExpression->expressionType; break;
        case HLSLBinaryOp_Mod:          op = " % "; dstType1 = dstType2 = &kIntType; break;
        case HLSLBinaryOp_Less:         op = " < "; dstType1 = dstType2 = commonScalarType(binaryExpression->expression1->expressionType, binaryExpression->expression2->expressionType); break;
        case HLSLBinaryOp_Greater:      op = " > "; dstType1 = dstType2 = commonScalarType(binaryExpression->expression1->expressionType, binaryExpression->expression2->expressionType); break;
        case HLSLBinaryOp_LessEqual:    op = " <= "; dstType1 = dstType2 = commonScalarType(binaryExpression->expression1->expressionType, binaryExpression->expression2->expressionType); break;
        case HLSLBinaryOp_GreaterEqual: op = " >= "; dstType1 = dstType2 = commonScalarType(binaryExpression->expression1->expressionType, binaryExpression->expression2->expressionType); break;
        case HLSLBinaryOp_Equal:        op = " == "; dstType1 = dstType2 = commonScalarType(binaryExpression->expression1->expressionType, binaryExpression->expression2->expressionType); break;
        case HLSLBinaryOp_NotEqual:     op = " != "; dstType1 = dstType2 = commonScalarType(binaryExpression->expression1->expressionType, binaryExpression->expression2->expressionType); break;
        case HLSLBinaryOp_Assign:       op = " = ";  dstType2 = &binaryExpression->expressionType; break;
        case HLSLBinaryOp_AddAssign:    op = " += "; dstType2 = &binaryExpression->expressionType; break;
        case HLSLBinaryOp_SubAssign:    op = " -= "; dstType2 = &binaryExpression->expressionType; break;
        case HLSLBinaryOp_MulAssign:    op = " *= "; dstType2 = &binaryExpression->expressionType; break;
        case HLSLBinaryOp_DivAssign:    op = " /= "; dstType2 = &binaryExpression->expressionType; break;
        case HLSLBinaryOp_And:          op = " && "; dstType1 = dstType2 = &binaryExpression->expressionType; break;
        case HLSLBinaryOp_Or:           op = " || "; dstType1 = dstType2 = &binaryExpression->expressionType; break;
        case HLSLBinaryOp_BitAnd:       op = " & "; dstType1 = dstType2 = commonScalarType(binaryExpression->expression1->expressionType, binaryExpression->expression2->expressionType); break;
        case HLSLBinaryOp_BitOr:        op = " | "; dstType1 = dstType2 = commonScalarType(binaryExpression->expression1->expressionType, binaryExpression->expression2->expressionType); break;
        case HLSLBinaryOp_BitXor:       op = " ^ "; dstType1 = dstType2 = commonScalarType(binaryExpression->expression1->expressionType, binaryExpression->expression2->expressionType); break;
        default:
          ASSERT(0);
        }
        if ((m_version == Version_110 || m_version == Version_120 || m_version == Version_100_ES) && binaryExpression->binaryOp == HLSLBinaryOp_Mod) {
          m_writer.Write("(int(mod(");
          OutputExpression(binaryExpression->expression1, dstType1);
          m_writer.Write(",");
          OutputExpression(binaryExpression->expression2, dstType2);
          m_writer.Write(")))");
        }
        else {
          m_writer.Write("(");
          OutputExpression(binaryExpression->expression1, dstType1);
          m_writer.Write("%s", op);
          OutputExpression(binaryExpression->expression2, dstType2);
          m_writer.Write(")");
        }
      }
    }
    else if (expression->nodeType == HLSLNodeType_ConditionalExpression)
    {
      HLSLConditionalExpression* conditionalExpression = static_cast<HLSLConditionalExpression*>(expression);
      if (IsVectorType(conditionalExpression->condition->expressionType))
      {
        m_writer.Write("%s", m_bvecTernary);
        m_writer.Write("( ");
        OutputExpression(conditionalExpression->condition);
        m_writer.Write(", ");
        OutputExpression(conditionalExpression->trueExpression, &conditionalExpression->expressionType);
        m_writer.Write(", ");
        OutputExpression(conditionalExpression->falseExpression, &conditionalExpression->expressionType);
        m_writer.Write(" )");
      }
      else
      {
        m_writer.Write("((");
        OutputExpression(conditionalExpression->condition, &kBoolType);
        m_writer.Write(")?(");
        OutputExpression(conditionalExpression->trueExpression, dstType);
        m_writer.Write("):(");
        OutputExpression(conditionalExpression->falseExpression, dstType);
        m_writer.Write("))");
      }
    }
    else if (expression->nodeType == HLSLNodeType_MemberAccess)
    {

      HLSLMemberAccess* memberAccess = static_cast<HLSLMemberAccess*>(expression);

      if (memberAccess->object->expressionType.baseType == HLSLBaseType_Float ||
        memberAccess->object->expressionType.baseType == HLSLBaseType_Int ||
        memberAccess->object->expressionType.baseType == HLSLBaseType_Uint)
      {
        // Handle swizzling on scalar values.
        size_t swizzleLength = strlen(memberAccess->field);
        if (swizzleLength == 2)
        {
          m_writer.Write("%s", m_scalarSwizzle2Function);
        }
        else if (swizzleLength == 3)
        {
          m_writer.Write("%s", m_scalarSwizzle3Function);
        }
        else if (swizzleLength == 4)
        {
          m_writer.Write("%s", m_scalarSwizzle4Function);
        }
        m_writer.Write("(");
        OutputExpression(memberAccess->object);
        m_writer.Write(")");
      }
      else
      {
        m_writer.Write("(");
        OutputExpression(memberAccess->object);
        m_writer.Write(")");

        if (IsMatrixType(memberAccess->object->expressionType.baseType))
        {
          // Handle HLSL matrix "swizzling".
          // TODO: Properly handle multiple element selection such as _m00_m12
          const char* n = memberAccess->field;
          while (n[0] != 0)
          {
            if (n[0] != '_')
            {
              ASSERT(0);
              break;
            }
            ++n;
            char base = '1';
            if (n[0] == 'm')
            {
              base = '0';
              ++n;
            }
            if (isdigit(n[0]) && isdigit(n[1]))
            {
              m_writer.Write("[%d][%d]", n[1] - base, n[0] - base);
              n += 2;
            }
            else
            {
              ASSERT(0);
              break;
            }
          }
        }
        else
        {
          m_writer.Write(".%s", memberAccess->field);
        }

      }

    }
    else if (expression->nodeType == HLSLNodeType_ArrayAccess)
    {
      HLSLArrayAccess* arrayAccess = static_cast<HLSLArrayAccess*>(expression);

      if (!arrayAccess->array->expressionType.array &&
        IsMatrixType(arrayAccess->array->expressionType.baseType))
      {
        // GLSL access a matrix as m[c][r] while HLSL is m[r][c], so use our
        // special row access function to convert.
        m_writer.Write("%s(", m_matrixRowFunction);
        OutputExpression(arrayAccess->array);
        m_writer.Write(",");
        OutputExpression(arrayAccess->index);
        m_writer.Write(")");
      }
      else
      {
        OutputExpression(arrayAccess->array);
        m_writer.Write("[");
        OutputExpression(arrayAccess->index);
        m_writer.Write("]");
      }

    }
    else if (expression->nodeType == HLSLNodeType_FunctionCall)
    {
      HLSLFunctionCall* functionCall = static_cast<HLSLFunctionCall*>(expression);

      // Handle intrinsic funtions that are different between HLSL and GLSL.
      bool handled = false;
      const char* functionName = functionCall->function->name;

      if (String_Equal(functionName, "mul"))
      {
        HLSLExpression* argument[2];
        if (GetFunctionArguments(functionCall, argument, 2) != 2)
        {
          Error("mul expects 2 arguments");
          return;
        }

        const HLSLType& type0 = functionCall->function->argument->type;
        const HLSLType& type1 = functionCall->function->argument->nextArgument->type;

        if (IsVectorType(type0.baseType) && IsVectorType(type1.baseType))
        {
          m_writer.Write("dot((");
          OutputExpression(argument[0], &type0);
          m_writer.Write("),(");
          OutputExpression(argument[1], &type1);
          m_writer.Write("))");
        }
        else
        {
          m_writer.Write("((");
          OutputExpression(argument[0], &type0);
          m_writer.Write(")*(");
          OutputExpression(argument[1], &type1);
          m_writer.Write("))");
        }

        handled = true;
      }
      else if (String_Equal(functionName, "saturate"))
      {
        HLSLExpression* argument[1];
        if (GetFunctionArguments(functionCall, argument, 1) != 1)
        {
          Error("saturate expects 1 argument");
          return;
        }
        m_writer.Write("clamp(");
        OutputExpression(argument[0]);
        HLSLBaseType baseType = argument[0]->expressionType.baseType;
        switch (baseType) {
        case HLSLBaseType_Float:
        case HLSLBaseType_Float2:
        case HLSLBaseType_Float3:
        case HLSLBaseType_Float4:
          m_writer.Write(", 0.0, 1.0)");
          break;

        case HLSLBaseType_Int:
        case HLSLBaseType_Int2:
        case HLSLBaseType_Int3:
        case HLSLBaseType_Int4:
        case HLSLBaseType_Uint:
        case HLSLBaseType_Uint2:
        case HLSLBaseType_Uint3:
        case HLSLBaseType_Uint4:
          m_writer.Write(", 0, 1)");
          break;

        default:
          Error("saturate unhandled type: %s", GetTypeName(argument[0]->expressionType));
          break;
        }
        handled = true;
      }
      else if (String_Equal(functionName, "rsqrt"))
      {
        HLSLExpression* argument[1];
        if (GetFunctionArguments(functionCall, argument, 1) != 1)
        {
          Error("rsqrt expects 1 argument");
          return;
        }
        m_writer.Write("inversesqrt(");
        OutputExpression(argument[0]);
        m_writer.Write(")");
        handled = true;
      }

      if (!handled)
      {
        OutputIdentifier(functionName);
        m_writer.Write("(");
        OutputExpressionList(functionCall->argument, functionCall->function->argument);
        m_writer.Write(")");
      }
    }
    else
    {
      m_writer.Write("<unknown expression>");
    }

    if (cast)
    {
      if (IsVectorType(dstType->baseType) || IsMatrixType(dstType->baseType))
      {
        CompleteConstructorArguments(expression, dstType->baseType);
      }

      m_writer.Write(")");
    }

  }

  void GLSLGenerator::OutputIdentifier(const char* name)
  {

    // Remap intrinstic functions.
    if (String_Equal(name, "tex2D"))
    {
      name = m_versionLegacy ? "texture2D" : "texture";
    }
    else if (String_Equal(name, "tex2Dproj"))
    {
      name = m_versionLegacy ? "texture2DProj" : "textureProj";
    }
    else if (String_Equal(name, "texCUBE"))
    {
      name = m_versionLegacy ? "textureCube" : "texture";
    }
    else if (String_Equal(name, "tex3D"))
    {
      name = m_versionLegacy ? "texture3D" : "texture";
    }
    else if (String_Equal(name, "clip"))
    {
      name = m_clipFunction;
    }
    else if (String_Equal(name, "tex2Dlod"))
    {
      name = m_tex2DlodFunction;
    }
    else if (String_Equal(name, "tex2Dbias"))
    {
      name = m_tex2DbiasFunction;
    }
    else if (String_Equal(name, "tex2Dgrad"))
    {
      name = m_tex2DgradFunction;
    }
    else if (String_Equal(name, "tex2DArray"))
    {
      name = "texture";
    }
    else if (String_Equal(name, "texCUBEbias"))
    {
      name = m_texCUBEbiasFunction;
    }
    else if (String_Equal(name, "texCUBElod"))
    {
      name = m_texCUBElodFunction;
    }
    else if (String_Equal(name, "atan2"))
    {
      name = "atan";
    }
    else if (String_Equal(name, "sincos"))
    {
      name = m_sinCosFunction;
    }
    else if (String_Equal(name, "fmod"))
    {
      // mod is not the same as fmod if the parameter is negative!
      // The equivalent of fmod(x, y) is x - y * floor(x/y)
      // We use the mod version for performance.
      name = "mod";
    }
    else if (String_Equal(name, "lerp"))
    {
      name = "mix";
    }
    else if (String_Equal(name, "frac"))
    {
      name = "fract";
    }
    else if (String_Equal(name, "ddx"))
    {
      name = "dFdx";
    }
    else if (String_Equal(name, "ddy"))
    {
      name = "dFdy";
    }
    else if (String_Equal(name, "modf"))
    {
      name = m_modfFunction;
    }
    else
    {
      // The identifier could be a GLSL reserved word (if it's not also a HLSL reserved word).
      name = GetSafeIdentifierName(name);
    }
    m_writer.Write("%s", name);

  }

  void GLSLGenerator::OutputArguments(HLSLArgument* argument)
  {
    int numArgs = 0;
    while (argument != nullptr)
    {
      if (numArgs > 0)
      {
        m_writer.Write(", ");
      }

      switch (argument->modifier)
      {
      case HLSLArgumentModifier_In:
        m_writer.Write("in ");
        break;
      case HLSLArgumentModifier_Out:
        m_writer.Write("out ");
        break;
      case HLSLArgumentModifier_Inout:
        m_writer.Write("inout ");
        break;
      default:
        break;
      }

      OutputDeclaration(argument->type, argument->name);
      argument = argument->nextArgument;
      ++numArgs;
    }
  }

  void GLSLGenerator::OutputStatements(int indent, HLSLStatement* statement, const HLSLType* returnType)
  {

    while (statement != nullptr)
    {
      if (statement->hidden)
      {
        statement = statement->nextStatement;
        continue;
      }

      if (statement->nodeType == HLSLNodeType_Declaration)
      {
        HLSLDeclaration* declaration = static_cast<HLSLDeclaration*>(statement);

        // GLSL doesn't seem have texture uniforms, so just ignore them.
        if (declaration->type.baseType != HLSLBaseType_Texture)
        {
          bool skipAssignment = true;
          if (indent != 0)
          {
            skipAssignment = false;
          }

          m_writer.BeginLine(indent, declaration->fileName, declaration->line);
          if (indent == 0 && (declaration->type.flags & HLSLTypeFlag_Uniform))
          {
            // At the top level, we need the "uniform" keyword.
            m_writer.Write("uniform ");
            skipAssignment = false;
          }
          OutputDeclaration(declaration, skipAssignment);
          m_writer.EndLine(";");
        }
      }
      else if (statement->nodeType == HLSLNodeType_Struct)
      {
        HLSLStruct* structure = static_cast<HLSLStruct*>(statement);
        m_writer.WriteLine(indent, "struct %s {", structure->name);
        HLSLStructField* field = structure->field;
        while (field != nullptr)
        {
          m_writer.BeginLine(indent + 1, field->fileName, field->line);
          OutputDeclaration(field->type, field->name);
          m_writer.Write(";");
          m_writer.EndLine();
          field = field->nextField;
        }
        m_writer.WriteLine(indent, "};");
      }
      else if (statement->nodeType == HLSLNodeType_Buffer)
      {
        HLSLBuffer* buffer = static_cast<HLSLBuffer*>(statement);
        OutputBuffer(indent, buffer);
      }
      else if (statement->nodeType == HLSLNodeType_Function)
      {
        HLSLFunction* function = static_cast<HLSLFunction*>(statement);

        // Use an alternate name for the function which is supposed to be entry point
        // so that we can supply our own function which will be the actual entry point.
        const char* functionName = GetSafeIdentifierName(function->name);
        const char* returnTypeName = GetTypeName(function->returnType);

        m_writer.BeginLine(indent, function->fileName, function->line);
        m_writer.Write("%s %s(", returnTypeName, functionName);

        OutputArguments(function->argument);

        if (function->forward)
        {
          m_writer.WriteLine(indent, ");");
        }
        else
        {
          m_writer.Write(") {");
          m_writer.EndLine();

          OutputStatements(indent + 1, function->statement, &function->returnType);
          m_writer.WriteLine(indent, "}");
        }
      }
      else if (statement->nodeType == HLSLNodeType_ExpressionStatement)
      {
        HLSLExpressionStatement* expressionStatement = static_cast<HLSLExpressionStatement*>(statement);
        m_writer.BeginLine(indent, statement->fileName, statement->line);
        OutputExpression(expressionStatement->expression);
        m_writer.EndLine(";");
      }
      else if (statement->nodeType == HLSLNodeType_ReturnStatement)
      {
        HLSLReturnStatement* returnStatement = static_cast<HLSLReturnStatement*>(statement);
        if (returnStatement->expression != nullptr)
        {
          m_writer.BeginLine(indent, returnStatement->fileName, returnStatement->line);
          m_writer.Write("return ");
          OutputExpression(returnStatement->expression, returnType);
          m_writer.EndLine(";");
        }
        else
        {
          m_writer.WriteLineTagged(indent, returnStatement->fileName, returnStatement->line, "return;");
        }
      }
      else if (statement->nodeType == HLSLNodeType_DiscardStatement)
      {
        HLSLDiscardStatement* discardStatement = static_cast<HLSLDiscardStatement*>(statement);
        if (m_target == Target_FragmentShader)
        {
          m_writer.WriteLineTagged(indent, discardStatement->fileName, discardStatement->line, "discard;");
        }
      }
      else if (statement->nodeType == HLSLNodeType_BreakStatement)
      {
        HLSLBreakStatement* breakStatement = static_cast<HLSLBreakStatement*>(statement);
        m_writer.WriteLineTagged(indent, breakStatement->fileName, breakStatement->line, "break;");
      }
      else if (statement->nodeType == HLSLNodeType_ContinueStatement)
      {
        HLSLContinueStatement* continueStatement = static_cast<HLSLContinueStatement*>(statement);
        m_writer.WriteLineTagged(indent, continueStatement->fileName, continueStatement->line, "continue;");
      }
      else if (statement->nodeType == HLSLNodeType_IfStatement)
      {
        HLSLIfStatement* ifStatement = static_cast<HLSLIfStatement*>(statement);
        m_writer.BeginLine(indent, ifStatement->fileName, ifStatement->line);
        m_writer.Write("if (");
        OutputExpression(ifStatement->condition, &kBoolType);
        m_writer.Write(") {");
        m_writer.EndLine();
        OutputStatements(indent + 1, ifStatement->statement, returnType);
        m_writer.WriteLine(indent, "}");
        if (ifStatement->elseStatement != nullptr)
        {
          m_writer.WriteLine(indent, "else {");
          OutputStatements(indent + 1, ifStatement->elseStatement, returnType);
          m_writer.WriteLine(indent, "}");
        }
      }
      else if (statement->nodeType == HLSLNodeType_ForStatement)
      {
        HLSLForStatement* forStatement = static_cast<HLSLForStatement*>(statement);
        m_writer.BeginLine(indent, forStatement->fileName, forStatement->line);
        m_writer.Write("for (");
        if (forStatement->initialization != nullptr)
        {
          OutputDeclaration(forStatement->initialization, false);
        }
        else
        {
          OutputExpression(forStatement->initializationWithoutType);
        }
        m_writer.Write("; ");
        OutputExpression(forStatement->condition, &kBoolType);
        m_writer.Write("; ");
        OutputExpression(forStatement->increment);
        m_writer.Write(") {");
        m_writer.EndLine();
        OutputStatements(indent + 1, forStatement->statement, returnType);
        m_writer.WriteLine(indent, "}");
      }
      else if (statement->nodeType == HLSLNodeType_WhileStatement)
      {
        HLSLWhileStatement* whileStatement = static_cast<HLSLWhileStatement*>(statement);
        m_writer.BeginLine(indent, whileStatement->fileName, whileStatement->line);
        m_writer.Write("while (");
        OutputExpression(whileStatement->condition, &kBoolType);
        m_writer.Write(") {");
        m_writer.EndLine();
        OutputStatements(indent + 1, whileStatement->statement, returnType);
        m_writer.WriteLine(indent, "}");
      }
      else if (statement->nodeType == HLSLNodeType_BlockStatement)
      {
        HLSLBlockStatement* blockStatement = static_cast<HLSLBlockStatement*>(statement);
        m_writer.WriteLine(indent, "{");
        OutputStatements(indent + 1, blockStatement->statement, returnType);
        m_writer.WriteLine(indent, "}");
      }
      else
      {
        // Unhanded statement type.
        ASSERT(0);
      }

      statement = statement->nextStatement;

    }

  }

  void GLSLGenerator::OutputBuffer(int indent, HLSLBuffer* buffer)
  {
    // Empty uniform blocks cause compilation errors on NVIDIA, so don't emit them.
    if (buffer->field == nullptr)
      return;

    if (m_options.flags & Flag_EmulateConstantBuffer)
    {
      unsigned int size = 0;
      LayoutBuffer(buffer, size);

      unsigned int uniformSize = (size + 3) / 4;

      m_writer.WriteLineTagged(indent, buffer->fileName, buffer->line, "uniform vec4 %s%s[%d];", m_options.constantBufferPrefix, buffer->name, uniformSize);
    }
    else
    {
      m_writer.WriteLineTagged(indent, buffer->fileName, buffer->line, "layout (std140) uniform %s%s {", m_options.constantBufferPrefix, buffer->name);
      HLSLDeclaration* field = buffer->field;
      while (field != nullptr)
      {
        m_writer.BeginLine(indent + 1, field->fileName, field->line);
        OutputDeclaration(field->type, field->name);
        m_writer.Write(";");
        m_writer.EndLine();
        field = (HLSLDeclaration*)field->nextStatement;
      }
      m_writer.WriteLine(indent, "};");
    }
  }

  inline void alignForWrite(unsigned int& offset, unsigned int size)
  {
    ASSERT(size <= 4);

    if (offset / 4 != (offset + size - 1) / 4)
      offset = (offset + 3) & ~3;
  }

  void GLSLGenerator::LayoutBuffer(HLSLBuffer* buffer, unsigned int& offset)
  {
    for (HLSLDeclaration* field = buffer->field; field; field = (HLSLDeclaration*)field->nextStatement)
    {
      LayoutBuffer(field->type, offset);
    }
  }

  void GLSLGenerator::LayoutBuffer(const HLSLType& type, unsigned int& offset)
  {
    LayoutBufferAlign(type, offset);

    if (type.array)
    {
      int arraySize = 0;
      m_tree->GetExpressionValue(type.arraySize, arraySize);

      unsigned int elementSize = 0;
      LayoutBufferElement(type, elementSize);

      unsigned int alignedElementSize = (elementSize + 3) & ~3;

      offset += alignedElementSize * arraySize;
    }
    else
    {
      LayoutBufferElement(type, offset);
    }
  }

  void GLSLGenerator::LayoutBufferElement(const HLSLType& type, unsigned int& offset)
  {
    if (type.baseType == HLSLBaseType_Float)
    {
      offset += 1;
    }
    else if (type.baseType == HLSLBaseType_Float2)
    {
      offset += 2;
    }
    else if (type.baseType == HLSLBaseType_Float3)
    {
      offset += 3;
    }
    else if (type.baseType == HLSLBaseType_Float4)
    {
      offset += 4;
    }
    else if (type.baseType == HLSLBaseType_Float4x4)
    {
      offset += 16;
    }
    else if (type.baseType == HLSLBaseType_UserDefined)
    {
      HLSLStruct* st = m_tree->FindGlobalStruct(type.typeName);

      if (st)
      {
        for (HLSLStructField* field = st->field; field; field = field->nextField)
        {
          LayoutBuffer(field->type, offset);
        }
      }
      else
      {
        Error("Unknown type %s", type.typeName);
      }
    }
    else
    {
      Error("Constant buffer layout is not supported for %s", GetTypeName(type));
    }
  }

  void GLSLGenerator::LayoutBufferAlign(const HLSLType& type, unsigned int& offset)
  {
    if (type.array)
    {
      alignForWrite(offset, 4);
    }
    else if (type.baseType == HLSLBaseType_Float)
    {
      alignForWrite(offset, 1);
    }
    else if (type.baseType == HLSLBaseType_Float2)
    {
      alignForWrite(offset, 2);
    }
    else if (type.baseType == HLSLBaseType_Float3)
    {
      alignForWrite(offset, 3);
    }
    else if (type.baseType == HLSLBaseType_Float4)
    {
      alignForWrite(offset, 4);
    }
    else if (type.baseType == HLSLBaseType_Float4x4)
    {
      alignForWrite(offset, 4);
    }
    else if (type.baseType == HLSLBaseType_UserDefined)
    {
      alignForWrite(offset, 4);
    }
    else
    {
      Error("Constant buffer layout is not supported for %s", GetTypeName(type));
    }
  }

  HLSLBuffer* GLSLGenerator::GetBufferAccessExpression(HLSLExpression* expression)
  {
    if (expression->nodeType == HLSLNodeType_IdentifierExpression)
    {
      HLSLIdentifierExpression* identifierExpression = static_cast<HLSLIdentifierExpression*>(expression);

      if (identifierExpression->global)
      {
        HLSLDeclaration* declaration = m_tree->FindGlobalDeclaration(identifierExpression->name);

        if (declaration && declaration->buffer)
          return declaration->buffer;
      }
    }
    else if (expression->nodeType == HLSLNodeType_MemberAccess)
    {
      HLSLMemberAccess* memberAccess = static_cast<HLSLMemberAccess*>(expression);

      if (memberAccess->object->expressionType.baseType == HLSLBaseType_UserDefined)
        return GetBufferAccessExpression(memberAccess->object);
    }
    else if (expression->nodeType == HLSLNodeType_ArrayAccess)
    {
      HLSLArrayAccess* arrayAccess = static_cast<HLSLArrayAccess*>(expression);

      if (arrayAccess->array->expressionType.array)
        return GetBufferAccessExpression(arrayAccess->array);
    }

    return 0;
  }

  void GLSLGenerator::OutputBufferAccessExpression(HLSLBuffer* buffer, HLSLExpression* expression, const HLSLType& type, unsigned int postOffset)
  {
    if (type.array)
    {
      Error("Constant buffer access is not supported for arrays (use indexing instead)");
    }
    else if (type.baseType == HLSLBaseType_Float)
    {
      m_writer.Write("%s%s[", m_options.constantBufferPrefix, buffer->name);
      unsigned int index = OutputBufferAccessIndex(expression, postOffset);
      m_writer.Write("%d].%c", index / 4, "xyzw"[index % 4]);
    }
    else if (type.baseType == HLSLBaseType_Float2)
    {
      m_writer.Write("%s%s[", m_options.constantBufferPrefix, buffer->name);
      unsigned int index = OutputBufferAccessIndex(expression, postOffset);
      m_writer.Write("%d].%s", index / 4, index % 4 == 0 ? "xy" : index % 4 == 1 ? "yz" : "zw");
    }
    else if (type.baseType == HLSLBaseType_Float3)
    {
      m_writer.Write("%s%s[", m_options.constantBufferPrefix, buffer->name);
      unsigned int index = OutputBufferAccessIndex(expression, postOffset);
      m_writer.Write("%d].%s", index / 4, index % 4 == 0 ? "xyz" : "yzw");
    }
    else if (type.baseType == HLSLBaseType_Float4)
    {
      m_writer.Write("%s%s[", m_options.constantBufferPrefix, buffer->name);
      unsigned int index = OutputBufferAccessIndex(expression, postOffset);
      ASSERT(index % 4 == 0);
      m_writer.Write("%d]", index / 4);
    }
    else if (type.baseType == HLSLBaseType_Float4x4)
    {
      m_writer.Write("mat4(");
      for (int i = 0; i < 4; ++i)
      {
        m_writer.Write("%s%s[", m_options.constantBufferPrefix, buffer->name);
        unsigned int index = OutputBufferAccessIndex(expression, postOffset + i * 4);
        ASSERT(index % 4 == 0);
        m_writer.Write("%d]%c", index / 4, i == 3 ? ')' : ',');
      }
    }
    else if (type.baseType == HLSLBaseType_UserDefined)
    {
      HLSLStruct* st = m_tree->FindGlobalStruct(type.typeName);

      if (st)
      {
        m_writer.Write("%s(", st->name);

        unsigned int offset = postOffset;

        for (HLSLStructField* field = st->field; field; field = field->nextField)
        {
          OutputBufferAccessExpression(buffer, expression, field->type, offset);

          if (field->nextField)
            m_writer.Write(",");

          LayoutBuffer(field->type, offset);
        }

        m_writer.Write(")");
      }
      else
      {
        Error("Unknown type %s", type.typeName);
      }
    }
    else
    {
      Error("Constant buffer layout is not supported for %s", GetTypeName(type));
    }
  }

  unsigned int GLSLGenerator::OutputBufferAccessIndex(HLSLExpression* expression, unsigned int postOffset)
  {
    if (expression->nodeType == HLSLNodeType_IdentifierExpression)
    {
      HLSLIdentifierExpression* identifierExpression = static_cast<HLSLIdentifierExpression*>(expression);
      ASSERT(identifierExpression->global);

      HLSLDeclaration* declaration = m_tree->FindGlobalDeclaration(identifierExpression->name);
      ASSERT(declaration);

      HLSLBuffer* buffer = declaration->buffer;
      ASSERT(buffer);

      unsigned int offset = 0;

      for (HLSLDeclaration* field = buffer->field; field; field = (HLSLDeclaration*)field->nextStatement)
      {
        if (field == declaration)
        {
          LayoutBufferAlign(field->type, offset);
          break;
        }

        LayoutBuffer(field->type, offset);
      }

      return offset + postOffset;
    }
    else if (expression->nodeType == HLSLNodeType_MemberAccess)
    {
      HLSLMemberAccess* memberAccess = static_cast<HLSLMemberAccess*>(expression);

      const HLSLType& type = memberAccess->object->expressionType;
      ASSERT(type.baseType == HLSLBaseType_UserDefined);

      HLSLStruct* st = m_tree->FindGlobalStruct(type.typeName);

      if (st)
      {
        unsigned int offset = 0;

        for (HLSLStructField* field = st->field; field; field = field->nextField)
        {
          if (field->name == memberAccess->field)
          {
            LayoutBufferAlign(field->type, offset);
            break;
          }

          LayoutBuffer(field->type, offset);
        }

        return offset + OutputBufferAccessIndex(memberAccess->object, postOffset);
      }
      else
      {
        Error("Unknown type %s", type.typeName);
      }
    }
    else if (expression->nodeType == HLSLNodeType_ArrayAccess)
    {
      HLSLArrayAccess* arrayAccess = static_cast<HLSLArrayAccess*>(expression);

      const HLSLType& type = arrayAccess->array->expressionType;
      ASSERT(type.array);

      unsigned int elementSize = 0;
      LayoutBufferElement(type, elementSize);

      unsigned int alignedElementSize = (elementSize + 3) & ~3;

      int arrayIndex = 0;
      if (m_tree->GetExpressionValue(arrayAccess->index, arrayIndex))
      {
        unsigned int offset = arrayIndex * alignedElementSize;

        return offset + OutputBufferAccessIndex(arrayAccess->array, postOffset);
      }
      else
      {
        m_writer.Write("%d*(", alignedElementSize / 4);
        OutputExpression(arrayAccess->index);
        m_writer.Write(")+");

        return OutputBufferAccessIndex(arrayAccess->array, postOffset);
      }
    }
    else
    {
      ASSERT(!"IsBufferAccessExpression should have returned false");
    }

    return 0;
  }

  HLSLFunction* GLSLGenerator::FindFunction(HLSLRoot* root, const char* name)
  {
    HLSLStatement* statement = root->statement;
    while (statement != nullptr)
    {
      if (statement->nodeType == HLSLNodeType_Function)
      {
        HLSLFunction* function = static_cast<HLSLFunction*>(statement);
        if (String_Equal(function->name, name))
        {
          return function;
        }
      }
      statement = statement->nextStatement;
    }
    return nullptr;
  }

  HLSLStruct* GLSLGenerator::FindStruct(HLSLRoot* root, const char* name)
  {
    HLSLStatement* statement = root->statement;
    while (statement != nullptr)
    {
      if (statement->nodeType == HLSLNodeType_Struct)
      {
        HLSLStruct* structDeclaration = static_cast<HLSLStruct*>(statement);
        if (String_Equal(structDeclaration->name, name))
        {
          return structDeclaration;
        }
      }
      statement = statement->nextStatement;
    }
    return nullptr;
  }


  const char* GLSLGenerator::GetAttribQualifier(AttributeModifier modifier)
  {
    if (m_versionLegacy)
    {
      if (m_target == Target_VertexShader)
        return (modifier == AttributeModifier_In) ? "attribute" : "varying";
      else
        return (modifier == AttributeModifier_In) ? "varying" : "out";
    }
    else
    {
      return (modifier == AttributeModifier_In) ? "in" : "out";
    }
  }

  void GLSLGenerator::OutputAttribute(const HLSLType& type, const char* semantic, AttributeModifier modifier)
  {
    const char* qualifier = GetAttribQualifier(modifier);
    const char* prefix = (modifier == AttributeModifier_In) ? m_inAttribPrefix : m_outAttribPrefix;

    HLSLRoot* root = m_tree->GetRoot();
    if (type.baseType == HLSLBaseType_UserDefined)
    {
      // If the argument is a struct with semantics specified, we need to
      // grab them.
      HLSLStruct* structDeclaration = FindStruct(root, type.typeName);
      ASSERT(structDeclaration != nullptr);
      HLSLStructField* field = structDeclaration->field;
      while (field != nullptr)
      {
        if (field->semantic != nullptr && GetBuiltInSemantic(field->semantic, modifier) == nullptr)
        {
          m_writer.Write("%s ", qualifier);
          char attribName[64];
          String_Printf(attribName, 64, "%s%s", prefix, field->semantic);
          OutputDeclaration(field->type, attribName);
          m_writer.EndLine(";");
        }
        field = field->nextField;
      }
    }
    else if (semantic != nullptr && GetBuiltInSemantic(semantic, modifier) == nullptr)
    {
      m_writer.Write("%s ", qualifier);
      char attribName[64];
      String_Printf(attribName, 64, "%s%s", prefix, semantic);
      OutputDeclaration(type, attribName);
      m_writer.EndLine(";");
    }
  }

  void GLSLGenerator::OutputAttributes(HLSLFunction* entryFunction)
  {
    // Write out the input/output attributes to the shader.
    HLSLArgument* argument = entryFunction->argument;
    while (argument != nullptr)
    {
      if (argument->modifier == HLSLArgumentModifier_None || argument->modifier == HLSLArgumentModifier_In)
        OutputAttribute(argument->type, argument->semantic, AttributeModifier_In);
      if (argument->modifier == HLSLArgumentModifier_Out)
        OutputAttribute(argument->type, argument->semantic, AttributeModifier_Out);

      argument = argument->nextArgument;
    }

    // Write out the output attributes from the shader.
    OutputAttribute(entryFunction->returnType, entryFunction->semantic, AttributeModifier_Out);
  }

  void GLSLGenerator::OutputSetOutAttribute(const char* semantic, const char* resultName)
  {
    int outputIndex = -1;
    const char* builtInSemantic = GetBuiltInSemantic(semantic, AttributeModifier_Out, &outputIndex);
    if (builtInSemantic != nullptr)
    {
      if (String_Equal(builtInSemantic, "gl_Position"))
      {
        if (m_options.flags & Flag_FlipPositionOutput)
        {
          // Mirror the y-coordinate when we're outputing from
          // the vertex shader so that we match the D3D texture
          // coordinate origin convention in render-to-texture
          // operations.
          // We also need to convert the normalized device
          // coordinates from the D3D convention of 0 to 1 to the
          // OpenGL convention of -1 to 1.
          m_writer.WriteLine(1, "vec4 temp = %s;", resultName);
          m_writer.WriteLine(1, "%s = temp * vec4(1,-1,2,1) - vec4(0,0,temp.w,0);", builtInSemantic);
        }
        else
        {
          m_writer.WriteLine(1, "%s = %s;", builtInSemantic, resultName);
        }

        m_outputPosition = true;
      }
      else if (String_Equal(builtInSemantic, "gl_FragDepth"))
      {
        // If the value goes outside of the 0 to 1 range, the
        // fragment will be rejected unlike in D3D, so clamp it.
        m_writer.WriteLine(1, "%s = clamp(float(%s), 0.0, 1.0);", builtInSemantic, resultName);
      }
      else if (outputIndex >= 0)
      {
        m_writer.WriteLine(1, "%s[%d] = %s;", builtInSemantic, outputIndex, resultName);
      }
      else
      {
        m_writer.WriteLine(1, "%s = %s;", builtInSemantic, resultName);
      }
    }
    else if (m_target == Target_FragmentShader)
    {
      Error("Output attribute %s does not map to any built-ins", semantic);
    }
    else
    {
      m_writer.WriteLine(1, "%s%s = %s;", m_outAttribPrefix, semantic, resultName);
    }
  }

  void GLSLGenerator::OutputEntryCaller(HLSLFunction* entryFunction)
  {
    HLSLRoot* root = m_tree->GetRoot();

    m_writer.WriteLine(0, "void main() {");

    // Create local variables for each of the parameters we'll need to pass
    // into the entry point function.
    HLSLArgument* argument = entryFunction->argument;
    while (argument != nullptr)
    {
      m_writer.BeginLine(1);
      OutputDeclaration(argument->type, argument->name);
      m_writer.EndLine(";");

      if (argument->modifier != HLSLArgumentModifier_Out)
      {
        // Set the value for the local variable.
        if (argument->type.baseType == HLSLBaseType_UserDefined)
        {
          HLSLStruct* structDeclaration = FindStruct(root, argument->type.typeName);
          ASSERT(structDeclaration != nullptr);
          HLSLStructField* field = structDeclaration->field;
          while (field != nullptr)
          {
            if (field->semantic != nullptr)
            {
              const char* builtInSemantic = GetBuiltInSemantic(field->semantic, AttributeModifier_In);
              if (builtInSemantic)
              {
                m_writer.WriteLine(1, "%s.%s = %s;", GetSafeIdentifierName(argument->name), GetSafeIdentifierName(field->name), builtInSemantic);
              }
              else
              {
                m_writer.WriteLine(1, "%s.%s = %s%s;", GetSafeIdentifierName(argument->name), GetSafeIdentifierName(field->name), m_inAttribPrefix, field->semantic);
              }
            }
            field = field->nextField;
          }
        }
        else if (argument->semantic != nullptr)
        {
          const char* builtInSemantic = GetBuiltInSemantic(argument->semantic, AttributeModifier_In);
          if (builtInSemantic)
          {
            m_writer.WriteLine(1, "%s = %s;", GetSafeIdentifierName(argument->name), builtInSemantic);
          }
          else
          {
            m_writer.WriteLine(1, "%s = %s%s;", GetSafeIdentifierName(argument->name), m_inAttribPrefix, argument->semantic);
          }
        }
      }

      argument = argument->nextArgument;
    }


    // Initialize global variables
    for (HLSLDeclaration* declaration : globalVarsAssignments)
    {
      m_writer.BeginLine(1, declaration->fileName, declaration->line);
      OutputDeclarationBody(declaration->type, GetSafeIdentifierName(declaration->name));

      OutputDeclarationAssignment(declaration);
      m_writer.EndLine(";");
    }


    const char* resultName = "result";

    // Call the original entry function.
    m_writer.BeginLine(1);
    if (entryFunction->returnType.baseType != HLSLBaseType_Void)
      m_writer.Write("%s %s = ", GetTypeName(entryFunction->returnType), resultName);
    m_writer.Write("%s(", m_entryName);

    int numArgs = 0;
    argument = entryFunction->argument;
    while (argument != nullptr)
    {
      if (numArgs > 0)
      {
        m_writer.Write(", ");
      }

      m_writer.Write("%s", GetSafeIdentifierName(argument->name));

      argument = argument->nextArgument;
      ++numArgs;
    }
    m_writer.EndLine(");");

    // Copy values from the result into the out attributes as necessary.
    argument = entryFunction->argument;
    while (argument != nullptr)
    {
      if (argument->modifier == HLSLArgumentModifier_Out && argument->semantic)
        OutputSetOutAttribute(argument->semantic, GetSafeIdentifierName(argument->name));

      argument = argument->nextArgument;
    }

    if (entryFunction->returnType.baseType == HLSLBaseType_UserDefined)
    {
      HLSLStruct* structDeclaration = FindStruct(root, entryFunction->returnType.typeName);
      ASSERT(structDeclaration != nullptr);
      HLSLStructField* field = structDeclaration->field;
      while (field != nullptr)
      {
        char fieldResultName[1024];
        String_Printf(fieldResultName, sizeof(fieldResultName), "%s.%s", resultName, field->name);
        OutputSetOutAttribute(field->semantic, fieldResultName);
        field = field->nextField;
      }
    }
    else if (entryFunction->semantic != nullptr)
    {
      OutputSetOutAttribute(entryFunction->semantic, resultName);
    }

    m_writer.WriteLine(0, "}");
  }

  void GLSLGenerator::OutputDeclaration(HLSLDeclaration* declaration, const bool skipAssignment)
  {
    OutputDeclarationType(declaration->type);

    HLSLDeclaration* lastDecl = nullptr;
    while (declaration)
    {
      if (lastDecl)
        m_writer.Write(", ");

      OutputDeclarationBody(declaration->type, GetSafeIdentifierName(declaration->name));

      if (declaration->assignment != nullptr)
      {
        if (!skipAssignment)
        {
          OutputDeclarationAssignment(declaration);
        }
        else
        {
          globalVarsAssignments.push_back(declaration);
        }
      }

      lastDecl = declaration;
      declaration = declaration->nextDeclaration;
    }
  }

  void GLSLGenerator::OutputDeclarationAssignment(HLSLDeclaration* declaration)
  {
    m_writer.Write(" = ");
    if (declaration->type.array)
    {
      m_writer.Write("%s[]( ", GetTypeName(declaration->type));
      OutputExpressionList(declaration->assignment);
      m_writer.Write(" )");
    }
    else
    {
      bool matrixCtorNeeded = false;
      if (IsMatrixType(declaration->type.baseType))
      {
        matrixCtor ctor = matrixCtorBuilder(declaration->type, declaration->assignment);
        if (std::find(matrixCtors.cbegin(), matrixCtors.cend(), ctor) != matrixCtors.cend())
        {
          matrixCtorNeeded = true;
        }
      }

      if (matrixCtorNeeded)
      {
        // Matrix contructors needs to be adapted since GLSL access a matrix as m[c][r] while HLSL is m[r][c]
        matrixCtor ctor = matrixCtorBuilder(declaration->type, declaration->assignment);
        m_writer.Write("%s(", matrixCtorsId[ctor].c_str());
        OutputExpressionList(declaration->assignment);
        m_writer.Write(")");
      }
      else
      {
        m_writer.Write("%s( ", GetTypeName(declaration->type));
        OutputExpressionList(declaration->assignment);
        m_writer.Write(" )");
      }
    }
  }

  void GLSLGenerator::OutputDeclaration(const HLSLType& type, const char* name)
  {
    OutputDeclarationType(type);
    OutputDeclarationBody(type, name);
  }

  void GLSLGenerator::OutputDeclarationType(const HLSLType& type)
  {
    m_writer.Write("%s ", GetTypeName(type));
  }

  void GLSLGenerator::OutputDeclarationBody(const HLSLType& type, const char* name)
  {
    if (!type.array)
    {
      m_writer.Write("%s", GetSafeIdentifierName(name));
    }
    else
    {
      m_writer.Write("%s[", GetSafeIdentifierName(name));
      if (type.arraySize != nullptr)
      {
        OutputExpression(type.arraySize);
      }
      m_writer.Write("]");
    }
  }

  void GLSLGenerator::OutputCast(const HLSLType& type)
  {
    if ((m_version == Version_110 || m_version == Version_120) && type.baseType == HLSLBaseType_Float3x3)
      m_writer.Write("%s", m_matrixCtorFunction);
    else
      OutputDeclaration(type, "");
  }

  void GLSLGenerator::Error(const char* format, ...)
  {
    // It's not always convenient to stop executing when an error occurs,
    // so just track once we've hit an error and stop reporting them until
    // we successfully bail out of execution.
    if (m_error)
    {
      return;
    }
    m_error = true;

    va_list arg;
    va_start(arg, format);
    Log_ErrorArgList(format, arg);
    va_end(arg);
  }

  const char* GLSLGenerator::GetSafeIdentifierName(const char* name) const
  {
    for (int i = 0; i < s_numReservedWords; ++i)
    {
      if (String_Equal(s_reservedWord[i], name))
      {
        return m_reservedWord[i];
      }
    }
    return name;
  }

  bool GLSLGenerator::ChooseUniqueName(const char* base, char* dst, int dstLength) const
  {
    for (int i = 0; i < 1024; ++i)
    {
      String_Printf(dst, dstLength, "%s%d", base, i);
      if (!m_tree->GetContainsString(dst))
      {
        return true;
      }
    }
    return false;
  }

  const char* GLSLGenerator::GetBuiltInSemantic(const char* semantic, AttributeModifier modifier, int* outputIndex)
  {
    if (outputIndex)
      *outputIndex = -1;

    if (m_target == Target_VertexShader && modifier == AttributeModifier_Out && String_Equal(semantic, "POSITION"))
      return "gl_Position";

    if (m_target == Target_VertexShader && modifier == AttributeModifier_Out && String_Equal(semantic, "SV_Position"))
      return "gl_Position";

    if (m_target == Target_VertexShader && modifier == AttributeModifier_Out && String_Equal(semantic, "PSIZE"))
      return "gl_PointSize";

    if (m_target == Target_VertexShader && modifier == AttributeModifier_In && String_Equal(semantic, "SV_InstanceID"))
      return "gl_InstanceID";

    if (m_target == Target_FragmentShader && modifier == AttributeModifier_Out && String_Equal(semantic, "SV_Depth"))
      return "gl_FragDepth";

    if (m_target == Target_FragmentShader && modifier == AttributeModifier_In && String_Equal(semantic, "SV_Position"))
      return "gl_FragCoord";

    if (m_target == Target_FragmentShader && modifier == AttributeModifier_Out)
    {
      int index = -1;

      if (strncmp(semantic, "COLOR", 5) == 0)
        index = atoi(semantic + 5);
      else if (strncmp(semantic, "SV_Target", 9) == 0)
        index = atoi(semantic + 9);

      if (index >= 0)
      {
        if (m_outputTargets <= index)
          m_outputTargets = index + 1;

        if (outputIndex)
          *outputIndex = index;

        return m_versionLegacy ? "gl_FragData" : "rast_FragData";
      }
    }

    return nullptr;
  }

  void GLSLGenerator::CompleteConstructorArguments(HLSLExpression* expression, HLSLBaseType dstType)
  {
    int nbComponentsProvided = 0;
    int nbComponentsNeeded = 0;

    const BaseTypeDescription& dstTypeDesc = baseTypeDescriptions[dstType];
    nbComponentsNeeded = dstTypeDesc.numComponents * dstTypeDesc.height;

    const BaseTypeDescription& srcTypeDesc = baseTypeDescriptions[expression->expressionType.baseType];
    nbComponentsProvided = srcTypeDesc.numComponents * srcTypeDesc.height;
    if (IsMatrixType(expression->expressionType.baseType) ||
      IsVectorType(expression->expressionType.baseType))
    {
      for (int i = nbComponentsProvided; i < nbComponentsNeeded; i++)
      {
        m_writer.Write(", 0");
      }
    }
  }


  void GLSLGenerator::OutputMatrixCtors() {
    for (matrixCtor& ctor : matrixCtors)
    {
      m_writer.Write("%s %s(",
        GetTypeName(HLSLType(ctor.matrixType)),
        matrixCtorsId[ctor].c_str());
      int argNum = 0;
      for (HLSLBaseType argType : ctor.argumentTypes)
      {
        if (argNum == 0)
        {
          m_writer.Write("%s %c", GetTypeName(HLSLType(argType)), 'a' + argNum);
        }
        else
        {
          m_writer.Write(", %s %c", GetTypeName(HLSLType(argType)), 'a' + argNum);
        }
        argNum++;
      }
      m_writer.Write(") { return %s(", GetTypeName(HLSLType(ctor.matrixType)));

      const BaseTypeDescription& ctorTypeDesc = baseTypeDescriptions[ctor.matrixType];
      std::vector<std::string> args(ctorTypeDesc.numComponents * ctorTypeDesc.height, "0");
      int argNumIn = 0;
      int argNumOut = 0;
      for (HLSLBaseType argType : ctor.argumentTypes)
      {
        std::string arg;
        arg += 'a' + argNumIn;

        if (IsScalarType(argType))
        {
          int index = (argNumOut % ctorTypeDesc.height) * (ctorTypeDesc.numComponents) +
            (argNumOut / ctorTypeDesc.height);
          args[index] = arg;
          argNumOut++;
        }
        else if (IsVectorType(argType))
        {
          const BaseTypeDescription& argTypeDesc = baseTypeDescriptions[argType];
          for (int dim = 0; dim < argTypeDesc.numComponents; dim++)
          {
            std::string argVect = arg + ".";
            argVect += "xyzw"[dim];
            int index = (argNumOut % ctorTypeDesc.height) * (ctorTypeDesc.numComponents) +
              (argNumOut / ctorTypeDesc.height);
            args[index] = argVect;
            argNumOut++;
          }
        }

        argNumIn++;
      }

      bool first = true;
      for (std::string& arg : args)
      {
        if (!first)
        {
          m_writer.Write(",%s", arg.c_str());
        }
        else
        {
          m_writer.Write("%s", arg.c_str());
        }
        first = false;
      }

      m_writer.Write("); }");
      m_writer.EndLine();
    }
  }
}





#ifndef __etc1_h__
#define __etc1_h__

#define ETC1_ENCODED_BLOCK_SIZE 8
#define ETC1_DECODED_BLOCK_SIZE 48

#ifndef ETC1_RGB8_OES
#define ETC1_RGB8_OES 0x8D64
#endif

typedef unsigned char etc1_byte;
typedef int etc1_bool;
typedef unsigned int etc1_uint32;

#ifdef __cplusplus
extern "C" {
#endif

  void etc1_encode_block(const etc1_byte* pIn, etc1_uint32 validPixelMask, etc1_byte* pOut);

  void etc1_decode_block(const etc1_byte* pIn, etc1_byte* pOut);


  etc1_uint32 etc1_get_encoded_data_size(etc1_uint32 width, etc1_uint32 height);

  int etc1_encode_image(const etc1_byte* pIn, etc1_uint32 width, etc1_uint32 height,
    etc1_uint32 pixelSize, etc1_uint32 stride, etc1_byte* pOut);

  int etc1_decode_image(const etc1_byte* pIn, etc1_byte* pOut,
    etc1_uint32 width, etc1_uint32 height,
    etc1_uint32 pixelSize, etc1_uint32 stride);

#define ETC_PKM_HEADER_SIZE 16

  void etc1_pkm_format_header(etc1_byte* pHeader, etc1_uint32 width, etc1_uint32 height);

  etc1_bool etc1_pkm_is_valid(const etc1_byte* pHeader);

  etc1_uint32 etc1_pkm_get_width(const etc1_byte* pHeader);

  etc1_uint32 etc1_pkm_get_height(const etc1_byte* pHeader);

#ifdef __cplusplus
}
#endif

#endif


static const int kModifierTable[] = {
  /* 0 */2, 8, -2, -8,
  /* 1 */5, 17, -5, -17,
  /* 2 */9, 29, -9, -29,
  /* 3 */13, 42, -13, -42,
  /* 4 */18, 60, -18, -60,
  /* 5 */24, 80, -24, -80,
  /* 6 */33, 106, -33, -106,
  /* 7 */47, 183, -47, -183 };

static const int kLookup[8] = { 0, 1, 2, 3, -4, -3, -2, -1 };

static inline etc1_byte clamp(int x) {
  return (etc1_byte)(x >= 0 ? (x < 255 ? x : 255) : 0);
}

static
inline int convert4To8(int b) {
  int c = b & 0xf;
  return (c << 4) | c;
}

static
inline int convert5To8(int b) {
  int c = b & 0x1f;
  return (c << 3) | (c >> 2);
}

static
inline int convert6To8(int b) {
  int c = b & 0x3f;
  return (c << 2) | (c >> 4);
}

static
inline int divideBy255(int d) {
  return (d + 128 + (d >> 8)) >> 8;
}

static
inline int convert8To4(int b) {
  //int c = b & 0xff;
  return divideBy255(b * 15);
}

static
inline int convert8To5(int b) {
  //int c = b & 0xff;
  return divideBy255(b * 31);
}

static
inline int convertDiff(int base, int diff) {
  return convert5To8((0x1f & base) + kLookup[0x7 & diff]);
}

static
void decode_subblock(etc1_byte* pOut, int r, int g, int b, const int* table,
  etc1_uint32 low, etc1_bool second, etc1_bool flipped) {
  int baseX = 0;
  int baseY = 0;
  int i;

  if (second) {
    if (flipped) {
      baseY = 2;
    }
    else {
      baseX = 2;
    }
  }
  for (i = 0; i < 8; i++) {
    int x, y;
    if (flipped) {
      x = baseX + (i >> 1);
      y = baseY + (i & 1);
    }
    else {
      x = baseX + (i >> 2);
      y = baseY + (i & 3);
    }
    int k = y + (x * 4);
    int offset = ((low >> k) & 1) | ((low >> (k + 15)) & 2);
    int delta = table[offset];
    etc1_byte* q = pOut + 3 * (x + 4 * y);
    *q++ = clamp(r + delta);
    *q++ = clamp(g + delta);
    *q++ = clamp(b + delta);
  }
}

// Input is an ETC1 compressed version of the data.
// Output is a 4 x 4 square of 3-byte pixels in form R, G, B

void etc1_decode_block(const etc1_byte* pIn, etc1_byte* pOut) {
  etc1_uint32 high = (pIn[0] << 24) | (pIn[1] << 16) | (pIn[2] << 8) | pIn[3];
  etc1_uint32 low = (pIn[4] << 24) | (pIn[5] << 16) | (pIn[6] << 8) | pIn[7];
  int r1, r2, g1, g2, b1, b2;
  if (high & 2) {
    // differential
    int rBase = high >> 27;
    int gBase = high >> 19;
    int bBase = high >> 11;
    r1 = convert5To8(rBase);
    r2 = convertDiff(rBase, high >> 24);
    g1 = convert5To8(gBase);
    g2 = convertDiff(gBase, high >> 16);
    b1 = convert5To8(bBase);
    b2 = convertDiff(bBase, high >> 8);
  }
  else {
    // not differential
    r1 = convert4To8(high >> 28);
    r2 = convert4To8(high >> 24);
    g1 = convert4To8(high >> 20);
    g2 = convert4To8(high >> 16);
    b1 = convert4To8(high >> 12);
    b2 = convert4To8(high >> 8);
  }
  int tableIndexA = 7 & (high >> 5);
  int tableIndexB = 7 & (high >> 2);
  const int* tableA = kModifierTable + tableIndexA * 4;
  const int* tableB = kModifierTable + tableIndexB * 4;
  etc1_bool flipped = (high & 1) != 0;
  decode_subblock(pOut, r1, g1, b1, tableA, low, 0, flipped);
  decode_subblock(pOut, r2, g2, b2, tableB, low, 1, flipped);
}

typedef struct {
  etc1_uint32 high;
  etc1_uint32 low;
  etc1_uint32 score; // Lower is more accurate
} etc_compressed;

static
inline void take_best(etc_compressed* a, const etc_compressed* b) {
  if (a->score > b->score) {
    *a = *b;
  }
}

static
void etc_average_colors_subblock(const etc1_byte* pIn, etc1_uint32 inMask,
  etc1_byte* pColors, etc1_bool flipped, etc1_bool second) {
  int r = 0;
  int g = 0;
  int b = 0;
  int y, x;

  if (flipped) {
    int by = 0;
    if (second) {
      by = 2;
    }
    for (y = 0; y < 2; y++) {
      int yy = by + y;
      for (x = 0; x < 4; x++) {
        int i = x + 4 * yy;
        if (inMask & (1 << i)) {
          const etc1_byte* p = pIn + i * 3;
          r += *(p++);
          g += *(p++);
          b += *(p++);
        }
      }
    }
  }
  else {
    int bx = 0;
    if (second) {
      bx = 2;
    }
    for (y = 0; y < 4; y++) {
      for (x = 0; x < 2; x++) {
        int xx = bx + x;
        int i = xx + 4 * y;
        if (inMask & (1 << i)) {
          const etc1_byte* p = pIn + i * 3;
          r += *(p++);
          g += *(p++);
          b += *(p++);
        }
      }
    }
  }
  pColors[0] = (etc1_byte)((r + 4) >> 3);
  pColors[1] = (etc1_byte)((g + 4) >> 3);
  pColors[2] = (etc1_byte)((b + 4) >> 3);
}

static
inline int square(int x) {
  return x * x;
}

static etc1_uint32 chooseModifier(const etc1_byte* pBaseColors,
  const etc1_byte* pIn, etc1_uint32* pLow, int bitIndex,
  const int* pModifierTable) {
  etc1_uint32 bestScore = ~0;
  int bestIndex = 0;
  int pixelR = pIn[0];
  int pixelG = pIn[1];
  int pixelB = pIn[2];
  int r = pBaseColors[0];
  int g = pBaseColors[1];
  int b = pBaseColors[2];
  int i;
  for (i = 0; i < 4; i++) {
    int modifier = pModifierTable[i];
    int decodedG = clamp(g + modifier);
    etc1_uint32 score = (etc1_uint32)(6 * square(decodedG - pixelG));
    if (score >= bestScore) {
      continue;
    }
    int decodedR = clamp(r + modifier);
    score += (etc1_uint32)(3 * square(decodedR - pixelR));
    if (score >= bestScore) {
      continue;
    }
    int decodedB = clamp(b + modifier);
    score += (etc1_uint32)square(decodedB - pixelB);
    if (score < bestScore) {
      bestScore = score;
      bestIndex = i;
    }
  }
  etc1_uint32 lowMask = (((bestIndex >> 1) << 16) | (bestIndex & 1))
    << bitIndex;
  *pLow |= lowMask;
  return bestScore;
}

static
void etc_encode_subblock_helper(const etc1_byte* pIn, etc1_uint32 inMask,
  etc_compressed* pCompressed, etc1_bool flipped, etc1_bool second,
  const etc1_byte* pBaseColors, const int* pModifierTable) {
  int score = pCompressed->score;
  int y, x;
  if (flipped) {
    int by = 0;
    if (second) {
      by = 2;
    }
    for (y = 0; y < 2; y++) {
      int yy = by + y;
      for (x = 0; x < 4; x++) {
        int i = x + 4 * yy;
        if (inMask & (1 << i)) {
          score += chooseModifier(pBaseColors, pIn + i * 3,
            &pCompressed->low, yy + x * 4, pModifierTable);
        }
      }
    }
  }
  else {
    int bx = 0;
    if (second) {
      bx = 2;
    }
    for (y = 0; y < 4; y++) {
      for (x = 0; x < 2; x++) {
        int xx = bx + x;
        int i = xx + 4 * y;
        if (inMask & (1 << i)) {
          score += chooseModifier(pBaseColors, pIn + i * 3,
            &pCompressed->low, y + xx * 4, pModifierTable);
        }
      }
    }
  }
  pCompressed->score = score;
}

static etc1_bool inRange4bitSigned(int color) {
  return color >= -4 && color <= 3;
}

static void etc_encodeBaseColors(etc1_byte* pBaseColors,
  const etc1_byte* pColors, etc_compressed* pCompressed) {
  int r1, g1, b1, r2 = 0, g2 = 0, b2 = 0; // 8 bit base colors for sub-blocks
  etc1_bool differential;
  {
    int r51 = convert8To5(pColors[0]);
    int g51 = convert8To5(pColors[1]);
    int b51 = convert8To5(pColors[2]);
    int r52 = convert8To5(pColors[3]);
    int g52 = convert8To5(pColors[4]);
    int b52 = convert8To5(pColors[5]);

    r1 = convert5To8(r51);
    g1 = convert5To8(g51);
    b1 = convert5To8(b51);

    int dr = r52 - r51;
    int dg = g52 - g51;
    int db = b52 - b51;

    differential = inRange4bitSigned(dr) && inRange4bitSigned(dg)
      && inRange4bitSigned(db);
    if (differential) {
      r2 = convert5To8(r51 + dr);
      g2 = convert5To8(g51 + dg);
      b2 = convert5To8(b51 + db);
      pCompressed->high |= (r51 << 27) | ((7 & dr) << 24) | (g51 << 19)
        | ((7 & dg) << 16) | (b51 << 11) | ((7 & db) << 8) | 2;
    }
  }

  if (!differential) {
    int r41 = convert8To4(pColors[0]);
    int g41 = convert8To4(pColors[1]);
    int b41 = convert8To4(pColors[2]);
    int r42 = convert8To4(pColors[3]);
    int g42 = convert8To4(pColors[4]);
    int b42 = convert8To4(pColors[5]);
    r1 = convert4To8(r41);
    g1 = convert4To8(g41);
    b1 = convert4To8(b41);
    r2 = convert4To8(r42);
    g2 = convert4To8(g42);
    b2 = convert4To8(b42);
    pCompressed->high |= (r41 << 28) | (r42 << 24) | (g41 << 20) | (g42
      << 16) | (b41 << 12) | (b42 << 8);
  }
  pBaseColors[0] = r1;
  pBaseColors[1] = g1;
  pBaseColors[2] = b1;
  pBaseColors[3] = r2;
  pBaseColors[4] = g2;
  pBaseColors[5] = b2;
}

static
void etc_encode_block_helper(const etc1_byte* pIn, etc1_uint32 inMask,
  const etc1_byte* pColors, etc_compressed* pCompressed, etc1_bool flipped) {
  int i;

  pCompressed->score = ~0;
  pCompressed->high = (flipped ? 1 : 0);
  pCompressed->low = 0;

  etc1_byte pBaseColors[6];

  etc_encodeBaseColors(pBaseColors, pColors, pCompressed);

  int originalHigh = pCompressed->high;

  const int* pModifierTable = kModifierTable;
  for (i = 0; i < 8; i++, pModifierTable += 4) {
    etc_compressed temp;
    temp.score = 0;
    temp.high = originalHigh | (i << 5);
    temp.low = 0;
    etc_encode_subblock_helper(pIn, inMask, &temp, flipped, 0,
      pBaseColors, pModifierTable);
    take_best(pCompressed, &temp);
  }
  pModifierTable = kModifierTable;
  etc_compressed firstHalf = *pCompressed;
  for (i = 0; i < 8; i++, pModifierTable += 4) {
    etc_compressed temp;
    temp.score = firstHalf.score;
    temp.high = firstHalf.high | (i << 2);
    temp.low = firstHalf.low;
    etc_encode_subblock_helper(pIn, inMask, &temp, flipped, 1,
      pBaseColors + 3, pModifierTable);
    if (i == 0) {
      *pCompressed = temp;
    }
    else {
      take_best(pCompressed, &temp);
    }
  }
}

static void writeBigEndian(etc1_byte* pOut, etc1_uint32 d) {
  pOut[0] = (etc1_byte)(d >> 24);
  pOut[1] = (etc1_byte)(d >> 16);
  pOut[2] = (etc1_byte)(d >> 8);
  pOut[3] = (etc1_byte)d;
}

// Input is a 4 x 4 square of 3-byte pixels in form R, G, B
// inmask is a 16-bit mask where bit (1 << (x + y * 4)) tells whether the corresponding (x,y)
// pixel is valid or not. Invalid pixel color values are ignored when compressing.
// Output is an ETC1 compressed version of the data.

void etc1_encode_block(const etc1_byte* pIn, etc1_uint32 inMask,
  etc1_byte* pOut) {
  etc1_byte colors[6];
  etc1_byte flippedColors[6];
  etc_average_colors_subblock(pIn, inMask, colors, 0, 0);
  etc_average_colors_subblock(pIn, inMask, colors + 3, 0, 1);
  etc_average_colors_subblock(pIn, inMask, flippedColors, 1, 0);
  etc_average_colors_subblock(pIn, inMask, flippedColors + 3, 1, 1);

  etc_compressed a, b;
  etc_encode_block_helper(pIn, inMask, colors, &a, 0);
  etc_encode_block_helper(pIn, inMask, flippedColors, &b, 1);
  take_best(&a, &b);
  writeBigEndian(pOut, a.high);
  writeBigEndian(pOut + 4, a.low);
}

// Return the size of the encoded image data (does not include size of PKM header).

etc1_uint32 etc1_get_encoded_data_size(etc1_uint32 width, etc1_uint32 height) {
  return (((width + 3) & ~3) * ((height + 3) & ~3)) >> 1;
}

// Encode an entire image.
// pIn - pointer to the image data. Formatted such that the Red component of
//       pixel (x,y) is at pIn + pixelSize * x + stride * y + redOffset;
// pOut - pointer to encoded data. Must be large enough to store entire encoded image.

int etc1_encode_image(const etc1_byte* pIn, etc1_uint32 width, etc1_uint32 height,
  etc1_uint32 pixelSize, etc1_uint32 stride, etc1_byte* pOut) {
  if (pixelSize < 2 || pixelSize > 3) {
    return -1;
  }
  static const unsigned short kYMask[] = { 0x0, 0xf, 0xff, 0xfff, 0xffff };
  static const unsigned short kXMask[] = { 0x0, 0x1111, 0x3333, 0x7777,
          0xffff };
  etc1_byte block[ETC1_DECODED_BLOCK_SIZE];
  etc1_byte encoded[ETC1_ENCODED_BLOCK_SIZE];
  etc1_uint32 y, x, cy, cx;

  etc1_uint32 encodedWidth = (width + 3) & ~3;
  etc1_uint32 encodedHeight = (height + 3) & ~3;

  for (y = 0; y < encodedHeight; y += 4) {
    etc1_uint32 yEnd = height - y;
    if (yEnd > 4) {
      yEnd = 4;
    }
    int ymask = kYMask[yEnd];
    for (x = 0; x < encodedWidth; x += 4) {
      etc1_uint32 xEnd = width - x;
      if (xEnd > 4) {
        xEnd = 4;
      }
      int mask = ymask & kXMask[xEnd];
      for (cy = 0; cy < yEnd; cy++) {
        etc1_byte* q = block + (cy * 4) * 3;
        const etc1_byte* p = pIn + pixelSize * x + stride * (y + cy);
        if (pixelSize == 3) {
          memcpy(q, p, xEnd * 3);
        }
        else {
          for (cx = 0; cx < xEnd; cx++) {
            int pixel = (p[1] << 8) | p[0];
            *q++ = convert5To8(pixel >> 11);
            *q++ = convert6To8(pixel >> 5);
            *q++ = convert5To8(pixel);
            p += pixelSize;
          }
        }
      }
      etc1_encode_block(block, mask, encoded);
      memcpy(pOut, encoded, sizeof(encoded));
      pOut += sizeof(encoded);
    }
  }
  return 0;
}

// Decode an entire image.
// pIn - pointer to encoded data.
// pOut - pointer to the image data. Will be written such that the Red component of
//       pixel (x,y) is at pIn + pixelSize * x + stride * y + redOffset. Must be
//        large enough to store entire image.


int etc1_decode_image(const etc1_byte* pIn, etc1_byte* pOut,
  etc1_uint32 width, etc1_uint32 height,
  etc1_uint32 pixelSize, etc1_uint32 stride) {
  if (pixelSize < 2 || pixelSize > 3) {
    return -1;
  }
  etc1_byte block[ETC1_DECODED_BLOCK_SIZE];

  etc1_uint32 encodedWidth = (width + 3) & ~3;
  etc1_uint32 encodedHeight = (height + 3) & ~3;

  etc1_uint32 y, x, cy, cx;

  for (y = 0; y < encodedHeight; y += 4) {
    etc1_uint32 yEnd = height - y;
    if (yEnd > 4) {
      yEnd = 4;
    }
    for (x = 0; x < encodedWidth; x += 4) {
      etc1_uint32 xEnd = width - x;
      if (xEnd > 4) {
        xEnd = 4;
      }
      etc1_decode_block(pIn, block);
      pIn += ETC1_ENCODED_BLOCK_SIZE;
      for (cy = 0; cy < yEnd; cy++) {
        const etc1_byte* q = block + (cy * 4) * 3;
        etc1_byte* p = pOut + pixelSize * x + stride * (y + cy);
        if (pixelSize == 3) {
          memcpy(p, q, xEnd * 3);
        }
        else {
          for (cx = 0; cx < xEnd; cx++) {
            etc1_byte r = *q++;
            etc1_byte g = *q++;
            etc1_byte b = *q++;
            etc1_uint32 pixel = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
            *p++ = (etc1_byte)pixel;
            *p++ = (etc1_byte)(pixel >> 8);
          }
        }
      }
    }
  }
  return 0;
}

static const char kMagic[] = { 'P', 'K', 'M', ' ', '1', '0' };

static const etc1_uint32 ETC1_PKM_FORMAT_OFFSET = 6;
static const etc1_uint32 ETC1_PKM_ENCODED_WIDTH_OFFSET = 8;
static const etc1_uint32 ETC1_PKM_ENCODED_HEIGHT_OFFSET = 10;
static const etc1_uint32 ETC1_PKM_WIDTH_OFFSET = 12;
static const etc1_uint32 ETC1_PKM_HEIGHT_OFFSET = 14;

static const etc1_uint32 ETC1_RGB_NO_MIPMAPS = 0;

static void writeBEUint16(etc1_byte* pOut, etc1_uint32 data) {
  pOut[0] = (etc1_byte)(data >> 8);
  pOut[1] = (etc1_byte)data;
}

static etc1_uint32 readBEUint16(const etc1_byte* pIn) {
  return (pIn[0] << 8) | pIn[1];
}

// Format a PKM header

void etc1_pkm_format_header(etc1_byte* pHeader, etc1_uint32 width, etc1_uint32 height) {
  memcpy(pHeader, kMagic, sizeof(kMagic));
  etc1_uint32 encodedWidth = (width + 3) & ~3;
  etc1_uint32 encodedHeight = (height + 3) & ~3;
  writeBEUint16(pHeader + ETC1_PKM_FORMAT_OFFSET, ETC1_RGB_NO_MIPMAPS);
  writeBEUint16(pHeader + ETC1_PKM_ENCODED_WIDTH_OFFSET, encodedWidth);
  writeBEUint16(pHeader + ETC1_PKM_ENCODED_HEIGHT_OFFSET, encodedHeight);
  writeBEUint16(pHeader + ETC1_PKM_WIDTH_OFFSET, width);
  writeBEUint16(pHeader + ETC1_PKM_HEIGHT_OFFSET, height);
}

// Check if a PKM header is correctly formatted.

etc1_bool etc1_pkm_is_valid(const etc1_byte* pHeader) {
  if (memcmp(pHeader, kMagic, sizeof(kMagic))) {
    return 0;
  }
  etc1_uint32 format = readBEUint16(pHeader + ETC1_PKM_FORMAT_OFFSET);
  etc1_uint32 encodedWidth = readBEUint16(pHeader + ETC1_PKM_ENCODED_WIDTH_OFFSET);
  etc1_uint32 encodedHeight = readBEUint16(pHeader + ETC1_PKM_ENCODED_HEIGHT_OFFSET);
  etc1_uint32 width = readBEUint16(pHeader + ETC1_PKM_WIDTH_OFFSET);
  etc1_uint32 height = readBEUint16(pHeader + ETC1_PKM_HEIGHT_OFFSET);
  return format == ETC1_RGB_NO_MIPMAPS &&
    encodedWidth >= width && encodedWidth - width < 4 &&
    encodedHeight >= height && encodedHeight - height < 4;
}

// Read the image width from a PKM header

etc1_uint32 etc1_pkm_get_width(const etc1_byte* pHeader) {
  return readBEUint16(pHeader + ETC1_PKM_WIDTH_OFFSET);
}

// Read the image height from a PKM header

etc1_uint32 etc1_pkm_get_height(const etc1_byte* pHeader) {
  return readBEUint16(pHeader + ETC1_PKM_HEIGHT_OFFSET);
}



/*
    Jonathan Dummer

    image helper functions

    MIT license
*/


/*	Upscaling the image uses simple bilinear interpolation	*/
int
up_scale_image
(
  const unsigned char* const orig,
  int width, int height, int channels,
  unsigned char* resampled,
  int resampled_width, int resampled_height
)
{
  float dx, dy;
  int x, y, c;

  /* error(s) check	*/
  if ((width < 1) || (height < 1) ||
    (resampled_width < 2) || (resampled_height < 2) ||
    (channels < 1) ||
    (nullptr == orig) || (nullptr == resampled))
  {
    /*	signify badness	*/
    return 0;
  }
  /*
  for each given pixel in the new map, find the exact location
  from the original map which would contribute to this guy
*/
  dx = (width - 1.0f) / (resampled_width - 1.0f);
  dy = (height - 1.0f) / (resampled_height - 1.0f);
  for (y = 0; y < resampled_height; ++y)
  {
    /* find the base y index and fractional offset from that	*/
    float sampley = y * dy;
    int inty = (int)sampley;
    /*	if( inty < 0 ) { inty = 0; } else	*/
    if (inty > height - 2) { inty = height - 2; }
    sampley -= inty;
    for (x = 0; x < resampled_width; ++x)
    {
      float samplex = x * dx;
      int intx = (int)samplex;
      int base_index;
      /* find the base x index and fractional offset from that	*/
      /*	if( intx < 0 ) { intx = 0; } else	*/
      if (intx > width - 2) { intx = width - 2; }
      samplex -= intx;
      /*	base index into the original image	*/
      base_index = (inty * width + intx) * channels;
      for (c = 0; c < channels; ++c)
      {
        /*	do the sampling	*/
        float value = 0.5f;
        value += orig[base_index]
          * (1.0f - samplex) * (1.0f - sampley);
        value += orig[base_index + channels]
          * (samplex) * (1.0f - sampley);
        value += orig[base_index + width * channels]
          * (1.0f - samplex) * (sampley);
        value += orig[base_index + width * channels + channels]
          * (samplex) * (sampley);
        /*	move to the next channel	*/
        ++base_index;
        /*	save the new value	*/
        resampled[y * resampled_width * channels + x * channels + c] =
          (unsigned char)(value);
      }
    }
  }
  /*	done	*/
  return 1;
}

int
mipmap_image
(
  const unsigned char* const orig,
  int width, int height, int channels,
  unsigned char* resampled,
  int block_size_x, int block_size_y
)
{
  int mip_width, mip_height;
  int i, j, c;

  /*	error check	*/
  if ((width < 1) || (height < 1) ||
    (channels < 1) || (orig == nullptr) ||
    (resampled == nullptr) ||
    (block_size_x < 1) || (block_size_y < 1))
  {
    /*	nothing to do	*/
    return 0;
  }
  mip_width = width / block_size_x;
  mip_height = height / block_size_y;
  if (mip_width < 1)
  {
    mip_width = 1;
  }
  if (mip_height < 1)
  {
    mip_height = 1;
  }
  for (j = 0; j < mip_height; ++j)
  {
    for (i = 0; i < mip_width; ++i)
    {
      for (c = 0; c < channels; ++c)
      {
        const int index = (j * block_size_y) * width * channels + (i * block_size_x) * channels + c;
        int sum_value;
        int u, v;
        int u_block = block_size_x;
        int v_block = block_size_y;
        int block_area;
        /*	do a bit of checking so we don't over-run the boundaries
          (necessary for non-square textures!)	*/
        if (block_size_x * (i + 1) > width)
        {
          u_block = width - i * block_size_y;
        }
        if (block_size_y * (j + 1) > height)
        {
          v_block = height - j * block_size_y;
        }
        block_area = u_block * v_block;
        /*	for this pixel, see what the average
          of all the values in the block are.
          note: start the sum at the rounding value, not at 0	*/
        sum_value = block_area >> 1;
        for (v = 0; v < v_block; ++v)
          for (u = 0; u < u_block; ++u)
          {
            sum_value += orig[index + v * width * channels + u * channels];
          }
        resampled[j * mip_width * channels + i * channels + c] = sum_value / block_area;
      }
    }
  }
  return 1;
}

int
scale_image_RGB_to_NTSC_safe
(
  unsigned char* orig,
  int width, int height, int channels
)
{
  const float scale_lo = 16.0f - 0.499f;
  const float scale_hi = 235.0f + 0.499f;
  int i, j;
  int nc = channels;
  unsigned char scale_LUT[256];
  /*	error check	*/
  if ((width < 1) || (height < 1) ||
    (channels < 1) || (orig == nullptr))
  {
    /*	nothing to do	*/
    return 0;
  }
  /*	set up the scaling Look Up Table	*/
  for (i = 0; i < 256; ++i)
  {
    scale_LUT[i] = (unsigned char)((scale_hi - scale_lo) * i / 255.0f + scale_lo);
  }
  /*	for channels = 2 or 4, ignore the alpha component	*/
  nc -= 1 - (channels & 1);
  /*	OK, go through the image and scale any non-alpha components	*/
  for (i = 0; i < width * height * channels; i += channels)
  {
    for (j = 0; j < nc; ++j)
    {
      orig[i + j] = scale_LUT[orig[i + j]];
    }
  }
  return 1;
}

unsigned char clamp_byte(int x) { return ((x) < 0 ? (0) : ((x) > 255 ? 255 : (x))); }

/*
  This function takes the RGB components of the image
  and converts them into YCoCg.  3 components will be
  re-ordered to CoYCg (for optimum DXT1 compression),
  while 4 components will be ordered CoCgAY (for DXT5
  compression).
*/
int
convert_RGB_to_YCoCg
(
  unsigned char* orig,
  int width, int height, int channels
)
{
  int i;
  /*	error check	*/
  if ((width < 1) || (height < 1) ||
    (channels < 3) || (channels > 4) ||
    (orig == nullptr))
  {
    /*	nothing to do	*/
    return -1;
  }
  /*	do the conversion	*/
  if (channels == 3)
  {
    for (i = 0; i < width * height * 3; i += 3)
    {
      int r = orig[i + 0];
      int g = (orig[i + 1] + 1) >> 1;
      int b = orig[i + 2];
      int tmp = (2 + r + b) >> 2;
      /*	Co	*/
      orig[i + 0] = clamp_byte(128 + ((r - b + 1) >> 1));
      /*	Y	*/
      orig[i + 1] = clamp_byte(g + tmp);
      /*	Cg	*/
      orig[i + 2] = clamp_byte(128 + g - tmp);
    }
  }
  else
  {
    for (i = 0; i < width * height * 4; i += 4)
    {
      int r = orig[i + 0];
      int g = (orig[i + 1] + 1) >> 1;
      int b = orig[i + 2];
      unsigned char a = orig[i + 3];
      int tmp = (2 + r + b) >> 2;
      /*	Co	*/
      orig[i + 0] = clamp_byte(128 + ((r - b + 1) >> 1));
      /*	Cg	*/
      orig[i + 1] = clamp_byte(128 + g - tmp);
      /*	Alpha	*/
      orig[i + 2] = a;
      /*	Y	*/
      orig[i + 3] = clamp_byte(g + tmp);
    }
  }
  /*	done	*/
  return 0;
}

/*
  This function takes the YCoCg components of the image
  and converts them into RGB.  See above.
*/
int
convert_YCoCg_to_RGB
(
  unsigned char* orig,
  int width, int height, int channels
)
{
  int i;
  /*	error check	*/
  if ((width < 1) || (height < 1) ||
    (channels < 3) || (channels > 4) ||
    (orig == nullptr))
  {
    /*	nothing to do	*/
    return -1;
  }
  /*	do the conversion	*/
  if (channels == 3)
  {
    for (i = 0; i < width * height * 3; i += 3)
    {
      int co = orig[i + 0] - 128;
      int y = orig[i + 1];
      int cg = orig[i + 2] - 128;
      /*	R	*/
      orig[i + 0] = clamp_byte(y + co - cg);
      /*	G	*/
      orig[i + 1] = clamp_byte(y + cg);
      /*	B	*/
      orig[i + 2] = clamp_byte(y - co - cg);
    }
  }
  else
  {
    for (i = 0; i < width * height * 4; i += 4)
    {
      int co = orig[i + 0] - 128;
      int cg = orig[i + 1] - 128;
      unsigned char a = orig[i + 2];
      int y = orig[i + 3];
      /*	R	*/
      orig[i + 0] = clamp_byte(y + co - cg);
      /*	G	*/
      orig[i + 1] = clamp_byte(y + cg);
      /*	B	*/
      orig[i + 2] = clamp_byte(y - co - cg);
      /*	A	*/
      orig[i + 3] = a;
    }
  }
  /*	done	*/
  return 0;
}

float
find_max_RGBE
(
  unsigned char* image,
  int width, int height
)
{
  float max_val = 0.0f;
  unsigned char* img = image;
  int i, j;
  for (i = width * height; i > 0; --i)
  {
    /* float scale = powf( 2.0f, img[3] - 128.0f ) / 255.0f; */
    float scale = (float)ldexp(1.0f / 255.0f, (int)(img[3]) - 128);
    for (j = 0; j < 3; ++j)
    {
      if (img[j] * scale > max_val)
      {
        max_val = img[j] * scale;
      }
    }
    /* next pixel */
    img += 4;
  }
  return max_val;
}

int
RGBE_to_RGBdivA
(
  unsigned char* image,
  int width, int height,
  int rescale_to_max
)
{
  /* local variables */
  int i, iv;
  unsigned char* img = image;
  float scale = 1.0f;
  /* error check */
  if ((!image) || (width < 1) || (height < 1))
  {
    return 0;
  }
  /* convert (note: no negative numbers, but 0.0 is possible) */
  if (rescale_to_max)
  {
    scale = 255.0f / find_max_RGBE(image, width, height);
  }
  for (i = width * height; i > 0; --i)
  {
    /* decode this pixel, and find the max */
    float r, g, b, e, m;
    /* e = scale * powf( 2.0f, img[3] - 128.0f ) / 255.0f; */
    e = scale * (float)ldexp(1.0f / 255.0f, (int)(img[3]) - 128);
    r = e * img[0];
    g = e * img[1];
    b = e * img[2];
    m = (r > g) ? r : g;
    m = (b > m) ? b : m;
    /* and encode it into RGBdivA */
    iv = (m != 0.0f) ? (int)(255.0f / m) : 1;
    iv = (iv < 1) ? 1 : iv;
    img[3] = (iv > 255) ? 255 : iv;
    iv = (int)(img[3] * r + 0.5f);
    img[0] = (iv > 255) ? 255 : iv;
    iv = (int)(img[3] * g + 0.5f);
    img[1] = (iv > 255) ? 255 : iv;
    iv = (int)(img[3] * b + 0.5f);
    img[2] = (iv > 255) ? 255 : iv;
    /* and on to the next pixel */
    img += 4;
  }
  return 1;
}

int
RGBE_to_RGBdivA2
(
  unsigned char* image,
  int width, int height,
  int rescale_to_max
)
{
  /* local variables */
  int i, iv;
  unsigned char* img = image;
  float scale = 1.0f;
  /* error check */
  if ((!image) || (width < 1) || (height < 1))
  {
    return 0;
  }
  /* convert (note: no negative numbers, but 0.0 is possible) */
  if (rescale_to_max)
  {
    scale = 255.0f * 255.0f / find_max_RGBE(image, width, height);
  }
  for (i = width * height; i > 0; --i)
  {
    /* decode this pixel, and find the max */
    float r, g, b, e, m;
    /* e = scale * powf( 2.0f, img[3] - 128.0f ) / 255.0f; */
    e = scale * (float)ldexp(1.0f / 255.0f, (int)(img[3]) - 128);
    r = e * img[0];
    g = e * img[1];
    b = e * img[2];
    m = (r > g) ? r : g;
    m = (b > m) ? b : m;
    /* and encode it into RGBdivA */
    iv = (m != 0.0f) ? (int)sqrtf(255.0f * 255.0f / m) : 1;
    iv = (iv < 1) ? 1 : iv;
    img[3] = (iv > 255) ? 255 : iv;
    iv = (int)(img[3] * img[3] * r / 255.0f + 0.5f);
    img[0] = (iv > 255) ? 255 : iv;
    iv = (int)(img[3] * img[3] * g / 255.0f + 0.5f);
    img[1] = (iv > 255) ? 255 : iv;
    iv = (int)(img[3] * img[3] * b / 255.0f + 0.5f);
    img[2] = (iv > 255) ? 255 : iv;
    /* and on to the next pixel */
    img += 4;
  }
  return 1;
}







/*
  Jonathan Dummer
  2007-07-31-10.32

  simple DXT compression / decompression code

  public domain
*/

#ifndef HEADER_IMAGE_DXT
#define HEADER_IMAGE_DXT

/**
  take an image and convert it to DXT1 (no alpha)
**/
unsigned char*
convert_image_to_DXT1
(
  const unsigned char* const uncompressed,
  int width, int height, int channels,
  int* out_size
);

/**
  take an image and convert it to DXT5 (with alpha)
**/
unsigned char*
convert_image_to_DXT5
(
  const unsigned char* const uncompressed,
  int width, int height, int channels,
  int* out_size
);

/**	A bunch of DirectDraw Surface structures and flags **/
typedef struct
{
  unsigned int    dwMagic;
  unsigned int    dwSize;
  unsigned int    dwFlags;
  unsigned int    dwHeight;
  unsigned int    dwWidth;
  unsigned int    dwPitchOrLinearSize;
  unsigned int    dwDepth;
  unsigned int    dwMipMapCount;
  unsigned int    dwReserved1[11];

  /*  DDPIXELFORMAT	*/
  struct
  {
    unsigned int    dwSize;
    unsigned int    dwFlags;
    unsigned int    dwFourCC;
    unsigned int    dwRGBBitCount;
    unsigned int    dwRBitMask;
    unsigned int    dwGBitMask;
    unsigned int    dwBBitMask;
    unsigned int    dwAlphaBitMask;
  }
  sPixelFormat;

  /*  DDCAPS2	*/
  struct
  {
    unsigned int    dwCaps1;
    unsigned int    dwCaps2;
    unsigned int    dwDDSX;
    unsigned int    dwReserved;
  }
  sCaps;
  unsigned int    dwReserved2;
}
DDS_header;

/*	the following constants were copied directly off the MSDN website	*/

/*	The dwFlags member of the original DDSURFACEDESC2 structure
  can be set to one or more of the following values.	*/
#define DDSD_CAPS	0x00000001
#define DDSD_HEIGHT	0x00000002
#define DDSD_WIDTH	0x00000004
#define DDSD_PITCH	0x00000008
#define DDSD_PIXELFORMAT	0x00001000
#define DDSD_MIPMAPCOUNT	0x00020000
#define DDSD_LINEARSIZE	0x00080000
#define DDSD_DEPTH	0x00800000

  /*	DirectDraw Pixel Format	*/
#define DDPF_ALPHAPIXELS	0x00000001
#define DDPF_FOURCC	0x00000004
#define DDPF_RGB	0x00000040

/*	The dwCaps1 member of the DDSCAPS2 structure can be
  set to one or more of the following values.	*/
#define DDSCAPS_COMPLEX	0x00000008
#define DDSCAPS_TEXTURE	0x00001000
#define DDSCAPS_MIPMAP	0x00400000

  /*	The dwCaps2 member of the DDSCAPS2 structure can be
    set to one or more of the following values.		*/
#define DDSCAPS2_CUBEMAP	0x00000200
#define DDSCAPS2_CUBEMAP_POSITIVEX	0x00000400
#define DDSCAPS2_CUBEMAP_NEGATIVEX	0x00000800
#define DDSCAPS2_CUBEMAP_POSITIVEY	0x00001000
#define DDSCAPS2_CUBEMAP_NEGATIVEY	0x00002000
#define DDSCAPS2_CUBEMAP_POSITIVEZ	0x00004000
#define DDSCAPS2_CUBEMAP_NEGATIVEZ	0x00008000
#define DDSCAPS2_VOLUME	0x00200000

#endif /* HEADER_IMAGE_DXT	*/


    /*	set this =1 if you want to use the covarince matrix method...
      which is better than my method of using standard deviations
      overall, except on the infintesimal chance that the power
      method fails for finding the largest eigenvector	*/
#define USE_COV_MAT	1

      /********* Function Prototypes *********/
      /*
        Takes a 4x4 block of pixels and compresses it into 8 bytes
        in DXT1 format (color only, no alpha).  Speed is valued
        over prettyness, at least for now.
      */
void compress_DDS_color_block(
  int channels,
  const unsigned char* const uncompressed,
  unsigned char compressed[8]);
/*
  Takes a 4x4 block of pixels and compresses the alpha
  component it into 8 bytes for use in DXT5 DDS files.
  Speed is valued over prettyness, at least for now.
*/
void compress_DDS_alpha_block(
  const unsigned char* const uncompressed,
  unsigned char compressed[8]);

unsigned char* convert_image_to_DXT1(
  const unsigned char* const uncompressed,
  int width, int height, int channels,
  int* out_size)
{
  unsigned char* compressed;
  int i, j, x, y;
  unsigned char ublock[16 * 3];
  unsigned char cblock[8];
  int index = 0, chan_step = 1;
  int block_count = 0;
  /*	error check	*/
  *out_size = 0;
  if ((width < 1) || (height < 1) ||
    (nullptr == uncompressed) ||
    (channels < 1) || (channels > 4))
  {
    return nullptr;
  }
  /*	for channels == 1 or 2, I do not step forward for R,G,B values	*/
  if (channels < 3)
  {
    chan_step = 0;
  }
  /*	get the RAM for the compressed image
    (8 bytes per 4x4 pixel block)	*/
  *out_size = ((width + 3) >> 2) * ((height + 3) >> 2) * 8;
  compressed = (unsigned char*)malloc(*out_size);
  /*	go through each block	*/
  for (j = 0; j < height; j += 4)
  {
    for (i = 0; i < width; i += 4)
    {
      /*	copy this block into a new one	*/
      int idx = 0;
      int mx = 4, my = 4;
      if (j + 4 >= height)
      {
        my = height - j;
      }
      if (i + 4 >= width)
      {
        mx = width - i;
      }
      for (y = 0; y < my; ++y)
      {
        for (x = 0; x < mx; ++x)
        {
          ublock[idx++] = uncompressed[(j + y) * width * channels + (i + x) * channels];
          ublock[idx++] = uncompressed[(j + y) * width * channels + (i + x) * channels + chan_step];
          ublock[idx++] = uncompressed[(j + y) * width * channels + (i + x) * channels + chan_step + chan_step];
        }
        for (x = mx; x < 4; ++x)
        {
          ublock[idx++] = ublock[0];
          ublock[idx++] = ublock[1];
          ublock[idx++] = ublock[2];
        }
      }
      for (y = my; y < 4; ++y)
      {
        for (x = 0; x < 4; ++x)
        {
          ublock[idx++] = ublock[0];
          ublock[idx++] = ublock[1];
          ublock[idx++] = ublock[2];
        }
      }
      /*	compress the block	*/
      ++block_count;
      compress_DDS_color_block(3, ublock, cblock);
      /*	copy the data from the block into the main block	*/
      for (x = 0; x < 8; ++x)
      {
        compressed[index++] = cblock[x];
      }
    }
  }
  return compressed;
}

unsigned char* convert_image_to_DXT5(
  const unsigned char* const uncompressed,
  int width, int height, int channels,
  int* out_size)
{
  unsigned char* compressed;
  int i, j, x, y;
  unsigned char ublock[16 * 4];
  unsigned char cblock[8];
  int index = 0, chan_step = 1;
  int block_count = 0, has_alpha;
  /*	error check	*/
  *out_size = 0;
  if ((width < 1) || (height < 1) ||
    (nullptr == uncompressed) ||
    (channels < 1) || (channels > 4))
  {
    return nullptr;
  }
  /*	for channels == 1 or 2, I do not step forward for R,G,B vales	*/
  if (channels < 3)
  {
    chan_step = 0;
  }
  /*	# channels = 1 or 3 have no alpha, 2 & 4 do have alpha	*/
  has_alpha = 1 - (channels & 1);
  /*	get the RAM for the compressed image
    (16 bytes per 4x4 pixel block)	*/
  *out_size = ((width + 3) >> 2) * ((height + 3) >> 2) * 16;
  compressed = (unsigned char*)malloc(*out_size);
  /*	go through each block	*/
  for (j = 0; j < height; j += 4)
  {
    for (i = 0; i < width; i += 4)
    {
      /*	local variables, and my block counter	*/
      int idx = 0;
      int mx = 4, my = 4;
      if (j + 4 >= height)
      {
        my = height - j;
      }
      if (i + 4 >= width)
      {
        mx = width - i;
      }
      for (y = 0; y < my; ++y)
      {
        for (x = 0; x < mx; ++x)
        {
          ublock[idx++] = uncompressed[(j + y) * width * channels + (i + x) * channels];
          ublock[idx++] = uncompressed[(j + y) * width * channels + (i + x) * channels + chan_step];
          ublock[idx++] = uncompressed[(j + y) * width * channels + (i + x) * channels + chan_step + chan_step];
          ublock[idx++] =
            has_alpha * uncompressed[(j + y) * width * channels + (i + x) * channels + channels - 1]
            + (1 - has_alpha) * 255;
        }
        for (x = mx; x < 4; ++x)
        {
          ublock[idx++] = ublock[0];
          ublock[idx++] = ublock[1];
          ublock[idx++] = ublock[2];
          ublock[idx++] = ublock[3];
        }
      }
      for (y = my; y < 4; ++y)
      {
        for (x = 0; x < 4; ++x)
        {
          ublock[idx++] = ublock[0];
          ublock[idx++] = ublock[1];
          ublock[idx++] = ublock[2];
          ublock[idx++] = ublock[3];
        }
      }
      /*	now compress the alpha block	*/
      compress_DDS_alpha_block(ublock, cblock);
      /*	copy the data from the compressed alpha block into the main buffer	*/
      for (x = 0; x < 8; ++x)
      {
        compressed[index++] = cblock[x];
      }
      /*	then compress the color block	*/
      ++block_count;
      compress_DDS_color_block(4, ublock, cblock);
      /*	copy the data from the compressed color block into the main buffer	*/
      for (x = 0; x < 8; ++x)
      {
        compressed[index++] = cblock[x];
      }
    }
  }
  return compressed;
}

/********* Helper Functions *********/
int convert_bit_range(int c, int from_bits, int to_bits)
{
  int b = (1 << (from_bits - 1)) + c * ((1 << to_bits) - 1);
  return (b + (b >> from_bits)) >> from_bits;
}

int rgb_to_565(int r, int g, int b)
{
  return
    (convert_bit_range(r, 8, 5) << 11) |
    (convert_bit_range(g, 8, 6) << 05) |
    (convert_bit_range(b, 8, 5) << 00);
}

void rgb_888_from_565(unsigned int c, int* r, int* g, int* b)
{
  *r = convert_bit_range((c >> 11) & 31, 5, 8);
  *g = convert_bit_range((c >> 05) & 63, 6, 8);
  *b = convert_bit_range((c >> 00) & 31, 5, 8);
}

void compute_color_line_STDEV(
  const unsigned char* const uncompressed,
  int channels,
  float point[3], float direction[3])
{
  const float inv_16 = 1.0f / 16.0f;
  int i;
  float sum_r = 0.0f, sum_g = 0.0f, sum_b = 0.0f;
  float sum_rr = 0.0f, sum_gg = 0.0f, sum_bb = 0.0f;
  float sum_rg = 0.0f, sum_rb = 0.0f, sum_gb = 0.0f;
  /*	calculate all data needed for the covariance matrix
    ( to compare with _rygdxt code)	*/
  for (i = 0; i < 16 * channels; i += channels)
  {
    sum_r += uncompressed[i + 0];
    sum_rr += uncompressed[i + 0] * uncompressed[i + 0];
    sum_g += uncompressed[i + 1];
    sum_gg += uncompressed[i + 1] * uncompressed[i + 1];
    sum_b += uncompressed[i + 2];
    sum_bb += uncompressed[i + 2] * uncompressed[i + 2];
    sum_rg += uncompressed[i + 0] * uncompressed[i + 1];
    sum_rb += uncompressed[i + 0] * uncompressed[i + 2];
    sum_gb += uncompressed[i + 1] * uncompressed[i + 2];
  }
  /*	convert the sums to averages	*/
  sum_r *= inv_16;
  sum_g *= inv_16;
  sum_b *= inv_16;
  /*	and convert the squares to the squares of the value - avg_value	*/
  sum_rr -= 16.0f * sum_r * sum_r;
  sum_gg -= 16.0f * sum_g * sum_g;
  sum_bb -= 16.0f * sum_b * sum_b;
  sum_rg -= 16.0f * sum_r * sum_g;
  sum_rb -= 16.0f * sum_r * sum_b;
  sum_gb -= 16.0f * sum_g * sum_b;
  /*	the point on the color line is the average	*/
  point[0] = sum_r;
  point[1] = sum_g;
  point[2] = sum_b;
#if USE_COV_MAT
  /*
    The following idea was from ryg.
    (https://mollyrocket.com/forums/viewtopic.php?t=392)
    The method worked great (less RMSE than mine) most of
    the time, but had some issues handling some simple
    boundary cases, like full green next to full red,
    which would generate a covariance matrix like this:

    | 1  -1  0 |
    | -1  1  0 |
    | 0   0  0 |

    For a given starting vector, the power method can
    generate all zeros!  So no starting with {1,1,1}
    as I was doing!  This kind of error is still a
    slight posibillity, but will be very rare.
  */
  /*	use the covariance matrix directly
    (1st iteration, don't use all 1.0 values!)	*/
  sum_r = 1.0f;
  sum_g = 2.718281828f;
  sum_b = 3.141592654f;
  direction[0] = sum_r * sum_rr + sum_g * sum_rg + sum_b * sum_rb;
  direction[1] = sum_r * sum_rg + sum_g * sum_gg + sum_b * sum_gb;
  direction[2] = sum_r * sum_rb + sum_g * sum_gb + sum_b * sum_bb;
  /*	2nd iteration, use results from the 1st guy	*/
  sum_r = direction[0];
  sum_g = direction[1];
  sum_b = direction[2];
  direction[0] = sum_r * sum_rr + sum_g * sum_rg + sum_b * sum_rb;
  direction[1] = sum_r * sum_rg + sum_g * sum_gg + sum_b * sum_gb;
  direction[2] = sum_r * sum_rb + sum_g * sum_gb + sum_b * sum_bb;
  /*	3rd iteration, use results from the 2nd guy	*/
  sum_r = direction[0];
  sum_g = direction[1];
  sum_b = direction[2];
  direction[0] = sum_r * sum_rr + sum_g * sum_rg + sum_b * sum_rb;
  direction[1] = sum_r * sum_rg + sum_g * sum_gg + sum_b * sum_gb;
  direction[2] = sum_r * sum_rb + sum_g * sum_gb + sum_b * sum_bb;
#else
  /*	use my standard deviation method
    (very robust, a tiny bit slower and less accurate)	*/
  direction[0] = sqrt(sum_rr);
  direction[1] = sqrt(sum_gg);
  direction[2] = sqrt(sum_bb);
  /*	which has a greater component	*/
  if (sum_gg > sum_rr)
  {
    /*	green has greater component, so base the other signs off of green	*/
    if (sum_rg < 0.0f)
    {
      direction[0] = -direction[0];
    }
    if (sum_gb < 0.0f)
    {
      direction[2] = -direction[2];
    }
  }
  else
  {
    /*	red has a greater component	*/
    if (sum_rg < 0.0f)
    {
      direction[1] = -direction[1];
    }
    if (sum_rb < 0.0f)
    {
      direction[2] = -direction[2];
    }
  }
#endif
}

void LSE_master_colors_max_min(
  int* cmax, int* cmin,
  int channels,
  const unsigned char* const uncompressed)
{
  int i, j;
  /*	the master colors	*/
  int c0[3], c1[3];
  /*	used for fitting the line	*/
  float sum_x[] = { 0.0f, 0.0f, 0.0f };
  float sum_x2[] = { 0.0f, 0.0f, 0.0f };
  float dot_max = 1.0f, dot_min = -1.0f;
  float vec_len2 = 0.0f;
  float dot;
  /*	error check	*/
  if ((channels < 3) || (channels > 4))
  {
    return;
  }
  compute_color_line_STDEV(uncompressed, channels, sum_x, sum_x2);
  vec_len2 = 1.0f / (0.00001f +
    sum_x2[0] * sum_x2[0] + sum_x2[1] * sum_x2[1] + sum_x2[2] * sum_x2[2]);
  /*	finding the max and min vector values	*/
  dot_max =
    (
      sum_x2[0] * uncompressed[0] +
      sum_x2[1] * uncompressed[1] +
      sum_x2[2] * uncompressed[2]
      );
  dot_min = dot_max;
  for (i = 1; i < 16; ++i)
  {
    dot =
      (
        sum_x2[0] * uncompressed[i * channels + 0] +
        sum_x2[1] * uncompressed[i * channels + 1] +
        sum_x2[2] * uncompressed[i * channels + 2]
        );
    if (dot < dot_min)
    {
      dot_min = dot;
    }
    else if (dot > dot_max)
    {
      dot_max = dot;
    }
  }
  /*	and the offset (from the average location)	*/
  dot = sum_x2[0] * sum_x[0] + sum_x2[1] * sum_x[1] + sum_x2[2] * sum_x[2];
  dot_min -= dot;
  dot_max -= dot;
  /*	post multiply by the scaling factor	*/
  dot_min *= vec_len2;
  dot_max *= vec_len2;
  /*	OK, build the master colors	*/
  for (i = 0; i < 3; ++i)
  {
    /*	color 0	*/
    c0[i] = (int)(0.5f + sum_x[i] + dot_max * sum_x2[i]);
    if (c0[i] < 0)
    {
      c0[i] = 0;
    }
    else if (c0[i] > 255)
    {
      c0[i] = 255;
    }
    /*	color 1	*/
    c1[i] = (int)(0.5f + sum_x[i] + dot_min * sum_x2[i]);
    if (c1[i] < 0)
    {
      c1[i] = 0;
    }
    else if (c1[i] > 255)
    {
      c1[i] = 255;
    }
  }
  /*	down_sample (with rounding?)	*/
  i = rgb_to_565(c0[0], c0[1], c0[2]);
  j = rgb_to_565(c1[0], c1[1], c1[2]);
  if (i > j)
  {
    *cmax = i;
    *cmin = j;
  }
  else
  {
    *cmax = j;
    *cmin = i;
  }
}

void
compress_DDS_color_block
(
  int channels,
  const unsigned char* const uncompressed,
  unsigned char compressed[8]
)
{
  /*	variables	*/
  int i;
  int next_bit;
  int enc_c0, enc_c1;
  int c0[4], c1[4];
  float color_line[] = { 0.0f, 0.0f, 0.0f, 0.0f };
  float vec_len2 = 0.0f, dot_offset = 0.0f;
  /*	stupid order	*/
  int swizzle4[] = { 0, 2, 3, 1 };
  /*	get the master colors	*/
  LSE_master_colors_max_min(&enc_c0, &enc_c1, channels, uncompressed);
  /*	store the 565 color 0 and color 1	*/
  compressed[0] = (enc_c0 >> 0) & 255;
  compressed[1] = (enc_c0 >> 8) & 255;
  compressed[2] = (enc_c1 >> 0) & 255;
  compressed[3] = (enc_c1 >> 8) & 255;
  /*	zero out the compressed data	*/
  compressed[4] = 0;
  compressed[5] = 0;
  compressed[6] = 0;
  compressed[7] = 0;
  /*	reconstitute the master color vectors	*/
  rgb_888_from_565(enc_c0, &c0[0], &c0[1], &c0[2]);
  rgb_888_from_565(enc_c1, &c1[0], &c1[1], &c1[2]);
  /*	the new vector	*/
  vec_len2 = 0.0f;
  for (i = 0; i < 3; ++i)
  {
    color_line[i] = (float)(c1[i] - c0[i]);
    vec_len2 += color_line[i] * color_line[i];
  }
  if (vec_len2 > 0.0f)
  {
    vec_len2 = 1.0f / vec_len2;
  }
  /*	pre-proform the scaling	*/
  color_line[0] *= vec_len2;
  color_line[1] *= vec_len2;
  color_line[2] *= vec_len2;
  /*	compute the offset (constant) portion of the dot product	*/
  dot_offset = color_line[0] * c0[0] + color_line[1] * c0[1] + color_line[2] * c0[2];
  /*	store the rest of the bits	*/
  next_bit = 8 * 4;
  for (i = 0; i < 16; ++i)
  {
    /*	find the dot product of this color, to place it on the line
      (should be [-1,1])	*/
    int next_value = 0;
    float dot_product =
      color_line[0] * uncompressed[i * channels + 0] +
      color_line[1] * uncompressed[i * channels + 1] +
      color_line[2] * uncompressed[i * channels + 2] -
      dot_offset;
    /*	map to [0,3]	*/
    next_value = (int)(dot_product * 3.0f + 0.5f);
    if (next_value > 3)
    {
      next_value = 3;
    }
    else if (next_value < 0)
    {
      next_value = 0;
    }
    /*	OK, store this value	*/
    compressed[next_bit >> 3] |= swizzle4[next_value] << (next_bit & 7);
    next_bit += 2;
  }
  /*	done compressing to DXT1	*/
}

void
compress_DDS_alpha_block
(
  const unsigned char* const uncompressed,
  unsigned char compressed[8]
)
{
  /*	variables	*/
  int i;
  int next_bit;
  int a0, a1;
  float scale_me;
  /*	stupid order	*/
  int swizzle8[] = { 1, 7, 6, 5, 4, 3, 2, 0 };
  /*	get the alpha limits (a0 > a1)	*/
  a0 = a1 = uncompressed[3];
  for (i = 4 + 3; i < 16 * 4; i += 4)
  {
    if (uncompressed[i] > a0)
    {
      a0 = uncompressed[i];
    }
    else if (uncompressed[i] < a1)
    {
      a1 = uncompressed[i];
    }
  }
  /*	store those limits, and zero the rest of the compressed dataset	*/
  compressed[0] = a0;
  compressed[1] = a1;
  /*	zero out the compressed data	*/
  compressed[2] = 0;
  compressed[3] = 0;
  compressed[4] = 0;
  compressed[5] = 0;
  compressed[6] = 0;
  compressed[7] = 0;
  /*	store the all of the alpha values	*/
  next_bit = 8 * 2;
  scale_me = 7.9999f / (a0 - a1);
  for (i = 3; i < 16 * 4; i += 4)
  {
    /*	convert this alpha value to a 3 bit number	*/
    int svalue;
    int value = (int)((uncompressed[i] - a1) * scale_me);
    svalue = swizzle8[value & 7];
    /*	OK, store this value, start with the 1st byte	*/
    compressed[next_bit >> 3] |= svalue << (next_bit & 7);
    if ((next_bit & 7) > 5)
    {
      /*	spans 2 bytes, fill in the start of the 2nd byte	*/
      compressed[1 + (next_bit >> 3)] |= svalue >> (8 - (next_bit & 7));
    }
    next_bit += 3;
  }
  /*	done compressing to DXT1	*/
}














#define SOIL_CHECK_FOR_GL_ERRORS 0

#if defined( __APPLE_CC__ ) || defined ( __APPLE__ )
#include <TargetConditionals.h>

#if defined( __IPHONE__ ) || ( defined( TARGET_OS_IPHONE ) && TARGET_OS_IPHONE ) || ( defined( TARGET_IPHONE_SIMULATOR ) && TARGET_IPHONE_SIMULATOR )
#define SOIL_PLATFORM_IOS
#include <dlfcn.h>
#else
#define SOIL_PLATFORM_OSX
#endif
#elif defined( __ANDROID__ ) || defined( ANDROID )
#define SOIL_PLATFORM_ANDROID_SDL
#elif ( defined ( linux ) || defined( __linux__ ) || defined( __FreeBSD__ ) || defined(__OpenBSD__) || defined( __NetBSD__ ) || defined( __DragonFly__ ) || defined( __SVR4 ) )
#define SOIL_X11_PLATFORM
#endif

#if ( defined( SOIL_PLATFORM_IOS ) || defined( SOIL_PLATFORM_ANDROID_SDL ) ) && ( !defined( SOIL_GLES1 ) && !defined( SOIL_GLES2 ) )
#define SOIL_GLES2
#endif

#if ( defined( SOIL_GLES2 ) || defined( SOIL_GLES1 ) ) && !defined( SOIL_NO_EGL ) && !defined( SOIL_PLATFORM_IOS )
#include <EGL/egl.h>
#endif

#if defined( SOIL_GLES2 )
#ifdef SOIL_PLATFORM_IOS
#include <OpenGLES/ES2/gl.h>
#include <OpenGLES/ES2/glext.h>
#else
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#endif

#define APIENTRY GL_APIENTRY
#elif defined( SOIL_GLES1 )
#ifndef GL_GLEXT_PROTOTYPES
#define GL_GLEXT_PROTOTYPES
#endif
#ifdef SOIL_PLATFORM_IOS
#include <OpenGLES/ES1/gl.h>
#include <OpenGLES/ES1/glext.h>
#else
#include <GLES/gl.h>
#include <GLES/glext.h>
#endif

#define APIENTRY GL_APIENTRY
#else

#if defined( __WIN32__ ) || defined( _WIN32 ) || defined( WIN32 )
#define SOIL_PLATFORM_WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <wingdi.h>
#include <GL/gl.h>

#ifndef GL_UNSIGNED_SHORT_4_4_4_4
#define GL_UNSIGNED_SHORT_4_4_4_4 0x8033
#endif
#ifndef GL_UNSIGNED_SHORT_5_5_5_1
#define GL_UNSIGNED_SHORT_5_5_5_1 0x8034
#endif
#ifndef GL_UNSIGNED_SHORT_5_6_5
#define GL_UNSIGNED_SHORT_5_6_5 0x8363
#endif
#elif defined(__APPLE__) || defined(__APPLE_CC__)
/*	I can't test this Apple stuff!	*/
#include <OpenGL/gl.h>
#include <Carbon/Carbon.h>
#define APIENTRY
#elif defined( SOIL_X11_PLATFORM )
#include <GL/gl.h>
#include <GL/glx.h>
#else
#include <GL/gl.h>
#endif

#endif

#ifndef GL_BGRA
#define GL_BGRA                                             0x80E1
#endif

#ifndef GL_RG
#define GL_RG                             0x8227
#endif

#ifndef HEADER_SIMPLE_OPENGL_IMAGE_LIBRARY
#define HEADER_SIMPLE_OPENGL_IMAGE_LIBRARY

#ifdef __cplusplus
extern "C" {
#endif

  enum
  {
    SOIL_LOAD_AUTO = 0,
    SOIL_LOAD_L = 1,
    SOIL_LOAD_LA = 2,
    SOIL_LOAD_RGB = 3,
    SOIL_LOAD_RGBA = 4
  };

  enum
  {
    SOIL_CREATE_NEW_ID = 0
  };

  enum
  {
    SOIL_FLAG_POWER_OF_TWO = 1,
    SOIL_FLAG_MIPMAPS = 2,
    SOIL_FLAG_TEXTURE_REPEATS = 4,
    SOIL_FLAG_MULTIPLY_ALPHA = 8,
    SOIL_FLAG_INVERT_Y = 16,
    SOIL_FLAG_COMPRESS_TO_DXT = 32,
    SOIL_FLAG_DDS_LOAD_DIRECT = 64,
    SOIL_FLAG_NTSC_SAFE_RGB = 128,
    SOIL_FLAG_CoCg_Y = 256,
    SOIL_FLAG_TEXTURE_RECTANGLE = 512,
    SOIL_FLAG_PVR_LOAD_DIRECT = 1024,
    SOIL_FLAG_ETC1_LOAD_DIRECT = 2048,
    SOIL_FLAG_GL_MIPMAPS = 4096
  };

  enum
  {
    SOIL_SAVE_TYPE_TGA = 0,
    SOIL_SAVE_TYPE_BMP = 1,
    SOIL_SAVE_TYPE_PNG = 2,
    SOIL_SAVE_TYPE_DDS = 3
  };

#define SOIL_DDS_CUBEMAP_FACE_ORDER "EWUDNS"

  enum
  {
    SOIL_HDR_RGBE = 0,
    SOIL_HDR_RGBdivA = 1,
    SOIL_HDR_RGBdivA2 = 2
  };

  unsigned int
    SOIL_load_OGL_texture
    (
      const char* filename,
      int force_channels,
      unsigned int reuse_texture_ID,
      unsigned int flags,
      int* width,
      int* height);

  unsigned int
    SOIL_load_OGL_texture_from_memory
    (const unsigned char* const buffer,
      unsigned int buffer_length,
      int force_channels,
      unsigned int reuse_texture_ID,
      unsigned int flags,
      int* width,
      int* height
    );

  unsigned char*
    SOIL_load_image
    (
      const char* filename,
      int* width, int* height, int* channels,
      int force_channels
    );

  unsigned char*
    SOIL_load_image_from_memory
    (
      const unsigned char* const buffer,
      int buffer_length,
      int* width, int* height, int* channels,
      int force_channels
    );

  void
    SOIL_free_image_data
    (
      unsigned char* img_data
    );

  unsigned int SOIL_direct_load_DDS(
    const char* filename,
    unsigned int reuse_texture_ID,
    int flags,
    int loading_as_cubemap);

  unsigned int SOIL_direct_load_DDS_from_memory(
    const unsigned char* const buffer,
    unsigned int buffer_length,
    unsigned int reuse_texture_ID,
    int flags,
    int loading_as_cubemap);

  unsigned int SOIL_direct_load_PVR(
    const char* filename,
    unsigned int reuse_texture_ID,
    int flags,
    int loading_as_cubemap);

  unsigned int SOIL_direct_load_PVR_from_memory(
    const unsigned char* const buffer,
    int buffer_length,
    unsigned int reuse_texture_ID,
    int flags,
    int loading_as_cubemap);

  unsigned int SOIL_direct_load_ETC1(const char* filename,
    unsigned int reuse_texture_ID,
    int flags);

  unsigned int SOIL_direct_load_ETC1_from_memory(const unsigned char* const buffer,
    int buffer_length,
    unsigned int reuse_texture_ID,
    int flags);

#ifdef __cplusplus
}
#endif

#endif /* HEADER_SIMPLE_OPENGL_IMAGE_LIBRARY	*/

#define STB_IMAGE_IMPLEMENTATION
/* stb_image - v2.05 - public domain image loader - http://nothings.org/stb_image.h
                                     no warranty implied; use at your own risk

   Do this:
      #define STB_IMAGE_IMPLEMENTATION
   before you include this file in *one* C or C++ file to create the implementation.

   // i.e. it should look like this:
   #include ...
   #include ...
   #include ...
   #define STB_IMAGE_IMPLEMENTATION
   #include "stb_image.h"

   You can #define STBI_ASSERT(x) before the #include to avoid using assert.h.
   And #define STBI_MALLOC, STBI_REALLOC, and STBI_FREE to avoid using malloc,realloc,free


   QUICK NOTES:
      Primarily of interest to game developers and other people who can
          avoid problematic images and only need the trivial interface

      JPEG baseline & progressive (12 bpc/arithmetic not supported, same as stock IJG lib)
      PNG 1/2/4/8-bit-per-channel (16 bpc not supported)

      TGA (not sure what subset, if a subset)
      BMP non-1bpp, non-RLE
      PSD (composited view only, no extra channels)

      GIF (*comp always reports as 4-channel)
      HDR (radiance rgbE format)
      PIC (Softimage PIC)
      PNM (PPM and PGM binary only)

      - decode from memory or through FILE (define STBI_NO_STDIO to remove code)
      - decode from arbitrary I/O callbacks
      - SIMD acceleration on x86/x64 (SSE2) and ARM (NEON)

   Full documentation under "DOCUMENTATION" below.


   Revision 2.00 release notes:

      - Progressive JPEG is now supported.

      - PPM and PGM binary formats are now supported, thanks to Ken Miller.

      - x86 platforms now make use of SSE2 SIMD instructions for
        JPEG decoding, and ARM platforms can use NEON SIMD if requested.
        This work was done by Fabian "ryg" Giesen. SSE2 is used by
        default, but NEON must be enabled explicitly; see docs.

        With other JPEG optimizations included in this version, we see
        2x speedup on a JPEG on an x86 machine, and a 1.5x speedup
        on a JPEG on an ARM machine, relative to previous versions of this
        library. The same results will not obtain for all JPGs and for all
        x86/ARM machines. (Note that progressive JPEGs are significantly
        slower to decode than regular JPEGs.) This doesn't mean that this
        is the fastest JPEG decoder in the land; rather, it brings it
        closer to parity with standard libraries. If you want the fastest
        decode, look elsewhere. (See "Philosophy" section of docs below.)

        See final bullet items below for more info on SIMD.

      - Added STBI_MALLOC, STBI_REALLOC, and STBI_FREE macros for replacing
        the memory allocator. Unlike other STBI libraries, these macros don't
        support a context parameter, so if you need to pass a context in to
        the allocator, you'll have to store it in a global or a thread-local
        variable.

      - Split existing STBI_NO_HDR flag into two flags, STBI_NO_HDR and
        STBI_NO_LINEAR.
            STBI_NO_HDR:     suppress implementation of .hdr reader format
            STBI_NO_LINEAR:  suppress high-dynamic-range light-linear float API

      - You can suppress implementation of any of the decoders to reduce
        your code footprint by #defining one or more of the following
        symbols before creating the implementation.

            STBI_NO_JPEG
            STBI_NO_PNG
            STBI_NO_BMP
            STBI_NO_PSD
            STBI_NO_TGA
            STBI_NO_GIF
            STBI_NO_HDR
            STBI_NO_PIC
            STBI_NO_PNM   (.ppm and .pgm)

      - You can request *only* certain decoders and suppress all other ones
        (this will be more forward-compatible, as addition of new decoders
        doesn't require you to disable them explicitly):

            STBI_ONLY_JPEG
            STBI_ONLY_PNG
            STBI_ONLY_BMP
            STBI_ONLY_PSD
            STBI_ONLY_TGA
            STBI_ONLY_GIF
            STBI_ONLY_HDR
            STBI_ONLY_PIC
            STBI_ONLY_PNM   (.ppm and .pgm)

         Note that you can define multiples of these, and you will get all
         of them ("only x" and "only y" is interpreted to mean "only x&y").

       - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still
         want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB

      - Compilation of all SIMD code can be suppressed with
            #define STBI_NO_SIMD
        It should not be necessary to disable SIMD unless you have issues
        compiling (e.g. using an x86 compiler which doesn't support SSE
        intrinsics or that doesn't support the method used to detect
        SSE2 support at run-time), and even those can be reported as
        bugs so I can refine the built-in compile-time checking to be
        smarter.

      - The old STBI_SIMD system which allowed installing a user-defined
        IDCT etc. has been removed. If you need this, don't upgrade. My
        assumption is that almost nobody was doing this, and those who
        were will find the built-in SIMD more satisfactory anyway.

      - RGB values computed for JPEG images are slightly different from
        previous versions of stb_image. (This is due to using less
        integer precision in SIMD.) The C code has been adjusted so
        that the same RGB values will be computed regardless of whether
        SIMD support is available, so your app should always produce
        consistent results. But these results are slightly different from
        previous versions. (Specifically, about 3% of available YCbCr values
        will compute different RGB results from pre-1.49 versions by +-1;
        most of the deviating values are one smaller in the G channel.)

      - If you must produce consistent results with previous versions of
        stb_image, #define STBI_JPEG_OLD and you will get the same results
        you used to; however, you will not get the SIMD speedups for
        the YCbCr-to-RGB conversion step (although you should still see
        significant JPEG speedup from the other changes).

        Please note that STBI_JPEG_OLD is a temporary feature; it will be
        removed in future versions of the library. It is only intended for
        near-term back-compatibility use.


   Latest revision history:
      2.05  (2015-04-19) fix bug in progressive JPEG handling, fix warning
      2.04  (2015-04-15) try to re-enable SIMD on MinGW 64-bit
      2.03  (2015-04-12) additional corruption checking
                         stbi_set_flip_vertically_on_load
                         fix NEON support; fix mingw support
      2.02  (2015-01-19) fix incorrect assert, fix warning
      2.01  (2015-01-17) fix various warnings
      2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG
      2.00  (2014-12-25) optimize JPEG, including x86 SSE2 & ARM NEON SIMD
                         progressive JPEG
                         PGM/PPM support
                         STBI_MALLOC,STBI_REALLOC,STBI_FREE
                         STBI_NO_*, STBI_ONLY_*
                         GIF bugfix
      1.48  (2014-12-14) fix incorrectly-named assert()
      1.47  (2014-12-14) 1/2/4-bit PNG support (both grayscale and paletted)
                         optimize PNG
                         fix bug in interlaced PNG with user-specified channel count

   See end of file for full revision history.


 ============================    Contributors    =========================

 Image formats                                Bug fixes & warning fixes
    Sean Barrett (jpeg, png, bmp)                Marc LeBlanc
    Nicolas Schulz (hdr, psd)                    Christpher Lloyd
    Jonathan Dummer (tga)                        Dave Moore
    Jean-Marc Lienher (gif)                      Won Chun
    Tom Seddon (pic)                             the Horde3D community
    Thatcher Ulrich (psd)                        Janez Zemva
    Ken Miller (pgm, ppm)                        Jonathan Blow
                                                 Laurent Gomila
                                                 Aruelien Pocheville
 Extensions, features                            Ryamond Barbiero
    Jetro Lauha (stbi_info)                      David Woo
    Martin "SpartanJ" Golini (stbi_info)         Martin Golini
    James "moose2000" Brown (iPhone PNG)         Roy Eltham
    Ben "Disch" Wenger (io callbacks)            Luke Graham
    Omar Cornut (1/2/4-bit PNG)                  Thomas Ruf
    Nicolas Guillemot (vertical flip)            John Bartholomew
                                                 Ken Hamada
 Optimizations & bugfixes                        Cort Stratton
    Fabian "ryg" Giesen                          Blazej Dariusz Roszkowski
    Arseny Kapoulkine                            Thibault Reuille
                                                 Paul Du Bois
                                                 Guillaume George
  If your name should be here but                Jerry Jansson
  isn't, let Sean know.                          Hayaki Saito
                                                 Johan Duparc
                                                 Ronny Chevalier
                                                 Michal Cichon
                                                 Tero Hanninen
                                                 Sergio Gonzalez
                                                 Cass Everitt
                                                 Engin Manap
                                                 Martins Mozeiko
                                                 Joseph Thomson
                                                 Phil Jordan

License:
   This software is in the public domain. Where that dedication is not
   recognized, you are granted a perpetual, irrevocable license to copy
   and modify this file however you want.

*/

#ifndef STBI_INCLUDE_STB_IMAGE_H
#define STBI_INCLUDE_STB_IMAGE_H

// DOCUMENTATION
//
// Limitations:
//    - no 16-bit-per-channel PNG
//    - no 12-bit-per-channel JPEG
//    - no JPEGs with arithmetic coding
//    - no 1-bit BMP
//    - GIF always returns *comp=4
//
// Basic usage (see HDR discussion below for HDR usage):
//    int x,y,n;
//    unsigned char *data = stbi_load(filename, &x, &y, &n, 0);
//    // ... process data if not nullptr ...
//    // ... x = width, y = height, n = # 8-bit components per pixel ...
//    // ... replace '0' with '1'..'4' to force that many components per pixel
//    // ... but 'n' will always be the number that it would have been if you said 0
//    stbi_image_free(data)
//
// Standard parameters:
//    int *x       -- outputs image width in pixels
//    int *y       -- outputs image height in pixels
//    int *comp    -- outputs # of image components in image file
//    int req_comp -- if non-zero, # of image components requested in result
//
// The return value from an image loader is an 'unsigned char *' which points
// to the pixel data, or nullptr on an allocation failure or if the image is
// corrupt or invalid. The pixel data consists of *y scanlines of *x pixels,
// with each pixel consisting of N interleaved 8-bit components; the first
// pixel pointed to is top-left-most in the image. There is no padding between
// image scanlines or between pixels, regardless of format. The number of
// components N is 'req_comp' if req_comp is non-zero, or *comp otherwise.
// If req_comp is non-zero, *comp has the number of components that _would_
// have been output otherwise. E.g. if you set req_comp to 4, you will always
// get RGBA output, but you can check *comp to see if it's trivially opaque
// because e.g. there were only 3 channels in the source image.
//
// An output image with N components has the following components interleaved
// in this order in each pixel:
//
//     N=#comp     components
//       1           grey
//       2           grey, alpha
//       3           red, green, blue
//       4           red, green, blue, alpha
//
// If image loading fails for any reason, the return value will be nullptr,
// and *x, *y, *comp will be unchanged. The function stbi_failure_reason()
// can be queried for an extremely brief, end-user unfriendly explanation
// of why the load failed. Define STBI_NO_FAILURE_STRINGS to avoid
// compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly
// more user-friendly ones.
//
// Paletted PNG, BMP, GIF, and PIC images are automatically depalettized.
//
// ===========================================================================
//
// Philosophy
//
// stb libraries are designed with the following priorities:
//
//    1. easy to use
//    2. easy to maintain
//    3. good performance
//
// Sometimes I let "good performance" creep up in priority over "easy to maintain",
// and for best performance I may provide less-easy-to-use APIs that give higher
// performance, in addition to the easy to use ones. Nevertheless, it's important
// to keep in mind that from the standpoint of you, a client of this library,
// all you care about is #1 and #3, and stb libraries do not emphasize #3 above all.
//
// Some secondary priorities arise directly from the first two, some of which
// make more explicit reasons why performance can't be emphasized.
//
//    - Portable ("ease of use")
//    - Small footprint ("easy to maintain")
//    - No dependencies ("ease of use")
//
// ===========================================================================
//
// I/O callbacks
//
// I/O callbacks allow you to read from arbitrary sources, like packaged
// files or some other source. Data read from callbacks are processed
// through a small internal buffer (currently 128 bytes) to try to reduce
// overhead.
//
// The three functions you must define are "read" (reads some bytes of data),
// "skip" (skips some bytes of data), "eof" (reports if the stream is at the end).
//
// ===========================================================================
//
// SIMD support
//
// The JPEG decoder will try to automatically use SIMD kernels on x86 when
// supported by the compiler. For ARM Neon support, you must explicitly
// request it.
//
// (The old do-it-yourself SIMD API is no longer supported in the current
// code.)
//
// On x86, SSE2 will automatically be used when available based on a run-time
// test; if not, the generic C versions are used as a fall-back. On ARM targets,
// the typical path is to have separate builds for NEON and non-NEON devices
// (at least this is true for iOS and Android). Therefore, the NEON support is
// toggled by a build flag: define STBI_NEON to get NEON loops.
//
// The output of the JPEG decoder is slightly different from versions where
// SIMD support was introduced (that is, for versions before 1.49). The
// difference is only +-1 in the 8-bit RGB channels, and only on a small
// fraction of pixels. You can force the pre-1.49 behavior by defining
// STBI_JPEG_OLD, but this will disable some of the SIMD decoding path
// and hence cost some performance.
//
// If for some reason you do not want to use any of SIMD code, or if
// you have issues compiling it, you can disable it entirely by
// defining STBI_NO_SIMD.
//
// ===========================================================================
//
// HDR image support   (disable by defining STBI_NO_HDR)
//
// stb_image now supports loading HDR images in general, and currently
// the Radiance .HDR file format, although the support is provided
// generically. You can still load any file through the existing interface;
// if you attempt to load an HDR file, it will be automatically remapped to
// LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1;
// both of these constants can be reconfigured through this interface:
//
//     stbi_hdr_to_ldr_gamma(2.2f);
//     stbi_hdr_to_ldr_scale(1.0f);
//
// (note, do not use _inverse_ constants; stbi_image will invert them
// appropriately).
//
// Additionally, there is a new, parallel interface for loading files as
// (linear) floats to preserve the full dynamic range:
//
//    float *data = stbi_loadf(filename, &x, &y, &n, 0);
//
// If you load LDR images through this interface, those images will
// be promoted to floating point values, run through the inverse of
// constants corresponding to the above:
//
//     stbi_ldr_to_hdr_scale(1.0f);
//     stbi_ldr_to_hdr_gamma(2.2f);
//
// Finally, given a filename (or an open file or memory block--see header
// file for details) containing image data, you can query for the "most
// appropriate" interface to use (that is, whether the image is HDR or
// not), using:
//
//     stbi_is_hdr(char *filename);
//
// ===========================================================================
//
// iPhone PNG support:
//
// By default we convert iphone-formatted PNGs back to RGB, even though
// they are internally encoded differently. You can disable this conversion
// by by calling stbi_convert_iphone_png_to_rgb(0), in which case
// you will always just get the native iphone "format" through (which
// is BGR stored in RGB).
//
// Call stbi_set_unpremultiply_on_load(1) as well to force a divide per
// pixel to remove any premultiplied alpha *only* if the image file explicitly
// says there's premultiplied data (currently only happens in iPhone images,
// and only if iPhone convert-to-rgb processing is on).
//


#ifndef STBI_NO_STDIO
#include <stdio.h>
#endif // STBI_NO_STDIO

#define STBI_VERSION 1

enum
{
  STBI_default = 0, // only used for req_comp

  STBI_grey = 1,
  STBI_grey_alpha = 2,
  STBI_rgb = 3,
  STBI_rgb_alpha = 4
};

typedef unsigned char stbi_uc;

#ifdef __cplusplus
extern "C" {
#endif

#ifdef STB_IMAGE_STATIC
#define STBIDEF static
#else
#define STBIDEF extern
#endif

  //////////////////////////////////////////////////////////////////////////////
  //
  // PRIMARY API - works on images of any type
  //

  //
  // load image by filename, open file, or memory buffer
  //

  typedef struct
  {
    int      (*read)  (void* user, char* data, int size);   // fill 'data' with 'size' bytes.  return number of bytes actually read
    void     (*skip)  (void* user, int n);                 // skip the next 'n' bytes, or 'unget' the last -n bytes if negative
    int      (*eof)   (void* user);                       // returns nonzero if we are at end of file/data
  } stbi_io_callbacks;

  STBIDEF stbi_uc* stbi_load(char              const* filename, int* x, int* y, int* comp, int req_comp);
  STBIDEF stbi_uc* stbi_load_from_memory(stbi_uc           const* buffer, int len, int* x, int* y, int* comp, int req_comp);
  STBIDEF stbi_uc* stbi_load_from_callbacks(stbi_io_callbacks const* clbk, void* user, int* x, int* y, int* comp, int req_comp);

#ifndef STBI_NO_STDIO
  STBIDEF stbi_uc* stbi_load_from_file(FILE* f, int* x, int* y, int* comp, int req_comp);
  // for stbi_load_from_file, file pointer is left pointing immediately after image
#endif

#ifndef STBI_NO_LINEAR
  STBIDEF float* stbi_loadf(char const* filename, int* x, int* y, int* comp, int req_comp);
  STBIDEF float* stbi_loadf_from_memory(stbi_uc const* buffer, int len, int* x, int* y, int* comp, int req_comp);
  STBIDEF float* stbi_loadf_from_callbacks(stbi_io_callbacks const* clbk, void* user, int* x, int* y, int* comp, int req_comp);

#ifndef STBI_NO_STDIO
  STBIDEF float* stbi_loadf_from_file(FILE* f, int* x, int* y, int* comp, int req_comp);
#endif
#endif

#ifndef STBI_NO_HDR
  STBIDEF void   stbi_hdr_to_ldr_gamma(float gamma);
  STBIDEF void   stbi_hdr_to_ldr_scale(float scale);
#endif

#ifndef STBI_NO_LINEAR
  STBIDEF void   stbi_ldr_to_hdr_gamma(float gamma);
  STBIDEF void   stbi_ldr_to_hdr_scale(float scale);
#endif // STBI_NO_HDR

  // stbi_is_hdr is always defined, but always returns false if STBI_NO_HDR
  STBIDEF int    stbi_is_hdr_from_callbacks(stbi_io_callbacks const* clbk, void* user);
  STBIDEF int    stbi_is_hdr_from_memory(stbi_uc const* buffer, int len);
#ifndef STBI_NO_STDIO
  STBIDEF int      stbi_is_hdr(char const* filename);
  STBIDEF int      stbi_is_hdr_from_file(FILE* f);
#endif // STBI_NO_STDIO


  // get a VERY brief reason for failure
  // NOT THREADSAFE
  STBIDEF const char* stbi_failure_reason(void);

  // free the loaded image -- this is just free()
  STBIDEF void     stbi_image_free(void* retval_from_stbi_load);

  // get image dimensions & components without fully decoding
  STBIDEF int      stbi_info_from_memory(stbi_uc const* buffer, int len, int* x, int* y, int* comp);
  STBIDEF int      stbi_info_from_callbacks(stbi_io_callbacks const* clbk, void* user, int* x, int* y, int* comp);

#ifndef STBI_NO_STDIO
  STBIDEF int      stbi_info(char const* filename, int* x, int* y, int* comp);
  STBIDEF int      stbi_info_from_file(FILE* f, int* x, int* y, int* comp);

#endif



  // for image formats that explicitly notate that they have premultiplied alpha,
  // we just return the colors as stored in the file. set this flag to force
  // unpremultiplication. results are undefined if the unpremultiply overflow.
  STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply);

  // indicate whether we should process iphone images back to canonical format,
  // or just pass them through "as-is"
  STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert);

  // flip the image vertically, so the first pixel in the output array is the bottom left
  STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip);

  // ZLIB client - used by PNG, available for other purposes

  STBIDEF char* stbi_zlib_decode_malloc_guesssize(const char* buffer, int len, int initial_size, int* outlen);
  STBIDEF char* stbi_zlib_decode_malloc_guesssize_headerflag(const char* buffer, int len, int initial_size, int* outlen, int parse_header);
  STBIDEF char* stbi_zlib_decode_malloc(const char* buffer, int len, int* outlen);
  STBIDEF int   stbi_zlib_decode_buffer(char* obuffer, int olen, const char* ibuffer, int ilen);

  STBIDEF char* stbi_zlib_decode_noheader_malloc(const char* buffer, int len, int* outlen);
  STBIDEF int   stbi_zlib_decode_noheader_buffer(char* obuffer, int olen, const char* ibuffer, int ilen);


#define STBI_NO_PKM
#define STBI_NO_DDS
#define STBI_NO_PVR
#define STBI_NO_EXT

#ifdef __cplusplus
}
#endif

//
//
////   end header file   /////////////////////////////////////////////////////
#endif // STBI_INCLUDE_STB_IMAGE_H

#ifdef STB_IMAGE_IMPLEMENTATION

#if defined(STBI_ONLY_JPEG) || defined(STBI_ONLY_PNG) || defined(STBI_ONLY_BMP) \
  || defined(STBI_ONLY_TGA) || defined(STBI_ONLY_GIF) || defined(STBI_ONLY_PSD) \
  || defined(STBI_ONLY_HDR) || defined(STBI_ONLY_PIC) || defined(STBI_ONLY_PNM) \
  || defined(STBI_ONLY_ZLIB)
#ifndef STBI_ONLY_JPEG
#define STBI_NO_JPEG
#endif
#ifndef STBI_ONLY_PNG
#define STBI_NO_PNG
#endif
#ifndef STBI_ONLY_BMP
#define STBI_NO_BMP
#endif
#ifndef STBI_ONLY_PSD
#define STBI_NO_PSD
#endif
#ifndef STBI_ONLY_TGA
#define STBI_NO_TGA
#endif
#ifndef STBI_ONLY_GIF
#define STBI_NO_GIF
#endif
#ifndef STBI_ONLY_HDR
#define STBI_NO_HDR
#endif
#ifndef STBI_ONLY_PIC
#define STBI_NO_PIC
#endif
#ifndef STBI_ONLY_PNM
#define STBI_NO_PNM
#endif
#endif

#if defined(STBI_NO_PNG) && !defined(STBI_SUPPORT_ZLIB) && !defined(STBI_NO_ZLIB)
#define STBI_NO_ZLIB
#endif




#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR)
#include <math.h>  // ldexp
#endif

#ifndef STBI_NO_STDIO
#include <stdio.h>
#endif

#ifndef STBI_ASSERT
#include <assert.h>
#define STBI_ASSERT(x) assert(x)
#endif


#ifndef _MSC_VER
#ifdef __cplusplus
#define stbi_inline inline
#else
#define stbi_inline
#endif
#else
#define stbi_inline __forceinline
#endif


#ifdef _MSC_VER
typedef unsigned short stbi__uint16;
typedef   signed short stbi__int16;
typedef unsigned int   stbi__uint32;
typedef   signed int   stbi__int32;
#else
#include <stdint.h>
typedef uint16_t stbi__uint16;
typedef int16_t  stbi__int16;
typedef uint32_t stbi__uint32;
typedef int32_t  stbi__int32;
#endif

// should produce compiler error if size is wrong
typedef unsigned char validate_uint32[sizeof(stbi__uint32) == 4 ? 1 : -1];

#ifdef _MSC_VER
#define STBI_NOTUSED(v)  (void)(v)
#else
#define STBI_NOTUSED(v)  (void)sizeof(v)
#endif

#ifdef _MSC_VER
#define STBI_HAS_LROTL
#endif

#ifdef STBI_HAS_LROTL
#define stbi_lrot(x,y)  _lrotl(x,y)
#else
#define stbi_lrot(x,y)  (((x) << (y)) | ((x) >> (32 - (y))))
#endif

#if defined(STBI_MALLOC) && defined(STBI_FREE) && defined(STBI_REALLOC)
// ok
#elif !defined(STBI_MALLOC) && !defined(STBI_FREE) && !defined(STBI_REALLOC)
// ok
#else
#error "Must define all or none of STBI_MALLOC, STBI_FREE, and STBI_REALLOC."
#endif

#ifndef STBI_MALLOC
#define STBI_MALLOC(sz)    malloc(sz)
#define STBI_REALLOC(p,sz) realloc(p,sz)
#define STBI_FREE(p)       free(p)
#endif

// x86/x64 detection
#if defined(__x86_64__) || defined(_M_X64)
#define STBI__X64_TARGET
#elif defined(__i386) || defined(_M_IX86)
#define STBI__X86_TARGET
#endif

#if defined(__GNUC__) && (defined(STBI__X86_TARGET) || defined(STBI__X64_TARGET)) && !defined(__SSE2__) && !defined(STBI_NO_SIMD)
// NOTE: not clear do we actually need this for the 64-bit path?
// gcc doesn't support sse2 intrinsics unless you compile with -msse2,
// (but compiling with -msse2 allows the compiler to use SSE2 everywhere;
// this is just broken and gcc are jerks for not fixing it properly
// http://www.virtualdub.org/blog/pivot/entry.php?id=363 )
#define STBI_NO_SIMD
#endif

#if defined(__MINGW32__) && !defined(__x86_64__) && !defined(STBI_NO_SIMD)
#define STBI_MINGW_ENABLE_SSE2
#define STBI_FORCE_STACK_ALIGN __attribute__((force_align_arg_pointer))
#else
#define STBI_FORCE_STACK_ALIGN
#endif

#if defined(__MINGW32__) && defined(STBI__X86_TARGET) && !defined(STBI_MINGW_ENABLE_SSE2) && !defined(STBI_NO_SIMD)
// Note that __MINGW32__ doesn't actually mean 32-bit, so we have to avoid STBI__X64_TARGET
//
// 32-bit MinGW wants ESP to be 16-byte aligned, but this is not in the
// Windows ABI and VC++ as well as Windows DLLs don't maintain that invariant.
// As a result, enabling SSE2 on 32-bit MinGW is dangerous when not
// simultaneously enabling "-mstackrealign".
//
// See https://github.com/nothings/stb/issues/81 for more information.
//
// So default to no SSE2 on 32-bit MinGW. If you've read this far and added
// -mstackrealign to your build settings, feel free to #define STBI_MINGW_ENABLE_SSE2.
#define STBI_NO_SIMD
#endif

#if !defined(STBI_NO_SIMD) && defined(STBI__X86_TARGET)
#define STBI_SSE2
#include <emmintrin.h>

#ifdef _MSC_VER

#if _MSC_VER >= 1400  // not VC6
#include <intrin.h> // __cpuid
static int stbi__cpuid3(void)
{
  int info[4];
  __cpuid(info, 1);
  return info[3];
}
#else
static int stbi__cpuid3(void)
{
  int res;
  __asm {
    mov  eax, 1
    cpuid
    mov  res, edx
  }
  return res;
}
#endif

#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name

static int stbi__sse2_available()
{
  int info3 = stbi__cpuid3();
  return ((info3 >> 26) & 1) != 0;
}
#else // assume GCC-style if not VC++
#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16)))

static int stbi__sse2_available()
{
#if defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 408 // GCC 4.8 or later
  // GCC 4.8+ has a nice way to do this
  return __builtin_cpu_supports("sse2");
#else
  // portable way to do this, preferably without using GCC inline ASM?
  // just bail for now.
  return 0;
#endif
}
#endif
#endif

// ARM NEON
#if defined(STBI_NO_SIMD) && defined(STBI_NEON)
#undef STBI_NEON
#endif

#ifdef STBI_NEON
#include <arm_neon.h>
// assume GCC or Clang on ARM targets
#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16)))
#endif

#ifndef STBI_SIMD_ALIGN
#define STBI_SIMD_ALIGN(type, name) type name
#endif

///////////////////////////////////////////////
//
//  stbi__context struct and start_xxx functions

// stbi__context structure is our basic context used by all images, so it
// contains all the IO context, plus some basic image information
typedef struct
{
  stbi__uint32 img_x, img_y;
  int img_n, img_out_n;

  stbi_io_callbacks io;
  void* io_user_data;

  int read_from_callbacks;
  int buflen;
  stbi_uc buffer_start[128];

  stbi_uc* img_buffer, * img_buffer_end;
  stbi_uc* img_buffer_original;
} stbi__context;


static void stbi__refill_buffer(stbi__context* s);

// initialize a memory-decode context
static void stbi__start_mem(stbi__context* s, stbi_uc const* buffer, int len)
{
  s->io.read = nullptr;
  s->read_from_callbacks = 0;
  s->img_buffer = s->img_buffer_original = (stbi_uc*)buffer;
  s->img_buffer_end = (stbi_uc*)buffer + len;
}

// initialize a callback-based context
static void stbi__start_callbacks(stbi__context* s, stbi_io_callbacks* c, void* user)
{
  s->io = *c;
  s->io_user_data = user;
  s->buflen = sizeof(s->buffer_start);
  s->read_from_callbacks = 1;
  s->img_buffer_original = s->buffer_start;
  stbi__refill_buffer(s);
}

#ifndef STBI_NO_STDIO

static int stbi__stdio_read(void* user, char* data, int size)
{
  return (int)fread(data, 1, size, (FILE*)user);
}

static void stbi__stdio_skip(void* user, int n)
{
  fseek((FILE*)user, n, SEEK_CUR);
}

static int stbi__stdio_eof(void* user)
{
  return feof((FILE*)user);
}

static stbi_io_callbacks stbi__stdio_callbacks =
{
   stbi__stdio_read,
   stbi__stdio_skip,
   stbi__stdio_eof,
};

static void stbi__start_file(stbi__context* s, FILE* f)
{
  stbi__start_callbacks(s, &stbi__stdio_callbacks, (void*)f);
}

//static void stop_file(stbi__context *s) { }

#endif // !STBI_NO_STDIO

static void stbi__rewind(stbi__context* s)
{
  // conceptually rewind SHOULD rewind to the beginning of the stream,
  // but we just rewind to the beginning of the initial buffer, because
  // we only use it after doing 'test', which only ever looks at at most 92 bytes
  s->img_buffer = s->img_buffer_original;
}

#ifndef STBI_NO_JPEG
static int      stbi__jpeg_test(stbi__context* s);
static stbi_uc* stbi__jpeg_load(stbi__context* s, int* x, int* y, int* comp, int req_comp);
static int      stbi__jpeg_info(stbi__context* s, int* x, int* y, int* comp);
#endif

#ifndef STBI_NO_PNG
static int      stbi__png_test(stbi__context* s);
static stbi_uc* stbi__png_load(stbi__context* s, int* x, int* y, int* comp, int req_comp);
static int      stbi__png_info(stbi__context* s, int* x, int* y, int* comp);
#endif

#ifndef STBI_NO_BMP
static int      stbi__bmp_test(stbi__context* s);
static stbi_uc* stbi__bmp_load(stbi__context* s, int* x, int* y, int* comp, int req_comp);
static int      stbi__bmp_info(stbi__context* s, int* x, int* y, int* comp);
#endif

#ifndef STBI_NO_TGA
static int      stbi__tga_test(stbi__context* s);
static stbi_uc* stbi__tga_load(stbi__context* s, int* x, int* y, int* comp, int req_comp);
static int      stbi__tga_info(stbi__context* s, int* x, int* y, int* comp);
#endif

#ifndef STBI_NO_PSD
static int      stbi__psd_test(stbi__context* s);
static stbi_uc* stbi__psd_load(stbi__context* s, int* x, int* y, int* comp, int req_comp);
static int      stbi__psd_info(stbi__context* s, int* x, int* y, int* comp);
#endif

#ifndef STBI_NO_HDR
static int      stbi__hdr_test(stbi__context* s);
static float* stbi__hdr_load(stbi__context* s, int* x, int* y, int* comp, int req_comp);
static int      stbi__hdr_info(stbi__context* s, int* x, int* y, int* comp);
#endif

#ifndef STBI_NO_PIC
static int      stbi__pic_test(stbi__context* s);
static stbi_uc* stbi__pic_load(stbi__context* s, int* x, int* y, int* comp, int req_comp);
static int      stbi__pic_info(stbi__context* s, int* x, int* y, int* comp);
#endif

#ifndef STBI_NO_GIF
static int      stbi__gif_test(stbi__context* s);
static stbi_uc* stbi__gif_load(stbi__context* s, int* x, int* y, int* comp, int req_comp);
static int      stbi__gif_info(stbi__context* s, int* x, int* y, int* comp);
#endif

#ifndef STBI_NO_PNM
static int      stbi__pnm_test(stbi__context* s);
static stbi_uc* stbi__pnm_load(stbi__context* s, int* x, int* y, int* comp, int req_comp);
static int      stbi__pnm_info(stbi__context* s, int* x, int* y, int* comp);
#endif

#ifndef STBI_NO_PNM
static int      stbi__pnm_test(stbi__context* s);
static stbi_uc* stbi__pnm_load(stbi__context* s, int* x, int* y, int* comp, int req_comp);
static int      stbi__pnm_info(stbi__context* s, int* x, int* y, int* comp);
#endif

// this is not threadsafe
static const char* stbi__g_failure_reason;

STBIDEF const char* stbi_failure_reason(void)
{
  return stbi__g_failure_reason;
}

static int stbi__err(const char* str)
{
  stbi__g_failure_reason = str;
  return 0;
}

static void* stbi__malloc(size_t size)
{
  return STBI_MALLOC(size);
}

// stbi__err - error
// stbi__errpf - error returning pointer to float
// stbi__errpuc - error returning pointer to unsigned char

#ifdef STBI_NO_FAILURE_STRINGS
#define stbi__err(x,y)  0
#elif defined(STBI_FAILURE_USERMSG)
#define stbi__err(x,y)  stbi__err(y)
#else
#define stbi__err(x,y)  stbi__err(x)
#endif

#define stbi__errpf(x,y)   ((float *) (stbi__err(x,y)?nullptr:nullptr))
#define stbi__errpuc(x,y)  ((unsigned char *) (stbi__err(x,y)?nullptr:nullptr))

STBIDEF void stbi_image_free(void* retval_from_stbi_load)
{
  STBI_FREE(retval_from_stbi_load);
}

#ifndef STBI_NO_LINEAR
static float* stbi__ldr_to_hdr(stbi_uc* data, int x, int y, int comp);
#endif

#ifndef STBI_NO_HDR
static stbi_uc* stbi__hdr_to_ldr(float* data, int x, int y, int comp);
#endif

static int stbi__vertically_flip_on_load = 0;

STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip)
{
  stbi__vertically_flip_on_load = flag_true_if_should_flip;
}

static unsigned char* stbi__load_main(stbi__context* s, int* x, int* y, int* comp, int req_comp)
{
#ifndef STBI_NO_JPEG
  if (stbi__jpeg_test(s)) return stbi__jpeg_load(s, x, y, comp, req_comp);
#endif
#ifndef STBI_NO_PNG
  if (stbi__png_test(s))  return stbi__png_load(s, x, y, comp, req_comp);
#endif
#ifndef STBI_NO_BMP
  if (stbi__bmp_test(s))  return stbi__bmp_load(s, x, y, comp, req_comp);
#endif
#ifndef STBI_NO_GIF
  if (stbi__gif_test(s))  return stbi__gif_load(s, x, y, comp, req_comp);
#endif
#ifndef STBI_NO_PSD
  if (stbi__psd_test(s))  return stbi__psd_load(s, x, y, comp, req_comp);
#endif
#ifndef STBI_NO_PIC
  if (stbi__pic_test(s))  return stbi__pic_load(s, x, y, comp, req_comp);
#endif
#ifndef STBI_NO_PNM
  if (stbi__pnm_test(s))  return stbi__pnm_load(s, x, y, comp, req_comp);
#endif
#ifndef STBI_NO_DDS
  if (stbi__dds_test(s))  return stbi__dds_load(s, x, y, comp, req_comp);
#endif
#ifndef STBI_NO_PVR
  if (stbi__pvr_test(s))  return stbi__pvr_load(s, x, y, comp, req_comp);
#endif
#ifndef STBI_NO_PKM
  if (stbi__pkm_test(s))  return stbi__pkm_load(s, x, y, comp, req_comp);
#endif
#ifndef STBI_NO_HDR
  if (stbi__hdr_test(s)) {
    float* hdr = stbi__hdr_load(s, x, y, comp, req_comp);
    return stbi__hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp);
  }
#endif

#ifndef STBI_NO_TGA
  // test tga last because it's a crappy test!
  if (stbi__tga_test(s))
    return stbi__tga_load(s, x, y, comp, req_comp);
#endif

  return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt");
}

static unsigned char* stbi__load_flip(stbi__context* s, int* x, int* y, int* comp, int req_comp)
{
  unsigned char* result = stbi__load_main(s, x, y, comp, req_comp);

  if (stbi__vertically_flip_on_load && result != nullptr) {
    int w = *x, h = *y;
    int depth = req_comp ? req_comp : *comp;
    int row, col, z;
    stbi_uc temp;

    // @OPTIMIZE: use a bigger temp buffer and memcpy multiple pixels at once
    for (row = 0; row < (h >> 1); row++) {
      for (col = 0; col < w; col++) {
        for (z = 0; z < depth; z++) {
          temp = result[(row * w + col) * depth + z];
          result[(row * w + col) * depth + z] = result[((h - row - 1) * w + col) * depth + z];
          result[((h - row - 1) * w + col) * depth + z] = temp;
        }
      }
    }
  }

  return result;
}

static void stbi__float_postprocess(float* result, int* x, int* y, int* comp, int req_comp)
{
  if (stbi__vertically_flip_on_load && result != nullptr) {
    int w = *x, h = *y;
    int depth = req_comp ? req_comp : *comp;
    int row, col, z;
    float temp;

    // @OPTIMIZE: use a bigger temp buffer and memcpy multiple pixels at once
    for (row = 0; row < (h >> 1); row++) {
      for (col = 0; col < w; col++) {
        for (z = 0; z < depth; z++) {
          temp = result[(row * w + col) * depth + z];
          result[(row * w + col) * depth + z] = result[((h - row - 1) * w + col) * depth + z];
          result[((h - row - 1) * w + col) * depth + z] = temp;
        }
      }
    }
  }
}


#ifndef STBI_NO_STDIO

static FILE* stbi__fopen(char const* filename, char const* mode)
{
  FILE* f;
#if defined(_MSC_VER) && _MSC_VER >= 1400
  if (0 != fopen_s(&f, filename, mode))
    f = 0;
#else
  f = fopen(filename, mode);
#endif
  return f;
}


STBIDEF stbi_uc* stbi_load(char const* filename, int* x, int* y, int* comp, int req_comp)
{
  FILE* f = stbi__fopen(filename, "rb");
  unsigned char* result;
  if (!f) return stbi__errpuc("can't fopen", "Unable to open file");
  result = stbi_load_from_file(f, x, y, comp, req_comp);
  fclose(f);
  return result;
}

STBIDEF stbi_uc* stbi_load_from_file(FILE* f, int* x, int* y, int* comp, int req_comp)
{
  unsigned char* result;
  stbi__context s;
  stbi__start_file(&s, f);
  result = stbi__load_flip(&s, x, y, comp, req_comp);
  if (result) {
    // need to 'unget' all the characters in the IO buffer
    fseek(f, -(int)(s.img_buffer_end - s.img_buffer), SEEK_CUR);
  }
  return result;
}
#endif //!STBI_NO_STDIO

STBIDEF stbi_uc* stbi_load_from_memory(stbi_uc const* buffer, int len, int* x, int* y, int* comp, int req_comp)
{
  stbi__context s;
  stbi__start_mem(&s, buffer, len);
  return stbi__load_flip(&s, x, y, comp, req_comp);
}

STBIDEF stbi_uc* stbi_load_from_callbacks(stbi_io_callbacks const* clbk, void* user, int* x, int* y, int* comp, int req_comp)
{
  stbi__context s;
  stbi__start_callbacks(&s, (stbi_io_callbacks*)clbk, user);
  return stbi__load_flip(&s, x, y, comp, req_comp);
}

#ifndef STBI_NO_LINEAR
static float* stbi__loadf_main(stbi__context* s, int* x, int* y, int* comp, int req_comp)
{
  unsigned char* data;
#ifndef STBI_NO_HDR
  if (stbi__hdr_test(s)) {
    float* hdr_data = stbi__hdr_load(s, x, y, comp, req_comp);
    if (hdr_data)
      stbi__float_postprocess(hdr_data, x, y, comp, req_comp);
    return hdr_data;
  }
#endif
  data = stbi__load_flip(s, x, y, comp, req_comp);
  if (data)
    return stbi__ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp);
  return stbi__errpf("unknown image type", "Image not of any known type, or corrupt");
}

STBIDEF float* stbi_loadf_from_memory(stbi_uc const* buffer, int len, int* x, int* y, int* comp, int req_comp)
{
  stbi__context s;
  stbi__start_mem(&s, buffer, len);
  return stbi__loadf_main(&s, x, y, comp, req_comp);
}

STBIDEF float* stbi_loadf_from_callbacks(stbi_io_callbacks const* clbk, void* user, int* x, int* y, int* comp, int req_comp)
{
  stbi__context s;
  stbi__start_callbacks(&s, (stbi_io_callbacks*)clbk, user);
  return stbi__loadf_main(&s, x, y, comp, req_comp);
}

#ifndef STBI_NO_STDIO
STBIDEF float* stbi_loadf(char const* filename, int* x, int* y, int* comp, int req_comp)
{
  float* result;
  FILE* f = stbi__fopen(filename, "rb");
  if (!f) return stbi__errpf("can't fopen", "Unable to open file");
  result = stbi_loadf_from_file(f, x, y, comp, req_comp);
  fclose(f);
  return result;
}

STBIDEF float* stbi_loadf_from_file(FILE* f, int* x, int* y, int* comp, int req_comp)
{
  stbi__context s;
  stbi__start_file(&s, f);
  return stbi__loadf_main(&s, x, y, comp, req_comp);
}
#endif // !STBI_NO_STDIO

#endif // !STBI_NO_LINEAR

// these is-hdr-or-not is defined independent of whether STBI_NO_LINEAR is
// defined, for API simplicity; if STBI_NO_LINEAR is defined, it always
// reports false!

STBIDEF int stbi_is_hdr_from_memory(stbi_uc const* buffer, int len)
{
#ifndef STBI_NO_HDR
  stbi__context s;
  stbi__start_mem(&s, buffer, len);
  return stbi__hdr_test(&s);
#else
  STBI_NOTUSED(buffer);
  STBI_NOTUSED(len);
  return 0;
#endif
}

#ifndef STBI_NO_STDIO
STBIDEF int      stbi_is_hdr(char const* filename)
{
  FILE* f = stbi__fopen(filename, "rb");
  int result = 0;
  if (f) {
    result = stbi_is_hdr_from_file(f);
    fclose(f);
  }
  return result;
}

STBIDEF int      stbi_is_hdr_from_file(FILE* f)
{
#ifndef STBI_NO_HDR
  stbi__context s;
  stbi__start_file(&s, f);
  return stbi__hdr_test(&s);
#else
  return 0;
#endif
}
#endif // !STBI_NO_STDIO

STBIDEF int      stbi_is_hdr_from_callbacks(stbi_io_callbacks const* clbk, void* user)
{
#ifndef STBI_NO_HDR
  stbi__context s;
  stbi__start_callbacks(&s, (stbi_io_callbacks*)clbk, user);
  return stbi__hdr_test(&s);
#else
  return 0;
#endif
}

static float stbi__h2l_gamma_i = 1.0f / 2.2f, stbi__h2l_scale_i = 1.0f;
static float stbi__l2h_gamma = 2.2f, stbi__l2h_scale = 1.0f;

#ifndef STBI_NO_LINEAR
STBIDEF void   stbi_ldr_to_hdr_gamma(float gamma) { stbi__l2h_gamma = gamma; }
STBIDEF void   stbi_ldr_to_hdr_scale(float scale) { stbi__l2h_scale = scale; }
#endif

STBIDEF void   stbi_hdr_to_ldr_gamma(float gamma) { stbi__h2l_gamma_i = 1 / gamma; }
STBIDEF void   stbi_hdr_to_ldr_scale(float scale) { stbi__h2l_scale_i = 1 / scale; }


//////////////////////////////////////////////////////////////////////////////
//
// Common code used by all image loaders
//

enum
{
  STBI__SCAN_load = 0,
  STBI__SCAN_type,
  STBI__SCAN_header
};

static void stbi__refill_buffer(stbi__context* s)
{
  int n = (s->io.read)(s->io_user_data, (char*)s->buffer_start, s->buflen);
  if (n == 0) {
    // at end of file, treat same as if from memory, but need to handle case
    // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file
    s->read_from_callbacks = 0;
    s->img_buffer = s->buffer_start;
    s->img_buffer_end = s->buffer_start + 1;
    *s->img_buffer = 0;
  }
  else {
    s->img_buffer = s->buffer_start;
    s->img_buffer_end = s->buffer_start + n;
  }
}

stbi_inline static stbi_uc stbi__get8(stbi__context* s)
{
  if (s->img_buffer < s->img_buffer_end)
    return *s->img_buffer++;
  if (s->read_from_callbacks) {
    stbi__refill_buffer(s);
    return *s->img_buffer++;
  }
  return 0;
}

stbi_inline static int stbi__at_eof(stbi__context* s)
{
  if (s->io.read) {
    if (!(s->io.eof)(s->io_user_data)) return 0;
    // if feof() is true, check if buffer = end
    // special case: we've only got the special 0 character at the end
    if (s->read_from_callbacks == 0) return 1;
  }

  return s->img_buffer >= s->img_buffer_end;
}

static void stbi__skip(stbi__context* s, int n)
{
  if (n < 0) {
    s->img_buffer = s->img_buffer_end;
    return;
  }
  if (s->io.read) {
    int blen = (int)(s->img_buffer_end - s->img_buffer);
    if (blen < n) {
      s->img_buffer = s->img_buffer_end;
      (s->io.skip)(s->io_user_data, n - blen);
      return;
    }
  }
  s->img_buffer += n;
}

static int stbi__getn(stbi__context* s, stbi_uc* buffer, int n)
{
  if (s->io.read) {
    int blen = (int)(s->img_buffer_end - s->img_buffer);
    if (blen < n) {
      int res, count;

      memcpy(buffer, s->img_buffer, blen);

      count = (s->io.read)(s->io_user_data, (char*)buffer + blen, n - blen);
      res = (count == (n - blen));
      s->img_buffer = s->img_buffer_end;
      return res;
    }
  }

  if (s->img_buffer + n <= s->img_buffer_end) {
    memcpy(buffer, s->img_buffer, n);
    s->img_buffer += n;
    return 1;
  }
  else
    return 0;
}

static int stbi__get16be(stbi__context* s)
{
  int z = stbi__get8(s);
  return (z << 8) + stbi__get8(s);
}

static stbi__uint32 stbi__get32be(stbi__context* s)
{
  stbi__uint32 z = stbi__get16be(s);
  return (z << 16) + stbi__get16be(s);
}

static int stbi__get16le(stbi__context* s)
{
  int z = stbi__get8(s);
  return z + (stbi__get8(s) << 8);
}

static stbi__uint32 stbi__get32le(stbi__context* s)
{
  stbi__uint32 z = stbi__get16le(s);
  return z + (stbi__get16le(s) << 16);
}

#define STBI__BYTECAST(x)  ((stbi_uc) ((x) & 255))  // truncate int to byte without warnings


//////////////////////////////////////////////////////////////////////////////
//
//  generic converter from built-in img_n to req_comp
//    individual types do this automatically as much as possible (e.g. jpeg
//    does all cases internally since it needs to colorspace convert anyway,
//    and it never has alpha, so very few cases ). png can automatically
//    interleave an alpha=255 channel, but falls back to this for other cases
//
//  assume data buffer is malloced, so malloc a new one and free that one
//  only failure mode is malloc failing

static stbi_uc stbi__compute_y(int r, int g, int b)
{
  return (stbi_uc)(((r * 77) + (g * 150) + (29 * b)) >> 8);
}

static unsigned char* stbi__convert_format(unsigned char* data, int img_n, int req_comp, unsigned int x, unsigned int y)
{
  int i, j;
  unsigned char* good;

  if (req_comp == img_n) return data;
  STBI_ASSERT(req_comp >= 1 && req_comp <= 4);

  good = (unsigned char*)stbi__malloc(req_comp * x * y);
  if (good == nullptr) {
    STBI_FREE(data);
    return stbi__errpuc("outofmem", "Out of memory");
  }

  for (j = 0; j < (int)y; ++j) {
    unsigned char* src = data + j * x * img_n;
    unsigned char* dest = good + j * x * req_comp;

#define COMBO(a,b)  ((a)*8+(b))
#define CASE(a,b)   case COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b)
    // convert source image with img_n components to one with req_comp components;
    // avoid switch per pixel, so use switch per scanline and massive macros
    switch (COMBO(img_n, req_comp)) {
      CASE(1, 2) dest[0] = src[0], dest[1] = 255;
      break;
      CASE(1, 3) dest[0] = dest[1] = dest[2] = src[0];
      break;
      CASE(1, 4) dest[0] = dest[1] = dest[2] = src[0], dest[3] = 255;
      break;
      CASE(2, 1) dest[0] = src[0];
      break;
      CASE(2, 3) dest[0] = dest[1] = dest[2] = src[0];
      break;
      CASE(2, 4) dest[0] = dest[1] = dest[2] = src[0], dest[3] = src[1];
      break;
      CASE(3, 4) dest[0] = src[0], dest[1] = src[1], dest[2] = src[2], dest[3] = 255;
      break;
      CASE(3, 1) dest[0] = stbi__compute_y(src[0], src[1], src[2]);
      break;
      CASE(3, 2) dest[0] = stbi__compute_y(src[0], src[1], src[2]), dest[1] = 255;
      break;
      CASE(4, 1) dest[0] = stbi__compute_y(src[0], src[1], src[2]);
      break;
      CASE(4, 2) dest[0] = stbi__compute_y(src[0], src[1], src[2]), dest[1] = src[3];
      break;
      CASE(4, 3) dest[0] = src[0], dest[1] = src[1], dest[2] = src[2];
      break;
    default: STBI_ASSERT(0);
    }
#undef CASE
  }

  STBI_FREE(data);
  return good;
}

#ifndef STBI_NO_LINEAR
static float* stbi__ldr_to_hdr(stbi_uc* data, int x, int y, int comp)
{
  int i, k, n;
  float* output = (float*)stbi__malloc(x * y * comp * sizeof(float));
  if (output == nullptr) { STBI_FREE(data); return stbi__errpf("outofmem", "Out of memory"); }
  // compute number of non-alpha components
  if (comp & 1) n = comp; else n = comp - 1;
  for (i = 0; i < x * y; ++i) {
    for (k = 0; k < n; ++k) {
      output[i * comp + k] = (float)(pow(data[i * comp + k] / 255.0f, stbi__l2h_gamma) * stbi__l2h_scale);
    }
    if (k < comp) output[i * comp + k] = data[i * comp + k] / 255.0f;
  }
  STBI_FREE(data);
  return output;
}
#endif

#ifndef STBI_NO_HDR
#define stbi__float2int(x)   ((int) (x))
static stbi_uc* stbi__hdr_to_ldr(float* data, int x, int y, int comp)
{
  int i, k, n;
  stbi_uc* output = (stbi_uc*)stbi__malloc(x * y * comp);
  if (output == nullptr) { STBI_FREE(data); return stbi__errpuc("outofmem", "Out of memory"); }
  // compute number of non-alpha components
  if (comp & 1) n = comp; else n = comp - 1;
  for (i = 0; i < x * y; ++i) {
    for (k = 0; k < n; ++k) {
      float z = (float)pow(data[i * comp + k] * stbi__h2l_scale_i, stbi__h2l_gamma_i) * 255 + 0.5f;
      if (z < 0) z = 0;
      if (z > 255) z = 255;
      output[i * comp + k] = (stbi_uc)stbi__float2int(z);
    }
    if (k < comp) {
      float z = data[i * comp + k] * 255 + 0.5f;
      if (z < 0) z = 0;
      if (z > 255) z = 255;
      output[i * comp + k] = (stbi_uc)stbi__float2int(z);
    }
  }
  STBI_FREE(data);
  return output;
}
#endif

//////////////////////////////////////////////////////////////////////////////
//
//  "baseline" JPEG/JFIF decoder
//
//    simple implementation
//      - doesn't support delayed output of y-dimension
//      - simple interface (only one output format: 8-bit interleaved RGB)
//      - doesn't try to recover corrupt jpegs
//      - doesn't allow partial loading, loading multiple at once
//      - still fast on x86 (copying globals into locals doesn't help x86)
//      - allocates lots of intermediate memory (full size of all components)
//        - non-interleaved case requires this anyway
//        - allows good upsampling (see next)
//    high-quality
//      - upsampled channels are bilinearly interpolated, even across blocks
//      - quality integer IDCT derived from IJG's 'slow'
//    performance
//      - fast huffman; reasonable integer IDCT
//      - some SIMD kernels for common paths on targets with SSE2/NEON
//      - uses a lot of intermediate memory, could cache poorly

#ifndef STBI_NO_JPEG

// huffman decoding acceleration
#define FAST_BITS   9  // larger handles more cases; smaller stomps less cache

typedef struct
{
  stbi_uc  fast[1 << FAST_BITS];
  // weirdly, repacking this into AoS is a 10% speed loss, instead of a win
  stbi__uint16 code[256];
  stbi_uc  values[256];
  stbi_uc  size[257];
  unsigned int maxcode[18];
  int    delta[17];   // old 'firstsymbol' - old 'firstcode'
} stbi__huffman;

typedef struct
{
  stbi__context* s;
  stbi__huffman huff_dc[4];
  stbi__huffman huff_ac[4];
  stbi_uc dequant[4][64];
  stbi__int16 fast_ac[4][1 << FAST_BITS];

  // sizes for components, interleaved MCUs
  int img_h_max, img_v_max;
  int img_mcu_x, img_mcu_y;
  int img_mcu_w, img_mcu_h;

  // definition of jpeg image component
  struct
  {
    int id;
    int h, v;
    int tq;
    int hd, ha;
    int dc_pred;

    int x, y, w2, h2;
    stbi_uc* data;
    void* raw_data, * raw_coeff;
    stbi_uc* linebuf;
    short* coeff;   // progressive only
    int      coeff_w, coeff_h; // number of 8x8 coefficient blocks
  } img_comp[4];

  stbi__uint32   code_buffer; // jpeg entropy-coded buffer
  int            code_bits;   // number of valid bits
  unsigned char  marker;      // marker seen while filling entropy buffer
  int            nomore;      // flag if we saw a marker so must stop

  int            progressive;
  int            spec_start;
  int            spec_end;
  int            succ_high;
  int            succ_low;
  int            eob_run;

  int scan_n, order[4];
  int restart_interval, todo;

  // kernels
  void (*idct_block_kernel)(stbi_uc* out, int out_stride, short data[64]);
  void (*YCbCr_to_RGB_kernel)(stbi_uc* out, const stbi_uc* y, const stbi_uc* pcb, const stbi_uc* pcr, int count, int step);
  stbi_uc* (*resample_row_hv_2_kernel)(stbi_uc* out, stbi_uc* in_near, stbi_uc* in_far, int w, int hs);
} stbi__jpeg;

static int stbi__build_huffman(stbi__huffman* h, int* count)
{
  int i, j, k = 0, code;
  // build size list for each symbol (from JPEG spec)
  for (i = 0; i < 16; ++i)
    for (j = 0; j < count[i]; ++j)
      h->size[k++] = (stbi_uc)(i + 1);
  h->size[k] = 0;

  // compute actual symbols (from jpeg spec)
  code = 0;
  k = 0;
  for (j = 1; j <= 16; ++j) {
    // compute delta to add to code to compute symbol id
    h->delta[j] = k - code;
    if (h->size[k] == j) {
      while (h->size[k] == j)
        h->code[k++] = (stbi__uint16)(code++);
      if (code - 1 >= (1 << j)) return stbi__err("bad code lengths", "Corrupt JPEG");
    }
    // compute largest code + 1 for this size, preshifted as needed later
    h->maxcode[j] = code << (16 - j);
    code <<= 1;
  }
  h->maxcode[j] = 0xffffffff;

  // build non-spec acceleration table; 255 is flag for not-accelerated
  memset(h->fast, 255, 1 << FAST_BITS);
  for (i = 0; i < k; ++i) {
    int s = h->size[i];
    if (s <= FAST_BITS) {
      int c = h->code[i] << (FAST_BITS - s);
      int m = 1 << (FAST_BITS - s);
      for (j = 0; j < m; ++j) {
        h->fast[c + j] = (stbi_uc)i;
      }
    }
  }
  return 1;
}

// build a table that decodes both magnitude and value of small ACs in
// one go.
static void stbi__build_fast_ac(stbi__int16* fast_ac, stbi__huffman* h)
{
  int i;
  for (i = 0; i < (1 << FAST_BITS); ++i) {
    stbi_uc fast = h->fast[i];
    fast_ac[i] = 0;
    if (fast < 255) {
      int rs = h->values[fast];
      int run = (rs >> 4) & 15;
      int magbits = rs & 15;
      int len = h->size[fast];

      if (magbits && len + magbits <= FAST_BITS) {
        // magnitude code followed by receive_extend code
        int k = ((i << len) & ((1 << FAST_BITS) - 1)) >> (FAST_BITS - magbits);
        int m = 1 << (magbits - 1);
        if (k < m) k += (-1 << magbits) + 1;
        // if the result is small enough, we can fit it in fast_ac table
        if (k >= -128 && k <= 127)
          fast_ac[i] = (stbi__int16)((k << 8) + (run << 4) + (len + magbits));
      }
    }
  }
}

static void stbi__grow_buffer_unsafe(stbi__jpeg* j)
{
  do {
    int b = j->nomore ? 0 : stbi__get8(j->s);
    if (b == 0xff) {
      int c = stbi__get8(j->s);
      if (c != 0) {
        j->marker = (unsigned char)c;
        j->nomore = 1;
        return;
      }
    }
    j->code_buffer |= b << (24 - j->code_bits);
    j->code_bits += 8;
  } while (j->code_bits <= 24);
}

// (1 << n) - 1
static stbi__uint32 stbi__bmask[17] = { 0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535 };

// decode a jpeg huffman value from the bitstream
stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg* j, stbi__huffman* h)
{
  unsigned int temp;
  int c, k;

  if (j->code_bits < 16) stbi__grow_buffer_unsafe(j);

  // look at the top FAST_BITS and determine what symbol ID it is,
  // if the code is <= FAST_BITS
  c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS) - 1);
  k = h->fast[c];
  if (k < 255) {
    int s = h->size[k];
    if (s > j->code_bits)
      return -1;
    j->code_buffer <<= s;
    j->code_bits -= s;
    return h->values[k];
  }

  // naive test is to shift the code_buffer down so k bits are
  // valid, then test against maxcode. To speed this up, we've
  // preshifted maxcode left so that it has (16-k) 0s at the
  // end; in other words, regardless of the number of bits, it
  // wants to be compared against something shifted to have 16;
  // that way we don't need to shift inside the loop.
  temp = j->code_buffer >> 16;
  for (k = FAST_BITS + 1; ; ++k)
    if (temp < h->maxcode[k])
      break;
  if (k == 17) {
    // error! code not found
    j->code_bits -= 16;
    return -1;
  }

  if (k > j->code_bits)
    return -1;

  // convert the huffman code to the symbol id
  c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k];
  STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]);

  // convert the id to a symbol
  j->code_bits -= k;
  j->code_buffer <<= k;
  return h->values[c];
}

// bias[n] = (-1<<n) + 1
static int const stbi__jbias[16] = { 0,-1,-3,-7,-15,-31,-63,-127,-255,-511,-1023,-2047,-4095,-8191,-16383,-32767 };

// combined JPEG 'receive' and JPEG 'extend', since baseline
// always extends everything it receives.
stbi_inline static int stbi__extend_receive(stbi__jpeg* j, int n)
{
  unsigned int k;
  int sgn;
  if (j->code_bits < n) stbi__grow_buffer_unsafe(j);

  sgn = (stbi__int32)j->code_buffer >> 31; // sign bit is always in MSB
  k = stbi_lrot(j->code_buffer, n);
  STBI_ASSERT(n >= 0 && n < (int)(sizeof(stbi__bmask) / sizeof(*stbi__bmask)));
  j->code_buffer = k & ~stbi__bmask[n];
  k &= stbi__bmask[n];
  j->code_bits -= n;
  return k + (stbi__jbias[n] & ~sgn);
}

// get some unsigned bits
stbi_inline static int stbi__jpeg_get_bits(stbi__jpeg* j, int n)
{
  unsigned int k;
  if (j->code_bits < n) stbi__grow_buffer_unsafe(j);
  k = stbi_lrot(j->code_buffer, n);
  j->code_buffer = k & ~stbi__bmask[n];
  k &= stbi__bmask[n];
  j->code_bits -= n;
  return k;
}

stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg* j)
{
  unsigned int k;
  if (j->code_bits < 1) stbi__grow_buffer_unsafe(j);
  k = j->code_buffer;
  j->code_buffer <<= 1;
  --j->code_bits;
  return k & 0x80000000;
}

// given a value that's at position X in the zigzag stream,
// where does it appear in the 8x8 matrix coded as row-major?
static stbi_uc stbi__jpeg_dezigzag[64 + 15] =
{
    0,  1,  8, 16,  9,  2,  3, 10,
   17, 24, 32, 25, 18, 11,  4,  5,
   12, 19, 26, 33, 40, 48, 41, 34,
   27, 20, 13,  6,  7, 14, 21, 28,
   35, 42, 49, 56, 57, 50, 43, 36,
   29, 22, 15, 23, 30, 37, 44, 51,
   58, 59, 52, 45, 38, 31, 39, 46,
   53, 60, 61, 54, 47, 55, 62, 63,
   // let corrupt input sample past end
   63, 63, 63, 63, 63, 63, 63, 63,
   63, 63, 63, 63, 63, 63, 63
};

// decode one 64-entry block--
static int stbi__jpeg_decode_block(stbi__jpeg* j, short data[64], stbi__huffman* hdc, stbi__huffman* hac, stbi__int16* fac, int b, stbi_uc* dequant)
{
  int diff, dc, k;
  int t;

  if (j->code_bits < 16) stbi__grow_buffer_unsafe(j);
  t = stbi__jpeg_huff_decode(j, hdc);
  if (t < 0) return stbi__err("bad huffman code", "Corrupt JPEG");

  // 0 all the ac values now so we can do it 32-bits at a time
  memset(data, 0, 64 * sizeof(data[0]));

  diff = t ? stbi__extend_receive(j, t) : 0;
  dc = j->img_comp[b].dc_pred + diff;
  j->img_comp[b].dc_pred = dc;
  data[0] = (short)(dc * dequant[0]);

  // decode AC components, see JPEG spec
  k = 1;
  do {
    unsigned int zig;
    int c, r, s;
    if (j->code_bits < 16) stbi__grow_buffer_unsafe(j);
    c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS) - 1);
    r = fac[c];
    if (r) { // fast-AC path
      k += (r >> 4) & 15; // run
      s = r & 15; // combined length
      j->code_buffer <<= s;
      j->code_bits -= s;
      // decode into unzigzag'd location
      zig = stbi__jpeg_dezigzag[k++];
      data[zig] = (short)((r >> 8) * dequant[zig]);
    }
    else {
      int rs = stbi__jpeg_huff_decode(j, hac);
      if (rs < 0) return stbi__err("bad huffman code", "Corrupt JPEG");
      s = rs & 15;
      r = rs >> 4;
      if (s == 0) {
        if (rs != 0xf0) break; // end block
        k += 16;
      }
      else {
        k += r;
        // decode into unzigzag'd location
        zig = stbi__jpeg_dezigzag[k++];
        data[zig] = (short)(stbi__extend_receive(j, s) * dequant[zig]);
      }
    }
  } while (k < 64);
  return 1;
}

static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg* j, short data[64], stbi__huffman* hdc, int b)
{
  int diff, dc;
  int t;
  if (j->spec_end != 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG");

  if (j->code_bits < 16) stbi__grow_buffer_unsafe(j);

  if (j->succ_high == 0) {
    // first scan for DC coefficient, must be first
    memset(data, 0, 64 * sizeof(data[0])); // 0 all the ac values now
    t = stbi__jpeg_huff_decode(j, hdc);
    diff = t ? stbi__extend_receive(j, t) : 0;

    dc = j->img_comp[b].dc_pred + diff;
    j->img_comp[b].dc_pred = dc;
    data[0] = (short)(dc << j->succ_low);
  }
  else {
    // refinement scan for DC coefficient
    if (stbi__jpeg_get_bit(j))
      data[0] += (short)(1 << j->succ_low);
  }
  return 1;
}

// @OPTIMIZE: store non-zigzagged during the decode passes,
// and only de-zigzag when dequantizing
static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg* j, short data[64], stbi__huffman* hac, stbi__int16* fac)
{
  int k;
  if (j->spec_start == 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG");

  if (j->succ_high == 0) {
    int shift = j->succ_low;

    if (j->eob_run) {
      --j->eob_run;
      return 1;
    }

    k = j->spec_start;
    do {
      unsigned int zig;
      int c, r, s;
      if (j->code_bits < 16) stbi__grow_buffer_unsafe(j);
      c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS) - 1);
      r = fac[c];
      if (r) { // fast-AC path
        k += (r >> 4) & 15; // run
        s = r & 15; // combined length
        j->code_buffer <<= s;
        j->code_bits -= s;
        zig = stbi__jpeg_dezigzag[k++];
        data[zig] = (short)((r >> 8) << shift);
      }
      else {
        int rs = stbi__jpeg_huff_decode(j, hac);
        if (rs < 0) return stbi__err("bad huffman code", "Corrupt JPEG");
        s = rs & 15;
        r = rs >> 4;
        if (s == 0) {
          if (r < 15) {
            j->eob_run = (1 << r);
            if (r)
              j->eob_run += stbi__jpeg_get_bits(j, r);
            --j->eob_run;
            break;
          }
          k += 16;
        }
        else {
          k += r;
          zig = stbi__jpeg_dezigzag[k++];
          data[zig] = (short)(stbi__extend_receive(j, s) << shift);
        }
      }
    } while (k <= j->spec_end);
  }
  else {
    // refinement scan for these AC coefficients

    short bit = (short)(1 << j->succ_low);

    if (j->eob_run) {
      --j->eob_run;
      for (k = j->spec_start; k <= j->spec_end; ++k) {
        short* p = &data[stbi__jpeg_dezigzag[k]];
        if (*p != 0)
          if (stbi__jpeg_get_bit(j))
            if ((*p & bit) == 0) {
              if (*p > 0)
                *p += bit;
              else
                *p -= bit;
            }
      }
    }
    else {
      k = j->spec_start;
      do {
        int r, s;
        int rs = stbi__jpeg_huff_decode(j, hac); // @OPTIMIZE see if we can use the fast path here, advance-by-r is so slow, eh
        if (rs < 0) return stbi__err("bad huffman code", "Corrupt JPEG");
        s = rs & 15;
        r = rs >> 4;
        if (s == 0) {
          if (r < 15) {
            j->eob_run = (1 << r) - 1;
            if (r)
              j->eob_run += stbi__jpeg_get_bits(j, r);
            r = 64; // force end of block
          }
          else {
            // r=15 s=0 should write 16 0s, so we just do
            // a run of 15 0s and then write s (which is 0),
            // so we don't have to do anything special here
          }
        }
        else {
          if (s != 1) return stbi__err("bad huffman code", "Corrupt JPEG");
          // sign bit
          if (stbi__jpeg_get_bit(j))
            s = bit;
          else
            s = -bit;
        }

        // advance by r
        while (k <= j->spec_end) {
          short* p = &data[stbi__jpeg_dezigzag[k++]];
          if (*p != 0) {
            if (stbi__jpeg_get_bit(j))
              if ((*p & bit) == 0) {
                if (*p > 0)
                  *p += bit;
                else
                  *p -= bit;
              }
          }
          else {
            if (r == 0) {
              *p = (short)s;
              break;
            }
            --r;
          }
        }
      } while (k <= j->spec_end);
    }
  }
  return 1;
}

// take a -128..127 value and stbi__clamp it and convert to 0..255
stbi_inline static stbi_uc stbi__clamp(int x)
{
  // trick to use a single test to catch both cases
  if ((unsigned int)x > 255) {
    if (x < 0) return 0;
    if (x > 255) return 255;
  }
  return (stbi_uc)x;
}

#define stbi__f2f(x)  ((int) (((x) * 4096 + 0.5)))
#define stbi__fsh(x)  ((x) << 12)

// derived from jidctint -- DCT_ISLOW
#define STBI__IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \
   int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \
   p2 = s2;                                    \
   p3 = s6;                                    \
   p1 = (p2+p3) * stbi__f2f(0.5411961f);       \
   t2 = p1 + p3*stbi__f2f(-1.847759065f);      \
   t3 = p1 + p2*stbi__f2f( 0.765366865f);      \
   p2 = s0;                                    \
   p3 = s4;                                    \
   t0 = stbi__fsh(p2+p3);                      \
   t1 = stbi__fsh(p2-p3);                      \
   x0 = t0+t3;                                 \
   x3 = t0-t3;                                 \
   x1 = t1+t2;                                 \
   x2 = t1-t2;                                 \
   t0 = s7;                                    \
   t1 = s5;                                    \
   t2 = s3;                                    \
   t3 = s1;                                    \
   p3 = t0+t2;                                 \
   p4 = t1+t3;                                 \
   p1 = t0+t3;                                 \
   p2 = t1+t2;                                 \
   p5 = (p3+p4)*stbi__f2f( 1.175875602f);      \
   t0 = t0*stbi__f2f( 0.298631336f);           \
   t1 = t1*stbi__f2f( 2.053119869f);           \
   t2 = t2*stbi__f2f( 3.072711026f);           \
   t3 = t3*stbi__f2f( 1.501321110f);           \
   p1 = p5 + p1*stbi__f2f(-0.899976223f);      \
   p2 = p5 + p2*stbi__f2f(-2.562915447f);      \
   p3 = p3*stbi__f2f(-1.961570560f);           \
   p4 = p4*stbi__f2f(-0.390180644f);           \
   t3 += p1+p4;                                \
   t2 += p2+p3;                                \
   t1 += p2+p4;                                \
   t0 += p1+p3;

static void stbi__idct_block(stbi_uc* out, int out_stride, short data[64])
{
  int i, val[64], * v = val;
  stbi_uc* o;
  short* d = data;

  // columns
  for (i = 0; i < 8; ++i, ++d, ++v) {
    // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing
    if (d[8] == 0 && d[16] == 0 && d[24] == 0 && d[32] == 0
      && d[40] == 0 && d[48] == 0 && d[56] == 0) {
      //    no shortcut                 0     seconds
      //    (1|2|3|4|5|6|7)==0          0     seconds
      //    all separate               -0.047 seconds
      //    1 && 2|3 && 4|5 && 6|7:    -0.047 seconds
      int dcterm = d[0] << 2;
      v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm;
    }
    else {
      STBI__IDCT_1D(d[0], d[8], d[16], d[24], d[32], d[40], d[48], d[56])
        // constants scaled things up by 1<<12; let's bring them back
        // down, but keep 2 extra bits of precision
        x0 += 512; x1 += 512; x2 += 512; x3 += 512;
      v[0] = (x0 + t3) >> 10;
      v[56] = (x0 - t3) >> 10;
      v[8] = (x1 + t2) >> 10;
      v[48] = (x1 - t2) >> 10;
      v[16] = (x2 + t1) >> 10;
      v[40] = (x2 - t1) >> 10;
      v[24] = (x3 + t0) >> 10;
      v[32] = (x3 - t0) >> 10;
    }
  }

  for (i = 0, v = val, o = out; i < 8; ++i, v += 8, o += out_stride) {
    // no fast case since the first 1D IDCT spread components out
    STBI__IDCT_1D(v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7])
      // constants scaled things up by 1<<12, plus we had 1<<2 from first
      // loop, plus horizontal and vertical each scale by sqrt(8) so together
      // we've got an extra 1<<3, so 1<<17 total we need to remove.
      // so we want to round that, which means adding 0.5 * 1<<17,
      // aka 65536. Also, we'll end up with -128 to 127 that we want
      // to encode as 0..255 by adding 128, so we'll add that before the shift
      x0 += 65536 + (128 << 17);
    x1 += 65536 + (128 << 17);
    x2 += 65536 + (128 << 17);
    x3 += 65536 + (128 << 17);
    // tried computing the shifts into temps, or'ing the temps to see
    // if any were out of range, but that was slower
    o[0] = stbi__clamp((x0 + t3) >> 17);
    o[7] = stbi__clamp((x0 - t3) >> 17);
    o[1] = stbi__clamp((x1 + t2) >> 17);
    o[6] = stbi__clamp((x1 - t2) >> 17);
    o[2] = stbi__clamp((x2 + t1) >> 17);
    o[5] = stbi__clamp((x2 - t1) >> 17);
    o[3] = stbi__clamp((x3 + t0) >> 17);
    o[4] = stbi__clamp((x3 - t0) >> 17);
  }
}

#ifdef STBI_SSE2
// sse2 integer IDCT. not the fastest possible implementation but it
// produces bit-identical results to the generic C version so it's
// fully "transparent".
static void stbi__idct_simd(stbi_uc* out, int out_stride, short data[64])
{
  // This is constructed to match our regular (generic) integer IDCT exactly.
  __m128i row0, row1, row2, row3, row4, row5, row6, row7;
  __m128i tmp;

  // dot product constant: even elems=x, odd elems=y
#define dct_const(x,y)  _mm_setr_epi16((x),(y),(x),(y),(x),(y),(x),(y))

// out(0) = c0[even]*x + c0[odd]*y   (c0, x, y 16-bit, out 32-bit)
// out(1) = c1[even]*x + c1[odd]*y
#define dct_rot(out0,out1, x,y,c0,c1) \
      __m128i c0##lo = _mm_unpacklo_epi16((x),(y)); \
      __m128i c0##hi = _mm_unpackhi_epi16((x),(y)); \
      __m128i out0##_l = _mm_madd_epi16(c0##lo, c0); \
      __m128i out0##_h = _mm_madd_epi16(c0##hi, c0); \
      __m128i out1##_l = _mm_madd_epi16(c0##lo, c1); \
      __m128i out1##_h = _mm_madd_epi16(c0##hi, c1)

   // out = in << 12  (in 16-bit, out 32-bit)
#define dct_widen(out, in) \
      __m128i out##_l = _mm_srai_epi32(_mm_unpacklo_epi16(_mm_setzero_si128(), (in)), 4); \
      __m128i out##_h = _mm_srai_epi32(_mm_unpackhi_epi16(_mm_setzero_si128(), (in)), 4)

   // wide add
#define dct_wadd(out, a, b) \
      __m128i out##_l = _mm_add_epi32(a##_l, b##_l); \
      __m128i out##_h = _mm_add_epi32(a##_h, b##_h)

   // wide sub
#define dct_wsub(out, a, b) \
      __m128i out##_l = _mm_sub_epi32(a##_l, b##_l); \
      __m128i out##_h = _mm_sub_epi32(a##_h, b##_h)

   // butterfly a/b, add bias, then shift by "s" and pack
#define dct_bfly32o(out0, out1, a,b,bias,s) \
      { \
         __m128i abiased_l = _mm_add_epi32(a##_l, bias); \
         __m128i abiased_h = _mm_add_epi32(a##_h, bias); \
         dct_wadd(sum, abiased, b); \
         dct_wsub(dif, abiased, b); \
         out0 = _mm_packs_epi32(_mm_srai_epi32(sum_l, s), _mm_srai_epi32(sum_h, s)); \
         out1 = _mm_packs_epi32(_mm_srai_epi32(dif_l, s), _mm_srai_epi32(dif_h, s)); \
      }

   // 8-bit interleave step (for transposes)
#define dct_interleave8(a, b) \
      tmp = a; \
      a = _mm_unpacklo_epi8(a, b); \
      b = _mm_unpackhi_epi8(tmp, b)

   // 16-bit interleave step (for transposes)
#define dct_interleave16(a, b) \
      tmp = a; \
      a = _mm_unpacklo_epi16(a, b); \
      b = _mm_unpackhi_epi16(tmp, b)

#define dct_pass(bias,shift) \
      { \
         /* even part */ \
         dct_rot(t2e,t3e, row2,row6, rot0_0,rot0_1); \
         __m128i sum04 = _mm_add_epi16(row0, row4); \
         __m128i dif04 = _mm_sub_epi16(row0, row4); \
         dct_widen(t0e, sum04); \
         dct_widen(t1e, dif04); \
         dct_wadd(x0, t0e, t3e); \
         dct_wsub(x3, t0e, t3e); \
         dct_wadd(x1, t1e, t2e); \
         dct_wsub(x2, t1e, t2e); \
         /* odd part */ \
         dct_rot(y0o,y2o, row7,row3, rot2_0,rot2_1); \
         dct_rot(y1o,y3o, row5,row1, rot3_0,rot3_1); \
         __m128i sum17 = _mm_add_epi16(row1, row7); \
         __m128i sum35 = _mm_add_epi16(row3, row5); \
         dct_rot(y4o,y5o, sum17,sum35, rot1_0,rot1_1); \
         dct_wadd(x4, y0o, y4o); \
         dct_wadd(x5, y1o, y5o); \
         dct_wadd(x6, y2o, y5o); \
         dct_wadd(x7, y3o, y4o); \
         dct_bfly32o(row0,row7, x0,x7,bias,shift); \
         dct_bfly32o(row1,row6, x1,x6,bias,shift); \
         dct_bfly32o(row2,row5, x2,x5,bias,shift); \
         dct_bfly32o(row3,row4, x3,x4,bias,shift); \
      }

  __m128i rot0_0 = dct_const(stbi__f2f(0.5411961f), stbi__f2f(0.5411961f) + stbi__f2f(-1.847759065f));
  __m128i rot0_1 = dct_const(stbi__f2f(0.5411961f) + stbi__f2f(0.765366865f), stbi__f2f(0.5411961f));
  __m128i rot1_0 = dct_const(stbi__f2f(1.175875602f) + stbi__f2f(-0.899976223f), stbi__f2f(1.175875602f));
  __m128i rot1_1 = dct_const(stbi__f2f(1.175875602f), stbi__f2f(1.175875602f) + stbi__f2f(-2.562915447f));
  __m128i rot2_0 = dct_const(stbi__f2f(-1.961570560f) + stbi__f2f(0.298631336f), stbi__f2f(-1.961570560f));
  __m128i rot2_1 = dct_const(stbi__f2f(-1.961570560f), stbi__f2f(-1.961570560f) + stbi__f2f(3.072711026f));
  __m128i rot3_0 = dct_const(stbi__f2f(-0.390180644f) + stbi__f2f(2.053119869f), stbi__f2f(-0.390180644f));
  __m128i rot3_1 = dct_const(stbi__f2f(-0.390180644f), stbi__f2f(-0.390180644f) + stbi__f2f(1.501321110f));

  // rounding biases in column/row passes, see stbi__idct_block for explanation.
  __m128i bias_0 = _mm_set1_epi32(512);
  __m128i bias_1 = _mm_set1_epi32(65536 + (128 << 17));

  // load
  row0 = _mm_load_si128((const __m128i*) (data + 0 * 8));
  row1 = _mm_load_si128((const __m128i*) (data + 1 * 8));
  row2 = _mm_load_si128((const __m128i*) (data + 2 * 8));
  row3 = _mm_load_si128((const __m128i*) (data + 3 * 8));
  row4 = _mm_load_si128((const __m128i*) (data + 4 * 8));
  row5 = _mm_load_si128((const __m128i*) (data + 5 * 8));
  row6 = _mm_load_si128((const __m128i*) (data + 6 * 8));
  row7 = _mm_load_si128((const __m128i*) (data + 7 * 8));

  // column pass
  dct_pass(bias_0, 10);

  {
    // 16bit 8x8 transpose pass 1
    dct_interleave16(row0, row4);
    dct_interleave16(row1, row5);
    dct_interleave16(row2, row6);
    dct_interleave16(row3, row7);

    // transpose pass 2
    dct_interleave16(row0, row2);
    dct_interleave16(row1, row3);
    dct_interleave16(row4, row6);
    dct_interleave16(row5, row7);

    // transpose pass 3
    dct_interleave16(row0, row1);
    dct_interleave16(row2, row3);
    dct_interleave16(row4, row5);
    dct_interleave16(row6, row7);
  }

  // row pass
  dct_pass(bias_1, 17);

  {
    // pack
    __m128i p0 = _mm_packus_epi16(row0, row1); // a0a1a2a3...a7b0b1b2b3...b7
    __m128i p1 = _mm_packus_epi16(row2, row3);
    __m128i p2 = _mm_packus_epi16(row4, row5);
    __m128i p3 = _mm_packus_epi16(row6, row7);

    // 8bit 8x8 transpose pass 1
    dct_interleave8(p0, p2); // a0e0a1e1...
    dct_interleave8(p1, p3); // c0g0c1g1...

    // transpose pass 2
    dct_interleave8(p0, p1); // a0c0e0g0...
    dct_interleave8(p2, p3); // b0d0f0h0...

    // transpose pass 3
    dct_interleave8(p0, p2); // a0b0c0d0...
    dct_interleave8(p1, p3); // a4b4c4d4...

    // store
    _mm_storel_epi64((__m128i*) out, p0); out += out_stride;
    _mm_storel_epi64((__m128i*) out, _mm_shuffle_epi32(p0, 0x4e)); out += out_stride;
    _mm_storel_epi64((__m128i*) out, p2); out += out_stride;
    _mm_storel_epi64((__m128i*) out, _mm_shuffle_epi32(p2, 0x4e)); out += out_stride;
    _mm_storel_epi64((__m128i*) out, p1); out += out_stride;
    _mm_storel_epi64((__m128i*) out, _mm_shuffle_epi32(p1, 0x4e)); out += out_stride;
    _mm_storel_epi64((__m128i*) out, p3); out += out_stride;
    _mm_storel_epi64((__m128i*) out, _mm_shuffle_epi32(p3, 0x4e));
  }

#undef dct_const
#undef dct_rot
#undef dct_widen
#undef dct_wadd
#undef dct_wsub
#undef dct_bfly32o
#undef dct_interleave8
#undef dct_interleave16
#undef dct_pass
}

#endif // STBI_SSE2

#ifdef STBI_NEON

// NEON integer IDCT. should produce bit-identical
// results to the generic C version.
static void stbi__idct_simd(stbi_uc* out, int out_stride, short data[64])
{
  int16x8_t row0, row1, row2, row3, row4, row5, row6, row7;

  int16x4_t rot0_0 = vdup_n_s16(stbi__f2f(0.5411961f));
  int16x4_t rot0_1 = vdup_n_s16(stbi__f2f(-1.847759065f));
  int16x4_t rot0_2 = vdup_n_s16(stbi__f2f(0.765366865f));
  int16x4_t rot1_0 = vdup_n_s16(stbi__f2f(1.175875602f));
  int16x4_t rot1_1 = vdup_n_s16(stbi__f2f(-0.899976223f));
  int16x4_t rot1_2 = vdup_n_s16(stbi__f2f(-2.562915447f));
  int16x4_t rot2_0 = vdup_n_s16(stbi__f2f(-1.961570560f));
  int16x4_t rot2_1 = vdup_n_s16(stbi__f2f(-0.390180644f));
  int16x4_t rot3_0 = vdup_n_s16(stbi__f2f(0.298631336f));
  int16x4_t rot3_1 = vdup_n_s16(stbi__f2f(2.053119869f));
  int16x4_t rot3_2 = vdup_n_s16(stbi__f2f(3.072711026f));
  int16x4_t rot3_3 = vdup_n_s16(stbi__f2f(1.501321110f));

#define dct_long_mul(out, inq, coeff) \
   int32x4_t out##_l = vmull_s16(vget_low_s16(inq), coeff); \
   int32x4_t out##_h = vmull_s16(vget_high_s16(inq), coeff)

#define dct_long_mac(out, acc, inq, coeff) \
   int32x4_t out##_l = vmlal_s16(acc##_l, vget_low_s16(inq), coeff); \
   int32x4_t out##_h = vmlal_s16(acc##_h, vget_high_s16(inq), coeff)

#define dct_widen(out, inq) \
   int32x4_t out##_l = vshll_n_s16(vget_low_s16(inq), 12); \
   int32x4_t out##_h = vshll_n_s16(vget_high_s16(inq), 12)

  // wide add
#define dct_wadd(out, a, b) \
   int32x4_t out##_l = vaddq_s32(a##_l, b##_l); \
   int32x4_t out##_h = vaddq_s32(a##_h, b##_h)

// wide sub
#define dct_wsub(out, a, b) \
   int32x4_t out##_l = vsubq_s32(a##_l, b##_l); \
   int32x4_t out##_h = vsubq_s32(a##_h, b##_h)

// butterfly a/b, then shift using "shiftop" by "s" and pack
#define dct_bfly32o(out0,out1, a,b,shiftop,s) \
   { \
      dct_wadd(sum, a, b); \
      dct_wsub(dif, a, b); \
      out0 = vcombine_s16(shiftop(sum_l, s), shiftop(sum_h, s)); \
      out1 = vcombine_s16(shiftop(dif_l, s), shiftop(dif_h, s)); \
   }

#define dct_pass(shiftop, shift) \
   { \
      /* even part */ \
      int16x8_t sum26 = vaddq_s16(row2, row6); \
      dct_long_mul(p1e, sum26, rot0_0); \
      dct_long_mac(t2e, p1e, row6, rot0_1); \
      dct_long_mac(t3e, p1e, row2, rot0_2); \
      int16x8_t sum04 = vaddq_s16(row0, row4); \
      int16x8_t dif04 = vsubq_s16(row0, row4); \
      dct_widen(t0e, sum04); \
      dct_widen(t1e, dif04); \
      dct_wadd(x0, t0e, t3e); \
      dct_wsub(x3, t0e, t3e); \
      dct_wadd(x1, t1e, t2e); \
      dct_wsub(x2, t1e, t2e); \
      /* odd part */ \
      int16x8_t sum15 = vaddq_s16(row1, row5); \
      int16x8_t sum17 = vaddq_s16(row1, row7); \
      int16x8_t sum35 = vaddq_s16(row3, row5); \
      int16x8_t sum37 = vaddq_s16(row3, row7); \
      int16x8_t sumodd = vaddq_s16(sum17, sum35); \
      dct_long_mul(p5o, sumodd, rot1_0); \
      dct_long_mac(p1o, p5o, sum17, rot1_1); \
      dct_long_mac(p2o, p5o, sum35, rot1_2); \
      dct_long_mul(p3o, sum37, rot2_0); \
      dct_long_mul(p4o, sum15, rot2_1); \
      dct_wadd(sump13o, p1o, p3o); \
      dct_wadd(sump24o, p2o, p4o); \
      dct_wadd(sump23o, p2o, p3o); \
      dct_wadd(sump14o, p1o, p4o); \
      dct_long_mac(x4, sump13o, row7, rot3_0); \
      dct_long_mac(x5, sump24o, row5, rot3_1); \
      dct_long_mac(x6, sump23o, row3, rot3_2); \
      dct_long_mac(x7, sump14o, row1, rot3_3); \
      dct_bfly32o(row0,row7, x0,x7,shiftop,shift); \
      dct_bfly32o(row1,row6, x1,x6,shiftop,shift); \
      dct_bfly32o(row2,row5, x2,x5,shiftop,shift); \
      dct_bfly32o(row3,row4, x3,x4,shiftop,shift); \
   }

   // load
  row0 = vld1q_s16(data + 0 * 8);
  row1 = vld1q_s16(data + 1 * 8);
  row2 = vld1q_s16(data + 2 * 8);
  row3 = vld1q_s16(data + 3 * 8);
  row4 = vld1q_s16(data + 4 * 8);
  row5 = vld1q_s16(data + 5 * 8);
  row6 = vld1q_s16(data + 6 * 8);
  row7 = vld1q_s16(data + 7 * 8);

  // add DC bias
  row0 = vaddq_s16(row0, vsetq_lane_s16(1024, vdupq_n_s16(0), 0));

  // column pass
  dct_pass(vrshrn_n_s32, 10);

  // 16bit 8x8 transpose
  {
    // these three map to a single VTRN.16, VTRN.32, and VSWP, respectively.
    // whether compilers actually get this is another story, sadly.
#define dct_trn16(x, y) { int16x8x2_t t = vtrnq_s16(x, y); x = t.val[0]; y = t.val[1]; }
#define dct_trn32(x, y) { int32x4x2_t t = vtrnq_s32(vreinterpretq_s32_s16(x), vreinterpretq_s32_s16(y)); x = vreinterpretq_s16_s32(t.val[0]); y = vreinterpretq_s16_s32(t.val[1]); }
#define dct_trn64(x, y) { int16x8_t x0 = x; int16x8_t y0 = y; x = vcombine_s16(vget_low_s16(x0), vget_low_s16(y0)); y = vcombine_s16(vget_high_s16(x0), vget_high_s16(y0)); }

      // pass 1
    dct_trn16(row0, row1); // a0b0a2b2a4b4a6b6
    dct_trn16(row2, row3);
    dct_trn16(row4, row5);
    dct_trn16(row6, row7);

    // pass 2
    dct_trn32(row0, row2); // a0b0c0d0a4b4c4d4
    dct_trn32(row1, row3);
    dct_trn32(row4, row6);
    dct_trn32(row5, row7);

    // pass 3
    dct_trn64(row0, row4); // a0b0c0d0e0f0g0h0
    dct_trn64(row1, row5);
    dct_trn64(row2, row6);
    dct_trn64(row3, row7);

#undef dct_trn16
#undef dct_trn32
#undef dct_trn64
  }

  // row pass
  // vrshrn_n_s32 only supports shifts up to 16, we need
  // 17. so do a non-rounding shift of 16 first then follow
  // up with a rounding shift by 1.
  dct_pass(vshrn_n_s32, 16);

  {
    // pack and round
    uint8x8_t p0 = vqrshrun_n_s16(row0, 1);
    uint8x8_t p1 = vqrshrun_n_s16(row1, 1);
    uint8x8_t p2 = vqrshrun_n_s16(row2, 1);
    uint8x8_t p3 = vqrshrun_n_s16(row3, 1);
    uint8x8_t p4 = vqrshrun_n_s16(row4, 1);
    uint8x8_t p5 = vqrshrun_n_s16(row5, 1);
    uint8x8_t p6 = vqrshrun_n_s16(row6, 1);
    uint8x8_t p7 = vqrshrun_n_s16(row7, 1);

    // again, these can translate into one instruction, but often don't.
#define dct_trn8_8(x, y) { uint8x8x2_t t = vtrn_u8(x, y); x = t.val[0]; y = t.val[1]; }
#define dct_trn8_16(x, y) { uint16x4x2_t t = vtrn_u16(vreinterpret_u16_u8(x), vreinterpret_u16_u8(y)); x = vreinterpret_u8_u16(t.val[0]); y = vreinterpret_u8_u16(t.val[1]); }
#define dct_trn8_32(x, y) { uint32x2x2_t t = vtrn_u32(vreinterpret_u32_u8(x), vreinterpret_u32_u8(y)); x = vreinterpret_u8_u32(t.val[0]); y = vreinterpret_u8_u32(t.val[1]); }

      // sadly can't use interleaved stores here since we only write
      // 8 bytes to each scan line!

      // 8x8 8-bit transpose pass 1
    dct_trn8_8(p0, p1);
    dct_trn8_8(p2, p3);
    dct_trn8_8(p4, p5);
    dct_trn8_8(p6, p7);

    // pass 2
    dct_trn8_16(p0, p2);
    dct_trn8_16(p1, p3);
    dct_trn8_16(p4, p6);
    dct_trn8_16(p5, p7);

    // pass 3
    dct_trn8_32(p0, p4);
    dct_trn8_32(p1, p5);
    dct_trn8_32(p2, p6);
    dct_trn8_32(p3, p7);

    // store
    vst1_u8(out, p0); out += out_stride;
    vst1_u8(out, p1); out += out_stride;
    vst1_u8(out, p2); out += out_stride;
    vst1_u8(out, p3); out += out_stride;
    vst1_u8(out, p4); out += out_stride;
    vst1_u8(out, p5); out += out_stride;
    vst1_u8(out, p6); out += out_stride;
    vst1_u8(out, p7);

#undef dct_trn8_8
#undef dct_trn8_16
#undef dct_trn8_32
  }

#undef dct_long_mul
#undef dct_long_mac
#undef dct_widen
#undef dct_wadd
#undef dct_wsub
#undef dct_bfly32o
#undef dct_pass
}

#endif // STBI_NEON

#define STBI__MARKER_none  0xff
// if there's a pending marker from the entropy stream, return that
// otherwise, fetch from the stream and get a marker. if there's no
// marker, return 0xff, which is never a valid marker value
static stbi_uc stbi__get_marker(stbi__jpeg* j)
{
  stbi_uc x;
  if (j->marker != STBI__MARKER_none) { x = j->marker; j->marker = STBI__MARKER_none; return x; }
  x = stbi__get8(j->s);
  if (x != 0xff) return STBI__MARKER_none;
  while (x == 0xff)
    x = stbi__get8(j->s);
  return x;
}

// in each scan, we'll have scan_n components, and the order
// of the components is specified by order[]
#define STBI__RESTART(x)     ((x) >= 0xd0 && (x) <= 0xd7)

// after a restart interval, stbi__jpeg_reset the entropy decoder and
// the dc prediction
static void stbi__jpeg_reset(stbi__jpeg* j)
{
  j->code_bits = 0;
  j->code_buffer = 0;
  j->nomore = 0;
  j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = 0;
  j->marker = STBI__MARKER_none;
  j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff;
  j->eob_run = 0;
  // no more than 1<<31 MCUs if no restart_interal? that's plenty safe,
  // since we don't even allow 1<<30 pixels
}

static int stbi__parse_entropy_coded_data(stbi__jpeg* z)
{
  stbi__jpeg_reset(z);
  if (!z->progressive) {
    if (z->scan_n == 1) {
      int i, j;
      STBI_SIMD_ALIGN(short, data[64]);
      int n = z->order[0];
      // non-interleaved data, we just need to process one block at a time,
      // in trivial scanline order
      // number of blocks to do just depends on how many actual "pixels" this
      // component has, independent of interleaved MCU blocking and such
      int w = (z->img_comp[n].x + 7) >> 3;
      int h = (z->img_comp[n].y + 7) >> 3;
      for (j = 0; j < h; ++j) {
        for (i = 0; i < w; ++i) {
          int ha = z->img_comp[n].ha;
          if (!stbi__jpeg_decode_block(z, data, z->huff_dc + z->img_comp[n].hd, z->huff_ac + ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0;
          z->idct_block_kernel(z->img_comp[n].data + z->img_comp[n].w2 * j * 8 + i * 8, z->img_comp[n].w2, data);
          // every data block is an MCU, so countdown the restart interval
          if (--z->todo <= 0) {
            if (z->code_bits < 24) stbi__grow_buffer_unsafe(z);
            // if it's NOT a restart, then just bail, so we get corrupt data
            // rather than no data
            if (!STBI__RESTART(z->marker)) return 1;
            stbi__jpeg_reset(z);
          }
        }
      }
      return 1;
    }
    else { // interleaved
      int i, j, k, x, y;
      STBI_SIMD_ALIGN(short, data[64]);
      for (j = 0; j < z->img_mcu_y; ++j) {
        for (i = 0; i < z->img_mcu_x; ++i) {
          // scan an interleaved mcu... process scan_n components in order
          for (k = 0; k < z->scan_n; ++k) {
            int n = z->order[k];
            // scan out an mcu's worth of this component; that's just determined
            // by the basic H and V specified for the component
            for (y = 0; y < z->img_comp[n].v; ++y) {
              for (x = 0; x < z->img_comp[n].h; ++x) {
                int x2 = (i * z->img_comp[n].h + x) * 8;
                int y2 = (j * z->img_comp[n].v + y) * 8;
                int ha = z->img_comp[n].ha;
                if (!stbi__jpeg_decode_block(z, data, z->huff_dc + z->img_comp[n].hd, z->huff_ac + ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0;
                z->idct_block_kernel(z->img_comp[n].data + z->img_comp[n].w2 * y2 + x2, z->img_comp[n].w2, data);
              }
            }
          }
          // after all interleaved components, that's an interleaved MCU,
          // so now count down the restart interval
          if (--z->todo <= 0) {
            if (z->code_bits < 24) stbi__grow_buffer_unsafe(z);
            if (!STBI__RESTART(z->marker)) return 1;
            stbi__jpeg_reset(z);
          }
        }
      }
      return 1;
    }
  }
  else {
    if (z->scan_n == 1) {
      int i, j;
      int n = z->order[0];
      // non-interleaved data, we just need to process one block at a time,
      // in trivial scanline order
      // number of blocks to do just depends on how many actual "pixels" this
      // component has, independent of interleaved MCU blocking and such
      int w = (z->img_comp[n].x + 7) >> 3;
      int h = (z->img_comp[n].y + 7) >> 3;
      for (j = 0; j < h; ++j) {
        for (i = 0; i < w; ++i) {
          short* data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w);
          if (z->spec_start == 0) {
            if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n))
              return 0;
          }
          else {
            int ha = z->img_comp[n].ha;
            if (!stbi__jpeg_decode_block_prog_ac(z, data, &z->huff_ac[ha], z->fast_ac[ha]))
              return 0;
          }
          // every data block is an MCU, so countdown the restart interval
          if (--z->todo <= 0) {
            if (z->code_bits < 24) stbi__grow_buffer_unsafe(z);
            if (!STBI__RESTART(z->marker)) return 1;
            stbi__jpeg_reset(z);
          }
        }
      }
      return 1;
    }
    else { // interleaved
      int i, j, k, x, y;
      for (j = 0; j < z->img_mcu_y; ++j) {
        for (i = 0; i < z->img_mcu_x; ++i) {
          // scan an interleaved mcu... process scan_n components in order
          for (k = 0; k < z->scan_n; ++k) {
            int n = z->order[k];
            // scan out an mcu's worth of this component; that's just determined
            // by the basic H and V specified for the component
            for (y = 0; y < z->img_comp[n].v; ++y) {
              for (x = 0; x < z->img_comp[n].h; ++x) {
                int x2 = (i * z->img_comp[n].h + x);
                int y2 = (j * z->img_comp[n].v + y);
                short* data = z->img_comp[n].coeff + 64 * (x2 + y2 * z->img_comp[n].coeff_w);
                if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n))
                  return 0;
              }
            }
          }
          // after all interleaved components, that's an interleaved MCU,
          // so now count down the restart interval
          if (--z->todo <= 0) {
            if (z->code_bits < 24) stbi__grow_buffer_unsafe(z);
            if (!STBI__RESTART(z->marker)) return 1;
            stbi__jpeg_reset(z);
          }
        }
      }
      return 1;
    }
  }
}

static void stbi__jpeg_dequantize(short* data, stbi_uc* dequant)
{
  int i;
  for (i = 0; i < 64; ++i)
    data[i] *= dequant[i];
}

static void stbi__jpeg_finish(stbi__jpeg* z)
{
  if (z->progressive) {
    // dequantize and idct the data
    int i, j, n;
    for (n = 0; n < z->s->img_n; ++n) {
      int w = (z->img_comp[n].x + 7) >> 3;
      int h = (z->img_comp[n].y + 7) >> 3;
      for (j = 0; j < h; ++j) {
        for (i = 0; i < w; ++i) {
          short* data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w);
          stbi__jpeg_dequantize(data, z->dequant[z->img_comp[n].tq]);
          z->idct_block_kernel(z->img_comp[n].data + z->img_comp[n].w2 * j * 8 + i * 8, z->img_comp[n].w2, data);
        }
      }
    }
  }
}

static int stbi__process_marker(stbi__jpeg* z, int m)
{
  int L;
  switch (m) {
  case STBI__MARKER_none: // no marker found
    return stbi__err("expected marker", "Corrupt JPEG");

  case 0xDD: // DRI - specify restart interval
    if (stbi__get16be(z->s) != 4) return stbi__err("bad DRI len", "Corrupt JPEG");
    z->restart_interval = stbi__get16be(z->s);
    return 1;

  case 0xDB: // DQT - define quantization table
    L = stbi__get16be(z->s) - 2;
    while (L > 0) {
      int q = stbi__get8(z->s);
      int p = q >> 4;
      int t = q & 15, i;
      if (p != 0) return stbi__err("bad DQT type", "Corrupt JPEG");
      if (t > 3) return stbi__err("bad DQT table", "Corrupt JPEG");
      for (i = 0; i < 64; ++i)
        z->dequant[t][stbi__jpeg_dezigzag[i]] = stbi__get8(z->s);
      L -= 65;
    }
    return L == 0;

  case 0xC4: // DHT - define huffman table
    L = stbi__get16be(z->s) - 2;
    while (L > 0) {
      stbi_uc* v;
      int sizes[16], i, n = 0;
      int q = stbi__get8(z->s);
      int tc = q >> 4;
      int th = q & 15;
      if (tc > 1 || th > 3) return stbi__err("bad DHT header", "Corrupt JPEG");
      for (i = 0; i < 16; ++i) {
        sizes[i] = stbi__get8(z->s);
        n += sizes[i];
      }
      L -= 17;
      if (tc == 0) {
        if (!stbi__build_huffman(z->huff_dc + th, sizes)) return 0;
        v = z->huff_dc[th].values;
      }
      else {
        if (!stbi__build_huffman(z->huff_ac + th, sizes)) return 0;
        v = z->huff_ac[th].values;
      }
      for (i = 0; i < n; ++i)
        v[i] = stbi__get8(z->s);
      if (tc != 0)
        stbi__build_fast_ac(z->fast_ac[th], z->huff_ac + th);
      L -= n;
    }
    return L == 0;
  }
  // check for comment block or APP blocks
  if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) {
    stbi__skip(z->s, stbi__get16be(z->s) - 2);
    return 1;
  }
  return 0;
}

// after we see SOS
static int stbi__process_scan_header(stbi__jpeg* z)
{
  int i;
  int Ls = stbi__get16be(z->s);
  z->scan_n = stbi__get8(z->s);
  if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int)z->s->img_n) return stbi__err("bad SOS component count", "Corrupt JPEG");
  if (Ls != 6 + 2 * z->scan_n) return stbi__err("bad SOS len", "Corrupt JPEG");
  for (i = 0; i < z->scan_n; ++i) {
    int id = stbi__get8(z->s), which;
    int q = stbi__get8(z->s);
    for (which = 0; which < z->s->img_n; ++which)
      if (z->img_comp[which].id == id)
        break;
    if (which == z->s->img_n) return 0; // no match
    z->img_comp[which].hd = q >> 4;   if (z->img_comp[which].hd > 3) return stbi__err("bad DC huff", "Corrupt JPEG");
    z->img_comp[which].ha = q & 15;   if (z->img_comp[which].ha > 3) return stbi__err("bad AC huff", "Corrupt JPEG");
    z->order[i] = which;
  }

  {
    int aa;
    z->spec_start = stbi__get8(z->s);
    z->spec_end = stbi__get8(z->s); // should be 63, but might be 0
    aa = stbi__get8(z->s);
    z->succ_high = (aa >> 4);
    z->succ_low = (aa & 15);
    if (z->progressive) {
      if (z->spec_start > 63 || z->spec_end > 63 || z->spec_start > z->spec_end || z->succ_high > 13 || z->succ_low > 13)
        return stbi__err("bad SOS", "Corrupt JPEG");
    }
    else {
      if (z->spec_start != 0) return stbi__err("bad SOS", "Corrupt JPEG");
      if (z->succ_high != 0 || z->succ_low != 0) return stbi__err("bad SOS", "Corrupt JPEG");
      z->spec_end = 63;
    }
  }

  return 1;
}

static int stbi__process_frame_header(stbi__jpeg* z, int scan)
{
  stbi__context* s = z->s;
  int Lf, p, i, q, h_max = 1, v_max = 1, c;
  Lf = stbi__get16be(s);         if (Lf < 11) return stbi__err("bad SOF len", "Corrupt JPEG"); // JPEG
  p = stbi__get8(s);            if (p != 8) return stbi__err("only 8-bit", "JPEG format not supported: 8-bit only"); // JPEG baseline
  s->img_y = stbi__get16be(s);   if (s->img_y == 0) return stbi__err("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG
  s->img_x = stbi__get16be(s);   if (s->img_x == 0) return stbi__err("0 width", "Corrupt JPEG"); // JPEG requires
  c = stbi__get8(s);
  if (c != 3 && c != 1) return stbi__err("bad component count", "Corrupt JPEG");    // JFIF requires
  s->img_n = c;
  for (i = 0; i < c; ++i) {
    z->img_comp[i].data = nullptr;
    z->img_comp[i].linebuf = nullptr;
  }

  if (Lf != 8 + 3 * s->img_n) return stbi__err("bad SOF len", "Corrupt JPEG");

  for (i = 0; i < s->img_n; ++i) {
    z->img_comp[i].id = stbi__get8(s);
    if (z->img_comp[i].id != i + 1)   // JFIF requires
      if (z->img_comp[i].id != i)  // some version of jpegtran outputs non-JFIF-compliant files!
        return stbi__err("bad component ID", "Corrupt JPEG");
    q = stbi__get8(s);
    z->img_comp[i].h = (q >> 4);  if (!z->img_comp[i].h || z->img_comp[i].h > 4) return stbi__err("bad H", "Corrupt JPEG");
    z->img_comp[i].v = q & 15;    if (!z->img_comp[i].v || z->img_comp[i].v > 4) return stbi__err("bad V", "Corrupt JPEG");
    z->img_comp[i].tq = stbi__get8(s);  if (z->img_comp[i].tq > 3) return stbi__err("bad TQ", "Corrupt JPEG");
  }

  if (scan != STBI__SCAN_load) return 1;

  if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode");

  for (i = 0; i < s->img_n; ++i) {
    if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h;
    if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v;
  }

  // compute interleaved mcu info
  z->img_h_max = h_max;
  z->img_v_max = v_max;
  z->img_mcu_w = h_max * 8;
  z->img_mcu_h = v_max * 8;
  z->img_mcu_x = (s->img_x + z->img_mcu_w - 1) / z->img_mcu_w;
  z->img_mcu_y = (s->img_y + z->img_mcu_h - 1) / z->img_mcu_h;

  for (i = 0; i < s->img_n; ++i) {
    // number of effective pixels (e.g. for non-interleaved MCU)
    z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max - 1) / h_max;
    z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max - 1) / v_max;
    // to simplify generation, we'll allocate enough memory to decode
    // the bogus oversized data from using interleaved MCUs and their
    // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't
    // discard the extra data until colorspace conversion
    z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8;
    z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8;
    z->img_comp[i].raw_data = stbi__malloc(z->img_comp[i].w2 * z->img_comp[i].h2 + 15);

    if (z->img_comp[i].raw_data == nullptr) {
      for (--i; i >= 0; --i) {
        STBI_FREE(z->img_comp[i].raw_data);
        z->img_comp[i].data = nullptr;
      }
      return stbi__err("outofmem", "Out of memory");
    }
    // align blocks for idct using mmx/sse
    z->img_comp[i].data = (stbi_uc*)(((size_t)z->img_comp[i].raw_data + 15) & ~15);
    z->img_comp[i].linebuf = nullptr;
    if (z->progressive) {
      z->img_comp[i].coeff_w = (z->img_comp[i].w2 + 7) >> 3;
      z->img_comp[i].coeff_h = (z->img_comp[i].h2 + 7) >> 3;
      z->img_comp[i].raw_coeff = STBI_MALLOC(z->img_comp[i].coeff_w * z->img_comp[i].coeff_h * 64 * sizeof(short) + 15);
      z->img_comp[i].coeff = (short*)(((size_t)z->img_comp[i].raw_coeff + 15) & ~15);
    }
    else {
      z->img_comp[i].coeff = 0;
      z->img_comp[i].raw_coeff = 0;
    }
  }

  return 1;
}

// use comparisons since in some cases we handle more than one case (e.g. SOF)
#define stbi__DNL(x)         ((x) == 0xdc)
#define stbi__SOI(x)         ((x) == 0xd8)
#define stbi__EOI(x)         ((x) == 0xd9)
#define stbi__SOF(x)         ((x) == 0xc0 || (x) == 0xc1 || (x) == 0xc2)
#define stbi__SOS(x)         ((x) == 0xda)

#define stbi__SOF_progressive(x)   ((x) == 0xc2)

static int stbi__decode_jpeg_header(stbi__jpeg* z, int scan)
{
  int m;
  z->marker = STBI__MARKER_none; // initialize cached marker to empty
  m = stbi__get_marker(z);
  if (!stbi__SOI(m)) return stbi__err("no SOI", "Corrupt JPEG");
  if (scan == STBI__SCAN_type) return 1;
  m = stbi__get_marker(z);
  while (!stbi__SOF(m)) {
    if (!stbi__process_marker(z, m)) return 0;
    m = stbi__get_marker(z);
    while (m == STBI__MARKER_none) {
      // some files have extra padding after their blocks, so ok, we'll scan
      if (stbi__at_eof(z->s)) return stbi__err("no SOF", "Corrupt JPEG");
      m = stbi__get_marker(z);
    }
  }
  z->progressive = stbi__SOF_progressive(m);
  if (!stbi__process_frame_header(z, scan)) return 0;
  return 1;
}

// decode image to YCbCr format
static int stbi__decode_jpeg_image(stbi__jpeg* j)
{
  int m;
  for (m = 0; m < 4; m++) {
    j->img_comp[m].raw_data = nullptr;
    j->img_comp[m].raw_coeff = nullptr;
  }
  j->restart_interval = 0;
  if (!stbi__decode_jpeg_header(j, STBI__SCAN_load)) return 0;
  m = stbi__get_marker(j);
  while (!stbi__EOI(m)) {
    if (stbi__SOS(m)) {
      if (!stbi__process_scan_header(j)) return 0;
      if (!stbi__parse_entropy_coded_data(j)) return 0;
      if (j->marker == STBI__MARKER_none) {
        // handle 0s at the end of image data from IP Kamera 9060
        while (!stbi__at_eof(j->s)) {
          int x = stbi__get8(j->s);
          if (x == 255) {
            j->marker = stbi__get8(j->s);
            break;
          }
          else if (x != 0) {
            return stbi__err("junk before marker", "Corrupt JPEG");
          }
        }
        // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0
      }
    }
    else {
      if (!stbi__process_marker(j, m)) return 0;
    }
    m = stbi__get_marker(j);
  }
  if (j->progressive)
    stbi__jpeg_finish(j);
  return 1;
}

// static jfif-centered resampling (across block boundaries)

typedef stbi_uc* (*resample_row_func)(stbi_uc* out, stbi_uc* in0, stbi_uc* in1,
  int w, int hs);

#define stbi__div4(x) ((stbi_uc) ((x) >> 2))

static stbi_uc* resample_row_1(stbi_uc* out, stbi_uc* in_near, stbi_uc* in_far, int w, int hs)
{
  STBI_NOTUSED(out);
  STBI_NOTUSED(in_far);
  STBI_NOTUSED(w);
  STBI_NOTUSED(hs);
  return in_near;
}

static stbi_uc* stbi__resample_row_v_2(stbi_uc* out, stbi_uc* in_near, stbi_uc* in_far, int w, int hs)
{
  // need to generate two samples vertically for every one in input
  int i;
  STBI_NOTUSED(hs);
  for (i = 0; i < w; ++i)
    out[i] = stbi__div4(3 * in_near[i] + in_far[i] + 2);
  return out;
}

static stbi_uc* stbi__resample_row_h_2(stbi_uc* out, stbi_uc* in_near, stbi_uc* in_far, int w, int hs)
{
  // need to generate two samples horizontally for every one in input
  int i;
  stbi_uc* input = in_near;

  if (w == 1) {
    // if only one sample, can't do any interpolation
    out[0] = out[1] = input[0];
    return out;
  }

  out[0] = input[0];
  out[1] = stbi__div4(input[0] * 3 + input[1] + 2);
  for (i = 1; i < w - 1; ++i) {
    int n = 3 * input[i] + 2;
    out[i * 2 + 0] = stbi__div4(n + input[i - 1]);
    out[i * 2 + 1] = stbi__div4(n + input[i + 1]);
  }
  out[i * 2 + 0] = stbi__div4(input[w - 2] * 3 + input[w - 1] + 2);
  out[i * 2 + 1] = input[w - 1];

  STBI_NOTUSED(in_far);
  STBI_NOTUSED(hs);

  return out;
}

#define stbi__div16(x) ((stbi_uc) ((x) >> 4))

static stbi_uc* stbi__resample_row_hv_2(stbi_uc* out, stbi_uc* in_near, stbi_uc* in_far, int w, int hs)
{
  // need to generate 2x2 samples for every one in input
  int i, t0, t1;
  if (w == 1) {
    out[0] = out[1] = stbi__div4(3 * in_near[0] + in_far[0] + 2);
    return out;
  }

  t1 = 3 * in_near[0] + in_far[0];
  out[0] = stbi__div4(t1 + 2);
  for (i = 1; i < w; ++i) {
    t0 = t1;
    t1 = 3 * in_near[i] + in_far[i];
    out[i * 2 - 1] = stbi__div16(3 * t0 + t1 + 8);
    out[i * 2] = stbi__div16(3 * t1 + t0 + 8);
  }
  out[w * 2 - 1] = stbi__div4(t1 + 2);

  STBI_NOTUSED(hs);

  return out;
}

#if defined(STBI_SSE2) || defined(STBI_NEON)
static stbi_uc* stbi__resample_row_hv_2_simd(stbi_uc* out, stbi_uc* in_near, stbi_uc* in_far, int w, int hs)
{
  // need to generate 2x2 samples for every one in input
  int i = 0, t0, t1;

  if (w == 1) {
    out[0] = out[1] = stbi__div4(3 * in_near[0] + in_far[0] + 2);
    return out;
  }

  t1 = 3 * in_near[0] + in_far[0];
  // process groups of 8 pixels for as long as we can.
  // note we can't handle the last pixel in a row in this loop
  // because we need to handle the filter boundary conditions.
  for (; i < ((w - 1) & ~7); i += 8) {
#if defined(STBI_SSE2)
    // load and perform the vertical filtering pass
    // this uses 3*x + y = 4*x + (y - x)
    __m128i zero = _mm_setzero_si128();
    __m128i farb = _mm_loadl_epi64((__m128i*) (in_far + i));
    __m128i nearb = _mm_loadl_epi64((__m128i*) (in_near + i));
    __m128i farw = _mm_unpacklo_epi8(farb, zero);
    __m128i nearw = _mm_unpacklo_epi8(nearb, zero);
    __m128i diff = _mm_sub_epi16(farw, nearw);
    __m128i nears = _mm_slli_epi16(nearw, 2);
    __m128i curr = _mm_add_epi16(nears, diff); // current row

    // horizontal filter works the same based on shifted vers of current
    // row. "prev" is current row shifted right by 1 pixel; we need to
    // insert the previous pixel value (from t1).
    // "next" is current row shifted left by 1 pixel, with first pixel
    // of next block of 8 pixels added in.
    __m128i prv0 = _mm_slli_si128(curr, 2);
    __m128i nxt0 = _mm_srli_si128(curr, 2);
    __m128i prev = _mm_insert_epi16(prv0, t1, 0);
    __m128i next = _mm_insert_epi16(nxt0, 3 * in_near[i + 8] + in_far[i + 8], 7);

    // horizontal filter, polyphase implementation since it's convenient:
    // even pixels = 3*cur + prev = cur*4 + (prev - cur)
    // odd  pixels = 3*cur + next = cur*4 + (next - cur)
    // note the shared term.
    __m128i bias = _mm_set1_epi16(8);
    __m128i curs = _mm_slli_epi16(curr, 2);
    __m128i prvd = _mm_sub_epi16(prev, curr);
    __m128i nxtd = _mm_sub_epi16(next, curr);
    __m128i curb = _mm_add_epi16(curs, bias);
    __m128i even = _mm_add_epi16(prvd, curb);
    __m128i odd = _mm_add_epi16(nxtd, curb);

    // interleave even and odd pixels, then undo scaling.
    __m128i int0 = _mm_unpacklo_epi16(even, odd);
    __m128i int1 = _mm_unpackhi_epi16(even, odd);
    __m128i de0 = _mm_srli_epi16(int0, 4);
    __m128i de1 = _mm_srli_epi16(int1, 4);

    // pack and write output
    __m128i outv = _mm_packus_epi16(de0, de1);
    _mm_storeu_si128((__m128i*) (out + i * 2), outv);
#elif defined(STBI_NEON)
    // load and perform the vertical filtering pass
    // this uses 3*x + y = 4*x + (y - x)
    uint8x8_t farb = vld1_u8(in_far + i);
    uint8x8_t nearb = vld1_u8(in_near + i);
    int16x8_t diff = vreinterpretq_s16_u16(vsubl_u8(farb, nearb));
    int16x8_t nears = vreinterpretq_s16_u16(vshll_n_u8(nearb, 2));
    int16x8_t curr = vaddq_s16(nears, diff); // current row

    // horizontal filter works the same based on shifted vers of current
    // row. "prev" is current row shifted right by 1 pixel; we need to
    // insert the previous pixel value (from t1).
    // "next" is current row shifted left by 1 pixel, with first pixel
    // of next block of 8 pixels added in.
    int16x8_t prv0 = vextq_s16(curr, curr, 7);
    int16x8_t nxt0 = vextq_s16(curr, curr, 1);
    int16x8_t prev = vsetq_lane_s16(t1, prv0, 0);
    int16x8_t next = vsetq_lane_s16(3 * in_near[i + 8] + in_far[i + 8], nxt0, 7);

    // horizontal filter, polyphase implementation since it's convenient:
    // even pixels = 3*cur + prev = cur*4 + (prev - cur)
    // odd  pixels = 3*cur + next = cur*4 + (next - cur)
    // note the shared term.
    int16x8_t curs = vshlq_n_s16(curr, 2);
    int16x8_t prvd = vsubq_s16(prev, curr);
    int16x8_t nxtd = vsubq_s16(next, curr);
    int16x8_t even = vaddq_s16(curs, prvd);
    int16x8_t odd = vaddq_s16(curs, nxtd);

    // undo scaling and round, then store with even/odd phases interleaved
    uint8x8x2_t o;
    o.val[0] = vqrshrun_n_s16(even, 4);
    o.val[1] = vqrshrun_n_s16(odd, 4);
    vst2_u8(out + i * 2, o);
#endif

    // "previous" value for next iter
    t1 = 3 * in_near[i + 7] + in_far[i + 7];
  }

  t0 = t1;
  t1 = 3 * in_near[i] + in_far[i];
  out[i * 2] = stbi__div16(3 * t1 + t0 + 8);

  for (++i; i < w; ++i) {
    t0 = t1;
    t1 = 3 * in_near[i] + in_far[i];
    out[i * 2 - 1] = stbi__div16(3 * t0 + t1 + 8);
    out[i * 2] = stbi__div16(3 * t1 + t0 + 8);
  }
  out[w * 2 - 1] = stbi__div4(t1 + 2);

  STBI_NOTUSED(hs);

  return out;
}
#endif

static stbi_uc* stbi__resample_row_generic(stbi_uc* out, stbi_uc* in_near, stbi_uc* in_far, int w, int hs)
{
  // resample with nearest-neighbor
  int i, j;
  STBI_NOTUSED(in_far);
  for (i = 0; i < w; ++i)
    for (j = 0; j < hs; ++j)
      out[i * hs + j] = in_near[i];
  return out;
}

#ifdef STBI_JPEG_OLD
// this is the same YCbCr-to-RGB calculation that stb_image has used
// historically before the algorithm changes in 1.49
#define float2fixed(x)  ((int) ((x) * 65536 + 0.5))
static void stbi__YCbCr_to_RGB_row(stbi_uc* out, const stbi_uc* y, const stbi_uc* pcb, const stbi_uc* pcr, int count, int step)
{
  int i;
  for (i = 0; i < count; ++i) {
    int y_fixed = (y[i] << 16) + 32768; // rounding
    int r, g, b;
    int cr = pcr[i] - 128;
    int cb = pcb[i] - 128;
    r = y_fixed + cr * float2fixed(1.40200f);
    g = y_fixed - cr * float2fixed(0.71414f) - cb * float2fixed(0.34414f);
    b = y_fixed + cb * float2fixed(1.77200f);
    r >>= 16;
    g >>= 16;
    b >>= 16;
    if ((unsigned)r > 255) { if (r < 0) r = 0; else r = 255; }
    if ((unsigned)g > 255) { if (g < 0) g = 0; else g = 255; }
    if ((unsigned)b > 255) { if (b < 0) b = 0; else b = 255; }
    out[0] = (stbi_uc)r;
    out[1] = (stbi_uc)g;
    out[2] = (stbi_uc)b;
    out[3] = 255;
    out += step;
  }
}
#else
// this is a reduced-precision calculation of YCbCr-to-RGB introduced
// to make sure the code produces the same results in both SIMD and scalar
#define float2fixed(x)  (((int) ((x) * 4096.0f + 0.5f)) << 8)
static void stbi__YCbCr_to_RGB_row(stbi_uc* out, const stbi_uc* y, const stbi_uc* pcb, const stbi_uc* pcr, int count, int step)
{
  int i;
  for (i = 0; i < count; ++i) {
    int y_fixed = (y[i] << 20) + (1 << 19); // rounding
    int r, g, b;
    int cr = pcr[i] - 128;
    int cb = pcb[i] - 128;
    r = y_fixed + cr * float2fixed(1.40200f);
    g = y_fixed + (cr * -float2fixed(0.71414f)) + ((cb * -float2fixed(0.34414f)) & 0xffff0000);
    b = y_fixed + cb * float2fixed(1.77200f);
    r >>= 20;
    g >>= 20;
    b >>= 20;
    if ((unsigned)r > 255) { if (r < 0) r = 0; else r = 255; }
    if ((unsigned)g > 255) { if (g < 0) g = 0; else g = 255; }
    if ((unsigned)b > 255) { if (b < 0) b = 0; else b = 255; }
    out[0] = (stbi_uc)r;
    out[1] = (stbi_uc)g;
    out[2] = (stbi_uc)b;
    out[3] = 255;
    out += step;
  }
}
#endif

#if defined(STBI_SSE2) || defined(STBI_NEON)
static void stbi__YCbCr_to_RGB_simd(stbi_uc* out, stbi_uc const* y, stbi_uc const* pcb, stbi_uc const* pcr, int count, int step)
{
  int i = 0;

#ifdef STBI_SSE2
  // step == 3 is pretty ugly on the final interleave, and i'm not convinced
  // it's useful in practice (you wouldn't use it for textures, for example).
  // so just accelerate step == 4 case.
  if (step == 4) {
    // this is a fairly straightforward implementation and not super-optimized.
    __m128i signflip = _mm_set1_epi8(-0x80);
    __m128i cr_const0 = _mm_set1_epi16((short)(1.40200f * 4096.0f + 0.5f));
    __m128i cr_const1 = _mm_set1_epi16(-(short)(0.71414f * 4096.0f + 0.5f));
    __m128i cb_const0 = _mm_set1_epi16(-(short)(0.34414f * 4096.0f + 0.5f));
    __m128i cb_const1 = _mm_set1_epi16((short)(1.77200f * 4096.0f + 0.5f));
    __m128i y_bias = _mm_set1_epi8((char)(unsigned char)128);
    __m128i xw = _mm_set1_epi16(255); // alpha channel

    for (; i + 7 < count; i += 8) {
      // load
      __m128i y_bytes = _mm_loadl_epi64((__m128i*) (y + i));
      __m128i cr_bytes = _mm_loadl_epi64((__m128i*) (pcr + i));
      __m128i cb_bytes = _mm_loadl_epi64((__m128i*) (pcb + i));
      __m128i cr_biased = _mm_xor_si128(cr_bytes, signflip); // -128
      __m128i cb_biased = _mm_xor_si128(cb_bytes, signflip); // -128

      // unpack to short (and left-shift cr, cb by 8)
      __m128i yw = _mm_unpacklo_epi8(y_bias, y_bytes);
      __m128i crw = _mm_unpacklo_epi8(_mm_setzero_si128(), cr_biased);
      __m128i cbw = _mm_unpacklo_epi8(_mm_setzero_si128(), cb_biased);

      // color transform
      __m128i yws = _mm_srli_epi16(yw, 4);
      __m128i cr0 = _mm_mulhi_epi16(cr_const0, crw);
      __m128i cb0 = _mm_mulhi_epi16(cb_const0, cbw);
      __m128i cb1 = _mm_mulhi_epi16(cbw, cb_const1);
      __m128i cr1 = _mm_mulhi_epi16(crw, cr_const1);
      __m128i rws = _mm_add_epi16(cr0, yws);
      __m128i gwt = _mm_add_epi16(cb0, yws);
      __m128i bws = _mm_add_epi16(yws, cb1);
      __m128i gws = _mm_add_epi16(gwt, cr1);

      // descale
      __m128i rw = _mm_srai_epi16(rws, 4);
      __m128i bw = _mm_srai_epi16(bws, 4);
      __m128i gw = _mm_srai_epi16(gws, 4);

      // back to byte, set up for transpose
      __m128i brb = _mm_packus_epi16(rw, bw);
      __m128i gxb = _mm_packus_epi16(gw, xw);

      // transpose to interleave channels
      __m128i t0 = _mm_unpacklo_epi8(brb, gxb);
      __m128i t1 = _mm_unpackhi_epi8(brb, gxb);
      __m128i o0 = _mm_unpacklo_epi16(t0, t1);
      __m128i o1 = _mm_unpackhi_epi16(t0, t1);

      // store
      _mm_storeu_si128((__m128i*) (out + 0), o0);
      _mm_storeu_si128((__m128i*) (out + 16), o1);
      out += 32;
    }
  }
#endif

#ifdef STBI_NEON
  // in this version, step=3 support would be easy to add. but is there demand?
  if (step == 4) {
    // this is a fairly straightforward implementation and not super-optimized.
    uint8x8_t signflip = vdup_n_u8(0x80);
    int16x8_t cr_const0 = vdupq_n_s16((short)(1.40200f * 4096.0f + 0.5f));
    int16x8_t cr_const1 = vdupq_n_s16(-(short)(0.71414f * 4096.0f + 0.5f));
    int16x8_t cb_const0 = vdupq_n_s16(-(short)(0.34414f * 4096.0f + 0.5f));
    int16x8_t cb_const1 = vdupq_n_s16((short)(1.77200f * 4096.0f + 0.5f));

    for (; i + 7 < count; i += 8) {
      // load
      uint8x8_t y_bytes = vld1_u8(y + i);
      uint8x8_t cr_bytes = vld1_u8(pcr + i);
      uint8x8_t cb_bytes = vld1_u8(pcb + i);
      int8x8_t cr_biased = vreinterpret_s8_u8(vsub_u8(cr_bytes, signflip));
      int8x8_t cb_biased = vreinterpret_s8_u8(vsub_u8(cb_bytes, signflip));

      // expand to s16
      int16x8_t yws = vreinterpretq_s16_u16(vshll_n_u8(y_bytes, 4));
      int16x8_t crw = vshll_n_s8(cr_biased, 7);
      int16x8_t cbw = vshll_n_s8(cb_biased, 7);

      // color transform
      int16x8_t cr0 = vqdmulhq_s16(crw, cr_const0);
      int16x8_t cb0 = vqdmulhq_s16(cbw, cb_const0);
      int16x8_t cr1 = vqdmulhq_s16(crw, cr_const1);
      int16x8_t cb1 = vqdmulhq_s16(cbw, cb_const1);
      int16x8_t rws = vaddq_s16(yws, cr0);
      int16x8_t gws = vaddq_s16(vaddq_s16(yws, cb0), cr1);
      int16x8_t bws = vaddq_s16(yws, cb1);

      // undo scaling, round, convert to byte
      uint8x8x4_t o;
      o.val[0] = vqrshrun_n_s16(rws, 4);
      o.val[1] = vqrshrun_n_s16(gws, 4);
      o.val[2] = vqrshrun_n_s16(bws, 4);
      o.val[3] = vdup_n_u8(255);

      // store, interleaving r/g/b/a
      vst4_u8(out, o);
      out += 8 * 4;
    }
  }
#endif

  for (; i < count; ++i) {
    int y_fixed = (y[i] << 20) + (1 << 19); // rounding
    int r, g, b;
    int cr = pcr[i] - 128;
    int cb = pcb[i] - 128;
    r = y_fixed + cr * float2fixed(1.40200f);
    g = y_fixed + cr * -float2fixed(0.71414f) + ((cb * -float2fixed(0.34414f)) & 0xffff0000);
    b = y_fixed + cb * float2fixed(1.77200f);
    r >>= 20;
    g >>= 20;
    b >>= 20;
    if ((unsigned)r > 255) { if (r < 0) r = 0; else r = 255; }
    if ((unsigned)g > 255) { if (g < 0) g = 0; else g = 255; }
    if ((unsigned)b > 255) { if (b < 0) b = 0; else b = 255; }
    out[0] = (stbi_uc)r;
    out[1] = (stbi_uc)g;
    out[2] = (stbi_uc)b;
    out[3] = 255;
    out += step;
  }
}
#endif

// set up the kernels
static void stbi__setup_jpeg(stbi__jpeg* j)
{
  j->idct_block_kernel = stbi__idct_block;
  j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_row;
  j->resample_row_hv_2_kernel = stbi__resample_row_hv_2;

#ifdef STBI_SSE2
  if (stbi__sse2_available()) {
    j->idct_block_kernel = stbi__idct_simd;
#ifndef STBI_JPEG_OLD
    j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd;
#endif
    j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd;
  }
#endif

#ifdef STBI_NEON
  j->idct_block_kernel = stbi__idct_simd;
#ifndef STBI_JPEG_OLD
  j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd;
#endif
  j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd;
#endif
}

// clean up the temporary component buffers
static void stbi__cleanup_jpeg(stbi__jpeg* j)
{
  int i;
  for (i = 0; i < j->s->img_n; ++i) {
    if (j->img_comp[i].raw_data) {
      STBI_FREE(j->img_comp[i].raw_data);
      j->img_comp[i].raw_data = nullptr;
      j->img_comp[i].data = nullptr;
    }
    if (j->img_comp[i].raw_coeff) {
      STBI_FREE(j->img_comp[i].raw_coeff);
      j->img_comp[i].raw_coeff = 0;
      j->img_comp[i].coeff = 0;
    }
    if (j->img_comp[i].linebuf) {
      STBI_FREE(j->img_comp[i].linebuf);
      j->img_comp[i].linebuf = nullptr;
    }
  }
}

typedef struct
{
  resample_row_func resample;
  stbi_uc* line0, * line1;
  int hs, vs;   // expansion factor in each axis
  int w_lores; // horizontal pixels pre-expansion
  int ystep;   // how far through vertical expansion we are
  int ypos;    // which pre-expansion row we're on
} stbi__resample;

static stbi_uc* load_jpeg_image(stbi__jpeg* z, int* out_x, int* out_y, int* comp, int req_comp)
{
  int n, decode_n;
  z->s->img_n = 0; // make stbi__cleanup_jpeg safe

  // validate req_comp
  if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error");

  // load a jpeg image from whichever source, but leave in YCbCr format
  if (!stbi__decode_jpeg_image(z)) { stbi__cleanup_jpeg(z); return nullptr; }

  // determine actual number of components to generate
  n = req_comp ? req_comp : z->s->img_n;

  if (z->s->img_n == 3 && n < 3)
    decode_n = 1;
  else
    decode_n = z->s->img_n;

  // resample and color-convert
  {
    int k;
    unsigned int i, j;
    stbi_uc* output;
    stbi_uc* coutput[4];

    stbi__resample res_comp[4];

    for (k = 0; k < decode_n; ++k) {
      stbi__resample* r = &res_comp[k];

      // allocate line buffer big enough for upsampling off the edges
      // with upsample factor of 4
      z->img_comp[k].linebuf = (stbi_uc*)stbi__malloc(z->s->img_x + 3);
      if (!z->img_comp[k].linebuf) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); }

      r->hs = z->img_h_max / z->img_comp[k].h;
      r->vs = z->img_v_max / z->img_comp[k].v;
      r->ystep = r->vs >> 1;
      r->w_lores = (z->s->img_x + r->hs - 1) / r->hs;
      r->ypos = 0;
      r->line0 = r->line1 = z->img_comp[k].data;

      if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1;
      else if (r->hs == 1 && r->vs == 2) r->resample = stbi__resample_row_v_2;
      else if (r->hs == 2 && r->vs == 1) r->resample = stbi__resample_row_h_2;
      else if (r->hs == 2 && r->vs == 2) r->resample = z->resample_row_hv_2_kernel;
      else                               r->resample = stbi__resample_row_generic;
    }

    // can't error after this so, this is safe
    output = (stbi_uc*)stbi__malloc(n * z->s->img_x * z->s->img_y + 1);
    if (!output) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); }

    // now go ahead and resample
    for (j = 0; j < z->s->img_y; ++j) {
      stbi_uc* out = output + n * z->s->img_x * j;
      for (k = 0; k < decode_n; ++k) {
        stbi__resample* r = &res_comp[k];
        int y_bot = r->ystep >= (r->vs >> 1);
        coutput[k] = r->resample(z->img_comp[k].linebuf,
          y_bot ? r->line1 : r->line0,
          y_bot ? r->line0 : r->line1,
          r->w_lores, r->hs);
        if (++r->ystep >= r->vs) {
          r->ystep = 0;
          r->line0 = r->line1;
          if (++r->ypos < z->img_comp[k].y)
            r->line1 += z->img_comp[k].w2;
        }
      }
      if (n >= 3) {
        stbi_uc* y = coutput[0];
        if (z->s->img_n == 3) {
          z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n);
        }
        else
          for (i = 0; i < z->s->img_x; ++i) {
            out[0] = out[1] = out[2] = y[i];
            out[3] = 255; // not used if n==3
            out += n;
          }
      }
      else {
        stbi_uc* y = coutput[0];
        if (n == 1)
          for (i = 0; i < z->s->img_x; ++i) out[i] = y[i];
        else
          for (i = 0; i < z->s->img_x; ++i) *out++ = y[i], *out++ = 255;
      }
    }
    stbi__cleanup_jpeg(z);
    *out_x = z->s->img_x;
    *out_y = z->s->img_y;
    if (comp) *comp = z->s->img_n; // report original components, not output
    return output;
  }
}

static unsigned char* STBI_FORCE_STACK_ALIGN stbi__jpeg_load(stbi__context* s, int* x, int* y, int* comp, int req_comp)
{
  stbi__jpeg j;
  j.s = s;
  stbi__setup_jpeg(&j);
  return load_jpeg_image(&j, x, y, comp, req_comp);
}

static int stbi__jpeg_test(stbi__context* s)
{
  int r;
  stbi__jpeg j;
  j.s = s;
  stbi__setup_jpeg(&j);
  r = stbi__decode_jpeg_header(&j, STBI__SCAN_type);
  stbi__rewind(s);
  return r;
}

static int stbi__jpeg_info_raw(stbi__jpeg* j, int* x, int* y, int* comp)
{
  if (!stbi__decode_jpeg_header(j, STBI__SCAN_header)) {
    stbi__rewind(j->s);
    return 0;
  }
  if (x) *x = j->s->img_x;
  if (y) *y = j->s->img_y;
  if (comp) *comp = j->s->img_n;
  return 1;
}

static int stbi__jpeg_info(stbi__context* s, int* x, int* y, int* comp)
{
  stbi__jpeg j;
  j.s = s;
  return stbi__jpeg_info_raw(&j, x, y, comp);
}
#endif

// public domain zlib decode    v0.2  Sean Barrett 2006-11-18
//    simple implementation
//      - all input must be provided in an upfront buffer
//      - all output is written to a single output buffer (can malloc/realloc)
//    performance
//      - fast huffman

#ifndef STBI_NO_ZLIB

// fast-way is faster to check than jpeg huffman, but slow way is slower
#define STBI__ZFAST_BITS  9 // accelerate all cases in default tables
#define STBI__ZFAST_MASK  ((1 << STBI__ZFAST_BITS) - 1)

// zlib-style huffman encoding
// (jpegs packs from left, zlib from right, so can't share code)
typedef struct
{
  stbi__uint16 fast[1 << STBI__ZFAST_BITS];
  stbi__uint16 firstcode[16];
  int maxcode[17];
  stbi__uint16 firstsymbol[16];
  stbi_uc  size[288];
  stbi__uint16 value[288];
} stbi__zhuffman;

stbi_inline static int stbi__bitreverse16(int n)
{
  n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1);
  n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2);
  n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4);
  n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8);
  return n;
}

stbi_inline static int stbi__bit_reverse(int v, int bits)
{
  STBI_ASSERT(bits <= 16);
  // to bit reverse n bits, reverse 16 and shift
  // e.g. 11 bits, bit reverse and shift away 5
  return stbi__bitreverse16(v) >> (16 - bits);
}

static int stbi__zbuild_huffman(stbi__zhuffman* z, stbi_uc* sizelist, int num)
{
  int i, k = 0;
  int code, next_code[16], sizes[17];

  // DEFLATE spec for generating codes
  memset(sizes, 0, sizeof(sizes));
  memset(z->fast, 0, sizeof(z->fast));
  for (i = 0; i < num; ++i)
    ++sizes[sizelist[i]];
  sizes[0] = 0;
  for (i = 1; i < 16; ++i)
    if (sizes[i] > (1 << i))
      return stbi__err("bad sizes", "Corrupt PNG");
  code = 0;
  for (i = 1; i < 16; ++i) {
    next_code[i] = code;
    z->firstcode[i] = (stbi__uint16)code;
    z->firstsymbol[i] = (stbi__uint16)k;
    code = (code + sizes[i]);
    if (sizes[i])
      if (code - 1 >= (1 << i)) return stbi__err("bad codelengths", "Corrupt PNG");
    z->maxcode[i] = code << (16 - i); // preshift for inner loop
    code <<= 1;
    k += sizes[i];
  }
  z->maxcode[16] = 0x10000; // sentinel
  for (i = 0; i < num; ++i) {
    int s = sizelist[i];
    if (s) {
      int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s];
      stbi__uint16 fastv = (stbi__uint16)((s << 9) | i);
      z->size[c] = (stbi_uc)s;
      z->value[c] = (stbi__uint16)i;
      if (s <= STBI__ZFAST_BITS) {
        int k2 = stbi__bit_reverse(next_code[s], s);
        while (k2 < (1 << STBI__ZFAST_BITS)) {
          z->fast[k2] = fastv;
          k2 += (1 << s);
        }
      }
      ++next_code[s];
    }
  }
  return 1;
}

// zlib-from-memory implementation for PNG reading
//    because PNG allows splitting the zlib stream arbitrarily,
//    and it's annoying structurally to have PNG call ZLIB call PNG,
//    we require PNG read all the IDATs and combine them into a single
//    memory buffer

typedef struct
{
  stbi_uc* zbuffer, * zbuffer_end;
  int num_bits;
  stbi__uint32 code_buffer;

  char* zout;
  char* zout_start;
  char* zout_end;
  int   z_expandable;

  stbi__zhuffman z_length, z_distance;
} stbi__zbuf;

stbi_inline static stbi_uc stbi__zget8(stbi__zbuf* z)
{
  if (z->zbuffer >= z->zbuffer_end) return 0;
  return *z->zbuffer++;
}

static void stbi__fill_bits(stbi__zbuf* z)
{
  do {
    STBI_ASSERT(z->code_buffer < (1U << z->num_bits));
    z->code_buffer |= stbi__zget8(z) << z->num_bits;
    z->num_bits += 8;
  } while (z->num_bits <= 24);
}

stbi_inline static unsigned int stbi__zreceive(stbi__zbuf* z, int n)
{
  unsigned int k;
  if (z->num_bits < n) stbi__fill_bits(z);
  k = z->code_buffer & ((1 << n) - 1);
  z->code_buffer >>= n;
  z->num_bits -= n;
  return k;
}

static int stbi__zhuffman_decode_slowpath(stbi__zbuf* a, stbi__zhuffman* z)
{
  int b, s, k;
  // not resolved by fast table, so compute it the slow way
  // use jpeg approach, which requires MSbits at top
  k = stbi__bit_reverse(a->code_buffer, 16);
  for (s = STBI__ZFAST_BITS + 1; ; ++s)
    if (k < z->maxcode[s])
      break;
  if (s == 16) return -1; // invalid code!
  // code size is s, so:
  b = (k >> (16 - s)) - z->firstcode[s] + z->firstsymbol[s];
  STBI_ASSERT(z->size[b] == s);
  a->code_buffer >>= s;
  a->num_bits -= s;
  return z->value[b];
}

stbi_inline static int stbi__zhuffman_decode(stbi__zbuf* a, stbi__zhuffman* z)
{
  int b, s;
  if (a->num_bits < 16) stbi__fill_bits(a);
  b = z->fast[a->code_buffer & STBI__ZFAST_MASK];
  if (b) {
    s = b >> 9;
    a->code_buffer >>= s;
    a->num_bits -= s;
    return b & 511;
  }
  return stbi__zhuffman_decode_slowpath(a, z);
}

static int stbi__zexpand(stbi__zbuf* z, char* zout, int n)  // need to make room for n bytes
{
  char* q;
  int cur, limit;
  z->zout = zout;
  if (!z->z_expandable) return stbi__err("output buffer limit", "Corrupt PNG");
  cur = (int)(z->zout - z->zout_start);
  limit = (int)(z->zout_end - z->zout_start);
  while (cur + n > limit)
    limit *= 2;
  q = (char*)STBI_REALLOC(z->zout_start, limit);
  if (q == nullptr) return stbi__err("outofmem", "Out of memory");
  z->zout_start = q;
  z->zout = q + cur;
  z->zout_end = q + limit;
  return 1;
}

static int stbi__zlength_base[31] = {
   3,4,5,6,7,8,9,10,11,13,
   15,17,19,23,27,31,35,43,51,59,
   67,83,99,115,131,163,195,227,258,0,0 };

static int stbi__zlength_extra[31] =
{ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 };

static int stbi__zdist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,
257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0 };

static int stbi__zdist_extra[32] =
{ 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13 };

static int stbi__parse_huffman_block(stbi__zbuf* a)
{
  char* zout = a->zout;
  for (;;) {
    int z = stbi__zhuffman_decode(a, &a->z_length);
    if (z < 256) {
      if (z < 0) return stbi__err("bad huffman code", "Corrupt PNG"); // error in huffman codes
      if (zout >= a->zout_end) {
        if (!stbi__zexpand(a, zout, 1)) return 0;
        zout = a->zout;
      }
      *zout++ = (char)z;
    }
    else {
      stbi_uc* p;
      int len, dist;
      if (z == 256) {
        a->zout = zout;
        return 1;
      }
      z -= 257;
      len = stbi__zlength_base[z];
      if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]);
      z = stbi__zhuffman_decode(a, &a->z_distance);
      if (z < 0) return stbi__err("bad huffman code", "Corrupt PNG");
      dist = stbi__zdist_base[z];
      if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]);
      if (zout - a->zout_start < dist) return stbi__err("bad dist", "Corrupt PNG");
      if (zout + len > a->zout_end) {
        if (!stbi__zexpand(a, zout, len)) return 0;
        zout = a->zout;
      }
      p = (stbi_uc*)(zout - dist);
      if (dist == 1) { // run of one byte; common in images.
        stbi_uc v = *p;
        if (len) { do *zout++ = v; while (--len); }
      }
      else {
        if (len) { do *zout++ = *p++; while (--len); }
      }
    }
  }
}

static int stbi__compute_huffman_codes(stbi__zbuf* a)
{
  static stbi_uc length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 };
  stbi__zhuffman z_codelength;
  stbi_uc lencodes[286 + 32 + 137];//padding for maximum single op
  stbi_uc codelength_sizes[19];
  int i, n;

  int hlit = stbi__zreceive(a, 5) + 257;
  int hdist = stbi__zreceive(a, 5) + 1;
  int hclen = stbi__zreceive(a, 4) + 4;

  memset(codelength_sizes, 0, sizeof(codelength_sizes));
  for (i = 0; i < hclen; ++i) {
    int s = stbi__zreceive(a, 3);
    codelength_sizes[length_dezigzag[i]] = (stbi_uc)s;
  }
  if (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0;

  n = 0;
  while (n < hlit + hdist) {
    int c = stbi__zhuffman_decode(a, &z_codelength);
    if (c < 0 || c >= 19) return stbi__err("bad codelengths", "Corrupt PNG");
    if (c < 16)
      lencodes[n++] = (stbi_uc)c;
    else if (c == 16) {
      c = stbi__zreceive(a, 2) + 3;
      memset(lencodes + n, lencodes[n - 1], c);
      n += c;
    }
    else if (c == 17) {
      c = stbi__zreceive(a, 3) + 3;
      memset(lencodes + n, 0, c);
      n += c;
    }
    else {
      STBI_ASSERT(c == 18);
      c = stbi__zreceive(a, 7) + 11;
      memset(lencodes + n, 0, c);
      n += c;
    }
  }
  if (n != hlit + hdist) return stbi__err("bad codelengths", "Corrupt PNG");
  if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0;
  if (!stbi__zbuild_huffman(&a->z_distance, lencodes + hlit, hdist)) return 0;
  return 1;
}

static int stbi__parse_uncomperssed_block(stbi__zbuf* a)
{
  stbi_uc header[4];
  int len, nlen, k;
  if (a->num_bits & 7)
    stbi__zreceive(a, a->num_bits & 7); // discard
  // drain the bit-packed data into header
  k = 0;
  while (a->num_bits > 0) {
    header[k++] = (stbi_uc)(a->code_buffer & 255); // suppress MSVC run-time check
    a->code_buffer >>= 8;
    a->num_bits -= 8;
  }
  STBI_ASSERT(a->num_bits == 0);
  // now fill header the normal way
  while (k < 4)
    header[k++] = stbi__zget8(a);
  len = header[1] * 256 + header[0];
  nlen = header[3] * 256 + header[2];
  if (nlen != (len ^ 0xffff)) return stbi__err("zlib corrupt", "Corrupt PNG");
  if (a->zbuffer + len > a->zbuffer_end) return stbi__err("read past buffer", "Corrupt PNG");
  if (a->zout + len > a->zout_end)
    if (!stbi__zexpand(a, a->zout, len)) return 0;
  memcpy(a->zout, a->zbuffer, len);
  a->zbuffer += len;
  a->zout += len;
  return 1;
}

static int stbi__parse_zlib_header(stbi__zbuf* a)
{
  int cmf = stbi__zget8(a);
  int cm = cmf & 15;
  /* int cinfo = cmf >> 4; */
  int flg = stbi__zget8(a);
  if ((cmf * 256 + flg) % 31 != 0) return stbi__err("bad zlib header", "Corrupt PNG"); // zlib spec
  if (flg & 32) return stbi__err("no preset dict", "Corrupt PNG"); // preset dictionary not allowed in png
  if (cm != 8) return stbi__err("bad compression", "Corrupt PNG"); // DEFLATE required for png
  // window = 1 << (8 + cinfo)... but who cares, we fully buffer output
  return 1;
}

// @TODO: should statically initialize these for optimal thread safety
static stbi_uc stbi__zdefault_length[288], stbi__zdefault_distance[32];
static void stbi__init_zdefaults(void)
{
  int i;   // use <= to match clearly with spec
  for (i = 0; i <= 143; ++i)     stbi__zdefault_length[i] = 8;
  for (; i <= 255; ++i)     stbi__zdefault_length[i] = 9;
  for (; i <= 279; ++i)     stbi__zdefault_length[i] = 7;
  for (; i <= 287; ++i)     stbi__zdefault_length[i] = 8;

  for (i = 0; i <= 31; ++i)     stbi__zdefault_distance[i] = 5;
}

static int stbi__parse_zlib(stbi__zbuf* a, int parse_header)
{
  int final, type;
  if (parse_header)
    if (!stbi__parse_zlib_header(a)) return 0;
  a->num_bits = 0;
  a->code_buffer = 0;
  do {
    final = stbi__zreceive(a, 1);
    type = stbi__zreceive(a, 2);
    if (type == 0) {
      if (!stbi__parse_uncomperssed_block(a)) return 0;
    }
    else if (type == 3) {
      return 0;
    }
    else {
      if (type == 1) {
        // use fixed code lengths
        if (!stbi__zdefault_distance[31]) stbi__init_zdefaults();
        if (!stbi__zbuild_huffman(&a->z_length, stbi__zdefault_length, 288)) return 0;
        if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0;
      }
      else {
        if (!stbi__compute_huffman_codes(a)) return 0;
      }
      if (!stbi__parse_huffman_block(a)) return 0;
    }
  } while (!final);
  return 1;
}

static int stbi__do_zlib(stbi__zbuf* a, char* obuf, int olen, int exp, int parse_header)
{
  a->zout_start = obuf;
  a->zout = obuf;
  a->zout_end = obuf + olen;
  a->z_expandable = exp;

  return stbi__parse_zlib(a, parse_header);
}

STBIDEF char* stbi_zlib_decode_malloc_guesssize(const char* buffer, int len, int initial_size, int* outlen)
{
  stbi__zbuf a;
  char* p = (char*)stbi__malloc(initial_size);
  if (p == nullptr) return nullptr;
  a.zbuffer = (stbi_uc*)buffer;
  a.zbuffer_end = (stbi_uc*)buffer + len;
  if (stbi__do_zlib(&a, p, initial_size, 1, 1)) {
    if (outlen) *outlen = (int)(a.zout - a.zout_start);
    return a.zout_start;
  }
  else {
    STBI_FREE(a.zout_start);
    return nullptr;
  }
}

STBIDEF char* stbi_zlib_decode_malloc(char const* buffer, int len, int* outlen)
{
  return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen);
}

STBIDEF char* stbi_zlib_decode_malloc_guesssize_headerflag(const char* buffer, int len, int initial_size, int* outlen, int parse_header)
{
  stbi__zbuf a;
  char* p = (char*)stbi__malloc(initial_size);
  if (p == nullptr) return nullptr;
  a.zbuffer = (stbi_uc*)buffer;
  a.zbuffer_end = (stbi_uc*)buffer + len;
  if (stbi__do_zlib(&a, p, initial_size, 1, parse_header)) {
    if (outlen) *outlen = (int)(a.zout - a.zout_start);
    return a.zout_start;
  }
  else {
    STBI_FREE(a.zout_start);
    return nullptr;
  }
}

STBIDEF int stbi_zlib_decode_buffer(char* obuffer, int olen, char const* ibuffer, int ilen)
{
  stbi__zbuf a;
  a.zbuffer = (stbi_uc*)ibuffer;
  a.zbuffer_end = (stbi_uc*)ibuffer + ilen;
  if (stbi__do_zlib(&a, obuffer, olen, 0, 1))
    return (int)(a.zout - a.zout_start);
  else
    return -1;
}

STBIDEF char* stbi_zlib_decode_noheader_malloc(char const* buffer, int len, int* outlen)
{
  stbi__zbuf a;
  char* p = (char*)stbi__malloc(16384);
  if (p == nullptr) return nullptr;
  a.zbuffer = (stbi_uc*)buffer;
  a.zbuffer_end = (stbi_uc*)buffer + len;
  if (stbi__do_zlib(&a, p, 16384, 1, 0)) {
    if (outlen) *outlen = (int)(a.zout - a.zout_start);
    return a.zout_start;
  }
  else {
    STBI_FREE(a.zout_start);
    return nullptr;
  }
}

STBIDEF int stbi_zlib_decode_noheader_buffer(char* obuffer, int olen, const char* ibuffer, int ilen)
{
  stbi__zbuf a;
  a.zbuffer = (stbi_uc*)ibuffer;
  a.zbuffer_end = (stbi_uc*)ibuffer + ilen;
  if (stbi__do_zlib(&a, obuffer, olen, 0, 0))
    return (int)(a.zout - a.zout_start);
  else
    return -1;
}
#endif

// public domain "baseline" PNG decoder   v0.10  Sean Barrett 2006-11-18
//    simple implementation
//      - only 8-bit samples
//      - no CRC checking
//      - allocates lots of intermediate memory
//        - avoids problem of streaming data between subsystems
//        - avoids explicit window management
//    performance
//      - uses stb_zlib, a PD zlib implementation with fast huffman decoding

#ifndef STBI_NO_PNG
typedef struct
{
  stbi__uint32 length;
  stbi__uint32 type;
} stbi__pngchunk;

static stbi__pngchunk stbi__get_chunk_header(stbi__context* s)
{
  stbi__pngchunk c;
  c.length = stbi__get32be(s);
  c.type = stbi__get32be(s);
  return c;
}

static int stbi__check_png_header(stbi__context* s)
{
  static stbi_uc png_sig[8] = { 137,80,78,71,13,10,26,10 };
  int i;
  for (i = 0; i < 8; ++i)
    if (stbi__get8(s) != png_sig[i]) return stbi__err("bad png sig", "Not a PNG");
  return 1;
}

typedef struct
{
  stbi__context* s;
  stbi_uc* idata, * expanded, * out;
} stbi__png;


enum {
  STBI__F_none = 0,
  STBI__F_sub = 1,
  STBI__F_up = 2,
  STBI__F_avg = 3,
  STBI__F_paeth = 4,
  // synthetic filters used for first scanline to avoid needing a dummy row of 0s
  STBI__F_avg_first,
  STBI__F_paeth_first
};

static stbi_uc first_row_filter[5] =
{
   STBI__F_none,
   STBI__F_sub,
   STBI__F_none,
   STBI__F_avg_first,
   STBI__F_paeth_first
};

static int stbi__paeth(int a, int b, int c)
{
  int p = a + b - c;
  int pa = abs(p - a);
  int pb = abs(p - b);
  int pc = abs(p - c);
  if (pa <= pb && pa <= pc) return a;
  if (pb <= pc) return b;
  return c;
}

static stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 };

// create the png data from post-deflated data
static int stbi__create_png_image_raw(stbi__png* a, stbi_uc* raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color)
{
  stbi__context* s = a->s;
  stbi__uint32 i, j, stride = x * out_n;
  stbi__uint32 img_len, img_width_bytes;
  int k;
  int img_n = s->img_n; // copy it into a local for later

  STBI_ASSERT(out_n == s->img_n || out_n == s->img_n + 1);
  a->out = (stbi_uc*)stbi__malloc(x * y * out_n); // extra bytes to write off the end into
  if (!a->out) return stbi__err("outofmem", "Out of memory");

  img_width_bytes = (((img_n * x * depth) + 7) >> 3);
  img_len = (img_width_bytes + 1) * y;
  if (s->img_x == x && s->img_y == y) {
    if (raw_len != img_len) return stbi__err("not enough pixels", "Corrupt PNG");
  }
  else { // interlaced:
    if (raw_len < img_len) return stbi__err("not enough pixels", "Corrupt PNG");
  }

  for (j = 0; j < y; ++j) {
    stbi_uc* cur = a->out + stride * j;
    stbi_uc* prior = cur - stride;
    int filter = *raw++;
    int filter_bytes = img_n;
    int width = x;
    if (filter > 4)
      return stbi__err("invalid filter", "Corrupt PNG");

    if (depth < 8) {
      STBI_ASSERT(img_width_bytes <= x);
      cur += x * out_n - img_width_bytes; // store output to the rightmost img_len bytes, so we can decode in place
      filter_bytes = 1;
      width = img_width_bytes;
    }

    // if first row, use special filter that doesn't sample previous row
    if (j == 0) filter = first_row_filter[filter];

    // handle first byte explicitly
    for (k = 0; k < filter_bytes; ++k) {
      switch (filter) {
      case STBI__F_none: cur[k] = raw[k]; break;
      case STBI__F_sub: cur[k] = raw[k]; break;
      case STBI__F_up: cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break;
      case STBI__F_avg: cur[k] = STBI__BYTECAST(raw[k] + (prior[k] >> 1)); break;
      case STBI__F_paeth: cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(0, prior[k], 0)); break;
      case STBI__F_avg_first: cur[k] = raw[k]; break;
      case STBI__F_paeth_first: cur[k] = raw[k]; break;
      }
    }

    if (depth == 8) {
      if (img_n != out_n)
        cur[img_n] = 255; // first pixel
      raw += img_n;
      cur += out_n;
      prior += out_n;
    }
    else {
      raw += 1;
      cur += 1;
      prior += 1;
    }

    // this is a little gross, so that we don't switch per-pixel or per-component
    if (depth < 8 || img_n == out_n) {
      int nk = (width - 1) * img_n;
#define CASE(f) \
             case f:     \
                for (k=0; k < nk; ++k)
      switch (filter) {
        // "none" filter turns into a memcpy here; make that explicit.
      case STBI__F_none:         memcpy(cur, raw, nk);
        break;
        CASE(STBI__F_sub)          cur[k] = STBI__BYTECAST(raw[k] + cur[k - filter_bytes]);
        break;
        CASE(STBI__F_up)           cur[k] = STBI__BYTECAST(raw[k] + prior[k]);
        break;
        CASE(STBI__F_avg)          cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k - filter_bytes]) >> 1));
        break;
        CASE(STBI__F_paeth)        cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k - filter_bytes], prior[k], prior[k - filter_bytes]));
        break;
        CASE(STBI__F_avg_first)    cur[k] = STBI__BYTECAST(raw[k] + (cur[k - filter_bytes] >> 1));
        break;
        CASE(STBI__F_paeth_first)  cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k - filter_bytes], 0, 0));
        break;
      }
#undef CASE
      raw += nk;
    }
    else {
      STBI_ASSERT(img_n + 1 == out_n);
#define CASE(f) \
             case f:     \
                for (i=x-1; i >= 1; --i, cur[img_n]=255,raw+=img_n,cur+=out_n,prior+=out_n) \
                   for (k=0; k < img_n; ++k)
      switch (filter) {
        CASE(STBI__F_none)         cur[k] = raw[k];
        break;
        CASE(STBI__F_sub)          cur[k] = STBI__BYTECAST(raw[k] + cur[k - out_n]);
        break;
        CASE(STBI__F_up)           cur[k] = STBI__BYTECAST(raw[k] + prior[k]);
        break;
        CASE(STBI__F_avg)          cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k - out_n]) >> 1));
        break;
        CASE(STBI__F_paeth)        cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k - out_n], prior[k], prior[k - out_n]));
        break;
        CASE(STBI__F_avg_first)    cur[k] = STBI__BYTECAST(raw[k] + (cur[k - out_n] >> 1));
        break;
        CASE(STBI__F_paeth_first)  cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k - out_n], 0, 0));
        break;
      }
#undef CASE
    }
  }

  // we make a separate pass to expand bits to pixels; for performance,
  // this could run two scanlines behind the above code, so it won't
  // intefere with filtering but will still be in the cache.
  if (depth < 8) {
    for (j = 0; j < y; ++j) {
      stbi_uc* cur = a->out + stride * j;
      stbi_uc* in = a->out + stride * j + x * out_n - img_width_bytes;
      // unpack 1/2/4-bit into a 8-bit buffer. allows us to keep the common 8-bit path optimal at minimal cost for 1/2/4-bit
      // png guarante byte alignment, if width is not multiple of 8/4/2 we'll decode dummy trailing data that will be skipped in the later loop
      stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range

      // note that the final byte might overshoot and write more data than desired.
      // we can allocate enough data that this never writes out of memory, but it
      // could also overwrite the next scanline. can it overwrite non-empty data
      // on the next scanline? yes, consider 1-pixel-wide scanlines with 1-bit-per-pixel.
      // so we need to explicitly clamp the final ones

      if (depth == 4) {
        for (k = x * img_n; k >= 2; k -= 2, ++in) {
          *cur++ = scale * ((*in >> 4));
          *cur++ = scale * ((*in) & 0x0f);
        }
        if (k > 0) *cur++ = scale * ((*in >> 4));
      }
      else if (depth == 2) {
        for (k = x * img_n; k >= 4; k -= 4, ++in) {
          *cur++ = scale * ((*in >> 6));
          *cur++ = scale * ((*in >> 4) & 0x03);
          *cur++ = scale * ((*in >> 2) & 0x03);
          *cur++ = scale * ((*in) & 0x03);
        }
        if (k > 0) *cur++ = scale * ((*in >> 6));
        if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03);
        if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03);
      }
      else if (depth == 1) {
        for (k = x * img_n; k >= 8; k -= 8, ++in) {
          *cur++ = scale * ((*in >> 7));
          *cur++ = scale * ((*in >> 6) & 0x01);
          *cur++ = scale * ((*in >> 5) & 0x01);
          *cur++ = scale * ((*in >> 4) & 0x01);
          *cur++ = scale * ((*in >> 3) & 0x01);
          *cur++ = scale * ((*in >> 2) & 0x01);
          *cur++ = scale * ((*in >> 1) & 0x01);
          *cur++ = scale * ((*in) & 0x01);
        }
        if (k > 0) *cur++ = scale * ((*in >> 7));
        if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01);
        if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01);
        if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01);
        if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01);
        if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01);
        if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01);
      }
      if (img_n != out_n) {
        // insert alpha = 255
        stbi_uc* cur2 = a->out + stride * j;
        int i2;
        if (img_n == 1) {
          for (i2 = x - 1; i2 >= 0; --i2) {
            cur2[i2 * 2 + 1] = 255;
            cur2[i2 * 2 + 0] = cur2[i2];
          }
        }
        else {
          STBI_ASSERT(img_n == 3);
          for (i2 = x - 1; i2 >= 0; --i2) {
            cur2[i2 * 4 + 3] = 255;
            cur2[i2 * 4 + 2] = cur2[i2 * 3 + 2];
            cur2[i2 * 4 + 1] = cur2[i2 * 3 + 1];
            cur2[i2 * 4 + 0] = cur2[i2 * 3 + 0];
          }
        }
      }
    }
  }

  return 1;
}

static int stbi__create_png_image(stbi__png* a, stbi_uc* image_data, stbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced)
{
  stbi_uc* final;
  int p;
  if (!interlaced)
    return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color);

  // de-interlacing
  final = (stbi_uc*)stbi__malloc(a->s->img_x * a->s->img_y * out_n);
  for (p = 0; p < 7; ++p) {
    int xorig[] = { 0,4,0,2,0,1,0 };
    int yorig[] = { 0,0,4,0,2,0,1 };
    int xspc[] = { 8,8,4,4,2,2,1 };
    int yspc[] = { 8,8,8,4,4,2,2 };
    int i, j, x, y;
    // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1
    x = (a->s->img_x - xorig[p] + xspc[p] - 1) / xspc[p];
    y = (a->s->img_y - yorig[p] + yspc[p] - 1) / yspc[p];
    if (x && y) {
      stbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y;
      if (!stbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) {
        STBI_FREE(final);
        return 0;
      }
      for (j = 0; j < y; ++j) {
        for (i = 0; i < x; ++i) {
          int out_y = j * yspc[p] + yorig[p];
          int out_x = i * xspc[p] + xorig[p];
          memcpy(final + out_y * a->s->img_x * out_n + out_x * out_n,
            a->out + (j * x + i) * out_n, out_n);
        }
      }
      STBI_FREE(a->out);
      image_data += img_len;
      image_data_len -= img_len;
    }
  }
  a->out = final;

  return 1;
}

static int stbi__compute_transparency(stbi__png* z, stbi_uc tc[3], int out_n)
{
  stbi__context* s = z->s;
  stbi__uint32 i, pixel_count = s->img_x * s->img_y;
  stbi_uc* p = z->out;

  // compute color-based transparency, assuming we've
  // already got 255 as the alpha value in the output
  STBI_ASSERT(out_n == 2 || out_n == 4);

  if (out_n == 2) {
    for (i = 0; i < pixel_count; ++i) {
      p[1] = (p[0] == tc[0] ? 0 : 255);
      p += 2;
    }
  }
  else {
    for (i = 0; i < pixel_count; ++i) {
      if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2])
        p[3] = 0;
      p += 4;
    }
  }
  return 1;
}

static int stbi__expand_png_palette(stbi__png* a, stbi_uc* palette, int len, int pal_img_n)
{
  stbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y;
  stbi_uc* p, * temp_out, * orig = a->out;

  p = (stbi_uc*)stbi__malloc(pixel_count * pal_img_n);
  if (p == nullptr) return stbi__err("outofmem", "Out of memory");

  // between here and free(out) below, exitting would leak
  temp_out = p;

  if (pal_img_n == 3) {
    for (i = 0; i < pixel_count; ++i) {
      int n = orig[i] * 4;
      p[0] = palette[n];
      p[1] = palette[n + 1];
      p[2] = palette[n + 2];
      p += 3;
    }
  }
  else {
    for (i = 0; i < pixel_count; ++i) {
      int n = orig[i] * 4;
      p[0] = palette[n];
      p[1] = palette[n + 1];
      p[2] = palette[n + 2];
      p[3] = palette[n + 3];
      p += 4;
    }
  }
  STBI_FREE(a->out);
  a->out = temp_out;

  STBI_NOTUSED(len);

  return 1;
}

static int stbi__unpremultiply_on_load = 0;
static int stbi__de_iphone_flag = 0;

STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply)
{
  stbi__unpremultiply_on_load = flag_true_if_should_unpremultiply;
}

STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert)
{
  stbi__de_iphone_flag = flag_true_if_should_convert;
}

static void stbi__de_iphone(stbi__png* z)
{
  stbi__context* s = z->s;
  stbi__uint32 i, pixel_count = s->img_x * s->img_y;
  stbi_uc* p = z->out;

  if (s->img_out_n == 3) {  // convert bgr to rgb
    for (i = 0; i < pixel_count; ++i) {
      stbi_uc t = p[0];
      p[0] = p[2];
      p[2] = t;
      p += 3;
    }
  }
  else {
    STBI_ASSERT(s->img_out_n == 4);
    if (stbi__unpremultiply_on_load) {
      // convert bgr to rgb and unpremultiply
      for (i = 0; i < pixel_count; ++i) {
        stbi_uc a = p[3];
        stbi_uc t = p[0];
        if (a) {
          p[0] = p[2] * 255 / a;
          p[1] = p[1] * 255 / a;
          p[2] = t * 255 / a;
        }
        else {
          p[0] = p[2];
          p[2] = t;
        }
        p += 4;
      }
    }
    else {
      // convert bgr to rgb
      for (i = 0; i < pixel_count; ++i) {
        stbi_uc t = p[0];
        p[0] = p[2];
        p[2] = t;
        p += 4;
      }
    }
  }
}

#define STBI__PNG_TYPE(a,b,c,d)  (((a) << 24) + ((b) << 16) + ((c) << 8) + (d))

static int stbi__parse_png_file(stbi__png* z, int scan, int req_comp)
{
  stbi_uc palette[1024], pal_img_n = 0;
  stbi_uc has_trans = 0, tc[3];
  stbi__uint32 ioff = 0, idata_limit = 0, i, pal_len = 0;
  int first = 1, k, interlace = 0, color = 0, depth = 0, is_iphone = 0;
  stbi__context* s = z->s;

  z->expanded = nullptr;
  z->idata = nullptr;
  z->out = nullptr;

  if (!stbi__check_png_header(s)) return 0;

  if (scan == STBI__SCAN_type) return 1;

  for (;;) {
    stbi__pngchunk c = stbi__get_chunk_header(s);
    switch (c.type) {
    case STBI__PNG_TYPE('C', 'g', 'B', 'I'):
      is_iphone = 1;
      stbi__skip(s, c.length);
      break;
    case STBI__PNG_TYPE('I', 'H', 'D', 'R'): {
      int comp, filter;
      if (!first) return stbi__err("multiple IHDR", "Corrupt PNG");
      first = 0;
      if (c.length != 13) return stbi__err("bad IHDR len", "Corrupt PNG");
      s->img_x = stbi__get32be(s); if (s->img_x > (1 << 24)) return stbi__err("too large", "Very large image (corrupt?)");
      s->img_y = stbi__get32be(s); if (s->img_y > (1 << 24)) return stbi__err("too large", "Very large image (corrupt?)");
      depth = stbi__get8(s);  if (depth != 1 && depth != 2 && depth != 4 && depth != 8)  return stbi__err("1/2/4/8-bit only", "PNG not supported: 1/2/4/8-bit only");
      color = stbi__get8(s);  if (color > 6)         return stbi__err("bad ctype", "Corrupt PNG");
      if (color == 3) pal_img_n = 3; else if (color & 1) return stbi__err("bad ctype", "Corrupt PNG");
      comp = stbi__get8(s);  if (comp) return stbi__err("bad comp method", "Corrupt PNG");
      filter = stbi__get8(s);  if (filter) return stbi__err("bad filter method", "Corrupt PNG");
      interlace = stbi__get8(s); if (interlace > 1) return stbi__err("bad interlace method", "Corrupt PNG");
      if (!s->img_x || !s->img_y) return stbi__err("0-pixel image", "Corrupt PNG");
      if (!pal_img_n) {
        s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0);
        if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode");
        if (scan == STBI__SCAN_header) return 1;
      }
      else {
        // if paletted, then pal_n is our final components, and
        // img_n is # components to decompress/filter.
        s->img_n = 1;
        if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large", "Corrupt PNG");
        // if SCAN_header, have to scan to see if we have a tRNS
      }
      break;
    }

    case STBI__PNG_TYPE('P', 'L', 'T', 'E'): {
      if (first) return stbi__err("first not IHDR", "Corrupt PNG");
      if (c.length > 256 * 3) return stbi__err("invalid PLTE", "Corrupt PNG");
      pal_len = c.length / 3;
      if (pal_len * 3 != c.length) return stbi__err("invalid PLTE", "Corrupt PNG");
      for (i = 0; i < pal_len; ++i) {
        palette[i * 4 + 0] = stbi__get8(s);
        palette[i * 4 + 1] = stbi__get8(s);
        palette[i * 4 + 2] = stbi__get8(s);
        palette[i * 4 + 3] = 255;
      }
      break;
    }

    case STBI__PNG_TYPE('t', 'R', 'N', 'S'): {
      if (first) return stbi__err("first not IHDR", "Corrupt PNG");
      if (z->idata) return stbi__err("tRNS after IDAT", "Corrupt PNG");
      if (pal_img_n) {
        if (scan == STBI__SCAN_header) { s->img_n = 4; return 1; }
        if (pal_len == 0) return stbi__err("tRNS before PLTE", "Corrupt PNG");
        if (c.length > pal_len) return stbi__err("bad tRNS len", "Corrupt PNG");
        pal_img_n = 4;
        for (i = 0; i < c.length; ++i)
          palette[i * 4 + 3] = stbi__get8(s);
      }
      else {
        if (!(s->img_n & 1)) return stbi__err("tRNS with alpha", "Corrupt PNG");
        if (c.length != (stbi__uint32)s->img_n * 2) return stbi__err("bad tRNS len", "Corrupt PNG");
        has_trans = 1;
        for (k = 0; k < s->img_n; ++k)
          tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[depth]; // non 8-bit images will be larger
      }
      break;
    }

    case STBI__PNG_TYPE('I', 'D', 'A', 'T'): {
      if (first) return stbi__err("first not IHDR", "Corrupt PNG");
      if (pal_img_n && !pal_len) return stbi__err("no PLTE", "Corrupt PNG");
      if (scan == STBI__SCAN_header) { s->img_n = pal_img_n; return 1; }
      if ((int)(ioff + c.length) < (int)ioff) return 0;
      if (ioff + c.length > idata_limit) {
        stbi_uc* p;
        if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096;
        while (ioff + c.length > idata_limit)
          idata_limit *= 2;
        p = (stbi_uc*)STBI_REALLOC(z->idata, idata_limit); if (p == nullptr) return stbi__err("outofmem", "Out of memory");
        z->idata = p;
      }
      if (!stbi__getn(s, z->idata + ioff, c.length)) return stbi__err("outofdata", "Corrupt PNG");
      ioff += c.length;
      break;
    }

    case STBI__PNG_TYPE('I', 'E', 'N', 'D'): {
      stbi__uint32 raw_len, bpl;
      if (first) return stbi__err("first not IHDR", "Corrupt PNG");
      if (scan != STBI__SCAN_load) return 1;
      if (z->idata == nullptr) return stbi__err("no IDAT", "Corrupt PNG");
      // initial guess for decoded data size to avoid unnecessary reallocs
      bpl = (s->img_x * depth + 7) / 8; // bytes per line, per component
      raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */;
      z->expanded = (stbi_uc*)stbi_zlib_decode_malloc_guesssize_headerflag((char*)z->idata, ioff, raw_len, (int*)&raw_len, !is_iphone);
      if (z->expanded == nullptr) return 0; // zlib should set error
      STBI_FREE(z->idata); z->idata = nullptr;
      if ((req_comp == s->img_n + 1 && req_comp != 3 && !pal_img_n) || has_trans)
        s->img_out_n = s->img_n + 1;
      else
        s->img_out_n = s->img_n;
      if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, depth, color, interlace)) return 0;
      if (has_trans)
        if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0;
      if (is_iphone && stbi__de_iphone_flag && s->img_out_n > 2)
        stbi__de_iphone(z);
      if (pal_img_n) {
        // pal_img_n == 3 or 4
        s->img_n = pal_img_n; // record the actual colors we had
        s->img_out_n = pal_img_n;
        if (req_comp >= 3) s->img_out_n = req_comp;
        if (!stbi__expand_png_palette(z, palette, pal_len, s->img_out_n))
          return 0;
      }
      STBI_FREE(z->expanded); z->expanded = nullptr;
      return 1;
    }

    default:
      // if critical, fail
      if (first) return stbi__err("first not IHDR", "Corrupt PNG");
      if ((c.type & (1 << 29)) == 0) {
#ifndef STBI_NO_FAILURE_STRINGS
        // not threadsafe
        static char invalid_chunk[] = "XXXX PNG chunk not known";
        invalid_chunk[0] = STBI__BYTECAST(c.type >> 24);
        invalid_chunk[1] = STBI__BYTECAST(c.type >> 16);
        invalid_chunk[2] = STBI__BYTECAST(c.type >> 8);
        invalid_chunk[3] = STBI__BYTECAST(c.type >> 0);
#endif
        return stbi__err(invalid_chunk, "PNG not supported: unknown PNG chunk type");
      }
      stbi__skip(s, c.length);
      break;
    }
    // end of PNG chunk, read and skip CRC
    stbi__get32be(s);
  }
}

static unsigned char* stbi__do_png(stbi__png* p, int* x, int* y, int* n, int req_comp)
{
  unsigned char* result = nullptr;
  if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error");
  if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) {
    result = p->out;
    p->out = nullptr;
    if (req_comp && req_comp != p->s->img_out_n) {
      result = stbi__convert_format(result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y);
      p->s->img_out_n = req_comp;
      if (result == nullptr) return result;
    }
    *x = p->s->img_x;
    *y = p->s->img_y;
    if (n) *n = p->s->img_out_n;
  }
  STBI_FREE(p->out);      p->out = nullptr;
  STBI_FREE(p->expanded); p->expanded = nullptr;
  STBI_FREE(p->idata);    p->idata = nullptr;

  return result;
}

static unsigned char* stbi__png_load(stbi__context* s, int* x, int* y, int* comp, int req_comp)
{
  stbi__png p;
  p.s = s;
  return stbi__do_png(&p, x, y, comp, req_comp);
}

static int stbi__png_test(stbi__context* s)
{
  int r;
  r = stbi__check_png_header(s);
  stbi__rewind(s);
  return r;
}

static int stbi__png_info_raw(stbi__png* p, int* x, int* y, int* comp)
{
  if (!stbi__parse_png_file(p, STBI__SCAN_header, 0)) {
    stbi__rewind(p->s);
    return 0;
  }
  if (x) *x = p->s->img_x;
  if (y) *y = p->s->img_y;
  if (comp) *comp = p->s->img_n;
  return 1;
}

static int stbi__png_info(stbi__context* s, int* x, int* y, int* comp)
{
  stbi__png p;
  p.s = s;
  return stbi__png_info_raw(&p, x, y, comp);
}
#endif

// Microsoft/Windows BMP image

#ifndef STBI_NO_BMP
static int stbi__bmp_test_raw(stbi__context* s)
{
  int r;
  int sz;
  if (stbi__get8(s) != 'B') return 0;
  if (stbi__get8(s) != 'M') return 0;
  stbi__get32le(s); // discard filesize
  stbi__get16le(s); // discard reserved
  stbi__get16le(s); // discard reserved
  stbi__get32le(s); // discard data offset
  sz = stbi__get32le(s);
  r = (sz == 12 || sz == 40 || sz == 56 || sz == 108 || sz == 124);
  return r;
}

static int stbi__bmp_test(stbi__context* s)
{
  int r = stbi__bmp_test_raw(s);
  stbi__rewind(s);
  return r;
}


// returns 0..31 for the highest set bit
static int stbi__high_bit(unsigned int z)
{
  int n = 0;
  if (z == 0) return -1;
  if (z >= 0x10000) n += 16, z >>= 16;
  if (z >= 0x00100) n += 8, z >>= 8;
  if (z >= 0x00010) n += 4, z >>= 4;
  if (z >= 0x00004) n += 2, z >>= 2;
  if (z >= 0x00002) n += 1, z >>= 1;
  return n;
}

static int stbi__bitcount(unsigned int a)
{
  a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2
  a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4
  a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits
  a = (a + (a >> 8)); // max 16 per 8 bits
  a = (a + (a >> 16)); // max 32 per 8 bits
  return a & 0xff;
}

static int stbi__shiftsigned(int v, int shift, int bits)
{
  int result;
  int z = 0;

  if (shift < 0) v <<= -shift;
  else v >>= shift;
  result = v;

  z = bits;
  while (z < 8) {
    result += v >> z;
    z += bits;
  }
  return result;
}

static stbi_uc* stbi__bmp_load(stbi__context* s, int* x, int* y, int* comp, int req_comp)
{
  stbi_uc* out;
  unsigned int mr = 0, mg = 0, mb = 0, ma = 0, fake_a = 0;
  stbi_uc pal[256][4];
  int psize = 0, i, j, compress = 0, width;
  int bpp, flip_vertically, pad, target, offset, hsz;
  if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') return stbi__errpuc("not BMP", "Corrupt BMP");
  stbi__get32le(s); // discard filesize
  stbi__get16le(s); // discard reserved
  stbi__get16le(s); // discard reserved
  offset = stbi__get32le(s);
  hsz = stbi__get32le(s);
  if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) return stbi__errpuc("unknown BMP", "BMP type not supported: unknown");
  if (hsz == 12) {
    s->img_x = stbi__get16le(s);
    s->img_y = stbi__get16le(s);
  }
  else {
    s->img_x = stbi__get32le(s);
    s->img_y = stbi__get32le(s);
  }
  if (stbi__get16le(s) != 1) return stbi__errpuc("bad BMP", "bad BMP");
  bpp = stbi__get16le(s);
  if (bpp == 1) return stbi__errpuc("monochrome", "BMP type not supported: 1-bit");
  flip_vertically = ((int)s->img_y) > 0;
  s->img_y = abs((int)s->img_y);
  if (hsz == 12) {
    if (bpp < 24)
      psize = (offset - 14 - 24) / 3;
  }
  else {
    compress = stbi__get32le(s);
    if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE");
    stbi__get32le(s); // discard sizeof
    stbi__get32le(s); // discard hres
    stbi__get32le(s); // discard vres
    stbi__get32le(s); // discard colorsused
    stbi__get32le(s); // discard max important
    if (hsz == 40 || hsz == 56) {
      if (hsz == 56) {
        stbi__get32le(s);
        stbi__get32le(s);
        stbi__get32le(s);
        stbi__get32le(s);
      }
      if (bpp == 16 || bpp == 32) {
        mr = mg = mb = 0;
        if (compress == 0) {
          if (bpp == 32) {
            mr = 0xffu << 16;
            mg = 0xffu << 8;
            mb = 0xffu << 0;
            ma = 0xffu << 24;
            fake_a = 1; // @TODO: check for cases like alpha value is all 0 and switch it to 255
            STBI_NOTUSED(fake_a);
          }
          else {
            mr = 31u << 10;
            mg = 31u << 5;
            mb = 31u << 0;
          }
        }
        else if (compress == 3) {
          mr = stbi__get32le(s);
          mg = stbi__get32le(s);
          mb = stbi__get32le(s);
          // not documented, but generated by photoshop and handled by mspaint
          if (mr == mg && mg == mb) {
            // ?!?!?
            return stbi__errpuc("bad BMP", "bad BMP");
          }
        }
        else
          return stbi__errpuc("bad BMP", "bad BMP");
      }
    }
    else {
      STBI_ASSERT(hsz == 108 || hsz == 124);
      mr = stbi__get32le(s);
      mg = stbi__get32le(s);
      mb = stbi__get32le(s);
      ma = stbi__get32le(s);
      stbi__get32le(s); // discard color space
      for (i = 0; i < 12; ++i)
        stbi__get32le(s); // discard color space parameters
      if (hsz == 124) {
        stbi__get32le(s); // discard rendering intent
        stbi__get32le(s); // discard offset of profile data
        stbi__get32le(s); // discard size of profile data
        stbi__get32le(s); // discard reserved
      }
    }
    if (bpp < 16)
      psize = (offset - 14 - hsz) >> 2;
  }
  s->img_n = ma ? 4 : 3;
  if (req_comp && req_comp >= 3) // we can directly decode 3 or 4
    target = req_comp;
  else
    target = s->img_n; // if they want monochrome, we'll post-convert
  out = (stbi_uc*)stbi__malloc(target * s->img_x * s->img_y);
  if (!out) return stbi__errpuc("outofmem", "Out of memory");
  if (bpp < 16) {
    int z = 0;
    if (psize == 0 || psize > 256) { STBI_FREE(out); return stbi__errpuc("invalid", "Corrupt BMP"); }
    for (i = 0; i < psize; ++i) {
      pal[i][2] = stbi__get8(s);
      pal[i][1] = stbi__get8(s);
      pal[i][0] = stbi__get8(s);
      if (hsz != 12) stbi__get8(s);
      pal[i][3] = 255;
    }
    stbi__skip(s, offset - 14 - hsz - psize * (hsz == 12 ? 3 : 4));
    if (bpp == 4) width = (s->img_x + 1) >> 1;
    else if (bpp == 8) width = s->img_x;
    else { STBI_FREE(out); return stbi__errpuc("bad bpp", "Corrupt BMP"); }
    pad = (-width) & 3;
    for (j = 0; j < (int)s->img_y; ++j) {
      for (i = 0; i < (int)s->img_x; i += 2) {
        int v = stbi__get8(s), v2 = 0;
        if (bpp == 4) {
          v2 = v & 15;
          v >>= 4;
        }
        out[z++] = pal[v][0];
        out[z++] = pal[v][1];
        out[z++] = pal[v][2];
        if (target == 4) out[z++] = 255;
        if (i + 1 == (int)s->img_x) break;
        v = (bpp == 8) ? stbi__get8(s) : v2;
        out[z++] = pal[v][0];
        out[z++] = pal[v][1];
        out[z++] = pal[v][2];
        if (target == 4) out[z++] = 255;
      }
      stbi__skip(s, pad);
    }
  }
  else {
    int rshift = 0, gshift = 0, bshift = 0, ashift = 0, rcount = 0, gcount = 0, bcount = 0, acount = 0;
    int z = 0;
    int easy = 0;
    stbi__skip(s, offset - 14 - hsz);
    if (bpp == 24) width = 3 * s->img_x;
    else if (bpp == 16) width = 2 * s->img_x;
    else /* bpp = 32 and pad = 0 */ width = 0;
    pad = (-width) & 3;
    if (bpp == 24) {
      easy = 1;
    }
    else if (bpp == 32) {
      if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000)
        easy = 2;
    }
    if (!easy) {
      if (!mr || !mg || !mb) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); }
      // right shift amt to put high bit in position #7
      rshift = stbi__high_bit(mr) - 7; rcount = stbi__bitcount(mr);
      gshift = stbi__high_bit(mg) - 7; gcount = stbi__bitcount(mg);
      bshift = stbi__high_bit(mb) - 7; bcount = stbi__bitcount(mb);
      ashift = stbi__high_bit(ma) - 7; acount = stbi__bitcount(ma);
    }
    for (j = 0; j < (int)s->img_y; ++j) {
      if (easy) {
        for (i = 0; i < (int)s->img_x; ++i) {
          unsigned char a;
          out[z + 2] = stbi__get8(s);
          out[z + 1] = stbi__get8(s);
          out[z + 0] = stbi__get8(s);
          z += 3;
          a = (easy == 2 ? stbi__get8(s) : 255);
          if (target == 4) out[z++] = a;
        }
      }
      else {
        for (i = 0; i < (int)s->img_x; ++i) {
          stbi__uint32 v = (bpp == 16 ? (stbi__uint32)stbi__get16le(s) : stbi__get32le(s));
          int a;
          out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mr, rshift, rcount));
          out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount));
          out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mb, bshift, bcount));
          a = (ma ? stbi__shiftsigned(v & ma, ashift, acount) : 255);
          if (target == 4) out[z++] = STBI__BYTECAST(a);
        }
      }
      stbi__skip(s, pad);
    }
  }
  if (flip_vertically) {
    stbi_uc t;
    for (j = 0; j < (int)s->img_y >> 1; ++j) {
      stbi_uc* p1 = out + j * s->img_x * target;
      stbi_uc* p2 = out + (s->img_y - 1 - j) * s->img_x * target;
      for (i = 0; i < (int)s->img_x * target; ++i) {
        t = p1[i], p1[i] = p2[i], p2[i] = t;
      }
    }
  }

  if (req_comp && req_comp != target) {
    out = stbi__convert_format(out, target, req_comp, s->img_x, s->img_y);
    if (out == nullptr) return out; // stbi__convert_format frees input on failure
  }

  *x = s->img_x;
  *y = s->img_y;
  if (comp) *comp = s->img_n;
  return out;
}
#endif

// Targa Truevision - TGA
// by Jonathan Dummer
#ifndef STBI_NO_TGA
static int stbi__tga_info(stbi__context* s, int* x, int* y, int* comp)
{
  int tga_w, tga_h, tga_comp;
  int sz;
  stbi__get8(s);                   // discard Offset
  sz = stbi__get8(s);              // color type
  if (sz > 1) {
    stbi__rewind(s);
    return 0;      // only RGB or indexed allowed
  }
  sz = stbi__get8(s);              // image type
  // only RGB or grey allowed, +/- RLE
  if ((sz != 1) && (sz != 2) && (sz != 3) && (sz != 9) && (sz != 10) && (sz != 11)) return 0;
  stbi__skip(s, 9);
  tga_w = stbi__get16le(s);
  if (tga_w < 1) {
    stbi__rewind(s);
    return 0;   // test width
  }
  tga_h = stbi__get16le(s);
  if (tga_h < 1) {
    stbi__rewind(s);
    return 0;   // test height
  }
  sz = stbi__get8(s);               // bits per pixel
  // only RGB or RGBA or grey allowed
  if ((sz != 8) && (sz != 16) && (sz != 24) && (sz != 32)) {
    stbi__rewind(s);
    return 0;
  }
  tga_comp = sz;
  if (x) *x = tga_w;
  if (y) *y = tga_h;
  if (comp) *comp = tga_comp / 8;
  return 1;                   // seems to have passed everything
}

static int stbi__tga_test(stbi__context* s)
{
  int res;
  int sz;
  stbi__get8(s);      //   discard Offset
  sz = stbi__get8(s);   //   color type
  if (sz > 1) return 0;   //   only RGB or indexed allowed
  sz = stbi__get8(s);   //   image type
  if ((sz != 1) && (sz != 2) && (sz != 3) && (sz != 9) && (sz != 10) && (sz != 11)) return 0;   //   only RGB or grey allowed, +/- RLE
  stbi__get16be(s);      //   discard palette start
  stbi__get16be(s);      //   discard palette length
  stbi__get8(s);         //   discard bits per palette color entry
  stbi__get16be(s);      //   discard x origin
  stbi__get16be(s);      //   discard y origin
  if (stbi__get16be(s) < 1) return 0;      //   test width
  if (stbi__get16be(s) < 1) return 0;      //   test height
  sz = stbi__get8(s);   //   bits per pixel
  if ((sz != 8) && (sz != 16) && (sz != 24) && (sz != 32))
    res = 0;
  else
    res = 1;
  stbi__rewind(s);
  return res;
}

static stbi_uc* stbi__tga_load(stbi__context* s, int* x, int* y, int* comp, int req_comp)
{
  //   read in the TGA header stuff
  int tga_offset = stbi__get8(s);
  int tga_indexed = stbi__get8(s);
  int tga_image_type = stbi__get8(s);
  int tga_is_RLE = 0;
  int tga_palette_start = stbi__get16le(s);
  int tga_palette_len = stbi__get16le(s);
  int tga_palette_bits = stbi__get8(s);
  int tga_x_origin = stbi__get16le(s);
  int tga_y_origin = stbi__get16le(s);
  int tga_width = stbi__get16le(s);
  int tga_height = stbi__get16le(s);
  int tga_bits_per_pixel = stbi__get8(s);
  int tga_comp = tga_bits_per_pixel / 8;
  int tga_inverted = stbi__get8(s);
  //   image data
  unsigned char* tga_data;
  unsigned char* tga_palette = nullptr;
  int i, j;
  unsigned char raw_data[4];
  int RLE_count = 0;
  int RLE_repeating = 0;
  int read_next_pixel = 1;

  //   do a tiny bit of precessing
  if (tga_image_type >= 8)
  {
    tga_image_type -= 8;
    tga_is_RLE = 1;
  }
  /* int tga_alpha_bits = tga_inverted & 15; */
  tga_inverted = 1 - ((tga_inverted >> 5) & 1);

  //   error check
  if ( //(tga_indexed) ||
    (tga_width < 1) || (tga_height < 1) ||
    (tga_image_type < 1) || (tga_image_type > 3) ||
    ((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16) &&
      (tga_bits_per_pixel != 24) && (tga_bits_per_pixel != 32))
    )
  {
    return nullptr; // we don't report this as a bad TGA because we don't even know if it's TGA
  }

  //   If I'm paletted, then I'll use the number of bits from the palette
  if (tga_indexed)
  {
    tga_comp = tga_palette_bits / 8;
  }

  //   tga info
  *x = tga_width;
  *y = tga_height;
  if (comp) *comp = tga_comp;

  tga_data = (unsigned char*)stbi__malloc((size_t)tga_width * tga_height * tga_comp);
  if (!tga_data) return stbi__errpuc("outofmem", "Out of memory");

  // skip to the data's starting position (offset usually = 0)
  stbi__skip(s, tga_offset);

  if (!tga_indexed && !tga_is_RLE) {
    for (i = 0; i < tga_height; ++i) {
      int y2 = tga_inverted ? tga_height - i - 1 : i;
      stbi_uc* tga_row = tga_data + y2 * tga_width * tga_comp;
      stbi__getn(s, tga_row, tga_width * tga_comp);
    }
  }
  else {
    //   do I need to load a palette?
    if (tga_indexed)
    {
      //   any data to skip? (offset usually = 0)
      stbi__skip(s, tga_palette_start);
      //   load the palette
      tga_palette = (unsigned char*)stbi__malloc(tga_palette_len * tga_palette_bits / 8);
      if (!tga_palette) {
        STBI_FREE(tga_data);
        return stbi__errpuc("outofmem", "Out of memory");
      }
      if (!stbi__getn(s, tga_palette, tga_palette_len * tga_palette_bits / 8)) {
        STBI_FREE(tga_data);
        STBI_FREE(tga_palette);
        return stbi__errpuc("bad palette", "Corrupt TGA");
      }
    }
    //   load the data
    for (i = 0; i < tga_width * tga_height; ++i)
    {
      //   if I'm in RLE mode, do I need to get a RLE stbi__pngchunk?
      if (tga_is_RLE)
      {
        if (RLE_count == 0)
        {
          //   yep, get the next byte as a RLE command
          int RLE_cmd = stbi__get8(s);
          RLE_count = 1 + (RLE_cmd & 127);
          RLE_repeating = RLE_cmd >> 7;
          read_next_pixel = 1;
        }
        else if (!RLE_repeating)
        {
          read_next_pixel = 1;
        }
      }
      else
      {
        read_next_pixel = 1;
      }
      //   OK, if I need to read a pixel, do it now
      if (read_next_pixel)
      {
        //   load however much data we did have
        if (tga_indexed)
        {
          //   read in 1 byte, then perform the lookup
          int pal_idx = stbi__get8(s);
          if (pal_idx >= tga_palette_len)
          {
            //   invalid index
            pal_idx = 0;
          }
          pal_idx *= tga_bits_per_pixel / 8;
          for (j = 0; j * 8 < tga_bits_per_pixel; ++j)
          {
            raw_data[j] = tga_palette[pal_idx + j];
          }
        }
        else
        {
          //   read in the data raw
          for (j = 0; j * 8 < tga_bits_per_pixel; ++j)
          {
            raw_data[j] = stbi__get8(s);
          }
        }
        //   clear the reading flag for the next pixel
        read_next_pixel = 0;
      } // end of reading a pixel

      // copy data
      for (j = 0; j < tga_comp; ++j)
        tga_data[i * tga_comp + j] = raw_data[j];

      //   in case we're in RLE mode, keep counting down
      --RLE_count;
    }
    //   do I need to invert the image?
    if (tga_inverted)
    {
      for (j = 0; j * 2 < tga_height; ++j)
      {
        int index1 = j * tga_width * tga_comp;
        int index2 = (tga_height - 1 - j) * tga_width * tga_comp;
        for (i = tga_width * tga_comp; i > 0; --i)
        {
          unsigned char temp = tga_data[index1];
          tga_data[index1] = tga_data[index2];
          tga_data[index2] = temp;
          ++index1;
          ++index2;
        }
      }
    }
    //   clear my palette, if I had one
    if (tga_palette != nullptr)
    {
      STBI_FREE(tga_palette);
    }
  }

  // swap RGB
  if (tga_comp >= 3)
  {
    unsigned char* tga_pixel = tga_data;
    for (i = 0; i < tga_width * tga_height; ++i)
    {
      unsigned char temp = tga_pixel[0];
      tga_pixel[0] = tga_pixel[2];
      tga_pixel[2] = temp;
      tga_pixel += tga_comp;
    }
  }

  // convert to target component count
  if (req_comp && req_comp != tga_comp)
    tga_data = stbi__convert_format(tga_data, tga_comp, req_comp, tga_width, tga_height);

  //   the things I do to get rid of an error message, and yet keep
  //   Microsoft's C compilers happy... [8^(
  tga_palette_start = tga_palette_len = tga_palette_bits =
    tga_x_origin = tga_y_origin = 0;
  //   OK, done
  return tga_data;
}
#endif

// *************************************************************************************************
// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicolas Schulz, tweaked by STB

#ifndef STBI_NO_PSD
static int stbi__psd_test(stbi__context* s)
{
  int r = (stbi__get32be(s) == 0x38425053);
  stbi__rewind(s);
  return r;
}

static stbi_uc* stbi__psd_load(stbi__context* s, int* x, int* y, int* comp, int req_comp)
{
  int   pixelCount;
  int channelCount, compression;
  int channel, i, count, len;
  int w, h;
  stbi_uc* out;

  // Check identifier
  if (stbi__get32be(s) != 0x38425053)   // "8BPS"
    return stbi__errpuc("not PSD", "Corrupt PSD image");

  // Check file type version.
  if (stbi__get16be(s) != 1)
    return stbi__errpuc("wrong version", "Unsupported version of PSD image");

  // Skip 6 reserved bytes.
  stbi__skip(s, 6);

  // Read the number of channels (R, G, B, A, etc).
  channelCount = stbi__get16be(s);
  if (channelCount < 0 || channelCount > 16)
    return stbi__errpuc("wrong channel count", "Unsupported number of channels in PSD image");

  // Read the rows and columns of the image.
  h = stbi__get32be(s);
  w = stbi__get32be(s);

  // Make sure the depth is 8 bits.
  if (stbi__get16be(s) != 8)
    return stbi__errpuc("unsupported bit depth", "PSD bit depth is not 8 bit");

  // Make sure the color mode is RGB.
  // Valid options are:
  //   0: Bitmap
  //   1: Grayscale
  //   2: Indexed color
  //   3: RGB color
  //   4: CMYK color
  //   7: Multichannel
  //   8: Duotone
  //   9: Lab color
  if (stbi__get16be(s) != 3)
    return stbi__errpuc("wrong color format", "PSD is not in RGB color format");

  // Skip the Mode Data.  (It's the palette for indexed color; other info for other modes.)
  stbi__skip(s, stbi__get32be(s));

  // Skip the image resources.  (resolution, pen tool paths, etc)
  stbi__skip(s, stbi__get32be(s));

  // Skip the reserved data.
  stbi__skip(s, stbi__get32be(s));

  // Find out if the data is compressed.
  // Known values:
  //   0: no compression
  //   1: RLE compressed
  compression = stbi__get16be(s);
  if (compression > 1)
    return stbi__errpuc("bad compression", "PSD has an unknown compression format");

  // Create the destination image.
  out = (stbi_uc*)stbi__malloc(channelCount * w * h);
  if (!out) return stbi__errpuc("outofmem", "Out of memory");
  pixelCount = w * h;

  // Initialize the data to zero.
  //memset( out, 0, pixelCount * 4 );

  // Finally, the image data.
  if (compression) {
    // RLE as used by .PSD and .TIFF
    // Loop until you get the number of unpacked bytes you are expecting:
    //     Read the next source byte into n.
    //     If n is between 0 and 127 inclusive, copy the next n+1 bytes literally.
    //     Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times.
    //     Else if n is 128, noop.
    // Endloop

    // The RLE-compressed data is preceeded by a 2-byte data count for each row in the data,
    // which we're going to just skip.
    stbi__skip(s, h * channelCount * 2);

    // Read the RLE data by channel.
    for (channel = 0; channel < channelCount; channel++) {
      stbi_uc* p;

      p = out + channel;
      if (channel >= channelCount) {
        // Fill this channel with default data.
        for (i = 0; i < pixelCount; i++) *p = (channel == 3 ? 255 : 0), p += channelCount;
      }
      else {
        // Read the RLE data.
        count = 0;
        while (count < pixelCount) {
          len = stbi__get8(s);
          if (len == 128) {
            // No-op.
          }
          else if (len < 128) {
            // Copy next len+1 bytes literally.
            len++;
            count += len;
            while (len) {
              *p = stbi__get8(s);
              p += channelCount;
              len--;
            }
          }
          else if (len > 128) {
            stbi_uc   val;
            // Next -len+1 bytes in the dest are replicated from next source byte.
            // (Interpret len as a negative 8-bit int.)
            len ^= 0x0FF;
            len += 2;
            val = stbi__get8(s);
            count += len;
            while (len) {
              *p = val;
              p += channelCount;
              len--;
            }
          }
        }
      }
    }

  }
  else {
    // We're at the raw image data.  It's each channel in order (Red, Green, Blue, Alpha, ...)
    // where each channel consists of an 8-bit value for each pixel in the image.

    // Read the data by channel.
    for (channel = 0; channel < channelCount; channel++) {
      stbi_uc* p;

      p = out + channel;
      if (channel > channelCount) {
        // Fill this channel with default data.
        for (i = 0; i < pixelCount; i++) *p = channel == 3 ? 255 : 0, p += channelCount;
      }
      else {
        // Read the data.
        for (i = 0; i < pixelCount; i++)
          *p = stbi__get8(s), p += channelCount;
      }
    }
  }

  if (req_comp && req_comp != channelCount) {
    out = stbi__convert_format(out, channelCount, req_comp, w, h);
    if (out == nullptr) return out; // stbi__convert_format frees input on failure
  }

  if (comp) *comp = channelCount;
  *y = h;
  *x = w;

  return out;
}
#endif

// *************************************************************************************************
// Softimage PIC loader
// by Tom Seddon
//
// See http://softimage.wiki.softimage.com/index.php/INFO:_PIC_file_format
// See http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/softimagepic/

#ifndef STBI_NO_PIC
static int stbi__pic_is4(stbi__context* s, const char* str)
{
  int i;
  for (i = 0; i < 4; ++i)
    if (stbi__get8(s) != (stbi_uc)str[i])
      return 0;

  return 1;
}

static int stbi__pic_test_core(stbi__context* s)
{
  int i;

  if (!stbi__pic_is4(s, "\x53\x80\xF6\x34"))
    return 0;

  for (i = 0; i < 84; ++i)
    stbi__get8(s);

  if (!stbi__pic_is4(s, "PICT"))
    return 0;

  return 1;
}

typedef struct
{
  stbi_uc size, type, channel;
} stbi__pic_packet;

static stbi_uc* stbi__readval(stbi__context* s, int channel, stbi_uc* dest)
{
  int mask = 0x80, i;

  for (i = 0; i < 4; ++i, mask >>= 1) {
    if (channel & mask) {
      if (stbi__at_eof(s)) return stbi__errpuc("bad file", "PIC file too short");
      dest[i] = stbi__get8(s);
    }
  }

  return dest;
}

static void stbi__copyval(int channel, stbi_uc* dest, const stbi_uc* src)
{
  int mask = 0x80, i;

  for (i = 0; i < 4; ++i, mask >>= 1)
    if (channel & mask)
      dest[i] = src[i];
}

static stbi_uc* stbi__pic_load_core(stbi__context* s, int width, int height, int* comp, stbi_uc* result)
{
  int act_comp = 0, num_packets = 0, y, chained;
  stbi__pic_packet packets[10];

  // this will (should...) cater for even some bizarre stuff like having data
   // for the same channel in multiple packets.
  do {
    stbi__pic_packet* packet;

    if (num_packets == sizeof(packets) / sizeof(packets[0]))
      return stbi__errpuc("bad format", "too many packets");

    packet = &packets[num_packets++];

    chained = stbi__get8(s);
    packet->size = stbi__get8(s);
    packet->type = stbi__get8(s);
    packet->channel = stbi__get8(s);

    act_comp |= packet->channel;

    if (stbi__at_eof(s))          return stbi__errpuc("bad file", "file too short (reading packets)");
    if (packet->size != 8)  return stbi__errpuc("bad format", "packet isn't 8bpp");
  } while (chained);

  *comp = (act_comp & 0x10 ? 4 : 3); // has alpha channel?

  for (y = 0; y < height; ++y) {
    int packet_idx;

    for (packet_idx = 0; packet_idx < num_packets; ++packet_idx) {
      stbi__pic_packet* packet = &packets[packet_idx];
      stbi_uc* dest = result + y * width * 4;

      switch (packet->type) {
      default:
        return stbi__errpuc("bad format", "packet has bad compression type");

      case 0: {//uncompressed
        int x;

        for (x = 0; x < width; ++x, dest += 4)
          if (!stbi__readval(s, packet->channel, dest))
            return 0;
        break;
      }

      case 1://Pure RLE
      {
        int left = width, i;

        while (left > 0) {
          stbi_uc count, value[4];

          count = stbi__get8(s);
          if (stbi__at_eof(s))   return stbi__errpuc("bad file", "file too short (pure read count)");

          if (count > left)
            count = (stbi_uc)left;

          if (!stbi__readval(s, packet->channel, value))  return 0;

          for (i = 0; i < count; ++i, dest += 4)
            stbi__copyval(packet->channel, dest, value);
          left -= count;
        }
      }
      break;

      case 2: {//Mixed RLE
        int left = width;
        while (left > 0) {
          int count = stbi__get8(s), i;
          if (stbi__at_eof(s))  return stbi__errpuc("bad file", "file too short (mixed read count)");

          if (count >= 128) { // Repeated
            stbi_uc value[4];

            if (count == 128)
              count = stbi__get16be(s);
            else
              count -= 127;
            if (count > left)
              return stbi__errpuc("bad file", "scanline overrun");

            if (!stbi__readval(s, packet->channel, value))
              return 0;

            for (i = 0; i < count; ++i, dest += 4)
              stbi__copyval(packet->channel, dest, value);
          }
          else { // Raw
            ++count;
            if (count > left) return stbi__errpuc("bad file", "scanline overrun");

            for (i = 0; i < count; ++i, dest += 4)
              if (!stbi__readval(s, packet->channel, dest))
                return 0;
          }
          left -= count;
        }
        break;
      }
      }
    }
  }

  return result;
}

static stbi_uc* stbi__pic_load(stbi__context* s, int* px, int* py, int* comp, int req_comp)
{
  stbi_uc* result;
  int i, x, y;

  for (i = 0; i < 92; ++i)
    stbi__get8(s);

  x = stbi__get16be(s);
  y = stbi__get16be(s);
  if (stbi__at_eof(s))  return stbi__errpuc("bad file", "file too short (pic header)");
  if ((1 << 28) / x < y) return stbi__errpuc("too large", "Image too large to decode");

  stbi__get32be(s); //skip `ratio'
  stbi__get16be(s); //skip `fields'
  stbi__get16be(s); //skip `pad'

  // intermediate buffer is RGBA
  result = (stbi_uc*)stbi__malloc(x * y * 4);
  memset(result, 0xff, x * y * 4);

  if (!stbi__pic_load_core(s, x, y, comp, result)) {
    STBI_FREE(result);
    result = 0;
  }
  *px = x;
  *py = y;
  if (req_comp == 0) req_comp = *comp;
  result = stbi__convert_format(result, 4, req_comp, x, y);

  return result;
}

static int stbi__pic_test(stbi__context* s)
{
  int r = stbi__pic_test_core(s);
  stbi__rewind(s);
  return r;
}
#endif

// *************************************************************************************************
// GIF loader -- public domain by Jean-Marc Lienher -- simplified/shrunk by stb

#ifndef STBI_NO_GIF
typedef struct
{
  stbi__int16 prefix;
  stbi_uc first;
  stbi_uc suffix;
} stbi__gif_lzw;

typedef struct
{
  int w, h;
  stbi_uc* out;                 // output buffer (always 4 components)
  int flags, bgindex, ratio, transparent, eflags;
  stbi_uc  pal[256][4];
  stbi_uc lpal[256][4];
  stbi__gif_lzw codes[4096];
  stbi_uc* color_table;
  int parse, step;
  int lflags;
  int start_x, start_y;
  int max_x, max_y;
  int cur_x, cur_y;
  int line_size;
} stbi__gif;

static int stbi__gif_test_raw(stbi__context* s)
{
  int sz;
  if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') return 0;
  sz = stbi__get8(s);
  if (sz != '9' && sz != '7') return 0;
  if (stbi__get8(s) != 'a') return 0;
  return 1;
}

static int stbi__gif_test(stbi__context* s)
{
  int r = stbi__gif_test_raw(s);
  stbi__rewind(s);
  return r;
}

static void stbi__gif_parse_colortable(stbi__context* s, stbi_uc pal[256][4], int num_entries, int transp)
{
  int i;
  for (i = 0; i < num_entries; ++i) {
    pal[i][2] = stbi__get8(s);
    pal[i][1] = stbi__get8(s);
    pal[i][0] = stbi__get8(s);
    pal[i][3] = transp == i ? 0 : 255;
  }
}

static int stbi__gif_header(stbi__context* s, stbi__gif* g, int* comp, int is_info)
{
  stbi_uc version;
  if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8')
    return stbi__err("not GIF", "Corrupt GIF");

  version = stbi__get8(s);
  if (version != '7' && version != '9')    return stbi__err("not GIF", "Corrupt GIF");
  if (stbi__get8(s) != 'a')                return stbi__err("not GIF", "Corrupt GIF");

  stbi__g_failure_reason = "";
  g->w = stbi__get16le(s);
  g->h = stbi__get16le(s);
  g->flags = stbi__get8(s);
  g->bgindex = stbi__get8(s);
  g->ratio = stbi__get8(s);
  g->transparent = -1;

  if (comp != 0) *comp = 4;  // can't actually tell whether it's 3 or 4 until we parse the comments

  if (is_info) return 1;

  if (g->flags & 0x80)
    stbi__gif_parse_colortable(s, g->pal, 2 << (g->flags & 7), -1);

  return 1;
}

static int stbi__gif_info_raw(stbi__context* s, int* x, int* y, int* comp)
{
  stbi__gif g;
  if (!stbi__gif_header(s, &g, comp, 1)) {
    stbi__rewind(s);
    return 0;
  }
  if (x) *x = g.w;
  if (y) *y = g.h;
  return 1;
}

static void stbi__out_gif_code(stbi__gif* g, stbi__uint16 code)
{
  stbi_uc* p, * c;

  // recurse to decode the prefixes, since the linked-list is backwards,
  // and working backwards through an interleaved image would be nasty
  if (g->codes[code].prefix >= 0)
    stbi__out_gif_code(g, g->codes[code].prefix);

  if (g->cur_y >= g->max_y) return;

  p = &g->out[g->cur_x + g->cur_y];
  c = &g->color_table[g->codes[code].suffix * 4];

  if (c[3] >= 128) {
    p[0] = c[2];
    p[1] = c[1];
    p[2] = c[0];
    p[3] = c[3];
  }
  g->cur_x += 4;

  if (g->cur_x >= g->max_x) {
    g->cur_x = g->start_x;
    g->cur_y += g->step;

    while (g->cur_y >= g->max_y && g->parse > 0) {
      g->step = (1 << g->parse) * g->line_size;
      g->cur_y = g->start_y + (g->step >> 1);
      --g->parse;
    }
  }
}

static stbi_uc* stbi__process_gif_raster(stbi__context* s, stbi__gif* g)
{
  stbi_uc lzw_cs;
  stbi__int32 len, code;
  stbi__uint32 first;
  stbi__int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear;
  stbi__gif_lzw* p;

  lzw_cs = stbi__get8(s);
  if (lzw_cs > 12) return nullptr;
  clear = 1 << lzw_cs;
  first = 1;
  codesize = lzw_cs + 1;
  codemask = (1 << codesize) - 1;
  bits = 0;
  valid_bits = 0;
  for (code = 0; code < clear; code++) {
    g->codes[code].prefix = -1;
    g->codes[code].first = (stbi_uc)code;
    g->codes[code].suffix = (stbi_uc)code;
  }

  // support no starting clear code
  avail = clear + 2;
  oldcode = -1;

  len = 0;
  for (;;) {
    if (valid_bits < codesize) {
      if (len == 0) {
        len = stbi__get8(s); // start new block
        if (len == 0)
          return g->out;
      }
      --len;
      bits |= (stbi__int32)stbi__get8(s) << valid_bits;
      valid_bits += 8;
    }
    else {
      stbi__int32 code2 = bits & codemask;
      bits >>= codesize;
      valid_bits -= codesize;
      // @OPTIMIZE: is there some way we can accelerate the non-clear path?
      if (code2 == clear) {  // clear code
        codesize = lzw_cs + 1;
        codemask = (1 << codesize) - 1;
        avail = clear + 2;
        oldcode = -1;
        first = 0;
      }
      else if (code2 == clear + 1) { // end of stream code
        stbi__skip(s, len);
        while ((len = stbi__get8(s)) > 0)
          stbi__skip(s, len);
        return g->out;
      }
      else if (code2 <= avail) {
        if (first) return stbi__errpuc("no clear code", "Corrupt GIF");

        if (oldcode >= 0) {
          p = &g->codes[avail++];
          if (avail > 4096)        return stbi__errpuc("too many codes", "Corrupt GIF");
          p->prefix = (stbi__int16)oldcode;
          p->first = g->codes[oldcode].first;
          p->suffix = (code2 == avail) ? p->first : g->codes[code2].first;
        }
        else if (code2 == avail)
          return stbi__errpuc("illegal code in raster", "Corrupt GIF");

        stbi__out_gif_code(g, (stbi__uint16)code2);

        if ((avail & codemask) == 0 && avail <= 0x0FFF) {
          codesize++;
          codemask = (1 << codesize) - 1;
        }

        oldcode = code2;
      }
      else {
        return stbi__errpuc("illegal code in raster", "Corrupt GIF");
      }
    }
  }
}

static void stbi__fill_gif_background(stbi__gif* g)
{
  int i;
  stbi_uc* c = g->pal[g->bgindex];
  // @OPTIMIZE: write a dword at a time
  for (i = 0; i < g->w * g->h * 4; i += 4) {
    stbi_uc* p = &g->out[i];
    p[0] = c[2];
    p[1] = c[1];
    p[2] = c[0];
    p[3] = c[3];
  }
}

// this function is designed to support animated gifs, although stb_image doesn't support it
static stbi_uc* stbi__gif_load_next(stbi__context* s, stbi__gif* g, int* comp, int req_comp)
{
  int i;
  stbi_uc* old_out = 0;

  if (g->out == 0) {
    if (!stbi__gif_header(s, g, comp, 0))     return 0; // stbi__g_failure_reason set by stbi__gif_header
    g->out = (stbi_uc*)stbi__malloc(4 * g->w * g->h);
    if (g->out == 0)                      return stbi__errpuc("outofmem", "Out of memory");
    stbi__fill_gif_background(g);
  }
  else {
    // animated-gif-only path
    if (((g->eflags & 0x1C) >> 2) == 3) {
      old_out = g->out;
      g->out = (stbi_uc*)stbi__malloc(4 * g->w * g->h);
      if (g->out == 0)                   return stbi__errpuc("outofmem", "Out of memory");
      memcpy(g->out, old_out, g->w * g->h * 4);
    }
  }

  for (;;) {
    switch (stbi__get8(s)) {
    case 0x2C: /* Image Descriptor */
    {
      stbi__int32 x, y, w, h;
      stbi_uc* o;

      x = stbi__get16le(s);
      y = stbi__get16le(s);
      w = stbi__get16le(s);
      h = stbi__get16le(s);
      if (((x + w) > (g->w)) || ((y + h) > (g->h)))
        return stbi__errpuc("bad Image Descriptor", "Corrupt GIF");

      g->line_size = g->w * 4;
      g->start_x = x * 4;
      g->start_y = y * g->line_size;
      g->max_x = g->start_x + w * 4;
      g->max_y = g->start_y + h * g->line_size;
      g->cur_x = g->start_x;
      g->cur_y = g->start_y;

      g->lflags = stbi__get8(s);

      if (g->lflags & 0x40) {
        g->step = 8 * g->line_size; // first interlaced spacing
        g->parse = 3;
      }
      else {
        g->step = g->line_size;
        g->parse = 0;
      }

      if (g->lflags & 0x80) {
        stbi__gif_parse_colortable(s, g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1);
        g->color_table = (stbi_uc*)g->lpal;
      }
      else if (g->flags & 0x80) {
        for (i = 0; i < 256; ++i)  // @OPTIMIZE: stbi__jpeg_reset only the previous transparent
          g->pal[i][3] = 255;
        if (g->transparent >= 0 && (g->eflags & 0x01))
          g->pal[g->transparent][3] = 0;
        g->color_table = (stbi_uc*)g->pal;
      }
      else
        return stbi__errpuc("missing color table", "Corrupt GIF");

      o = stbi__process_gif_raster(s, g);
      if (o == nullptr) return nullptr;

      if (req_comp && req_comp != 4)
        o = stbi__convert_format(o, 4, req_comp, g->w, g->h);
      return o;
    }

    case 0x21: // Comment Extension.
    {
      int len;
      if (stbi__get8(s) == 0xF9) { // Graphic Control Extension.
        len = stbi__get8(s);
        if (len == 4) {
          g->eflags = stbi__get8(s);
          stbi__get16le(s); // delay
          g->transparent = stbi__get8(s);
        }
        else {
          stbi__skip(s, len);
          break;
        }
      }
      while ((len = stbi__get8(s)) != 0)
        stbi__skip(s, len);
      break;
    }

    case 0x3B: // gif stream termination code
      return (stbi_uc*)s; // using '1' causes warning on some compilers

    default:
      return stbi__errpuc("unknown code", "Corrupt GIF");
    }
  }
}

static stbi_uc* stbi__gif_load(stbi__context* s, int* x, int* y, int* comp, int req_comp)
{
  stbi_uc* u = 0;
  stbi__gif g;
  memset(&g, 0, sizeof(g));

  u = stbi__gif_load_next(s, &g, comp, req_comp);
  if (u == (stbi_uc*)s) u = 0;  // end of animated gif marker
  if (u) {
    *x = g.w;
    *y = g.h;
  }

  return u;
}

static int stbi__gif_info(stbi__context* s, int* x, int* y, int* comp)
{
  return stbi__gif_info_raw(s, x, y, comp);
}
#endif

// *************************************************************************************************
// Radiance RGBE HDR loader
// originally by Nicolas Schulz
#ifndef STBI_NO_HDR
static int stbi__hdr_test_core(stbi__context* s)
{
  const char* signature = "#?RADIANCE\n";
  int i;
  for (i = 0; signature[i]; ++i)
    if (stbi__get8(s) != signature[i])
      return 0;
  return 1;
}

static int stbi__hdr_test(stbi__context* s)
{
  int r = stbi__hdr_test_core(s);
  stbi__rewind(s);
  return r;
}

#define STBI__HDR_BUFLEN  1024
static char* stbi__hdr_gettoken(stbi__context* z, char* buffer)
{
  int len = 0;
  char c = '\0';

  c = (char)stbi__get8(z);

  while (!stbi__at_eof(z) && c != '\n') {
    buffer[len++] = c;
    if (len == STBI__HDR_BUFLEN - 1) {
      // flush to end of line
      while (!stbi__at_eof(z) && stbi__get8(z) != '\n')
        ;
      break;
    }
    c = (char)stbi__get8(z);
  }

  buffer[len] = 0;
  return buffer;
}

static void stbi__hdr_convert(float* output, stbi_uc* input, int req_comp)
{
  if (input[3] != 0) {
    float f1;
    // Exponent
    f1 = (float)ldexp(1.0f, input[3] - (int)(128 + 8));
    if (req_comp <= 2)
      output[0] = (input[0] + input[1] + input[2]) * f1 / 3;
    else {
      output[0] = input[0] * f1;
      output[1] = input[1] * f1;
      output[2] = input[2] * f1;
    }
    if (req_comp == 2) output[1] = 1;
    if (req_comp == 4) output[3] = 1;
  }
  else {
    switch (req_comp) {
    case 4: output[3] = 1; /* fallthrough */
    case 3: output[0] = output[1] = output[2] = 0;
      break;
    case 2: output[1] = 1; /* fallthrough */
    case 1: output[0] = 0;
      break;
    }
  }
}

static float* stbi__hdr_load(stbi__context* s, int* x, int* y, int* comp, int req_comp)
{
  char buffer[STBI__HDR_BUFLEN];
  char* token;
  int valid = 0;
  int width, height;
  stbi_uc* scanline;
  float* hdr_data;
  int len;
  unsigned char count, value;
  int i, j, k, c1, c2, z;


  // Check identifier
  if (strcmp(stbi__hdr_gettoken(s, buffer), "#?RADIANCE") != 0)
    return stbi__errpf("not HDR", "Corrupt HDR image");

  // Parse header
  for (;;) {
    token = stbi__hdr_gettoken(s, buffer);
    if (token[0] == 0) break;
    if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1;
  }

  if (!valid)    return stbi__errpf("unsupported format", "Unsupported HDR format");

  // Parse width and height
  // can't use sscanf() if we're not using stdio!
  token = stbi__hdr_gettoken(s, buffer);
  if (strncmp(token, "-Y ", 3))  return stbi__errpf("unsupported data layout", "Unsupported HDR format");
  token += 3;
  height = (int)strtol(token, &token, 10);
  while (*token == ' ') ++token;
  if (strncmp(token, "+X ", 3))  return stbi__errpf("unsupported data layout", "Unsupported HDR format");
  token += 3;
  width = (int)strtol(token, nullptr, 10);

  *x = width;
  *y = height;

  if (comp) *comp = 3;
  if (req_comp == 0) req_comp = 3;

  // Read data
  hdr_data = (float*)stbi__malloc(height * width * req_comp * sizeof(float));

  // Load image data
  // image data is stored as some number of sca
  if (width < 8 || width >= 32768) {
    // Read flat data
    for (j = 0; j < height; ++j) {
      for (i = 0; i < width; ++i) {
        stbi_uc rgbe[4];
      main_decode_loop:
        stbi__getn(s, rgbe, 4);
        stbi__hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp);
      }
    }
  }
  else {
    // Read RLE-encoded data
    scanline = nullptr;

    for (j = 0; j < height; ++j) {
      c1 = stbi__get8(s);
      c2 = stbi__get8(s);
      len = stbi__get8(s);
      if (c1 != 2 || c2 != 2 || (len & 0x80)) {
        // not run-length encoded, so we have to actually use THIS data as a decoded
        // pixel (note this can't be a valid pixel--one of RGB must be >= 128)
        stbi_uc rgbe[4];
        rgbe[0] = (stbi_uc)c1;
        rgbe[1] = (stbi_uc)c2;
        rgbe[2] = (stbi_uc)len;
        rgbe[3] = (stbi_uc)stbi__get8(s);
        stbi__hdr_convert(hdr_data, rgbe, req_comp);
        i = 1;
        j = 0;
        STBI_FREE(scanline);
        goto main_decode_loop; // yes, this makes no sense
      }
      len <<= 8;
      len |= stbi__get8(s);
      if (len != width) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("invalid decoded scanline length", "corrupt HDR"); }
      if (scanline == nullptr) scanline = (stbi_uc*)stbi__malloc(width * 4);

      for (k = 0; k < 4; ++k) {
        i = 0;
        while (i < width) {
          count = stbi__get8(s);
          if (count > 128) {
            // Run
            value = stbi__get8(s);
            count -= 128;
            for (z = 0; z < count; ++z)
              scanline[i++ * 4 + k] = value;
          }
          else {
            // Dump
            for (z = 0; z < count; ++z)
              scanline[i++ * 4 + k] = stbi__get8(s);
          }
        }
      }
      for (i = 0; i < width; ++i)
        stbi__hdr_convert(hdr_data + (j * width + i) * req_comp, scanline + i * 4, req_comp);
    }
    STBI_FREE(scanline);
  }

  return hdr_data;
}

static int stbi__hdr_info(stbi__context* s, int* x, int* y, int* comp)
{
  char buffer[STBI__HDR_BUFLEN];
  char* token;
  int valid = 0;

  if (strcmp(stbi__hdr_gettoken(s, buffer), "#?RADIANCE") != 0) {
    stbi__rewind(s);
    return 0;
  }

  for (;;) {
    token = stbi__hdr_gettoken(s, buffer);
    if (token[0] == 0) break;
    if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1;
  }

  if (!valid) {
    stbi__rewind(s);
    return 0;
  }
  token = stbi__hdr_gettoken(s, buffer);
  if (strncmp(token, "-Y ", 3)) {
    stbi__rewind(s);
    return 0;
  }
  token += 3;
  *y = (int)strtol(token, &token, 10);
  while (*token == ' ') ++token;
  if (strncmp(token, "+X ", 3)) {
    stbi__rewind(s);
    return 0;
  }
  token += 3;
  *x = (int)strtol(token, nullptr, 10);
  *comp = 3;
  return 1;
}
#endif // STBI_NO_HDR

#ifndef STBI_NO_BMP
static int stbi__bmp_info(stbi__context* s, int* x, int* y, int* comp)
{
  int hsz;
  if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') {
    stbi__rewind(s);
    return 0;
  }
  stbi__skip(s, 12);
  hsz = stbi__get32le(s);
  if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) {
    stbi__rewind(s);
    return 0;
  }
  if (hsz == 12) {
    *x = stbi__get16le(s);
    *y = stbi__get16le(s);
  }
  else {
    *x = stbi__get32le(s);
    *y = stbi__get32le(s);
  }
  if (stbi__get16le(s) != 1) {
    stbi__rewind(s);
    return 0;
  }
  *comp = stbi__get16le(s) / 8;
  return 1;
}
#endif

#ifndef STBI_NO_PSD
static int stbi__psd_info(stbi__context* s, int* x, int* y, int* comp)
{
  int channelCount;
  if (stbi__get32be(s) != 0x38425053) {
    stbi__rewind(s);
    return 0;
  }
  if (stbi__get16be(s) != 1) {
    stbi__rewind(s);
    return 0;
  }
  stbi__skip(s, 6);
  channelCount = stbi__get16be(s);
  if (channelCount < 0 || channelCount > 16) {
    stbi__rewind(s);
    return 0;
  }
  *y = stbi__get32be(s);
  *x = stbi__get32be(s);
  if (stbi__get16be(s) != 8) {
    stbi__rewind(s);
    return 0;
  }
  if (stbi__get16be(s) != 3) {
    stbi__rewind(s);
    return 0;
  }
  *comp = 4;
  return 1;
}
#endif

#ifndef STBI_NO_PIC
static int stbi__pic_info(stbi__context* s, int* x, int* y, int* comp)
{
  int act_comp = 0, num_packets = 0, chained;
  stbi__pic_packet packets[10];

  stbi__skip(s, 92);

  *x = stbi__get16be(s);
  *y = stbi__get16be(s);
  if (stbi__at_eof(s))  return 0;
  if ((*x) != 0 && (1 << 28) / (*x) < (*y)) {
    stbi__rewind(s);
    return 0;
  }

  stbi__skip(s, 8);

  do {
    stbi__pic_packet* packet;

    if (num_packets == sizeof(packets) / sizeof(packets[0]))
      return 0;

    packet = &packets[num_packets++];
    chained = stbi__get8(s);
    packet->size = stbi__get8(s);
    packet->type = stbi__get8(s);
    packet->channel = stbi__get8(s);
    act_comp |= packet->channel;

    if (stbi__at_eof(s)) {
      stbi__rewind(s);
      return 0;
    }
    if (packet->size != 8) {
      stbi__rewind(s);
      return 0;
    }
  } while (chained);

  *comp = (act_comp & 0x10 ? 4 : 3);

  return 1;
}
#endif

// *************************************************************************************************
// Portable Gray Map and Portable Pixel Map loader
// by Ken Miller
//
// PGM: http://netpbm.sourceforge.net/doc/pgm.html
// PPM: http://netpbm.sourceforge.net/doc/ppm.html
//
// Known limitations:
//    Does not support comments in the header section
//    Does not support ASCII image data (formats P2 and P3)
//    Does not support 16-bit-per-channel

#ifndef STBI_NO_PNM

static int      stbi__pnm_test(stbi__context* s)
{
  char p, t;
  p = (char)stbi__get8(s);
  t = (char)stbi__get8(s);
  if (p != 'P' || (t != '5' && t != '6')) {
    stbi__rewind(s);
    return 0;
  }
  return 1;
}

static stbi_uc* stbi__pnm_load(stbi__context* s, int* x, int* y, int* comp, int req_comp)
{
  stbi_uc* out;
  if (!stbi__pnm_info(s, (int*)&s->img_x, (int*)&s->img_y, (int*)&s->img_n))
    return 0;
  *x = s->img_x;
  *y = s->img_y;
  *comp = s->img_n;

  out = (stbi_uc*)stbi__malloc(s->img_n * s->img_x * s->img_y);
  if (!out) return stbi__errpuc("outofmem", "Out of memory");
  stbi__getn(s, out, s->img_n * s->img_x * s->img_y);

  if (req_comp && req_comp != s->img_n) {
    out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y);
    if (out == nullptr) return out; // stbi__convert_format frees input on failure
  }
  return out;
}

static int      stbi__pnm_isspace(char c)
{
  return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r';
}

static void     stbi__pnm_skip_whitespace(stbi__context* s, char* c)
{
  while (!stbi__at_eof(s) && stbi__pnm_isspace(*c))
    *c = (char)stbi__get8(s);
}

static int      stbi__pnm_isdigit(char c)
{
  return c >= '0' && c <= '9';
}

static int      stbi__pnm_getinteger(stbi__context* s, char* c)
{
  int value = 0;

  while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) {
    value = value * 10 + (*c - '0');
    *c = (char)stbi__get8(s);
  }

  return value;
}

static int      stbi__pnm_info(stbi__context* s, int* x, int* y, int* comp)
{
  int maxv;
  char c, p, t;

  stbi__rewind(s);

  // Get identifier
  p = (char)stbi__get8(s);
  t = (char)stbi__get8(s);
  if (p != 'P' || (t != '5' && t != '6')) {
    stbi__rewind(s);
    return 0;
  }

  *comp = (t == '6') ? 3 : 1;  // '5' is 1-component .pgm; '6' is 3-component .ppm

  c = (char)stbi__get8(s);
  stbi__pnm_skip_whitespace(s, &c);

  *x = stbi__pnm_getinteger(s, &c); // read width
  stbi__pnm_skip_whitespace(s, &c);

  *y = stbi__pnm_getinteger(s, &c); // read height
  stbi__pnm_skip_whitespace(s, &c);

  maxv = stbi__pnm_getinteger(s, &c);  // read max value

  if (maxv > 255)
    return stbi__err("max value > 255", "PPM image not 8-bit");
  else
    return 1;
}
#endif

static int stbi__info_main(stbi__context* s, int* x, int* y, int* comp)
{
#ifndef STBI_NO_JPEG
  if (stbi__jpeg_info(s, x, y, comp)) return 1;
#endif

#ifndef STBI_NO_PNG
  if (stbi__png_info(s, x, y, comp))  return 1;
#endif

#ifndef STBI_NO_GIF
  if (stbi__gif_info(s, x, y, comp))  return 1;
#endif

#ifndef STBI_NO_BMP
  if (stbi__bmp_info(s, x, y, comp))  return 1;
#endif

#ifndef STBI_NO_PSD
  if (stbi__psd_info(s, x, y, comp))  return 1;
#endif

#ifndef STBI_NO_PIC
  if (stbi__pic_info(s, x, y, comp))  return 1;
#endif

#ifndef STBI_NO_PNM
  if (stbi__pnm_info(s, x, y, comp))  return 1;
#endif

#ifndef STBI_NO_HDR
  if (stbi__hdr_info(s, x, y, comp))  return 1;
#endif

  // test tga last because it's a crappy test!
#ifndef STBI_NO_TGA
  if (stbi__tga_info(s, x, y, comp))
    return 1;
#endif
  return stbi__err("unknown image type", "Image not of any known type, or corrupt");
}

#ifndef STBI_NO_STDIO
STBIDEF int stbi_info(char const* filename, int* x, int* y, int* comp)
{
  FILE* f = stbi__fopen(filename, "rb");
  int result;
  if (!f) return stbi__err("can't fopen", "Unable to open file");
  result = stbi_info_from_file(f, x, y, comp);
  fclose(f);
  return result;
}

STBIDEF int stbi_info_from_file(FILE* f, int* x, int* y, int* comp)
{
  int r;
  stbi__context s;
  long pos = ftell(f);
  stbi__start_file(&s, f);
  r = stbi__info_main(&s, x, y, comp);
  fseek(f, pos, SEEK_SET);
  return r;
}
#endif // !STBI_NO_STDIO

STBIDEF int stbi_info_from_memory(stbi_uc const* buffer, int len, int* x, int* y, int* comp)
{
  stbi__context s;
  stbi__start_mem(&s, buffer, len);
  return stbi__info_main(&s, x, y, comp);
}

STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const* c, void* user, int* x, int* y, int* comp)
{
  stbi__context s;
  stbi__start_callbacks(&s, (stbi_io_callbacks*)c, user);
  return stbi__info_main(&s, x, y, comp);
}


#endif // STB_IMAGE_IMPLEMENTATION





#ifndef PVR_HELPER_H
#define PVR_HELPER_H

// Taken from PowerVR SDK

/*!***************************************************************************
 Describes the header of a PVR header-texture
 *****************************************************************************/
typedef struct
{
  unsigned int dwHeaderSize;			/*!< size of the structure */
  unsigned int dwHeight;				/*!< height of surface to be created */
  unsigned int dwWidth;				/*!< width of input surface */
  unsigned int dwMipMapCount;			/*!< number of mip-map levels requested */
  unsigned int dwpfFlags;				/*!< pixel format flags */
  unsigned int dwTextureDataSize;		/*!< Total size in bytes */
  unsigned int dwBitCount;			/*!< number of bits per pixel  */
  unsigned int dwRBitMask;			/*!< mask for red bit */
  unsigned int dwGBitMask;			/*!< mask for green bits */
  unsigned int dwBBitMask;			/*!< mask for blue bits */
  unsigned int dwAlphaBitMask;		/*!< mask for alpha channel */
  unsigned int dwPVR;					/*!< magic number identifying pvr file */
  unsigned int dwNumSurfs;			/*!< the number of surfaces present in the pvr */
} PVR_Texture_Header;

/*****************************************************************************
 * ENUMS
 *****************************************************************************/

enum PixelType
{
  MGLPT_ARGB_4444 = 0x00,
  MGLPT_ARGB_1555,
  MGLPT_RGB_565,
  MGLPT_RGB_555,
  MGLPT_RGB_888,
  MGLPT_ARGB_8888,
  MGLPT_ARGB_8332,
  MGLPT_I_8,
  MGLPT_AI_88,
  MGLPT_1_BPP,
  MGLPT_VY1UY0,
  MGLPT_Y1VY0U,
  MGLPT_PVRTC2,
  MGLPT_PVRTC4,
  MGLPT_PVRTC2_2,
  MGLPT_PVRTC2_4,

  OGL_RGBA_4444 = 0x10,
  OGL_RGBA_5551,
  OGL_RGBA_8888,
  OGL_RGB_565,
  OGL_RGB_555,
  OGL_RGB_888,
  OGL_I_8,
  OGL_AI_88,
  OGL_PVRTC2,
  OGL_PVRTC4,

  // OGL_BGRA_8888 extension
  OGL_BGRA_8888,

  D3D_DXT1 = 0x20,
  D3D_DXT2,
  D3D_DXT3,
  D3D_DXT4,
  D3D_DXT5,

  D3D_RGB_332,
  D3D_AI_44,
  D3D_LVU_655,
  D3D_XLVU_8888,
  D3D_QWVU_8888,

  //10 bits per channel
  D3D_ABGR_2101010,
  D3D_ARGB_2101010,
  D3D_AWVU_2101010,

  //16 bits per channel
  D3D_GR_1616,
  D3D_VU_1616,
  D3D_ABGR_16161616,

  //HDR formats
  D3D_R16F,
  D3D_GR_1616F,
  D3D_ABGR_16161616F,

  //32 bits per channel
  D3D_R32F,
  D3D_GR_3232F,
  D3D_ABGR_32323232F,

  // Ericsson
  ETC_RGB_4BPP,
  ETC_RGBA_EXPLICIT,
  ETC_RGBA_INTERPOLATED,

  // DX10


  ePT_DX10_R32G32B32A32_FLOAT = 0x50,
  ePT_DX10_R32G32B32A32_UINT,
  ePT_DX10_R32G32B32A32_SINT,

  ePT_DX10_R32G32B32_FLOAT,
  ePT_DX10_R32G32B32_UINT,
  ePT_DX10_R32G32B32_SINT,

  ePT_DX10_R16G16B16A16_FLOAT,
  ePT_DX10_R16G16B16A16_UNORM,
  ePT_DX10_R16G16B16A16_UINT,
  ePT_DX10_R16G16B16A16_SNORM,
  ePT_DX10_R16G16B16A16_SINT,

  ePT_DX10_R32G32_FLOAT,
  ePT_DX10_R32G32_UINT,
  ePT_DX10_R32G32_SINT,

  ePT_DX10_R10G10B10A2_UNORM,
  ePT_DX10_R10G10B10A2_UINT,

  ePT_DX10_R11G11B10_FLOAT,

  ePT_DX10_R8G8B8A8_UNORM,
  ePT_DX10_R8G8B8A8_UNORM_SRGB,
  ePT_DX10_R8G8B8A8_UINT,
  ePT_DX10_R8G8B8A8_SNORM,
  ePT_DX10_R8G8B8A8_SINT,

  ePT_DX10_R16G16_FLOAT,
  ePT_DX10_R16G16_UNORM,
  ePT_DX10_R16G16_UINT,
  ePT_DX10_R16G16_SNORM,
  ePT_DX10_R16G16_SINT,

  ePT_DX10_R32_FLOAT,
  ePT_DX10_R32_UINT,
  ePT_DX10_R32_SINT,

  ePT_DX10_R8G8_UNORM,
  ePT_DX10_R8G8_UINT,
  ePT_DX10_R8G8_SNORM,
  ePT_DX10_R8G8_SINT,

  ePT_DX10_R16_FLOAT,
  ePT_DX10_R16_UNORM,
  ePT_DX10_R16_UINT,
  ePT_DX10_R16_SNORM,
  ePT_DX10_R16_SINT,

  ePT_DX10_R8_UNORM,
  ePT_DX10_R8_UINT,
  ePT_DX10_R8_SNORM,
  ePT_DX10_R8_SINT,

  ePT_DX10_A8_UNORM,
  ePT_DX10_R1_UNORM,
  ePT_DX10_R9G9B9E5_SHAREDEXP,
  ePT_DX10_R8G8_B8G8_UNORM,
  ePT_DX10_G8R8_G8B8_UNORM,

  ePT_DX10_BC1_UNORM,
  ePT_DX10_BC1_UNORM_SRGB,

  ePT_DX10_BC2_UNORM,
  ePT_DX10_BC2_UNORM_SRGB,

  ePT_DX10_BC3_UNORM,
  ePT_DX10_BC3_UNORM_SRGB,

  ePT_DX10_BC4_UNORM,
  ePT_DX10_BC4_SNORM,

  ePT_DX10_BC5_UNORM,
  ePT_DX10_BC5_SNORM,

  //ePT_DX10_B5G6R5_UNORM,			// defined but obsolete - won't actually load in DX10
  //ePT_DX10_B5G5R5A1_UNORM,
  //ePT_DX10_B8G8R8A8_UNORM,
  //ePT_DX10_B8G8R8X8_UNORM,

  // OpenVG

  /* RGB{A,X} channel ordering */
  ePT_VG_sRGBX_8888 = 0x90,
  ePT_VG_sRGBA_8888,
  ePT_VG_sRGBA_8888_PRE,
  ePT_VG_sRGB_565,
  ePT_VG_sRGBA_5551,
  ePT_VG_sRGBA_4444,
  ePT_VG_sL_8,
  ePT_VG_lRGBX_8888,
  ePT_VG_lRGBA_8888,
  ePT_VG_lRGBA_8888_PRE,
  ePT_VG_lL_8,
  ePT_VG_A_8,
  ePT_VG_BW_1,

  /* {A,X}RGB channel ordering */
  ePT_VG_sXRGB_8888,
  ePT_VG_sARGB_8888,
  ePT_VG_sARGB_8888_PRE,
  ePT_VG_sARGB_1555,
  ePT_VG_sARGB_4444,
  ePT_VG_lXRGB_8888,
  ePT_VG_lARGB_8888,
  ePT_VG_lARGB_8888_PRE,

  /* BGR{A,X} channel ordering */
  ePT_VG_sBGRX_8888,
  ePT_VG_sBGRA_8888,
  ePT_VG_sBGRA_8888_PRE,
  ePT_VG_sBGR_565,
  ePT_VG_sBGRA_5551,
  ePT_VG_sBGRA_4444,
  ePT_VG_lBGRX_8888,
  ePT_VG_lBGRA_8888,
  ePT_VG_lBGRA_8888_PRE,

  /* {A,X}BGR channel ordering */
  ePT_VG_sXBGR_8888,
  ePT_VG_sABGR_8888,
  ePT_VG_sABGR_8888_PRE,
  ePT_VG_sABGR_1555,
  ePT_VG_sABGR_4444,
  ePT_VG_lXBGR_8888,
  ePT_VG_lABGR_8888,
  ePT_VG_lABGR_8888_PRE,

  // max cap for iterating
  END_OF_PIXEL_TYPES,

  MGLPT_NOTYPE = 0xff

};

#define PVRTEX_MIPMAP		(1<<8)		// has mip map levels
#define PVRTEX_TWIDDLE		(1<<9)		// is twiddled
#define PVRTEX_BUMPMAP		(1<<10)		// has normals encoded for a bump map
#define PVRTEX_TILING		(1<<11)		// is bordered for tiled pvr
#define PVRTEX_CUBEMAP		(1<<12)		// is a cubemap/skybox
#define PVRTEX_FALSEMIPCOL	(1<<13)		//
#define PVRTEX_VOLUME		(1<<14)
#define PVRTEX_PIXELTYPE	0xff			// pixel type is always in the last 16bits of the flags
#define PVRTEX_IDENTIFIER	0x21525650	// the pvr identifier is the characters 'P','V','R'

#define PVRTEX_V1_HEADER_SIZE 44			// old header size was 44 for identification purposes

#define PVRTC2_MIN_TEXWIDTH		16
#define PVRTC2_MIN_TEXHEIGHT	8
#define PVRTC4_MIN_TEXWIDTH		8
#define PVRTC4_MIN_TEXHEIGHT	8
#define ETC_MIN_TEXWIDTH		4
#define ETC_MIN_TEXHEIGHT		4
#define DXT_MIN_TEXWIDTH		4
#define DXT_MIN_TEXHEIGHT		4

#endif




typedef struct {
  char aName[6];
  unsigned short iBlank;
  unsigned char iPaddedWidthMSB;
  unsigned char iPaddedWidthLSB;
  unsigned char iPaddedHeightMSB;
  unsigned char iPaddedHeightLSB;
  unsigned char iWidthMSB;
  unsigned char iWidthLSB;
  unsigned char iHeightMSB;
  unsigned char iHeightLSB;
} PKMHeader;
#define PKM_HEADER_SIZE 16




/*	error reporting	*/
const char* result_string_pointer = "SOIL initialized";

/*	for loading cube maps	*/
enum {
  SOIL_CAPABILITY_UNKNOWN = -1,
  SOIL_CAPABILITY_NONE = 0,
  SOIL_CAPABILITY_PRESENT = 1
};
static int has_cubemap_capability = SOIL_CAPABILITY_UNKNOWN;
#define SOIL_TEXTURE_WRAP_R					0x8072
#define SOIL_CLAMP_TO_EDGE					0x812F
#define SOIL_NORMAL_MAP						0x8511
#define SOIL_REFLECTION_MAP					0x8512
#define SOIL_TEXTURE_CUBE_MAP				0x8513
#define SOIL_TEXTURE_BINDING_CUBE_MAP		0x8514
#define SOIL_TEXTURE_CUBE_MAP_POSITIVE_X	0x8515
#define SOIL_TEXTURE_CUBE_MAP_NEGATIVE_X	0x8516
#define SOIL_TEXTURE_CUBE_MAP_POSITIVE_Y	0x8517
#define SOIL_TEXTURE_CUBE_MAP_NEGATIVE_Y	0x8518
#define SOIL_TEXTURE_CUBE_MAP_POSITIVE_Z	0x8519
#define SOIL_TEXTURE_CUBE_MAP_NEGATIVE_Z	0x851A
#define SOIL_PROXY_TEXTURE_CUBE_MAP			0x851B
#define SOIL_MAX_CUBE_MAP_TEXTURE_SIZE		0x851C
/*	for non-power-of-two texture	*/
#define SOIL_IS_POW2( v ) ( ( v & ( v - 1 ) ) == 0 )
static int has_NPOT_capability = SOIL_CAPABILITY_PRESENT;
/*	for texture rectangles	*/
static int has_tex_rectangle_capability = SOIL_CAPABILITY_PRESENT;
#define SOIL_TEXTURE_RECTANGLE_ARB				0x84F5
#define SOIL_MAX_RECTANGLE_TEXTURE_SIZE_ARB		0x84F8
/*	for using DXT compression	*/
static int has_DXT_capability = SOIL_CAPABILITY_PRESENT;
#define SOIL_RGB_S3TC_DXT1		0x83F0
#define SOIL_RGBA_S3TC_DXT1		0x83F1
#define SOIL_RGBA_S3TC_DXT3		0x83F2
#define SOIL_RGBA_S3TC_DXT5		0x83F3
typedef void (APIENTRY* P_SOIL_GLCOMPRESSEDTEXIMAGE2DPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid* data);
static P_SOIL_GLCOMPRESSEDTEXIMAGE2DPROC soilGlCompressedTexImage2D = nullptr;

typedef void (APIENTRY* P_SOIL_GLGENERATEMIPMAPPROC)(GLenum target);
static P_SOIL_GLGENERATEMIPMAPPROC soilGlGenerateMipmap = nullptr;

static int has_gen_mipmap_capability = SOIL_CAPABILITY_PRESENT;

static int has_PVR_capability = SOIL_CAPABILITY_PRESENT;
static int has_BGRA8888_capability = SOIL_CAPABILITY_UNKNOWN;
int query_BGRA8888_capability(void);
static int has_ETC1_capability = SOIL_CAPABILITY_UNKNOWN;
int query_ETC1_capability(void);

/* GL_IMG_texture_compression_pvrtc */
#define SOIL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG                      0x8C00
#define SOIL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG                      0x8C01
#define SOIL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG                     0x8C02
#define SOIL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG                     0x8C03
#define SOIL_GL_ETC1_RGB8_OES                                     0x8D64

#if defined( SOIL_X11_PLATFORM ) || defined( SOIL_PLATFORM_WIN32 ) || defined( SOIL_PLATFORM_OSX )
typedef const GLubyte* (APIENTRY* P_SOIL_glGetStringiFunc) (GLenum, GLuint);
static P_SOIL_glGetStringiFunc soilGlGetStringiFunc = nullptr;

static int isAtLeastGL3()
{
  static int is_gl3 = SOIL_CAPABILITY_UNKNOWN;

  if (SOIL_CAPABILITY_UNKNOWN == is_gl3)
  {
    const char* verstr = (const char*)glGetString(GL_VERSION);
    is_gl3 = (verstr && (atoi(verstr) >= 3));
  }

  return is_gl3;
}
#endif

#ifdef SOIL_PLATFORM_WIN32
static int soilTestWinProcPointer(const PROC pTest)
{
  ptrdiff_t iTest;
  if (!pTest) return 0;
  iTest = (ptrdiff_t)pTest;
  if (iTest == 1 || iTest == 2 || iTest == 3 || iTest == -1) return 0;
  return 1;
}
#endif

/*	other functions	*/
unsigned int
SOIL_internal_create_OGL_texture
(
  const unsigned char* const data,
  int* width, int* height, int channels,
  unsigned int reuse_texture_ID,
  unsigned int flags,
  unsigned int opengl_texture_type,
  unsigned int opengl_texture_target,
  unsigned int texture_check_size_enum
);

/*	and the code magic begins here [8^)	*/
unsigned int
SOIL_load_OGL_texture
(
  const char* filename,
  int force_channels,
  unsigned int reuse_texture_ID,
  unsigned int flags,
  int* width,
  int* height
)
{
  /*	variables	*/
  unsigned char* img;
  int channels;
  unsigned int tex_id;
  /*	does the user want direct uploading of the image as a DDS file?	*/
  if (flags & SOIL_FLAG_DDS_LOAD_DIRECT)
  {
    /*	1st try direct loading of the image as a DDS file
      note: direct uploading will only load what is in the
      DDS file, no MIPmaps will be generated, the image will
      not be flipped, etc.	*/
    tex_id = SOIL_direct_load_DDS(filename, reuse_texture_ID, flags, 0);
    if (tex_id)
    {
      /*	hey, it worked!!	*/
      return tex_id;
    }
  }

  if (flags & SOIL_FLAG_PVR_LOAD_DIRECT)
  {
    tex_id = SOIL_direct_load_PVR(filename, reuse_texture_ID, flags, 0);
    if (tex_id)
    {
      /*	hey, it worked!!	*/
      return tex_id;
    }
  }

  if (flags & SOIL_FLAG_ETC1_LOAD_DIRECT)
  {
    tex_id = SOIL_direct_load_ETC1(filename, reuse_texture_ID, flags);
    if (tex_id)
    {
      /*	hey, it worked!!	*/
      return tex_id;
    }
  }

  /*	try to load the image	*/
  img = SOIL_load_image(filename, width, height, &channels, force_channels);
  /*	channels holds the original number of channels, which may have been forced	*/
  if ((force_channels >= 1) && (force_channels <= 4))
  {
    channels = force_channels;
  }
  if (nullptr == img)
  {
    /*	image loading failed	*/
    result_string_pointer = stbi_failure_reason();
    return 0;
  }
  /*	OK, make it a texture!	*/
  tex_id = SOIL_internal_create_OGL_texture(
    img, width, height, channels,
    reuse_texture_ID, flags,
    GL_TEXTURE_2D, GL_TEXTURE_2D,
    GL_MAX_TEXTURE_SIZE);
  /*	and nuke the image data	*/
  SOIL_free_image_data(img);
  /*	and return the handle, such as it is	*/
  return tex_id;
}


unsigned int
SOIL_load_OGL_texture_from_memory
(
  const unsigned char* const buffer,
  unsigned int buffer_length,
  int force_channels,
  unsigned int reuse_texture_ID,
  unsigned int flags,
  int* width,
  int* height
)
{
  /*	variables	*/
  unsigned char* img;
  int channels;
  unsigned int tex_id;
  /*	does the user want direct uploading of the image as a DDS file?	*/
  if (flags & SOIL_FLAG_DDS_LOAD_DIRECT)
  {
    /*	1st try direct loading of the image as a DDS file
      note: direct uploading will only load what is in the
      DDS file, no MIPmaps will be generated, the image will
      not be flipped, etc.	*/
    tex_id = SOIL_direct_load_DDS_from_memory(
      buffer, buffer_length,
      reuse_texture_ID, flags, 0);
    if (tex_id)
    {
      /*	hey, it worked!!	*/
      return tex_id;
    }
  }

  if (flags & SOIL_FLAG_PVR_LOAD_DIRECT)
  {
    tex_id = SOIL_direct_load_PVR_from_memory(
      buffer, buffer_length,
      reuse_texture_ID, flags, 0);
    if (tex_id)
    {
      /*	hey, it worked!!	*/
      return tex_id;
    }
  }

  if (flags & SOIL_FLAG_ETC1_LOAD_DIRECT)
  {
    tex_id = SOIL_direct_load_ETC1_from_memory(
      buffer, buffer_length,
      reuse_texture_ID, flags);
    if (tex_id)
    {
      /*	hey, it worked!!	*/
      return tex_id;
    }
  }

  /*	try to load the image	*/
  img = SOIL_load_image_from_memory(
    buffer, buffer_length,
    width, height, &channels,
    force_channels);
  /*	channels holds the original number of channels, which may have been forced	*/
  if ((force_channels >= 1) && (force_channels <= 4))
  {
    channels = force_channels;
  }
  if (nullptr == img)
  {
    /*	image loading failed	*/
    result_string_pointer = stbi_failure_reason();
    return 0;
  }
  /*	OK, make it a texture!	*/
  tex_id = SOIL_internal_create_OGL_texture(
    img, width, height, channels,
    reuse_texture_ID, flags,
    GL_TEXTURE_2D, GL_TEXTURE_2D,
    GL_MAX_TEXTURE_SIZE);
  /*	and nuke the image data	*/
  SOIL_free_image_data(img);
  /*	and return the handle, such as it is	*/
  return tex_id;
}

#if SOIL_CHECK_FOR_GL_ERRORS
void check_for_GL_errors(const char* calling_location)
{
  /*	check for errors	*/
  GLenum err_code = glGetError();
  while (GL_NO_ERROR != err_code)
  {
    printf("OpenGL Error @ %s: %i", calling_location, err_code);
    err_code = glGetError();
  }
}
#else
void check_for_GL_errors(const char* calling_location)
{
  /*	no check for errors	*/
}
#endif

static void createMipmaps(const unsigned char* const img,
  int width, int height, int channels,
  unsigned int flags,
  unsigned int opengl_texture_target,
  unsigned int internal_texture_format,
  unsigned int original_texture_format,
  int DXT_mode)
{
  if (flags & SOIL_FLAG_GL_MIPMAPS)
  {
    soilGlGenerateMipmap(opengl_texture_target);
  }
  else
  {
    int MIPlevel = 1;
    int MIPwidth = (width + 1) / 2;
    int MIPheight = (height + 1) / 2;
    unsigned char* resampled = (unsigned char*)malloc(channels * MIPwidth * MIPheight);

    while (((1 << MIPlevel) <= width) || ((1 << MIPlevel) <= height))
    {
      /*	do this MIPmap level	*/
      mipmap_image(
        img, width, height, channels,
        resampled,
        (1 << MIPlevel), (1 << MIPlevel));

      /*  upload the MIPmaps	*/
      if (DXT_mode == SOIL_CAPABILITY_PRESENT)
      {
        /*	user wants me to do the DXT conversion!	*/
        int DDS_size;
        unsigned char* DDS_data = nullptr;
        if ((channels & 1) == 1)
        {
          /*	RGB, use DXT1	*/
          DDS_data = convert_image_to_DXT1(
            resampled, MIPwidth, MIPheight, channels, &DDS_size);
        }
        else
        {
          /*	RGBA, use DXT5	*/
          DDS_data = convert_image_to_DXT5(
            resampled, MIPwidth, MIPheight, channels, &DDS_size);
        }
        if (DDS_data)
        {
          soilGlCompressedTexImage2D(
            opengl_texture_target, MIPlevel,
            internal_texture_format, MIPwidth, MIPheight, 0,
            DDS_size, DDS_data);
          check_for_GL_errors("glCompressedTexImage2D");
          SOIL_free_image_data(DDS_data);
        }
        else
        {
          /*	my compression failed, try the OpenGL driver's version	*/
          glTexImage2D(
            opengl_texture_target, MIPlevel,
            internal_texture_format, MIPwidth, MIPheight, 0,
            original_texture_format, GL_UNSIGNED_BYTE, resampled);
          check_for_GL_errors("glTexImage2D");
        }
      }
      else
      {
        /*	user want OpenGL to do all the work!	*/
        glTexImage2D(
          opengl_texture_target, MIPlevel,
          internal_texture_format, MIPwidth, MIPheight, 0,
          original_texture_format, GL_UNSIGNED_BYTE, resampled);
        check_for_GL_errors("glTexImage2D");
      }
      /*	prep for the next level	*/
      ++MIPlevel;
      MIPwidth = (MIPwidth + 1) / 2;
      MIPheight = (MIPheight + 1) / 2;
    }

    SOIL_free_image_data(resampled);
  }
}

unsigned int
SOIL_internal_create_OGL_texture
(
  const unsigned char* const data,
  int* width, int* height, int channels,
  unsigned int reuse_texture_ID,
  unsigned int flags,
  unsigned int opengl_texture_type,
  unsigned int opengl_texture_target,
  unsigned int texture_check_size_enum
)
{
  /*	variables	*/
  unsigned char* img = nullptr;
  unsigned int tex_id;
  unsigned int internal_texture_format = 0, original_texture_format = 0;
  int DXT_mode = SOIL_CAPABILITY_UNKNOWN;
  int max_supported_size;
  int iwidth = *width;
  int iheight = *height;
  int needCopy;
  GLint unpack_aligment;

  /*	how large of a texture can this OpenGL implementation handle?	*/
  /*	texture_check_size_enum will be GL_MAX_TEXTURE_SIZE or SOIL_MAX_CUBE_MAP_TEXTURE_SIZE	*/
  glGetIntegerv(texture_check_size_enum, &max_supported_size);


  needCopy = ((flags & SOIL_FLAG_INVERT_Y) ||
    (flags & SOIL_FLAG_NTSC_SAFE_RGB) ||
    (flags & SOIL_FLAG_MULTIPLY_ALPHA) ||
    (flags & SOIL_FLAG_CoCg_Y)
    );

  /*	create a copy the image data only if needed */
  if (needCopy) {
    img = (unsigned char*)malloc(iwidth * iheight * channels);
    memcpy(img, data, iwidth * iheight * channels);
  }

  /*	does the user want me to invert the image?	*/
  if (flags & SOIL_FLAG_INVERT_Y)
  {
    int i, j;
    for (j = 0; j * 2 < iheight; ++j)
    {
      int index1 = j * iwidth * channels;
      int index2 = (iheight - 1 - j) * iwidth * channels;
      for (i = iwidth * channels; i > 0; --i)
      {
        unsigned char temp = img[index1];
        img[index1] = img[index2];
        img[index2] = temp;
        ++index1;
        ++index2;
      }
    }
  }
  /*	does the user want me to scale the colors into the NTSC safe RGB range?	*/
  if (flags & SOIL_FLAG_NTSC_SAFE_RGB)
  {
    scale_image_RGB_to_NTSC_safe(img, iwidth, iheight, channels);
  }
  /*	does the user want me to convert from straight to pre-multiplied alpha?
    (and do we even _have_ alpha?)	*/
  if (flags & SOIL_FLAG_MULTIPLY_ALPHA)
  {
    int i;
    switch (channels)
    {
    case 2:
      for (i = 0; i < 2 * iwidth * iheight; i += 2)
      {
        img[i] = (img[i] * img[i + 1] + 128) >> 8;
      }
      break;
    case 4:
      for (i = 0; i < 4 * iwidth * iheight; i += 4)
      {
        img[i + 0] = (img[i + 0] * img[i + 3] + 128) >> 8;
        img[i + 1] = (img[i + 1] * img[i + 3] + 128) >> 8;
        img[i + 2] = (img[i + 2] * img[i + 3] + 128) >> 8;
      }
      break;
    default:
      /*	no other number of channels contains alpha data	*/
      break;
    }
  }

  /*	now, if it is too large...	*/
  if ((iwidth > max_supported_size) || (iheight > max_supported_size))
  {
    /*	I've already made it a power of two, so simply use the MIPmapping
      code to reduce its size to the allowable maximum.	*/
    unsigned char* resampled;
    int reduce_block_x = 1, reduce_block_y = 1;
    int new_width, new_height;
    if (iwidth > max_supported_size)
    {
      reduce_block_x = iwidth / max_supported_size;
    }
    if (iheight > max_supported_size)
    {
      reduce_block_y = iheight / max_supported_size;
    }
    new_width = iwidth / reduce_block_x;
    new_height = iheight / reduce_block_y;
    resampled = (unsigned char*)malloc(channels * new_width * new_height);
    /*	perform the actual reduction	*/
    mipmap_image(nullptr != img ? img : data, iwidth, iheight, channels,
      resampled, reduce_block_x, reduce_block_y);
    /*	nuke the old guy, then point it at the new guy	*/
    SOIL_free_image_data(img);
    img = resampled;
    *width = new_width;
    *height = new_height;
    iwidth = new_width;
    iheight = new_height;
  }
  /*	does the user want us to use YCoCg color space?	*/
  if (flags & SOIL_FLAG_CoCg_Y)
  {
    /*	this will only work with RGB and RGBA images */
    convert_RGB_to_YCoCg(img, iwidth, iheight, channels);
  }
  /*	create the OpenGL texture ID handle
    (note: allowing a forced texture ID lets me reload a texture)	*/
  tex_id = reuse_texture_ID;
  if (tex_id == 0)
  {
    glGenTextures(1, &tex_id);
  }
  check_for_GL_errors("glGenTextures");
  /* Note: sometimes glGenTextures fails (usually no OpenGL context)	*/
  if (tex_id)
  {
    /*	and what type am I using as the internal texture format?	*/
    switch (channels)
    {
    case 1:
      original_texture_format = GL_LUMINANCE;
      break;
    case 2:
      original_texture_format = GL_LUMINANCE_ALPHA;
      break;
    case 3:
      original_texture_format = GL_RGB;
      break;
    case 4:
      original_texture_format = GL_RGBA;
      break;
    }
    internal_texture_format = original_texture_format;
    /*	does the user want me to, and can I, save as DXT?	*/
    if (flags & SOIL_FLAG_COMPRESS_TO_DXT)
    {
      /*	I can use DXT, whether I compress it or OpenGL does	*/
      if ((channels & 1) == 1)
      {
        /*	1 or 3 channels = DXT1	*/
        internal_texture_format = SOIL_RGB_S3TC_DXT1;
      }
      else
      {
        /*	2 or 4 channels = DXT5	*/
        internal_texture_format = SOIL_RGBA_S3TC_DXT5;
      }
    }
    /*  bind an OpenGL texture ID	*/
    glBindTexture(opengl_texture_type, tex_id);
    check_for_GL_errors("glBindTexture");

    /* set the unpack aligment */
    glGetIntegerv(GL_UNPACK_ALIGNMENT, &unpack_aligment);
    if (1 != unpack_aligment)
    {
      glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    }

    /*  upload the main image	*/
    if (DXT_mode == SOIL_CAPABILITY_PRESENT)
    {
      /*	user wants me to do the DXT conversion!	*/
      int DDS_size;
      unsigned char* DDS_data = nullptr;
      if ((channels & 1) == 1)
      {
        /*	RGB, use DXT1	*/
        DDS_data = convert_image_to_DXT1(nullptr != img ? img : data, iwidth, iheight, channels, &DDS_size);
      }
      else
      {
        /*	RGBA, use DXT5	*/
        DDS_data = convert_image_to_DXT5(nullptr != img ? img : data, iwidth, iheight, channels, &DDS_size);
      }
      if (DDS_data)
      {
        soilGlCompressedTexImage2D(
          opengl_texture_target, 0,
          internal_texture_format, iwidth, iheight, 0,
          DDS_size, DDS_data);
        check_for_GL_errors("glCompressedTexImage2D");
        SOIL_free_image_data(DDS_data);
        /*	printf( "Internal DXT compressor\n" );	*/
      }
      else
      {
        /*	my compression failed, try the OpenGL driver's version	*/
        glTexImage2D(
          opengl_texture_target, 0,
          internal_texture_format, iwidth, iheight, 0,
          original_texture_format, GL_UNSIGNED_BYTE, nullptr != img ? img : data);
        check_for_GL_errors("glTexImage2D");
        /*	printf( "OpenGL DXT compressor\n" );	*/
      }
    }
    else
    {
      /*	user want OpenGL to do all the work!	*/
      glTexImage2D(
        opengl_texture_target, 0,
        internal_texture_format, iwidth, iheight, 0,
        original_texture_format, GL_UNSIGNED_BYTE, nullptr != img ? img : data);

      check_for_GL_errors("glTexImage2D");
      /*printf( "OpenGL DXT compressor\n" );	*/
    }

    /*	are any MIPmaps desired?	*/
    if (flags & SOIL_FLAG_MIPMAPS || flags & SOIL_FLAG_GL_MIPMAPS)
    {
      createMipmaps(nullptr != img ? img : data, iwidth, iheight, channels, flags, opengl_texture_target, internal_texture_format, original_texture_format, DXT_mode);

      /*	instruct OpenGL to use the MIPmaps	*/
      glTexParameteri(opengl_texture_type, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
      glTexParameteri(opengl_texture_type, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
      check_for_GL_errors("GL_TEXTURE_MIN/MAG_FILTER");
    }
    else
    {
      /*	instruct OpenGL _NOT_ to use the MIPmaps	*/
      glTexParameteri(opengl_texture_type, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
      glTexParameteri(opengl_texture_type, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
      check_for_GL_errors("GL_TEXTURE_MIN/MAG_FILTER");
    }

    /* recover the unpack aligment */
    if (1 != unpack_aligment)
    {
      glPixelStorei(GL_UNPACK_ALIGNMENT, unpack_aligment);
    }

    /*	does the user want clamping, or wrapping?	*/
    if (flags & SOIL_FLAG_TEXTURE_REPEATS)
    {
      glTexParameteri(opengl_texture_type, GL_TEXTURE_WRAP_S, GL_REPEAT);
      glTexParameteri(opengl_texture_type, GL_TEXTURE_WRAP_T, GL_REPEAT);
      if (opengl_texture_type == SOIL_TEXTURE_CUBE_MAP)
      {
        /*	SOIL_TEXTURE_WRAP_R is invalid if cubemaps aren't supported	*/
        glTexParameteri(opengl_texture_type, SOIL_TEXTURE_WRAP_R, GL_REPEAT);
      }
      check_for_GL_errors("GL_TEXTURE_WRAP_*");
    }
    else
    {
      unsigned int clamp_mode = SOIL_CLAMP_TO_EDGE;
      /* unsigned int clamp_mode = GL_CLAMP; */
      glTexParameteri(opengl_texture_type, GL_TEXTURE_WRAP_S, clamp_mode);
      glTexParameteri(opengl_texture_type, GL_TEXTURE_WRAP_T, clamp_mode);
      if (opengl_texture_type == SOIL_TEXTURE_CUBE_MAP)
      {
        /*	SOIL_TEXTURE_WRAP_R is invalid if cubemaps aren't supported	*/
        glTexParameteri(opengl_texture_type, SOIL_TEXTURE_WRAP_R, clamp_mode);
      }
      check_for_GL_errors("GL_TEXTURE_WRAP_*");
    }
    /*	done	*/
    result_string_pointer = "Image loaded as an OpenGL texture";
  }
  else
  {
    /*	failed	*/
    result_string_pointer = "Failed to generate an OpenGL texture name; missing OpenGL context?";
  }

  SOIL_free_image_data(img);

  return tex_id;
}

unsigned char*
SOIL_load_image
(
  const char* filename,
  int* width, int* height, int* channels,
  int force_channels
)
{
  unsigned char* result = stbi_load(filename,
    width, height, channels, force_channels);
  if (result == nullptr)
  {
    result_string_pointer = stbi_failure_reason();
  }
  else
  {
    result_string_pointer = "Image loaded";
  }
  return result;
}

unsigned char*
SOIL_load_image_from_memory
(
  const unsigned char* const buffer,
  int buffer_length,
  int* width, int* height, int* channels,
  int force_channels
)
{
  unsigned char* result = stbi_load_from_memory(
    buffer, buffer_length,
    width, height, channels,
    force_channels);
  if (result == nullptr)
  {
    result_string_pointer = stbi_failure_reason();
  }
  else
  {
    result_string_pointer = "Image loaded from memory";
  }
  return result;
}

void
SOIL_free_image_data
(
  unsigned char* img_data
)
{
  if (img_data)
    free((void*)img_data);
}

unsigned int SOIL_direct_load_DDS_from_memory(
  const unsigned char* const buffer,
  unsigned int buffer_length,
  unsigned int reuse_texture_ID,
  int flags,
  int loading_as_cubemap)
{
  /*	variables	*/
  DDS_header header;
  unsigned int buffer_index = 0;
  unsigned int tex_ID = 0;
  /*	file reading variables	*/
  unsigned int S3TC_type = 0;
  unsigned char* DDS_data;
  unsigned int DDS_main_size;
  unsigned int DDS_full_size;
  unsigned int width, height;
  int mipmaps, cubemap, uncompressed, block_size = 16;
  unsigned int flag;
  unsigned int cf_target, ogl_target_start, ogl_target_end;
  unsigned int opengl_texture_type;
  int i;
  /*	1st off, does the filename even exist?	*/
  if (nullptr == buffer)
  {
    /*	we can't do it!	*/
    result_string_pointer = "nullptr buffer";
    return 0;
  }
  if (buffer_length < sizeof(DDS_header))
  {
    /*	we can't do it!	*/
    result_string_pointer = "DDS file was too small to contain the DDS header";
    return 0;
  }
  /*	try reading in the header	*/
  memcpy((void*)(&header), (const void*)buffer, sizeof(DDS_header));
  buffer_index = sizeof(DDS_header);
  /*	guilty until proven innocent	*/
  result_string_pointer = "Failed to read a known DDS header";
  /*	validate the header (warning, "goto"'s ahead, shield your eyes!!)	*/
  flag = ('D' << 0) | ('D' << 8) | ('S' << 16) | (' ' << 24);
  if (header.dwMagic != flag) { goto quick_exit; }
  if (header.dwSize != 124) { goto quick_exit; }
  /*	I need all of these	*/
  flag = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT;
  if ((header.dwFlags & flag) != flag) { goto quick_exit; }
  /*	According to the MSDN spec, the dwFlags should contain
    DDSD_LINEARSIZE if it's compressed, or DDSD_PITCH if
    uncompressed.  Some DDS writers do not conform to the
    spec, so I need to make my reader more tolerant	*/
    /*	I need one of these	*/
  flag = DDPF_FOURCC | DDPF_RGB;
  if ((header.sPixelFormat.dwFlags & flag) == 0) { goto quick_exit; }
  if (header.sPixelFormat.dwSize != 32) { goto quick_exit; }
  if ((header.sCaps.dwCaps1 & DDSCAPS_TEXTURE) == 0) { goto quick_exit; }
  /*	make sure it is a type we can upload	*/
  if ((header.sPixelFormat.dwFlags & DDPF_FOURCC) &&
    !(
      (header.sPixelFormat.dwFourCC == (('D' << 0) | ('X' << 8) | ('T' << 16) | ('1' << 24))) ||
      (header.sPixelFormat.dwFourCC == (('D' << 0) | ('X' << 8) | ('T' << 16) | ('3' << 24))) ||
      (header.sPixelFormat.dwFourCC == (('D' << 0) | ('X' << 8) | ('T' << 16) | ('5' << 24)))
      ))
  {
    goto quick_exit;
  }
  /*	OK, validated the header, let's load the image data	*/
  result_string_pointer = "DDS header loaded and validated";
  width = header.dwWidth;
  height = header.dwHeight;
  uncompressed = 1 - (header.sPixelFormat.dwFlags & DDPF_FOURCC) / DDPF_FOURCC;
  cubemap = (header.sCaps.dwCaps2 & DDSCAPS2_CUBEMAP) / DDSCAPS2_CUBEMAP;
  if (uncompressed)
  {
    S3TC_type = GL_RGB;
    block_size = 3;
    if (header.sPixelFormat.dwFlags & DDPF_ALPHAPIXELS)
    {
      S3TC_type = GL_RGBA;
      block_size = 4;
    }
    DDS_main_size = width * height * block_size;
  }
  else
  {
    /*	well, we know it is DXT1/3/5, because we checked above	*/
    switch ((header.sPixelFormat.dwFourCC >> 24) - '0')
    {
    case 1:
      S3TC_type = SOIL_RGBA_S3TC_DXT1;
      block_size = 8;
      break;
    case 3:
      S3TC_type = SOIL_RGBA_S3TC_DXT3;
      block_size = 16;
      break;
    case 5:
      S3TC_type = SOIL_RGBA_S3TC_DXT5;
      block_size = 16;
      break;
    }
    DDS_main_size = ((width + 3) >> 2) * ((height + 3) >> 2) * block_size;
  }
  if (cubemap)
  {
    /* does the user want a cubemap?	*/
    if (!loading_as_cubemap)
    {
      /*	we can't do it!	*/
      result_string_pointer = "DDS image was a cubemap";
      return 0;
    }
    ogl_target_start = SOIL_TEXTURE_CUBE_MAP_POSITIVE_X;
    ogl_target_end = SOIL_TEXTURE_CUBE_MAP_NEGATIVE_Z;
    opengl_texture_type = SOIL_TEXTURE_CUBE_MAP;
  }
  else
  {
    /* does the user want a non-cubemap?	*/
    if (loading_as_cubemap)
    {
      /*	we can't do it!	*/
      result_string_pointer = "DDS image was not a cubemap";
      return 0;
    }
    ogl_target_start = GL_TEXTURE_2D;
    ogl_target_end = GL_TEXTURE_2D;
    opengl_texture_type = GL_TEXTURE_2D;
  }
  if ((header.sCaps.dwCaps1 & DDSCAPS_MIPMAP) && (header.dwMipMapCount > 1))
  {
    int shift_offset;
    mipmaps = header.dwMipMapCount - 1;
    DDS_full_size = DDS_main_size;
    if (uncompressed)
    {
      /*	uncompressed DDS, simple MIPmap size calculation	*/
      shift_offset = 0;
    }
    else
    {
      /*	compressed DDS, MIPmap size calculation is block based	*/
      shift_offset = 2;
    }
    for (i = 1; i <= mipmaps; ++i)
    {
      int w, h;
      w = width >> (shift_offset + i);
      h = height >> (shift_offset + i);
      if (w < 1)
      {
        w = 1;
      }
      if (h < 1)
      {
        h = 1;
      }
      DDS_full_size += w * h * block_size;
    }
  }
  else
  {
    mipmaps = 0;
    DDS_full_size = DDS_main_size;
  }
  DDS_data = (unsigned char*)malloc(DDS_full_size);
  /*	got the image data RAM, create or use an existing OpenGL texture handle	*/
  tex_ID = reuse_texture_ID;
  if (tex_ID == 0)
  {
    glGenTextures(1, &tex_ID);
  }
  /*  bind an OpenGL texture ID	*/
  glBindTexture(opengl_texture_type, tex_ID);
  /*	do this for each face of the cubemap!	*/
  for (cf_target = ogl_target_start; cf_target <= ogl_target_end; ++cf_target)
  {
    if (buffer_index + DDS_full_size <= buffer_length)
    {
      unsigned int byte_offset = DDS_main_size;
      memcpy((void*)DDS_data, (const void*)(&buffer[buffer_index]), DDS_full_size);
      buffer_index += DDS_full_size;
      /*	upload the main chunk	*/
      if (uncompressed)
      {
        /*	and remember, DXT uncompressed uses BGR(A),
          so swap to RGB(A) for ALL MIPmap levels	*/
        for (i = 0; i < (int)DDS_full_size; i += block_size)
        {
          unsigned char temp = DDS_data[i];
          DDS_data[i] = DDS_data[i + 2];
          DDS_data[i + 2] = temp;
        }
        glTexImage2D(
          cf_target, 0,
          S3TC_type, width, height, 0,
          S3TC_type, GL_UNSIGNED_BYTE, DDS_data);
      }
      else
      {
        soilGlCompressedTexImage2D(
          cf_target, 0,
          S3TC_type, width, height, 0,
          DDS_main_size, DDS_data);
      }
      /*	upload the mipmaps, if we have them	*/
      for (i = 1; i <= mipmaps; ++i)
      {
        int w, h, mip_size;
        w = width >> i;
        h = height >> i;
        if (w < 1)
        {
          w = 1;
        }
        if (h < 1)
        {
          h = 1;
        }
        /*	upload this mipmap	*/
        if (uncompressed)
        {
          mip_size = w * h * block_size;
          glTexImage2D(
            cf_target, i,
            S3TC_type, w, h, 0,
            S3TC_type, GL_UNSIGNED_BYTE, &DDS_data[byte_offset]);
        }
        else
        {
          mip_size = ((w + 3) / 4) * ((h + 3) / 4) * block_size;
          soilGlCompressedTexImage2D(
            cf_target, i,
            S3TC_type, w, h, 0,
            mip_size, &DDS_data[byte_offset]);
        }
        /*	and move to the next mipmap	*/
        byte_offset += mip_size;
      }
      /*	it worked!	*/
      result_string_pointer = "DDS file loaded";
    }
    else
    {
      glDeleteTextures(1, &tex_ID);
      tex_ID = 0;
      cf_target = ogl_target_end + 1;
      result_string_pointer = "DDS file was too small for expected image data";
    }
  }/* end reading each face */
  SOIL_free_image_data(DDS_data);
  if (tex_ID)
  {
    /*	did I have MIPmaps?	*/
    if (mipmaps > 0)
    {
      /*	instruct OpenGL to use the MIPmaps	*/
      glTexParameteri(opengl_texture_type, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
      glTexParameteri(opengl_texture_type, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
    }
    else
    {
      /*	instruct OpenGL _NOT_ to use the MIPmaps	*/
      glTexParameteri(opengl_texture_type, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
      glTexParameteri(opengl_texture_type, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    }
    /*	does the user want clamping, or wrapping?	*/
    if (flags & SOIL_FLAG_TEXTURE_REPEATS)
    {
      glTexParameteri(opengl_texture_type, GL_TEXTURE_WRAP_S, GL_REPEAT);
      glTexParameteri(opengl_texture_type, GL_TEXTURE_WRAP_T, GL_REPEAT);
      glTexParameteri(opengl_texture_type, SOIL_TEXTURE_WRAP_R, GL_REPEAT);
    }
    else
    {
      unsigned int clamp_mode = SOIL_CLAMP_TO_EDGE;
      /* unsigned int clamp_mode = GL_CLAMP; */
      glTexParameteri(opengl_texture_type, GL_TEXTURE_WRAP_S, clamp_mode);
      glTexParameteri(opengl_texture_type, GL_TEXTURE_WRAP_T, clamp_mode);
      glTexParameteri(opengl_texture_type, SOIL_TEXTURE_WRAP_R, clamp_mode);
    }
  }

quick_exit:
  /*	report success or failure	*/
  return tex_ID;
}

unsigned int SOIL_direct_load_DDS(
  const char* filename,
  unsigned int reuse_texture_ID,
  int flags,
  int loading_as_cubemap)
{
  FILE* f;
  unsigned char* buffer;
  size_t buffer_length, bytes_read;
  unsigned int tex_ID = 0;
  /*	error checks	*/
  if (nullptr == filename)
  {
    result_string_pointer = "nullptr filename";
    return 0;
  }
  f = fopen(filename, "rb");
  if (nullptr == f)
  {
    /*	the file doesn't seem to exist (or be open-able)	*/
    result_string_pointer = "Can not find DDS file";
    return 0;
  }
  fseek(f, 0, SEEK_END);
  buffer_length = ftell(f);
  fseek(f, 0, SEEK_SET);
  buffer = (unsigned char*)malloc(buffer_length);
  if (nullptr == buffer)
  {
    result_string_pointer = "malloc failed";
    fclose(f);
    return 0;
  }
  bytes_read = fread((void*)buffer, 1, buffer_length, f);
  fclose(f);
  if (bytes_read < buffer_length)
  {
    /*	huh?	*/
    buffer_length = bytes_read;
  }
  /*	now try to do the loading	*/
  tex_ID = SOIL_direct_load_DDS_from_memory(
    (const unsigned char* const)buffer, (int)buffer_length,
    reuse_texture_ID, flags, loading_as_cubemap);
  SOIL_free_image_data(buffer);
  return tex_ID;
}

unsigned int SOIL_direct_load_PVR_from_memory(
  const unsigned char* const buffer,
  int buffer_length,
  unsigned int reuse_texture_ID,
  int flags,
  int loading_as_cubemap)
{
  PVR_Texture_Header* header = (PVR_Texture_Header*)buffer;
  int num_surfs = 1;
  GLuint tex_ID = 0;
  GLenum PVR_format = 0;
  GLenum PVR_type = GL_RGB;
  unsigned int opengl_texture_type = loading_as_cubemap ? SOIL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D;
  int is_PVRTC_supported = 1;
  int is_BGRA8888_supported = 1;
  int is_compressed_format_supported = 0;
  int is_compressed_format = 0;
  int mipmaps = 0;
  int i;
  GLint unpack_aligment;

  // Check the header size
  if (header->dwHeaderSize != sizeof(PVR_Texture_Header)) {
    if (header->dwHeaderSize == PVRTEX_V1_HEADER_SIZE) {
      result_string_pointer = "this is an old pvr ( update the PVR file )";

      if (loading_as_cubemap) {
        if (header->dwpfFlags & PVRTEX_CUBEMAP) {
          num_surfs = 6;
        }
        else {
          result_string_pointer = "tried to load a non-cubemap PVR as cubemap";
          return 0;
        }
      }
    }
    else {
      result_string_pointer = "invalid PVR header";

      return 0;
    }
  }
  else {
    if (loading_as_cubemap) {
      // Header V2
      if (header->dwNumSurfs < 1) {
        if (header->dwpfFlags & PVRTEX_CUBEMAP) {
          num_surfs = 6;
        }
        else {
          result_string_pointer = "tried to load a non-cubemap PVR as cubemap";
          return 0;
        }
      }
      else {
        num_surfs = header->dwNumSurfs;
      }
    }
  }

  // Check the magic identifier
  if (header->dwPVR != PVRTEX_IDENTIFIER) {
    result_string_pointer = "invalid PVR header";
    return 0;
  }

  /* Only accept untwiddled data UNLESS texture format is PVRTC */
  if (((header->dwpfFlags & PVRTEX_TWIDDLE) == PVRTEX_TWIDDLE)
    && ((header->dwpfFlags & PVRTEX_PIXELTYPE) != OGL_PVRTC2)
    && ((header->dwpfFlags & PVRTEX_PIXELTYPE) != OGL_PVRTC4))
  {
    // We need to load untwiddled textures -- hw will twiddle for us.
    result_string_pointer = "pvr is not compressed ( untwiddled texture )";
    return 0;
  }

  switch (header->dwpfFlags & PVRTEX_PIXELTYPE)
  {
  case OGL_RGBA_4444:
    PVR_format = GL_UNSIGNED_SHORT_4_4_4_4;
    PVR_type = GL_RGBA;
    break;
  case OGL_RGBA_5551:
    PVR_format = GL_UNSIGNED_SHORT_5_5_5_1;
    PVR_type = GL_RGBA;
    break;
  case OGL_RGBA_8888:
    PVR_format = GL_UNSIGNED_BYTE;
    PVR_type = GL_RGBA;
    break;
  case OGL_RGB_565:
    PVR_format = GL_UNSIGNED_SHORT_5_6_5;
    PVR_type = GL_RGB;
    break;
  case OGL_RGB_555:
    result_string_pointer = "failed: pixel type OGL_RGB_555 not supported.";
    return 0;
  case OGL_RGB_888:
    PVR_format = GL_UNSIGNED_BYTE;
    PVR_type = GL_RGB;
    break;
  case OGL_I_8:
    PVR_format = GL_UNSIGNED_BYTE;
    PVR_type = GL_LUMINANCE;
    break;
  case OGL_AI_88:
    PVR_format = GL_UNSIGNED_BYTE;
    PVR_type = GL_LUMINANCE_ALPHA;
    break;
  case MGLPT_PVRTC2:
  case OGL_PVRTC2:
    if (is_PVRTC_supported) {
      is_compressed_format_supported = is_compressed_format = 1;
      PVR_format = header->dwAlphaBitMask == 0 ? SOIL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG : SOIL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG;	// PVRTC2
    }
    else {
      result_string_pointer = "error: PVRTC2 not supported.Decompress the texture first.";
      return 0;
    }
    break;
  case MGLPT_PVRTC4:
  case OGL_PVRTC4:
    if (is_PVRTC_supported) {
      is_compressed_format_supported = is_compressed_format = 1;
      PVR_format = header->dwAlphaBitMask == 0 ? SOIL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG : SOIL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;	// PVRTC4
    }
    else {
      result_string_pointer = "error: PVRTC4 not supported. Decompress the texture first.";
      return 0;
    }
    break;
  case OGL_BGRA_8888:
    if (is_BGRA8888_supported) {
      PVR_format = GL_UNSIGNED_BYTE;
      PVR_type = GL_BGRA;
      break;
    }
    else {
      result_string_pointer = "Unable to load GL_BGRA texture as extension GL_IMG_texture_format_BGRA8888 is unsupported.";
      return 0;
    }
  default:											// NOT SUPPORTED
    result_string_pointer = "failed: pixel type not supported.";
    return 0;
  }

#ifdef SOIL_GLES1
  //  check that this data is cube map data or not.
  if (loading_as_cubemap) {
    result_string_pointer = "cube map textures are not available in GLES1.x.";
    return 0;
  }
#endif

  // load the texture up
  tex_ID = reuse_texture_ID;
  if (tex_ID == 0)
  {
    glGenTextures(1, &tex_ID);
  }

  glBindTexture(opengl_texture_type, tex_ID);

  if (glGetError()) {
    result_string_pointer = "failed: glBindTexture() failed.";
    return 0;
  }

  glGetIntegerv(GL_UNPACK_ALIGNMENT, &unpack_aligment);
  if (1 != unpack_aligment)
  {
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);				// Never have row-aligned in headers
  }

#define _MAX( a, b ) (( a <= b )? b : a)
  for (i = 0; i < num_surfs; i++) {
    char* texture_ptr = (char*)buffer + header->dwHeaderSize + header->dwTextureDataSize * i;
    char* cur_texture_ptr = 0;
    int	mipmap_level;
    unsigned int width = header->dwWidth, height = header->dwHeight;
    unsigned int compressed_image_size = 0;

    mipmaps = ((flags & SOIL_FLAG_MIPMAPS) && (header->dwpfFlags & PVRTEX_MIPMAP)) ? header->dwMipMapCount : 0;

    for (mipmap_level = 0; mipmap_level <= mipmaps; width = _MAX(width / 2, (unsigned int)1), height = _MAX(height / 2, (unsigned int)1), mipmap_level++) {
      // Do Alpha-swap if needed
      cur_texture_ptr = texture_ptr;

      // Load the Texture_
      /* If the texture is PVRTC then use GLCompressedTexImage2D */
      if (is_compressed_format) {
        /* Calculate how many bytes this MIP level occupies */
        if ((header->dwpfFlags & PVRTEX_PIXELTYPE) == OGL_PVRTC2) {
          compressed_image_size = (_MAX(width, PVRTC2_MIN_TEXWIDTH) * _MAX(height, PVRTC2_MIN_TEXHEIGHT) * header->dwBitCount + 7) / 8;
        }
        else {// PVRTC4 case
          compressed_image_size = (_MAX(width, PVRTC4_MIN_TEXWIDTH) * _MAX(height, PVRTC4_MIN_TEXHEIGHT) * header->dwBitCount + 7) / 8;
        }

        if (is_compressed_format_supported) {
          /* Load compressed texture data at selected MIP level */
          if (loading_as_cubemap) {
            soilGlCompressedTexImage2D(SOIL_TEXTURE_CUBE_MAP_POSITIVE_X + i, mipmap_level, PVR_format, width, height, 0, compressed_image_size, cur_texture_ptr);
          }
          else {
            soilGlCompressedTexImage2D(opengl_texture_type, mipmap_level, PVR_format, width, height, 0, compressed_image_size, cur_texture_ptr);
          }
        }
        else {
          result_string_pointer = "failed: GPU doesnt support compressed textures";
        }
      }
      else {
        /* Load uncompressed texture data at selected MIP level */
        if (loading_as_cubemap) {
          glTexImage2D(SOIL_TEXTURE_CUBE_MAP_POSITIVE_X + i, mipmap_level, PVR_type, width, height, 0, PVR_type, PVR_format, cur_texture_ptr);
        }
        else {
          glTexImage2D(opengl_texture_type, mipmap_level, PVR_type, width, height, 0, PVR_type, PVR_format, cur_texture_ptr);
        }
      }

      if (glGetError()) {
        result_string_pointer = "failed: glCompressedTexImage2D() failed.";
        if (1 != unpack_aligment)
        {
          glPixelStorei(GL_UNPACK_ALIGNMENT, unpack_aligment);
        }
        return 0;
      }

      // offset the texture pointer by one mip-map level
      /* PVRTC case */
      if (is_compressed_format) {
        texture_ptr += compressed_image_size;
      }
      else {
        /* New formula that takes into account bit counts inferior to 8 (e.g. 1 bpp) */
        texture_ptr += (width * height * header->dwBitCount + 7) / 8;
      }
    }
  }
#undef _MAX

  if (1 != unpack_aligment)
  {
    glPixelStorei(GL_UNPACK_ALIGNMENT, unpack_aligment);
  }

  if (tex_ID)
  {
    /*	did I have MIPmaps?	*/
    if (mipmaps)
    {
      /*	instruct OpenGL to use the MIPmaps	*/
      glTexParameteri(opengl_texture_type, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
      glTexParameteri(opengl_texture_type, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
    }
    else
    {
      /*	instruct OpenGL _NOT_ to use the MIPmaps	*/
      glTexParameteri(opengl_texture_type, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
      glTexParameteri(opengl_texture_type, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    }

    /*	does the user want clamping, or wrapping?	*/
    if (flags & SOIL_FLAG_TEXTURE_REPEATS)
    {
      glTexParameteri(opengl_texture_type, GL_TEXTURE_WRAP_S, GL_REPEAT);
      glTexParameteri(opengl_texture_type, GL_TEXTURE_WRAP_T, GL_REPEAT);
      glTexParameteri(opengl_texture_type, SOIL_TEXTURE_WRAP_R, GL_REPEAT);
    }
    else
    {
      unsigned int clamp_mode = SOIL_CLAMP_TO_EDGE;
      /* unsigned int clamp_mode = GL_CLAMP; */
      glTexParameteri(opengl_texture_type, GL_TEXTURE_WRAP_S, clamp_mode);
      glTexParameteri(opengl_texture_type, GL_TEXTURE_WRAP_T, clamp_mode);
      glTexParameteri(opengl_texture_type, SOIL_TEXTURE_WRAP_R, clamp_mode);
    }
  }

  return tex_ID;
}

unsigned int SOIL_direct_load_PVR(
  const char* filename,
  unsigned int reuse_texture_ID,
  int flags,
  int loading_as_cubemap)
{
  FILE* f;
  unsigned char* buffer;
  size_t buffer_length, bytes_read;
  unsigned int tex_ID = 0;
  /*	error checks	*/
  if (nullptr == filename)
  {
    result_string_pointer = "nullptr filename";
    return 0;
  }
  f = fopen(filename, "rb");
  if (nullptr == f)
  {
    /*	the file doesn't seem to exist (or be open-able)	*/
    result_string_pointer = "Can not find PVR file";
    return 0;
  }
  fseek(f, 0, SEEK_END);
  buffer_length = ftell(f);
  fseek(f, 0, SEEK_SET);
  buffer = (unsigned char*)malloc(buffer_length);
  if (nullptr == buffer)
  {
    result_string_pointer = "malloc failed";
    fclose(f);
    return 0;
  }
  bytes_read = fread((void*)buffer, 1, buffer_length, f);
  fclose(f);
  if (bytes_read < buffer_length)
  {
    /*	huh?	*/
    buffer_length = bytes_read;
  }
  /*	now try to do the loading	*/
  tex_ID = SOIL_direct_load_PVR_from_memory(
    (const unsigned char* const)buffer, (int)buffer_length,
    reuse_texture_ID, flags, loading_as_cubemap);
  SOIL_free_image_data(buffer);
  return tex_ID;
}

unsigned int SOIL_direct_load_ETC1_from_memory(
  const unsigned char* const buffer,
  int buffer_length,
  unsigned int reuse_texture_ID,
  int flags)
{
  GLuint tex_ID = 0;
  PKMHeader* header = (PKMHeader*)buffer;
  unsigned int opengl_texture_type = GL_TEXTURE_2D;
  unsigned int width;
  unsigned int height;
  unsigned long compressed_image_size = buffer_length - PKM_HEADER_SIZE;
  char* texture_ptr = (char*)buffer + PKM_HEADER_SIZE;
  GLint unpack_aligment;

  if (0 != strcmp(header->aName, "PKM 10")) {
    result_string_pointer = "error: PKM 10 header not found.";
    return 0;
  }

  width = (header->iWidthMSB << 8) | header->iWidthLSB;
  height = (header->iHeightMSB << 8) | header->iHeightLSB;
  compressed_image_size = (((width + 3) & ~3) * ((height + 3) & ~3)) >> 1;

  // load the texture up
  tex_ID = reuse_texture_ID;
  if (tex_ID == 0)
  {
    glGenTextures(1, &tex_ID);
  }

  glBindTexture(opengl_texture_type, tex_ID);

  if (glGetError()) {
    result_string_pointer = "failed: glBindTexture() failed.";
    return 0;
  }

  glGetIntegerv(GL_UNPACK_ALIGNMENT, &unpack_aligment);
  if (1 != unpack_aligment)
  {
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);				// Never have row-aligned in headers
  }

  soilGlCompressedTexImage2D(opengl_texture_type, 0, SOIL_GL_ETC1_RGB8_OES, width, height, 0, compressed_image_size, texture_ptr);

  if (glGetError()) {
    result_string_pointer = "failed: glCompressedTexImage2D() failed.";

    if (1 != unpack_aligment)
    {
      glPixelStorei(GL_UNPACK_ALIGNMENT, unpack_aligment);
    }
    return 0;
  }

  if (1 != unpack_aligment)
  {
    glPixelStorei(GL_UNPACK_ALIGNMENT, unpack_aligment);
  }

  if (tex_ID)
  {
    /* No MIPmaps for ETC1 */
    glTexParameteri(opengl_texture_type, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(opengl_texture_type, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

    /*	does the user want clamping, or wrapping?	*/
    if (flags & SOIL_FLAG_TEXTURE_REPEATS)
    {
      glTexParameteri(opengl_texture_type, GL_TEXTURE_WRAP_S, GL_REPEAT);
      glTexParameteri(opengl_texture_type, GL_TEXTURE_WRAP_T, GL_REPEAT);
      glTexParameteri(opengl_texture_type, SOIL_TEXTURE_WRAP_R, GL_REPEAT);
    }
    else
    {
      unsigned int clamp_mode = SOIL_CLAMP_TO_EDGE;
      /* unsigned int clamp_mode = GL_CLAMP; */
      glTexParameteri(opengl_texture_type, GL_TEXTURE_WRAP_S, clamp_mode);
      glTexParameteri(opengl_texture_type, GL_TEXTURE_WRAP_T, clamp_mode);
      glTexParameteri(opengl_texture_type, SOIL_TEXTURE_WRAP_R, clamp_mode);
    }
  }

  return tex_ID;
}

unsigned int SOIL_direct_load_ETC1(const char* filename,
  unsigned int reuse_texture_ID,
  int flags)
{
  FILE* f;
  unsigned char* buffer;
  size_t buffer_length, bytes_read;
  unsigned int tex_ID = 0;
  /*	error checks	*/
  if (nullptr == filename)
  {
    result_string_pointer = "nullptr filename";
    return 0;
  }
  f = fopen(filename, "rb");
  if (nullptr == f)
  {
    /*	the file doesn't seem to exist (or be open-able)	*/
    result_string_pointer = "Can not find PVR file";
    return 0;
  }
  fseek(f, 0, SEEK_END);
  buffer_length = ftell(f);
  fseek(f, 0, SEEK_SET);
  buffer = (unsigned char*)malloc(buffer_length);
  if (nullptr == buffer)
  {
    result_string_pointer = "malloc failed";
    fclose(f);
    return 0;
  }
  bytes_read = fread((void*)buffer, 1, buffer_length, f);
  fclose(f);
  if (bytes_read < buffer_length)
  {
    /*	huh?	*/
    buffer_length = bytes_read;
  }
  /*	now try to do the loading	*/
  tex_ID = SOIL_direct_load_ETC1_from_memory(
    (const unsigned char* const)buffer, (int)buffer_length,
    reuse_texture_ID, flags);
  SOIL_free_image_data(buffer);
  return tex_ID;
}

namespace glm
{
  using std::size_t;
  typedef int length_t;
}//namespace glm

namespace glm
{
  template<typename T, std::size_t N>
  constexpr std::size_t countof(T const (&)[N])
  {
    return N;
  }
}//namespace glm

namespace glm {
  namespace detail
  {
    template<typename T>
    struct is_int
    {
      enum test { value = 0 };
    };

    template<>
    struct is_int<unsigned int>
    {
      enum test { value = ~0 };
    };

    template<>
    struct is_int<signed int>
    {
      enum test { value = ~0 };
    };
  }//namespace detail

  typedef unsigned int	uint;
}//namespace glm

///////////////////////////////////////////////////////////////////////////////////
// 64-bit int

namespace glm {
  namespace detail
  {
    typedef std::uint64_t						uint64;
    typedef std::int64_t						int64;
  }//namespace detail
}//namespace glm

namespace glm {
  namespace detail
  {
    using std::make_unsigned;
  }//namespace detail
}//namespace glm

namespace glm
{
  /// Qualify GLM types in term of alignment (packed, aligned) and precision in term of ULPs (lowp, mediump, highp)
  enum qualifier
  {
    packed_highp, ///< Typed data is tightly packed in memory and operations are executed with high precision in term of ULPs
    packed_mediump, ///< Typed data is tightly packed in memory  and operations are executed with medium precision in term of ULPs for higher performance
    packed_lowp, ///< Typed data is tightly packed in memory  and operations are executed with low precision in term of ULPs to maximize performance
    highp = packed_highp, ///< By default highp qualifier is also packed
    mediump = packed_mediump, ///< By default mediump qualifier is also packed
    lowp = packed_lowp, ///< By default lowp qualifier is also packed
    packed = packed_highp, ///< By default packed qualifier is also high precision
    defaultp = highp
  };

  typedef qualifier precision;

  template<length_t L, typename T, qualifier Q = defaultp> struct vec;
  template<length_t C, length_t R, typename T, qualifier Q = defaultp> struct mat;
  template<typename T, qualifier Q = defaultp> struct qua;


  template <typename T, qualifier Q = defaultp> using tvec1 = vec<1, T, Q>;
  template <typename T, qualifier Q = defaultp> using tvec2 = vec<2, T, Q>;
  template <typename T, qualifier Q = defaultp> using tvec3 = vec<3, T, Q>;
  template <typename T, qualifier Q = defaultp> using tvec4 = vec<4, T, Q>;
  template <typename T, qualifier Q = defaultp> using tmat2x2 = mat<2, 2, T, Q>;
  template <typename T, qualifier Q = defaultp> using tmat2x3 = mat<2, 3, T, Q>;
  template <typename T, qualifier Q = defaultp> using tmat2x4 = mat<2, 4, T, Q>;
  template <typename T, qualifier Q = defaultp> using tmat3x2 = mat<3, 2, T, Q>;
  template <typename T, qualifier Q = defaultp> using tmat3x3 = mat<3, 3, T, Q>;
  template <typename T, qualifier Q = defaultp> using tmat3x4 = mat<3, 4, T, Q>;
  template <typename T, qualifier Q = defaultp> using tmat4x2 = mat<4, 2, T, Q>;
  template <typename T, qualifier Q = defaultp> using tmat4x3 = mat<4, 3, T, Q>;
  template <typename T, qualifier Q = defaultp> using tmat4x4 = mat<4, 4, T, Q>;
  template <typename T, qualifier Q = defaultp> using tquat = qua<T, Q>;


  namespace detail
  {
    template<glm::qualifier P>
    struct is_aligned
    {
      static const bool value = false;
    };

    template<length_t L, typename T, bool is_aligned>
    struct storage
    {
      typedef struct type {
        T data[L];
      } type;
    };


    template<length_t L, typename T>
    struct storage<L, T, true>
    {
      typedef struct alignas(L * sizeof(T)) type {
        T data[L];
      } type;
    };

    template<typename T>
    struct storage<3, T, true>
    {
      typedef struct alignas(4 * sizeof(T)) type {
        T data[4];
      } type;
    };

    enum genTypeEnum
    {
      GENTYPE_VEC,
      GENTYPE_MAT,
      GENTYPE_QUAT
    };

    template <typename genType>
    struct genTypeTrait
    {};

    template <length_t C, length_t R, typename T>
    struct genTypeTrait<mat<C, R, T> >
    {
      static const genTypeEnum GENTYPE = GENTYPE_MAT;
    };

    template<typename genType, genTypeEnum type>
    struct init_gentype
    {
    };

    template<typename genType>
    struct init_gentype<genType, GENTYPE_QUAT>
    {
      inline constexpr static genType identity()
      {
        return genType(1, 0, 0, 0);
      }
    };

    template<typename genType>
    struct init_gentype<genType, GENTYPE_MAT>
    {
      inline constexpr static genType identity()
      {
        return genType(1);
      }
    };
  }//namespace detail
}//namespace glm

namespace glm
{
  template<typename T, qualifier Q>
  struct vec<4, T, Q>
  {
    // -- Implementation detail --

    typedef T value_type;
    typedef vec<4, T, Q> type;
    typedef vec<4, bool, Q> bool_type;

    // -- Data --
    union { T x, r, s; };
    union { T y, g, t; };
    union { T z, b, p; };
    union { T w, a, q; };

    // -- Component accesses --

    typedef length_t length_type;

    /// Return the count of components of the vector
    static constexpr length_type length() { return 4; }

    constexpr T& operator[](length_type i);
    constexpr T const& operator[](length_type i) const;

    // -- Implicit basic constructors --

    constexpr vec() = default;
    constexpr vec(vec<4, T, Q> const& v) = default;
    template<qualifier P>
    constexpr vec(vec<4, T, P> const& v);

    // -- Explicit basic constructors --

    constexpr explicit vec(T scalar);
    constexpr vec(T x, T y, T z, T w);

    // -- Conversion scalar constructors --

    template<typename U, qualifier P>
    constexpr explicit vec(vec<1, U, P> const& v);

    /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification)
    template<typename X, typename Y, typename Z, typename W>
    constexpr vec(X _x, Y _y, Z _z, W _w);
    template<typename X, typename Y, typename Z, typename W>
    constexpr vec(vec<1, X, Q> const& _x, Y _y, Z _z, W _w);
    template<typename X, typename Y, typename Z, typename W>
    constexpr vec(X _x, vec<1, Y, Q> const& _y, Z _z, W _w);
    template<typename X, typename Y, typename Z, typename W>
    constexpr vec(vec<1, X, Q> const& _x, vec<1, Y, Q> const& _y, Z _z, W _w);
    template<typename X, typename Y, typename Z, typename W>
    constexpr vec(X _x, Y _y, vec<1, Z, Q> const& _z, W _w);
    template<typename X, typename Y, typename Z, typename W>
    constexpr vec(vec<1, X, Q> const& _x, Y _y, vec<1, Z, Q> const& _z, W _w);
    template<typename X, typename Y, typename Z, typename W>
    constexpr vec(X _x, vec<1, Y, Q> const& _y, vec<1, Z, Q> const& _z, W _w);
    template<typename X, typename Y, typename Z, typename W>
    constexpr vec(vec<1, X, Q> const& _x, vec<1, Y, Q> const& _y, vec<1, Z, Q> const& _z, W _w);
    template<typename X, typename Y, typename Z, typename W>
    constexpr vec(vec<1, X, Q> const& _x, Y _y, Z _z, vec<1, W, Q> const& _w);
    template<typename X, typename Y, typename Z, typename W>
    constexpr vec(X _x, vec<1, Y, Q> const& _y, Z _z, vec<1, W, Q> const& _w);
    template<typename X, typename Y, typename Z, typename W>
    constexpr vec(vec<1, X, Q> const& _x, vec<1, Y, Q> const& _y, Z _z, vec<1, W, Q> const& _w);
    template<typename X, typename Y, typename Z, typename W>
    constexpr vec(X _x, Y _y, vec<1, Z, Q> const& _z, vec<1, W, Q> const& _w);
    template<typename X, typename Y, typename Z, typename W>
    constexpr vec(vec<1, X, Q> const& _x, Y _y, vec<1, Z, Q> const& _z, vec<1, W, Q> const& _w);
    template<typename X, typename Y, typename Z, typename W>
    constexpr vec(X _x, vec<1, Y, Q> const& _y, vec<1, Z, Q> const& _z, vec<1, W, Q> const& _w);
    template<typename X, typename Y, typename Z, typename W>
    constexpr vec(vec<1, X, Q> const& _x, vec<1, Y, Q> const& _Y, vec<1, Z, Q> const& _z, vec<1, W, Q> const& _w);

    // -- Conversion vector constructors --

    /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification)
    template<typename A, typename B, typename C, qualifier P>
    constexpr vec(vec<2, A, P> const& _xy, B _z, C _w);
    /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification)
    template<typename A, typename B, typename C, qualifier P>
    constexpr vec(vec<2, A, P> const& _xy, vec<1, B, P> const& _z, C _w);
    /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification)
    template<typename A, typename B, typename C, qualifier P>
    constexpr vec(vec<2, A, P> const& _xy, B _z, vec<1, C, P> const& _w);
    /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification)
    template<typename A, typename B, typename C, qualifier P>
    constexpr vec(vec<2, A, P> const& _xy, vec<1, B, P> const& _z, vec<1, C, P> const& _w);
    /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification)
    template<typename A, typename B, typename C, qualifier P>
    constexpr vec(A _x, vec<2, B, P> const& _yz, C _w);
    /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification)
    template<typename A, typename B, typename C, qualifier P>
    constexpr vec(vec<1, A, P> const& _x, vec<2, B, P> const& _yz, C _w);
    /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification)
    template<typename A, typename B, typename C, qualifier P>
    constexpr vec(A _x, vec<2, B, P> const& _yz, vec<1, C, P> const& _w);
    /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification)
    template<typename A, typename B, typename C, qualifier P>
    constexpr vec(vec<1, A, P> const& _x, vec<2, B, P> const& _yz, vec<1, C, P> const& _w);
    /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification)
    template<typename A, typename B, typename C, qualifier P>
    constexpr vec(A _x, B _y, vec<2, C, P> const& _zw);
    /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification)
    template<typename A, typename B, typename C, qualifier P>
    constexpr vec(vec<1, A, P> const& _x, B _y, vec<2, C, P> const& _zw);
    /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification)
    template<typename A, typename B, typename C, qualifier P>
    constexpr vec(A _x, vec<1, B, P> const& _y, vec<2, C, P> const& _zw);
    /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification)
    template<typename A, typename B, typename C, qualifier P>
    constexpr vec(vec<1, A, P> const& _x, vec<1, B, P> const& _y, vec<2, C, P> const& _zw);
    /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification)
    template<typename A, typename B, qualifier P>
    constexpr vec(vec<3, A, P> const& _xyz, B _w);
    /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification)
    template<typename A, typename B, qualifier P>
    constexpr vec(vec<3, A, P> const& _xyz, vec<1, B, P> const& _w);
    /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification)
    template<typename A, typename B, qualifier P>
    constexpr vec(A _x, vec<3, B, P> const& _yzw);
    /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification)
    template<typename A, typename B, qualifier P>
    constexpr vec(vec<1, A, P> const& _x, vec<3, B, P> const& _yzw);
    /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification)
    template<typename A, typename B, qualifier P>
    constexpr vec(vec<2, A, P> const& _xy, vec<2, B, P> const& _zw);

    /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification)
    template<typename U, qualifier P>
    constexpr  vec(vec<4, U, P> const& v);

    // -- Swizzle constructors --

    // -- Unary arithmetic operators --

    constexpr vec<4, T, Q>& operator=(vec<4, T, Q> const& v) = default;

    template<typename U>
    constexpr vec<4, T, Q>& operator=(vec<4, U, Q> const& v);
    template<typename U>
    constexpr vec<4, T, Q>& operator+=(U scalar);
    template<typename U>
    constexpr vec<4, T, Q>& operator+=(vec<1, U, Q> const& v);
    template<typename U>
    constexpr vec<4, T, Q>& operator+=(vec<4, U, Q> const& v);
    template<typename U>
    constexpr vec<4, T, Q>& operator-=(U scalar);
    template<typename U>
    constexpr vec<4, T, Q>& operator-=(vec<1, U, Q> const& v);
    template<typename U>
    constexpr vec<4, T, Q>& operator-=(vec<4, U, Q> const& v);
    template<typename U>
    constexpr vec<4, T, Q>& operator*=(U scalar);
    template<typename U>
    constexpr vec<4, T, Q>& operator*=(vec<1, U, Q> const& v);
    template<typename U>
    constexpr vec<4, T, Q>& operator*=(vec<4, U, Q> const& v);
    template<typename U>
    constexpr vec<4, T, Q>& operator/=(U scalar);
    template<typename U>
    constexpr vec<4, T, Q>& operator/=(vec<1, U, Q> const& v);
    template<typename U>
    constexpr vec<4, T, Q>& operator/=(vec<4, U, Q> const& v);

    // -- Increment and decrement operators --

    constexpr vec<4, T, Q>& operator++();
    constexpr vec<4, T, Q>& operator--();
    constexpr vec<4, T, Q> operator++(int);
    constexpr vec<4, T, Q> operator--(int);

    // -- Unary bit operators --

    template<typename U>
    constexpr vec<4, T, Q>& operator%=(U scalar);
    template<typename U>
    constexpr vec<4, T, Q>& operator%=(vec<1, U, Q> const& v);
    template<typename U>
    constexpr vec<4, T, Q>& operator%=(vec<4, U, Q> const& v);
    template<typename U>
    constexpr vec<4, T, Q>& operator&=(U scalar);
    template<typename U>
    constexpr vec<4, T, Q>& operator&=(vec<1, U, Q> const& v);
    template<typename U>
    constexpr vec<4, T, Q>& operator&=(vec<4, U, Q> const& v);
    template<typename U>
    constexpr vec<4, T, Q>& operator|=(U scalar);
    template<typename U>
    constexpr vec<4, T, Q>& operator|=(vec<1, U, Q> const& v);
    template<typename U>
    constexpr vec<4, T, Q>& operator|=(vec<4, U, Q> const& v);
    template<typename U>
    constexpr vec<4, T, Q>& operator^=(U scalar);
    template<typename U>
    constexpr vec<4, T, Q>& operator^=(vec<1, U, Q> const& v);
    template<typename U>
    constexpr vec<4, T, Q>& operator^=(vec<4, U, Q> const& v);
    template<typename U>
    constexpr vec<4, T, Q>& operator<<=(U scalar);
    template<typename U>
    constexpr vec<4, T, Q>& operator<<=(vec<1, U, Q> const& v);
    template<typename U>
    constexpr vec<4, T, Q>& operator<<=(vec<4, U, Q> const& v);
    template<typename U>
    constexpr vec<4, T, Q>& operator>>=(U scalar);
    template<typename U>
    constexpr vec<4, T, Q>& operator>>=(vec<1, U, Q> const& v);
    template<typename U>
    constexpr vec<4, T, Q>& operator>>=(vec<4, U, Q> const& v);
  };

  // -- Unary operators --

  template<typename T, qualifier Q>
  constexpr vec<4, T, Q> operator+(vec<4, T, Q> const& v);

  template<typename T, qualifier Q>
  constexpr vec<4, T, Q> operator-(vec<4, T, Q> const& v);

  // -- Binary operators --

  template<typename T, qualifier Q>
  constexpr vec<4, T, Q> operator+(vec<4, T, Q> const& v, T const& scalar);

  template<typename T, qualifier Q>
  constexpr vec<4, T, Q> operator+(vec<4, T, Q> const& v1, vec<1, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<4, T, Q> operator+(T scalar, vec<4, T, Q> const& v);

  template<typename T, qualifier Q>
  constexpr vec<4, T, Q> operator+(vec<1, T, Q> const& v1, vec<4, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<4, T, Q> operator+(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<4, T, Q> operator-(vec<4, T, Q> const& v, T const& scalar);

  template<typename T, qualifier Q>
  constexpr vec<4, T, Q> operator-(vec<4, T, Q> const& v1, vec<1, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<4, T, Q> operator-(T scalar, vec<4, T, Q> const& v);

  template<typename T, qualifier Q>
  constexpr vec<4, T, Q> operator-(vec<1, T, Q> const& v1, vec<4, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<4, T, Q> operator-(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<4, T, Q> operator*(vec<4, T, Q> const& v, T const& scalar);

  template<typename T, qualifier Q>
  constexpr vec<4, T, Q> operator*(vec<4, T, Q> const& v1, vec<1, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<4, T, Q> operator*(T scalar, vec<4, T, Q> const& v);

  template<typename T, qualifier Q>
  constexpr vec<4, T, Q> operator*(vec<1, T, Q> const& v1, vec<4, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<4, T, Q> operator*(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<4, T, Q> operator/(vec<4, T, Q> const& v, T const& scalar);

  template<typename T, qualifier Q>
  constexpr vec<4, T, Q> operator/(vec<4, T, Q> const& v1, vec<1, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<4, T, Q> operator/(T scalar, vec<4, T, Q> const& v);

  template<typename T, qualifier Q>
  constexpr vec<4, T, Q> operator/(vec<1, T, Q> const& v1, vec<4, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<4, T, Q> operator/(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<4, T, Q> operator%(vec<4, T, Q> const& v, T scalar);

  template<typename T, qualifier Q>
  constexpr vec<4, T, Q> operator%(vec<4, T, Q> const& v, vec<1, T, Q> const& scalar);

  template<typename T, qualifier Q>
  constexpr vec<4, T, Q> operator%(T scalar, vec<4, T, Q> const& v);

  template<typename T, qualifier Q>
  constexpr vec<4, T, Q> operator%(vec<1, T, Q> const& scalar, vec<4, T, Q> const& v);

  template<typename T, qualifier Q>
  constexpr vec<4, T, Q> operator%(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<4, T, Q> operator&(vec<4, T, Q> const& v, T scalar);

  template<typename T, qualifier Q>
  constexpr vec<4, T, Q> operator&(vec<4, T, Q> const& v, vec<1, T, Q> const& scalar);

  template<typename T, qualifier Q>
  constexpr vec<4, T, Q> operator&(T scalar, vec<4, T, Q> const& v);

  template<typename T, qualifier Q>
  constexpr vec<4, T, Q> operator&(vec<1, T, Q> const& scalar, vec<4, T, Q> const& v);

  template<typename T, qualifier Q>
  constexpr vec<4, T, Q> operator&(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<4, T, Q> operator|(vec<4, T, Q> const& v, T scalar);

  template<typename T, qualifier Q>
  constexpr vec<4, T, Q> operator|(vec<4, T, Q> const& v, vec<1, T, Q> const& scalar);

  template<typename T, qualifier Q>
  constexpr vec<4, T, Q> operator|(T scalar, vec<4, T, Q> const& v);

  template<typename T, qualifier Q>
  constexpr vec<4, T, Q> operator|(vec<1, T, Q> const& scalar, vec<4, T, Q> const& v);

  template<typename T, qualifier Q>
  constexpr vec<4, T, Q> operator|(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<4, T, Q> operator^(vec<4, T, Q> const& v, T scalar);

  template<typename T, qualifier Q>
  constexpr vec<4, T, Q> operator^(vec<4, T, Q> const& v, vec<1, T, Q> const& scalar);

  template<typename T, qualifier Q>
  constexpr vec<4, T, Q> operator^(T scalar, vec<4, T, Q> const& v);

  template<typename T, qualifier Q>
  constexpr vec<4, T, Q> operator^(vec<1, T, Q> const& scalar, vec<4, T, Q> const& v);

  template<typename T, qualifier Q>
  constexpr vec<4, T, Q> operator^(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<4, T, Q> operator<<(vec<4, T, Q> const& v, T scalar);

  template<typename T, qualifier Q>
  constexpr vec<4, T, Q> operator<<(vec<4, T, Q> const& v, vec<1, T, Q> const& scalar);

  template<typename T, qualifier Q>
  constexpr vec<4, T, Q> operator<<(T scalar, vec<4, T, Q> const& v);

  template<typename T, qualifier Q>
  constexpr vec<4, T, Q> operator<<(vec<1, T, Q> const& scalar, vec<4, T, Q> const& v);

  template<typename T, qualifier Q>
  constexpr vec<4, T, Q> operator<<(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<4, T, Q> operator>>(vec<4, T, Q> const& v, T scalar);

  template<typename T, qualifier Q>
  constexpr vec<4, T, Q> operator>>(vec<4, T, Q> const& v, vec<1, T, Q> const& scalar);

  template<typename T, qualifier Q>
  constexpr vec<4, T, Q> operator>>(T scalar, vec<4, T, Q> const& v);

  template<typename T, qualifier Q>
  constexpr vec<4, T, Q> operator>>(vec<1, T, Q> const& scalar, vec<4, T, Q> const& v);

  template<typename T, qualifier Q>
  constexpr vec<4, T, Q> operator>>(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<4, T, Q> operator~(vec<4, T, Q> const& v);

  // -- Boolean operators --

  template<typename T, qualifier Q>
  constexpr bool operator==(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr bool operator!=(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2);

  template<qualifier Q>
  constexpr vec<4, bool, Q> operator&&(vec<4, bool, Q> const& v1, vec<4, bool, Q> const& v2);

  template<qualifier Q>
  constexpr vec<4, bool, Q> operator||(vec<4, bool, Q> const& v1, vec<4, bool, Q> const& v2);
}//namespace glm

namespace glm {
  namespace detail
  {
    template <typename T, bool isFloat>
    struct compute_equal
    {
      inline constexpr static bool call(T a, T b)
      {
        return a == b;
      }
    };
    /*
      template <typename T>
      struct compute_equal<T, true>
      {
        GLM_FUNC_QUALIFIER GLM_CONSTEXPR static bool call(T a, T b)
        {
          return detail::compute_abs<T, std::numeric_limits<T>::is_signed>::call(b - a) <= static_cast<T>(0);
          //return std::memcmp(&a, &b, sizeof(T)) == 0;
        }
      };
    */
  }//namespace detail
}//namespace glm


namespace glm {
  namespace detail
  {
    template<typename T, qualifier Q, bool Aligned>
    struct compute_vec4_add
    {
      inline constexpr static vec<4, T, Q> call(vec<4, T, Q> const& a, vec<4, T, Q> const& b)
      {
        return vec<4, T, Q>(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w);
      }
    };

    template<typename T, qualifier Q, bool Aligned>
    struct compute_vec4_sub
    {
      inline constexpr static vec<4, T, Q> call(vec<4, T, Q> const& a, vec<4, T, Q> const& b)
      {
        return vec<4, T, Q>(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w);
      }
    };

    template<typename T, qualifier Q, bool Aligned>
    struct compute_vec4_mul
    {
      inline constexpr static vec<4, T, Q> call(vec<4, T, Q> const& a, vec<4, T, Q> const& b)
      {
        return vec<4, T, Q>(a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w);
      }
    };

    template<typename T, qualifier Q, bool Aligned>
    struct compute_vec4_div
    {
      inline constexpr static vec<4, T, Q> call(vec<4, T, Q> const& a, vec<4, T, Q> const& b)
      {
        return vec<4, T, Q>(a.x / b.x, a.y / b.y, a.z / b.z, a.w / b.w);
      }
    };

    template<typename T, qualifier Q, bool Aligned>
    struct compute_vec4_mod
    {
      inline constexpr static vec<4, T, Q> call(vec<4, T, Q> const& a, vec<4, T, Q> const& b)
      {
        return vec<4, T, Q>(a.x % b.x, a.y % b.y, a.z % b.z, a.w % b.w);
      }
    };

    template<typename T, qualifier Q, int IsInt, std::size_t Size, bool Aligned>
    struct compute_vec4_and
    {
      inline constexpr static vec<4, T, Q> call(vec<4, T, Q> const& a, vec<4, T, Q> const& b)
      {
        return vec<4, T, Q>(a.x & b.x, a.y & b.y, a.z & b.z, a.w & b.w);
      }
    };

    template<typename T, qualifier Q, int IsInt, std::size_t Size, bool Aligned>
    struct compute_vec4_or
    {
      inline constexpr static vec<4, T, Q> call(vec<4, T, Q> const& a, vec<4, T, Q> const& b)
      {
        return vec<4, T, Q>(a.x | b.x, a.y | b.y, a.z | b.z, a.w | b.w);
      }
    };

    template<typename T, qualifier Q, int IsInt, std::size_t Size, bool Aligned>
    struct compute_vec4_xor
    {
      inline constexpr static vec<4, T, Q> call(vec<4, T, Q> const& a, vec<4, T, Q> const& b)
      {
        return vec<4, T, Q>(a.x ^ b.x, a.y ^ b.y, a.z ^ b.z, a.w ^ b.w);
      }
    };

    template<typename T, qualifier Q, int IsInt, std::size_t Size, bool Aligned>
    struct compute_vec4_shift_left
    {
      inline constexpr static vec<4, T, Q> call(vec<4, T, Q> const& a, vec<4, T, Q> const& b)
      {
        return vec<4, T, Q>(a.x << b.x, a.y << b.y, a.z << b.z, a.w << b.w);
      }
    };

    template<typename T, qualifier Q, int IsInt, std::size_t Size, bool Aligned>
    struct compute_vec4_shift_right
    {
      inline constexpr static vec<4, T, Q> call(vec<4, T, Q> const& a, vec<4, T, Q> const& b)
      {
        return vec<4, T, Q>(a.x >> b.x, a.y >> b.y, a.z >> b.z, a.w >> b.w);
      }
    };

    template<typename T, qualifier Q, int IsInt, std::size_t Size, bool Aligned>
    struct compute_vec4_equal
    {
      inline constexpr static bool call(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2)
      {
        return
          detail::compute_equal<T, std::numeric_limits<T>::is_iec559>::call(v1.x, v2.x) &&
          detail::compute_equal<T, std::numeric_limits<T>::is_iec559>::call(v1.y, v2.y) &&
          detail::compute_equal<T, std::numeric_limits<T>::is_iec559>::call(v1.z, v2.z) &&
          detail::compute_equal<T, std::numeric_limits<T>::is_iec559>::call(v1.w, v2.w);
      }
    };

    template<typename T, qualifier Q, int IsInt, std::size_t Size, bool Aligned>
    struct compute_vec4_nequal
    {
      inline constexpr static bool call(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2)
      {
        return !compute_vec4_equal<T, Q, detail::is_int<T>::value, sizeof(T) * 8, detail::is_aligned<Q>::value>::call(v1, v2);
      }
    };

    template<typename T, qualifier Q, int IsInt, std::size_t Size, bool Aligned>
    struct compute_vec4_bitwise_not
    {
      inline constexpr static vec<4, T, Q> call(vec<4, T, Q> const& v)
      {
        return vec<4, T, Q>(~v.x, ~v.y, ~v.z, ~v.w);
      }
    };
  }//namespace detail

    // -- Implicit basic constructors --















  template<typename T, qualifier Q>
  template<qualifier P>
  inline constexpr vec<4, T, Q>::vec(vec<4, T, P> const& v)
    : x(v.x), y(v.y), z(v.z), w(v.w)
  {}

  // -- Explicit basic constructors --

  template<typename T, qualifier Q>
  inline constexpr vec<4, T, Q>::vec(T scalar)
    : x(scalar), y(scalar), z(scalar), w(scalar)
  {}

  template <typename T, qualifier Q>
  inline constexpr vec<4, T, Q>::vec(T _x, T _y, T _z, T _w)
    : x(_x), y(_y), z(_z), w(_w)
  {}

  // -- Conversion scalar constructors --

  template<typename T, qualifier Q>
  template<typename U, qualifier P>
  inline constexpr vec<4, T, Q>::vec(vec<1, U, P> const& v)
    : x(static_cast<T>(v.x))
    , y(static_cast<T>(v.x))
    , z(static_cast<T>(v.x))
    , w(static_cast<T>(v.x))
  {}

  template<typename T, qualifier Q>
  template<typename X, typename Y, typename Z, typename W>
  inline constexpr vec<4, T, Q>::vec(X _x, Y _y, Z _z, W _w)
    : x(static_cast<T>(_x))
    , y(static_cast<T>(_y))
    , z(static_cast<T>(_z))
    , w(static_cast<T>(_w))
  {}

  template<typename T, qualifier Q>
  template<typename X, typename Y, typename Z, typename W>
  inline constexpr vec<4, T, Q>::vec(vec<1, X, Q> const& _x, Y _y, Z _z, W _w)
    : x(static_cast<T>(_x.x))
    , y(static_cast<T>(_y))
    , z(static_cast<T>(_z))
    , w(static_cast<T>(_w))
  {}

  template<typename T, qualifier Q>
  template<typename X, typename Y, typename Z, typename W>
  inline constexpr vec<4, T, Q>::vec(X _x, vec<1, Y, Q> const& _y, Z _z, W _w)
    : x(static_cast<T>(_x))
    , y(static_cast<T>(_y.x))
    , z(static_cast<T>(_z))
    , w(static_cast<T>(_w))
  {}

  template<typename T, qualifier Q>
  template<typename X, typename Y, typename Z, typename W>
  inline constexpr vec<4, T, Q>::vec(vec<1, X, Q> const& _x, vec<1, Y, Q> const& _y, Z _z, W _w)
    : x(static_cast<T>(_x.x))
    , y(static_cast<T>(_y.x))
    , z(static_cast<T>(_z))
    , w(static_cast<T>(_w))
  {}

  template<typename T, qualifier Q>
  template<typename X, typename Y, typename Z, typename W>
  inline constexpr vec<4, T, Q>::vec(X _x, Y _y, vec<1, Z, Q> const& _z, W _w)
    : x(static_cast<T>(_x))
    , y(static_cast<T>(_y))
    , z(static_cast<T>(_z.x))
    , w(static_cast<T>(_w))
  {}

  template<typename T, qualifier Q>
  template<typename X, typename Y, typename Z, typename W>
  inline constexpr vec<4, T, Q>::vec(vec<1, X, Q> const& _x, Y _y, vec<1, Z, Q> const& _z, W _w)
    : x(static_cast<T>(_x.x))
    , y(static_cast<T>(_y))
    , z(static_cast<T>(_z.x))
    , w(static_cast<T>(_w))
  {}

  template<typename T, qualifier Q>
  template<typename X, typename Y, typename Z, typename W>
  inline constexpr vec<4, T, Q>::vec(X _x, vec<1, Y, Q> const& _y, vec<1, Z, Q> const& _z, W _w)
    : x(static_cast<T>(_x))
    , y(static_cast<T>(_y.x))
    , z(static_cast<T>(_z.x))
    , w(static_cast<T>(_w))
  {}

  template<typename T, qualifier Q>
  template<typename X, typename Y, typename Z, typename W>
  inline constexpr vec<4, T, Q>::vec(vec<1, X, Q> const& _x, vec<1, Y, Q> const& _y, vec<1, Z, Q> const& _z, W _w)
    : x(static_cast<T>(_x.x))
    , y(static_cast<T>(_y.x))
    , z(static_cast<T>(_z.x))
    , w(static_cast<T>(_w))
  {}

  template<typename T, qualifier Q>
  template<typename X, typename Y, typename Z, typename W>
  inline constexpr vec<4, T, Q>::vec(vec<1, X, Q> const& _x, Y _y, Z _z, vec<1, W, Q> const& _w)
    : x(static_cast<T>(_x.x))
    , y(static_cast<T>(_y))
    , z(static_cast<T>(_z))
    , w(static_cast<T>(_w.x))
  {}

  template<typename T, qualifier Q>
  template<typename X, typename Y, typename Z, typename W>
  inline constexpr vec<4, T, Q>::vec(X _x, vec<1, Y, Q> const& _y, Z _z, vec<1, W, Q> const& _w)
    : x(static_cast<T>(_x))
    , y(static_cast<T>(_y.x))
    , z(static_cast<T>(_z))
    , w(static_cast<T>(_w.x))
  {}

  template<typename T, qualifier Q>
  template<typename X, typename Y, typename Z, typename W>
  inline constexpr vec<4, T, Q>::vec(vec<1, X, Q> const& _x, vec<1, Y, Q> const& _y, Z _z, vec<1, W, Q> const& _w)
    : x(static_cast<T>(_x.x))
    , y(static_cast<T>(_y.x))
    , z(static_cast<T>(_z))
    , w(static_cast<T>(_w.x))
  {}

  template<typename T, qualifier Q>
  template<typename X, typename Y, typename Z, typename W>
  inline constexpr vec<4, T, Q>::vec(X _x, Y _y, vec<1, Z, Q> const& _z, vec<1, W, Q> const& _w)
    : x(static_cast<T>(_x))
    , y(static_cast<T>(_y))
    , z(static_cast<T>(_z.x))
    , w(static_cast<T>(_w.x))
  {}

  template<typename T, qualifier Q>
  template<typename X, typename Y, typename Z, typename W>
  inline constexpr vec<4, T, Q>::vec(vec<1, X, Q> const& _x, Y _y, vec<1, Z, Q> const& _z, vec<1, W, Q> const& _w)
    : x(static_cast<T>(_x.x))
    , y(static_cast<T>(_y))
    , z(static_cast<T>(_z.x))
    , w(static_cast<T>(_w.x))
  {}

  template<typename T, qualifier Q>
  template<typename X, typename Y, typename Z, typename W>
  inline constexpr vec<4, T, Q>::vec(X _x, vec<1, Y, Q> const& _y, vec<1, Z, Q> const& _z, vec<1, W, Q> const& _w)
    : x(static_cast<T>(_x))
    , y(static_cast<T>(_y.x))
    , z(static_cast<T>(_z.x))
    , w(static_cast<T>(_w.x))
  {}

  template<typename T, qualifier Q>
  template<typename X, typename Y, typename Z, typename W>
  inline constexpr vec<4, T, Q>::vec(vec<1, X, Q> const& _x, vec<1, Y, Q> const& _y, vec<1, Z, Q> const& _z, vec<1, W, Q> const& _w)
    : x(static_cast<T>(_x.x))
    , y(static_cast<T>(_y.x))
    , z(static_cast<T>(_z.x))
    , w(static_cast<T>(_w.x))
  {}

  // -- Conversion vector constructors --

  template<typename T, qualifier Q>
  template<typename A, typename B, typename C, qualifier P>
  inline constexpr vec<4, T, Q>::vec(vec<2, A, P> const& _xy, B _z, C _w)
    : x(static_cast<T>(_xy.x))
    , y(static_cast<T>(_xy.y))
    , z(static_cast<T>(_z))
    , w(static_cast<T>(_w))
  {}

  template<typename T, qualifier Q>
  template<typename A, typename B, typename C, qualifier P>
  inline constexpr vec<4, T, Q>::vec(vec<2, A, P> const& _xy, vec<1, B, P> const& _z, C _w)
    : x(static_cast<T>(_xy.x))
    , y(static_cast<T>(_xy.y))
    , z(static_cast<T>(_z.x))
    , w(static_cast<T>(_w))
  {}

  template<typename T, qualifier Q>
  template<typename A, typename B, typename C, qualifier P>
  inline constexpr vec<4, T, Q>::vec(vec<2, A, P> const& _xy, B _z, vec<1, C, P> const& _w)
    : x(static_cast<T>(_xy.x))
    , y(static_cast<T>(_xy.y))
    , z(static_cast<T>(_z))
    , w(static_cast<T>(_w.x))
  {}

  template<typename T, qualifier Q>
  template<typename A, typename B, typename C, qualifier P>
  inline constexpr vec<4, T, Q>::vec(vec<2, A, P> const& _xy, vec<1, B, P> const& _z, vec<1, C, P> const& _w)
    : x(static_cast<T>(_xy.x))
    , y(static_cast<T>(_xy.y))
    , z(static_cast<T>(_z.x))
    , w(static_cast<T>(_w.x))
  {}

  template<typename T, qualifier Q>
  template<typename A, typename B, typename C, qualifier P>
  inline constexpr vec<4, T, Q>::vec(A _x, vec<2, B, P> const& _yz, C _w)
    : x(static_cast<T>(_x))
    , y(static_cast<T>(_yz.x))
    , z(static_cast<T>(_yz.y))
    , w(static_cast<T>(_w))
  {}

  template<typename T, qualifier Q>
  template<typename A, typename B, typename C, qualifier P>
  inline constexpr vec<4, T, Q>::vec(vec<1, A, P> const& _x, vec<2, B, P> const& _yz, C _w)
    : x(static_cast<T>(_x.x))
    , y(static_cast<T>(_yz.x))
    , z(static_cast<T>(_yz.y))
    , w(static_cast<T>(_w))
  {}

  template<typename T, qualifier Q>
  template<typename A, typename B, typename C, qualifier P>
  inline constexpr vec<4, T, Q>::vec(A _x, vec<2, B, P> const& _yz, vec<1, C, P> const& _w)
    : x(static_cast<T>(_x))
    , y(static_cast<T>(_yz.x))
    , z(static_cast<T>(_yz.y))
    , w(static_cast<T>(_w.x))
  {}

  template<typename T, qualifier Q>
  template<typename A, typename B, typename C, qualifier P>
  inline constexpr vec<4, T, Q>::vec(vec<1, A, P> const& _x, vec<2, B, P> const& _yz, vec<1, C, P> const& _w)
    : x(static_cast<T>(_x.x))
    , y(static_cast<T>(_yz.x))
    , z(static_cast<T>(_yz.y))
    , w(static_cast<T>(_w.x))
  {}

  template<typename T, qualifier Q>
  template<typename A, typename B, typename C, qualifier P>
  inline constexpr vec<4, T, Q>::vec(A _x, B _y, vec<2, C, P> const& _zw)
    : x(static_cast<T>(_x))
    , y(static_cast<T>(_y))
    , z(static_cast<T>(_zw.x))
    , w(static_cast<T>(_zw.y))
  {}

  template<typename T, qualifier Q>
  template<typename A, typename B, typename C, qualifier P>
  inline constexpr vec<4, T, Q>::vec(vec<1, A, P> const& _x, B _y, vec<2, C, P> const& _zw)
    : x(static_cast<T>(_x.x))
    , y(static_cast<T>(_y))
    , z(static_cast<T>(_zw.x))
    , w(static_cast<T>(_zw.y))
  {}

  template<typename T, qualifier Q>
  template<typename A, typename B, typename C, qualifier P>
  inline constexpr vec<4, T, Q>::vec(A _x, vec<1, B, P> const& _y, vec<2, C, P> const& _zw)
    : x(static_cast<T>(_x))
    , y(static_cast<T>(_y.x))
    , z(static_cast<T>(_zw.x))
    , w(static_cast<T>(_zw.y))
  {}

  template<typename T, qualifier Q>
  template<typename A, typename B, typename C, qualifier P>
  inline constexpr vec<4, T, Q>::vec(vec<1, A, P> const& _x, vec<1, B, P> const& _y, vec<2, C, P> const& _zw)
    : x(static_cast<T>(_x.x))
    , y(static_cast<T>(_y.x))
    , z(static_cast<T>(_zw.x))
    , w(static_cast<T>(_zw.y))
  {}

  template<typename T, qualifier Q>
  template<typename A, typename B, qualifier P>
  inline constexpr vec<4, T, Q>::vec(vec<3, A, P> const& _xyz, B _w)
    : x(static_cast<T>(_xyz.x))
    , y(static_cast<T>(_xyz.y))
    , z(static_cast<T>(_xyz.z))
    , w(static_cast<T>(_w))
  {}

  template<typename T, qualifier Q>
  template<typename A, typename B, qualifier P>
  inline constexpr vec<4, T, Q>::vec(vec<3, A, P> const& _xyz, vec<1, B, P> const& _w)
    : x(static_cast<T>(_xyz.x))
    , y(static_cast<T>(_xyz.y))
    , z(static_cast<T>(_xyz.z))
    , w(static_cast<T>(_w.x))
  {}

  template<typename T, qualifier Q>
  template<typename A, typename B, qualifier P>
  inline constexpr vec<4, T, Q>::vec(A _x, vec<3, B, P> const& _yzw)
    : x(static_cast<T>(_x))
    , y(static_cast<T>(_yzw.x))
    , z(static_cast<T>(_yzw.y))
    , w(static_cast<T>(_yzw.z))
  {}

  template<typename T, qualifier Q>
  template<typename A, typename B, qualifier P>
  inline constexpr vec<4, T, Q>::vec(vec<1, A, P> const& _x, vec<3, B, P> const& _yzw)
    : x(static_cast<T>(_x.x))
    , y(static_cast<T>(_yzw.x))
    , z(static_cast<T>(_yzw.y))
    , w(static_cast<T>(_yzw.z))
  {}

  template<typename T, qualifier Q>
  template<typename A, typename B, qualifier P>
  inline constexpr vec<4, T, Q>::vec(vec<2, A, P> const& _xy, vec<2, B, P> const& _zw)
    : x(static_cast<T>(_xy.x))
    , y(static_cast<T>(_xy.y))
    , z(static_cast<T>(_zw.x))
    , w(static_cast<T>(_zw.y))
  {}

  template<typename T, qualifier Q>
  template<typename U, qualifier P>
  inline constexpr vec<4, T, Q>::vec(vec<4, U, P> const& v)
    : x(static_cast<T>(v.x))
    , y(static_cast<T>(v.y))
    , z(static_cast<T>(v.z))
    , w(static_cast<T>(v.w))
  {}

  // -- Component accesses --

  template<typename T, qualifier Q>
  inline constexpr T& vec<4, T, Q>::operator[](typename vec<4, T, Q>::length_type i)
  {
    assert(i >= 0 && i < this->length());
    switch (i)
    {
    default:
    case 0:
      return x;
    case 1:
      return y;
    case 2:
      return z;
    case 3:
      return w;
    }
  }

  template<typename T, qualifier Q>
  inline constexpr T const& vec<4, T, Q>::operator[](typename vec<4, T, Q>::length_type i) const
  {
    assert(i >= 0 && i < this->length());
    switch (i)
    {
    default:
    case 0:
      return x;
    case 1:
      return y;
    case 2:
      return z;
    case 3:
      return w;
    }
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<4, T, Q>& vec<4, T, Q>::operator=(vec<4, U, Q> const& v)
  {
    this->x = static_cast<T>(v.x);
    this->y = static_cast<T>(v.y);
    this->z = static_cast<T>(v.z);
    this->w = static_cast<T>(v.w);
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<4, T, Q>& vec<4, T, Q>::operator+=(U scalar)
  {
    return (*this = detail::compute_vec4_add<T, Q, detail::is_aligned<Q>::value>::call(*this, vec<4, T, Q>(scalar)));
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<4, T, Q>& vec<4, T, Q>::operator+=(vec<1, U, Q> const& v)
  {
    return (*this = detail::compute_vec4_add<T, Q, detail::is_aligned<Q>::value>::call(*this, vec<4, T, Q>(v.x)));
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<4, T, Q>& vec<4, T, Q>::operator+=(vec<4, U, Q> const& v)
  {
    return (*this = detail::compute_vec4_add<T, Q, detail::is_aligned<Q>::value>::call(*this, vec<4, T, Q>(v)));
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<4, T, Q>& vec<4, T, Q>::operator-=(U scalar)
  {
    return (*this = detail::compute_vec4_sub<T, Q, detail::is_aligned<Q>::value>::call(*this, vec<4, T, Q>(scalar)));
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<4, T, Q>& vec<4, T, Q>::operator-=(vec<1, U, Q> const& v)
  {
    return (*this = detail::compute_vec4_sub<T, Q, detail::is_aligned<Q>::value>::call(*this, vec<4, T, Q>(v.x)));
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<4, T, Q>& vec<4, T, Q>::operator-=(vec<4, U, Q> const& v)
  {
    return (*this = detail::compute_vec4_sub<T, Q, detail::is_aligned<Q>::value>::call(*this, vec<4, T, Q>(v)));
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<4, T, Q>& vec<4, T, Q>::operator*=(U scalar)
  {
    return (*this = detail::compute_vec4_mul<T, Q, detail::is_aligned<Q>::value>::call(*this, vec<4, T, Q>(scalar)));
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<4, T, Q>& vec<4, T, Q>::operator*=(vec<1, U, Q> const& v)
  {
    return (*this = detail::compute_vec4_mul<T, Q, detail::is_aligned<Q>::value>::call(*this, vec<4, T, Q>(v.x)));
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<4, T, Q>& vec<4, T, Q>::operator*=(vec<4, U, Q> const& v)
  {
    return (*this = detail::compute_vec4_mul<T, Q, detail::is_aligned<Q>::value>::call(*this, vec<4, T, Q>(v)));
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<4, T, Q>& vec<4, T, Q>::operator/=(U scalar)
  {
    return (*this = detail::compute_vec4_div<T, Q, detail::is_aligned<Q>::value>::call(*this, vec<4, T, Q>(scalar)));
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<4, T, Q>& vec<4, T, Q>::operator/=(vec<1, U, Q> const& v)
  {
    return (*this = detail::compute_vec4_div<T, Q, detail::is_aligned<Q>::value>::call(*this, vec<4, T, Q>(v.x)));
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<4, T, Q>& vec<4, T, Q>::operator/=(vec<4, U, Q> const& v)
  {
    return (*this = detail::compute_vec4_div<T, Q, detail::is_aligned<Q>::value>::call(*this, vec<4, T, Q>(v)));
  }

  // -- Increment and decrement operators --

  template<typename T, qualifier Q>
  inline constexpr vec<4, T, Q>& vec<4, T, Q>::operator++()
  {
    ++this->x;
    ++this->y;
    ++this->z;
    ++this->w;
    return *this;
  }

  template<typename T, qualifier Q>
  inline constexpr vec<4, T, Q>& vec<4, T, Q>::operator--()
  {
    --this->x;
    --this->y;
    --this->z;
    --this->w;
    return *this;
  }

  template<typename T, qualifier Q>
  inline constexpr vec<4, T, Q> vec<4, T, Q>::operator++(int)
  {
    vec<4, T, Q> Result(*this);
    ++*this;
    return Result;
  }

  template<typename T, qualifier Q>
  inline constexpr vec<4, T, Q> vec<4, T, Q>::operator--(int)
  {
    vec<4, T, Q> Result(*this);
    --*this;
    return Result;
  }

  // -- Unary bit operators --

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<4, T, Q>& vec<4, T, Q>::operator%=(U scalar)
  {
    return (*this = detail::compute_vec4_mod<T, Q, detail::is_aligned<Q>::value>::call(*this, vec<4, T, Q>(scalar)));
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<4, T, Q>& vec<4, T, Q>::operator%=(vec<1, U, Q> const& v)
  {
    return (*this = detail::compute_vec4_mod<T, Q, detail::is_aligned<Q>::value>::call(*this, vec<4, T, Q>(v)));
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<4, T, Q>& vec<4, T, Q>::operator%=(vec<4, U, Q> const& v)
  {
    return (*this = detail::compute_vec4_mod<T, Q, detail::is_aligned<Q>::value>::call(*this, vec<4, T, Q>(v)));
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<4, T, Q>& vec<4, T, Q>::operator&=(U scalar)
  {
    return (*this = detail::compute_vec4_and<T, Q, detail::is_int<T>::value, sizeof(T) * 8, detail::is_aligned<Q>::value>::call(*this, vec<4, T, Q>(scalar)));
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<4, T, Q>& vec<4, T, Q>::operator&=(vec<1, U, Q> const& v)
  {
    return (*this = detail::compute_vec4_and<T, Q, detail::is_int<T>::value, sizeof(T) * 8, detail::is_aligned<Q>::value>::call(*this, vec<4, T, Q>(v)));
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<4, T, Q>& vec<4, T, Q>::operator&=(vec<4, U, Q> const& v)
  {
    return (*this = detail::compute_vec4_and<T, Q, detail::is_int<T>::value, sizeof(T) * 8, detail::is_aligned<Q>::value>::call(*this, vec<4, T, Q>(v)));
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<4, T, Q>& vec<4, T, Q>::operator|=(U scalar)
  {
    return (*this = detail::compute_vec4_or<T, Q, detail::is_int<T>::value, sizeof(T) * 8, detail::is_aligned<Q>::value>::call(*this, vec<4, T, Q>(scalar)));
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<4, T, Q>& vec<4, T, Q>::operator|=(vec<1, U, Q> const& v)
  {
    return (*this = detail::compute_vec4_or<T, Q, detail::is_int<T>::value, sizeof(T) * 8, detail::is_aligned<Q>::value>::call(*this, vec<4, T, Q>(v)));
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<4, T, Q>& vec<4, T, Q>::operator|=(vec<4, U, Q> const& v)
  {
    return (*this = detail::compute_vec4_or<T, Q, detail::is_int<T>::value, sizeof(T) * 8, detail::is_aligned<Q>::value>::call(*this, vec<4, T, Q>(v)));
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<4, T, Q>& vec<4, T, Q>::operator^=(U scalar)
  {
    return (*this = detail::compute_vec4_xor<T, Q, detail::is_int<T>::value, sizeof(T) * 8, detail::is_aligned<Q>::value>::call(*this, vec<4, T, Q>(scalar)));
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<4, T, Q>& vec<4, T, Q>::operator^=(vec<1, U, Q> const& v)
  {
    return (*this = detail::compute_vec4_xor<T, Q, detail::is_int<T>::value, sizeof(T) * 8, detail::is_aligned<Q>::value>::call(*this, vec<4, T, Q>(v)));
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<4, T, Q>& vec<4, T, Q>::operator^=(vec<4, U, Q> const& v)
  {
    return (*this = detail::compute_vec4_xor<T, Q, detail::is_int<T>::value, sizeof(T) * 8, detail::is_aligned<Q>::value>::call(*this, vec<4, T, Q>(v)));
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<4, T, Q>& vec<4, T, Q>::operator<<=(U scalar)
  {
    return (*this = detail::compute_vec4_shift_left<T, Q, detail::is_int<T>::value, sizeof(T) * 8, detail::is_aligned<Q>::value>::call(*this, vec<4, T, Q>(scalar)));
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<4, T, Q>& vec<4, T, Q>::operator<<=(vec<1, U, Q> const& v)
  {
    return (*this = detail::compute_vec4_shift_left<T, Q, detail::is_int<T>::value, sizeof(T) * 8, detail::is_aligned<Q>::value>::call(*this, vec<4, T, Q>(v)));
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<4, T, Q>& vec<4, T, Q>::operator<<=(vec<4, U, Q> const& v)
  {
    return (*this = detail::compute_vec4_shift_left<T, Q, detail::is_int<T>::value, sizeof(T) * 8, detail::is_aligned<Q>::value>::call(*this, vec<4, T, Q>(v)));
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<4, T, Q>& vec<4, T, Q>::operator>>=(U scalar)
  {
    return (*this = detail::compute_vec4_shift_right<T, Q, detail::is_int<T>::value, sizeof(T) * 8, detail::is_aligned<Q>::value>::call(*this, vec<4, T, Q>(scalar)));
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<4, T, Q>& vec<4, T, Q>::operator>>=(vec<1, U, Q> const& v)
  {
    return (*this = detail::compute_vec4_shift_right<T, Q, detail::is_int<T>::value, sizeof(T) * 8, detail::is_aligned<Q>::value>::call(*this, vec<4, T, Q>(v)));
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<4, T, Q>& vec<4, T, Q>::operator>>=(vec<4, U, Q> const& v)
  {
    return (*this = detail::compute_vec4_shift_right<T, Q, detail::is_int<T>::value, sizeof(T) * 8, detail::is_aligned<Q>::value>::call(*this, vec<4, T, Q>(v)));
  }

  // -- Unary constant operators --

  template<typename T, qualifier Q>
  inline constexpr vec<4, T, Q> operator+(vec<4, T, Q> const& v)
  {
    return v;
  }

  template<typename T, qualifier Q>
  inline constexpr vec<4, T, Q> operator-(vec<4, T, Q> const& v)
  {
    return vec<4, T, Q>(0) -= v;
  }

  // -- Binary arithmetic operators --

  template<typename T, qualifier Q>
  inline constexpr vec<4, T, Q> operator+(vec<4, T, Q> const& v, T const& scalar)
  {
    return vec<4, T, Q>(v) += scalar;
  }

  template<typename T, qualifier Q>
  inline constexpr vec<4, T, Q> operator+(vec<4, T, Q> const& v1, vec<1, T, Q> const& v2)
  {
    return vec<4, T, Q>(v1) += v2;
  }

  template<typename T, qualifier Q>
  inline constexpr vec<4, T, Q> operator+(T scalar, vec<4, T, Q> const& v)
  {
    return vec<4, T, Q>(v) += scalar;
  }

  template<typename T, qualifier Q>
  inline constexpr vec<4, T, Q> operator+(vec<1, T, Q> const& v1, vec<4, T, Q> const& v2)
  {
    return vec<4, T, Q>(v2) += v1;
  }

  template<typename T, qualifier Q>
  inline constexpr vec<4, T, Q> operator+(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2)
  {
    return vec<4, T, Q>(v1) += v2;
  }

  template<typename T, qualifier Q>
  inline constexpr vec<4, T, Q> operator-(vec<4, T, Q> const& v, T const& scalar)
  {
    return vec<4, T, Q>(v) -= scalar;
  }

  template<typename T, qualifier Q>
  inline constexpr vec<4, T, Q> operator-(vec<4, T, Q> const& v1, vec<1, T, Q> const& v2)
  {
    return vec<4, T, Q>(v1) -= v2;
  }

  template<typename T, qualifier Q>
  inline constexpr vec<4, T, Q> operator-(T scalar, vec<4, T, Q> const& v)
  {
    return vec<4, T, Q>(scalar) -= v;
  }

  template<typename T, qualifier Q>
  inline constexpr vec<4, T, Q> operator-(vec<1, T, Q> const& v1, vec<4, T, Q> const& v2)
  {
    return vec<4, T, Q>(v1.x) -= v2;
  }

  template<typename T, qualifier Q>
  inline constexpr vec<4, T, Q> operator-(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2)
  {
    return vec<4, T, Q>(v1) -= v2;
  }

  template<typename T, qualifier Q>
  inline constexpr vec<4, T, Q> operator*(vec<4, T, Q> const& v, T const& scalar)
  {
    return vec<4, T, Q>(v) *= scalar;
  }

  template<typename T, qualifier Q>
  inline constexpr vec<4, T, Q> operator*(vec<4, T, Q> const& v1, vec<1, T, Q> const& v2)
  {
    return vec<4, T, Q>(v1) *= v2;
  }

  template<typename T, qualifier Q>
  inline constexpr vec<4, T, Q> operator*(T scalar, vec<4, T, Q> const& v)
  {
    return vec<4, T, Q>(v) *= scalar;
  }

  template<typename T, qualifier Q>
  inline constexpr vec<4, T, Q> operator*(vec<1, T, Q> const& v1, vec<4, T, Q> const& v2)
  {
    return vec<4, T, Q>(v2) *= v1;
  }

  template<typename T, qualifier Q>
  inline constexpr vec<4, T, Q> operator*(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2)
  {
    return vec<4, T, Q>(v1) *= v2;
  }

  template<typename T, qualifier Q>
  inline constexpr vec<4, T, Q> operator/(vec<4, T, Q> const& v, T const& scalar)
  {
    return vec<4, T, Q>(v) /= scalar;
  }

  template<typename T, qualifier Q>
  inline constexpr vec<4, T, Q> operator/(vec<4, T, Q> const& v1, vec<1, T, Q> const& v2)
  {
    return vec<4, T, Q>(v1) /= v2;
  }

  template<typename T, qualifier Q>
  inline constexpr vec<4, T, Q> operator/(T scalar, vec<4, T, Q> const& v)
  {
    return vec<4, T, Q>(scalar) /= v;
  }

  template<typename T, qualifier Q>
  inline constexpr vec<4, T, Q> operator/(vec<1, T, Q> const& v1, vec<4, T, Q> const& v2)
  {
    return vec<4, T, Q>(v1.x) /= v2;
  }

  template<typename T, qualifier Q>
  inline constexpr vec<4, T, Q> operator/(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2)
  {
    return vec<4, T, Q>(v1) /= v2;
  }

  // -- Binary bit operators --

  template<typename T, qualifier Q>
  inline constexpr vec<4, T, Q> operator%(vec<4, T, Q> const& v, T scalar)
  {
    return vec<4, T, Q>(v) %= scalar;
  }

  template<typename T, qualifier Q>
  inline constexpr vec<4, T, Q> operator%(vec<4, T, Q> const& v1, vec<1, T, Q> const& v2)
  {
    return vec<4, T, Q>(v1) %= v2.x;
  }

  template<typename T, qualifier Q>
  inline constexpr vec<4, T, Q> operator%(T scalar, vec<4, T, Q> const& v)
  {
    return vec<4, T, Q>(scalar) %= v;
  }

  template<typename T, qualifier Q>
  inline constexpr vec<4, T, Q> operator%(vec<1, T, Q> const& scalar, vec<4, T, Q> const& v)
  {
    return vec<4, T, Q>(scalar.x) %= v;
  }

  template<typename T, qualifier Q>
  inline constexpr vec<4, T, Q> operator%(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2)
  {
    return vec<4, T, Q>(v1) %= v2;
  }

  template<typename T, qualifier Q>
  inline constexpr vec<4, T, Q> operator&(vec<4, T, Q> const& v, T scalar)
  {
    return vec<4, T, Q>(v) &= scalar;
  }

  template<typename T, qualifier Q>
  inline constexpr vec<4, T, Q> operator&(vec<4, T, Q> const& v, vec<1, T, Q> const& scalar)
  {
    return vec<4, T, Q>(v) &= scalar;
  }

  template<typename T, qualifier Q>
  inline constexpr vec<4, T, Q> operator&(T scalar, vec<4, T, Q> const& v)
  {
    return vec<4, T, Q>(scalar) &= v;
  }

  template<typename T, qualifier Q>
  inline constexpr vec<4, T, Q> operator&(vec<1, T, Q> const& v1, vec<4, T, Q> const& v2)
  {
    return vec<4, T, Q>(v1.x) &= v2;
  }

  template<typename T, qualifier Q>
  inline constexpr vec<4, T, Q> operator&(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2)
  {
    return vec<4, T, Q>(v1) &= v2;
  }

  template<typename T, qualifier Q>
  inline constexpr vec<4, T, Q> operator|(vec<4, T, Q> const& v, T scalar)
  {
    return vec<4, T, Q>(v) |= scalar;
  }

  template<typename T, qualifier Q>
  inline constexpr vec<4, T, Q> operator|(vec<4, T, Q> const& v1, vec<1, T, Q> const& v2)
  {
    return vec<4, T, Q>(v1) |= v2.x;
  }

  template<typename T, qualifier Q>
  inline constexpr vec<4, T, Q> operator|(T scalar, vec<4, T, Q> const& v)
  {
    return vec<4, T, Q>(scalar) |= v;
  }

  template<typename T, qualifier Q>
  inline constexpr vec<4, T, Q> operator|(vec<1, T, Q> const& v1, vec<4, T, Q> const& v2)
  {
    return vec<4, T, Q>(v1.x) |= v2;
  }

  template<typename T, qualifier Q>
  inline constexpr vec<4, T, Q> operator|(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2)
  {
    return vec<4, T, Q>(v1) |= v2;
  }

  template<typename T, qualifier Q>
  inline constexpr vec<4, T, Q> operator^(vec<4, T, Q> const& v, T scalar)
  {
    return vec<4, T, Q>(v) ^= scalar;
  }

  template<typename T, qualifier Q>
  inline constexpr vec<4, T, Q> operator^(vec<4, T, Q> const& v1, vec<1, T, Q> const& v2)
  {
    return vec<4, T, Q>(v1) ^= v2.x;
  }

  template<typename T, qualifier Q>
  inline constexpr vec<4, T, Q> operator^(T scalar, vec<4, T, Q> const& v)
  {
    return vec<4, T, Q>(scalar) ^= v;
  }

  template<typename T, qualifier Q>
  inline constexpr vec<4, T, Q> operator^(vec<1, T, Q> const& v1, vec<4, T, Q> const& v2)
  {
    return vec<4, T, Q>(v1.x) ^= v2;
  }

  template<typename T, qualifier Q>
  inline constexpr vec<4, T, Q> operator^(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2)
  {
    return vec<4, T, Q>(v1) ^= v2;
  }

  template<typename T, qualifier Q>
  inline constexpr vec<4, T, Q> operator<<(vec<4, T, Q> const& v, T scalar)
  {
    return vec<4, T, Q>(v) <<= scalar;
  }

  template<typename T, qualifier Q>
  inline constexpr vec<4, T, Q> operator<<(vec<4, T, Q> const& v1, vec<1, T, Q> const& v2)
  {
    return vec<4, T, Q>(v1) <<= v2.x;
  }

  template<typename T, qualifier Q>
  inline constexpr vec<4, T, Q> operator<<(T scalar, vec<4, T, Q> const& v)
  {
    return vec<4, T, Q>(scalar) <<= v;
  }

  template<typename T, qualifier Q>
  inline constexpr vec<4, T, Q> operator<<(vec<1, T, Q> const& v1, vec<4, T, Q> const& v2)
  {
    return vec<4, T, Q>(v1.x) <<= v2;
  }

  template<typename T, qualifier Q>
  inline constexpr vec<4, T, Q> operator<<(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2)
  {
    return vec<4, T, Q>(v1) <<= v2;
  }

  template<typename T, qualifier Q>
  inline constexpr vec<4, T, Q> operator>>(vec<4, T, Q> const& v, T scalar)
  {
    return vec<4, T, Q>(v) >>= scalar;
  }

  template<typename T, qualifier Q>
  inline constexpr vec<4, T, Q> operator>>(vec<4, T, Q> const& v1, vec<1, T, Q> const& v2)
  {
    return vec<4, T, Q>(v1) >>= v2.x;
  }

  template<typename T, qualifier Q>
  inline constexpr vec<4, T, Q> operator>>(T scalar, vec<4, T, Q> const& v)
  {
    return vec<4, T, Q>(scalar) >>= v;
  }

  template<typename T, qualifier Q>
  inline constexpr vec<4, T, Q> operator>>(vec<1, T, Q> const& v1, vec<4, T, Q> const& v2)
  {
    return vec<4, T, Q>(v1.x) >>= v2;
  }

  template<typename T, qualifier Q>
  inline constexpr vec<4, T, Q> operator>>(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2)
  {
    return vec<4, T, Q>(v1) >>= v2;
  }

  template<typename T, qualifier Q>
  inline constexpr vec<4, T, Q> operator~(vec<4, T, Q> const& v)
  {
    return detail::compute_vec4_bitwise_not<T, Q, detail::is_int<T>::value, sizeof(T) * 8, detail::is_aligned<Q>::value>::call(v);
  }

  // -- Boolean operators --

  template<typename T, qualifier Q>
  inline constexpr bool operator==(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2)
  {
    return detail::compute_vec4_equal<T, Q, detail::is_int<T>::value, sizeof(T) * 8, detail::is_aligned<Q>::value>::call(v1, v2);
  }

  template<typename T, qualifier Q>
  inline constexpr bool operator!=(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2)
  {
    return detail::compute_vec4_nequal<T, Q, detail::is_int<T>::value, sizeof(T) * 8, detail::is_aligned<Q>::value>::call(v1, v2);
  }

  template<qualifier Q>
  inline constexpr vec<4, bool, Q> operator&&(vec<4, bool, Q> const& v1, vec<4, bool, Q> const& v2)
  {
    return vec<4, bool, Q>(v1.x && v2.x, v1.y && v2.y, v1.z && v2.z, v1.w && v2.w);
  }

  template<qualifier Q>
  inline constexpr vec<4, bool, Q> operator||(vec<4, bool, Q> const& v1, vec<4, bool, Q> const& v2)
  {
    return vec<4, bool, Q>(v1.x || v2.x, v1.y || v2.y, v1.z || v2.z, v1.w || v2.w);
  }
}//namespace glm

namespace glm
{
  template<typename T, qualifier Q>
  struct mat<4, 4, T, Q>
  {
    typedef vec<4, T, Q> col_type;
    typedef vec<4, T, Q> row_type;
    typedef mat<4, 4, T, Q> type;
    typedef mat<4, 4, T, Q> transpose_type;
    typedef T value_type;

  private:
    col_type value[4];

  public:
    // -- Accesses --

    typedef length_t length_type;
    static constexpr length_type length() { return 4; }

    col_type& operator[](length_type i);
    constexpr col_type const& operator[](length_type i) const;

    // -- Constructors --

    constexpr mat() = default;
    template<qualifier P>
    constexpr mat(mat<4, 4, T, P> const& m);

    explicit constexpr mat(T const& x);
    constexpr mat(
      T const& x0, T const& y0, T const& z0, T const& w0,
      T const& x1, T const& y1, T const& z1, T const& w1,
      T const& x2, T const& y2, T const& z2, T const& w2,
      T const& x3, T const& y3, T const& z3, T const& w3);
    constexpr mat(
      col_type const& v0,
      col_type const& v1,
      col_type const& v2,
      col_type const& v3);

    // -- Conversions --

    template<
      typename X1, typename Y1, typename Z1, typename W1,
      typename X2, typename Y2, typename Z2, typename W2,
      typename X3, typename Y3, typename Z3, typename W3,
      typename X4, typename Y4, typename Z4, typename W4>
    constexpr mat(
      X1 const& x1, Y1 const& y1, Z1 const& z1, W1 const& w1,
      X2 const& x2, Y2 const& y2, Z2 const& z2, W2 const& w2,
      X3 const& x3, Y3 const& y3, Z3 const& z3, W3 const& w3,
      X4 const& x4, Y4 const& y4, Z4 const& z4, W4 const& w4);

    template<typename V1, typename V2, typename V3, typename V4>
    constexpr mat(
      vec<4, V1, Q> const& v1,
      vec<4, V2, Q> const& v2,
      vec<4, V3, Q> const& v3,
      vec<4, V4, Q> const& v4);

    // -- Matrix conversions --

    template<typename U, qualifier P>
    constexpr mat(mat<4, 4, U, P> const& m);

    constexpr mat(mat<2, 2, T, Q> const& x);
    constexpr mat(mat<3, 3, T, Q> const& x);
    constexpr mat(mat<2, 3, T, Q> const& x);
    constexpr mat(mat<3, 2, T, Q> const& x);
    constexpr mat(mat<2, 4, T, Q> const& x);
    constexpr mat(mat<4, 2, T, Q> const& x);
    constexpr mat(mat<3, 4, T, Q> const& x);
    constexpr mat(mat<4, 3, T, Q> const& x);

    // -- Unary arithmetic operators --

    template<typename U>
    mat<4, 4, T, Q>& operator=(mat<4, 4, U, Q> const& m);
    template<typename U>
    mat<4, 4, T, Q>& operator+=(U s);
    template<typename U>
    mat<4, 4, T, Q>& operator+=(mat<4, 4, U, Q> const& m);
    template<typename U>
    mat<4, 4, T, Q>& operator-=(U s);
    template<typename U>
    mat<4, 4, T, Q>& operator-=(mat<4, 4, U, Q> const& m);
    template<typename U>
    mat<4, 4, T, Q>& operator*=(U s);
    template<typename U>
    mat<4, 4, T, Q>& operator*=(mat<4, 4, U, Q> const& m);
    template<typename U>
    mat<4, 4, T, Q>& operator/=(U s);
    template<typename U>
    mat<4, 4, T, Q>& operator/=(mat<4, 4, U, Q> const& m);

    // -- Increment and decrement operators --

    mat<4, 4, T, Q>& operator++();
    mat<4, 4, T, Q>& operator--();
    mat<4, 4, T, Q> operator++(int);
    mat<4, 4, T, Q> operator--(int);
  };

  // -- Unary operators --

  template<typename T, qualifier Q>
  mat<4, 4, T, Q> operator+(mat<4, 4, T, Q> const& m);

  template<typename T, qualifier Q>
  mat<4, 4, T, Q> operator-(mat<4, 4, T, Q> const& m);

  // -- Binary operators --

  template<typename T, qualifier Q>
  mat<4, 4, T, Q> operator+(mat<4, 4, T, Q> const& m, T const& s);

  template<typename T, qualifier Q>
  mat<4, 4, T, Q> operator+(T const& s, mat<4, 4, T, Q> const& m);

  template<typename T, qualifier Q>
  mat<4, 4, T, Q> operator+(mat<4, 4, T, Q> const& m1, mat<4, 4, T, Q> const& m2);

  template<typename T, qualifier Q>
  mat<4, 4, T, Q> operator-(mat<4, 4, T, Q> const& m, T const& s);

  template<typename T, qualifier Q>
  mat<4, 4, T, Q> operator-(T const& s, mat<4, 4, T, Q> const& m);

  template<typename T, qualifier Q>
  mat<4, 4, T, Q> operator-(mat<4, 4, T, Q> const& m1, mat<4, 4, T, Q> const& m2);

  template<typename T, qualifier Q>
  mat<4, 4, T, Q> operator*(mat<4, 4, T, Q> const& m, T const& s);

  template<typename T, qualifier Q>
  mat<4, 4, T, Q> operator*(T const& s, mat<4, 4, T, Q> const& m);

  template<typename T, qualifier Q>
  typename mat<4, 4, T, Q>::col_type operator*(mat<4, 4, T, Q> const& m, typename mat<4, 4, T, Q>::row_type const& v);

  template<typename T, qualifier Q>
  typename mat<4, 4, T, Q>::row_type operator*(typename mat<4, 4, T, Q>::col_type const& v, mat<4, 4, T, Q> const& m);

  template<typename T, qualifier Q>
  mat<2, 4, T, Q> operator*(mat<4, 4, T, Q> const& m1, mat<2, 4, T, Q> const& m2);

  template<typename T, qualifier Q>
  mat<3, 4, T, Q> operator*(mat<4, 4, T, Q> const& m1, mat<3, 4, T, Q> const& m2);

  template<typename T, qualifier Q>
  mat<4, 4, T, Q> operator*(mat<4, 4, T, Q> const& m1, mat<4, 4, T, Q> const& m2);

  template<typename T, qualifier Q>
  mat<4, 4, T, Q> operator/(mat<4, 4, T, Q> const& m, T const& s);

  template<typename T, qualifier Q>
  mat<4, 4, T, Q> operator/(T const& s, mat<4, 4, T, Q> const& m);

  template<typename T, qualifier Q>
  typename mat<4, 4, T, Q>::col_type operator/(mat<4, 4, T, Q> const& m, typename mat<4, 4, T, Q>::row_type const& v);

  template<typename T, qualifier Q>
  typename mat<4, 4, T, Q>::row_type operator/(typename mat<4, 4, T, Q>::col_type const& v, mat<4, 4, T, Q> const& m);

  template<typename T, qualifier Q>
  mat<4, 4, T, Q> operator/(mat<4, 4, T, Q> const& m1, mat<4, 4, T, Q> const& m2);

  // -- Boolean operators --

  template<typename T, qualifier Q>
  bool operator==(mat<4, 4, T, Q> const& m1, mat<4, 4, T, Q> const& m2);

  template<typename T, qualifier Q>
  bool operator!=(mat<4, 4, T, Q> const& m1, mat<4, 4, T, Q> const& m2);
}//namespace glm

namespace glm
{
  template<typename T, qualifier Q>
  struct vec<2, T, Q>
  {
    // -- Implementation detail --

    typedef T value_type;
    typedef vec<2, T, Q> type;
    typedef vec<2, bool, Q> bool_type;

    // -- Data --
    union { T x, r, s; };
    union { T y, g, t; };


    // -- Component accesses --

    /// Return the count of components of the vector
    typedef length_t length_type;
    static constexpr length_type length() { return 2; }

    constexpr T& operator[](length_type i);
    constexpr T const& operator[](length_type i) const;

    // -- Implicit basic constructors --

    constexpr vec() = default;
    constexpr vec(vec const& v) = default;
    template<qualifier P>
    constexpr vec(vec<2, T, P> const& v);

    // -- Explicit basic constructors --

    constexpr explicit vec(T scalar);
    constexpr vec(T x, T y);

    // -- Conversion constructors --

    template<typename U, qualifier P>
    constexpr explicit vec(vec<1, U, P> const& v);

    /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification)
    template<typename A, typename B>
    constexpr vec(A x, B y);
    template<typename A, typename B>
    constexpr vec(vec<1, A, Q> const& x, B y);
    template<typename A, typename B>
    constexpr vec(A x, vec<1, B, Q> const& y);
    template<typename A, typename B>
    constexpr vec(vec<1, A, Q> const& x, vec<1, B, Q> const& y);

    // -- Conversion vector constructors --

    /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification)
    template<typename U, qualifier P>
    constexpr  vec(vec<3, U, P> const& v);
    /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification)
    template<typename U, qualifier P>
    constexpr  vec(vec<4, U, P> const& v);

    /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification)
    template<typename U, qualifier P>
    constexpr  vec(vec<2, U, P> const& v);

    // -- Unary arithmetic operators --

    constexpr vec<2, T, Q>& operator=(vec const& v) = default;

    template<typename U>
    constexpr vec<2, T, Q>& operator=(vec<2, U, Q> const& v);
    template<typename U>
    constexpr vec<2, T, Q>& operator+=(U scalar);
    template<typename U>
    constexpr vec<2, T, Q>& operator+=(vec<1, U, Q> const& v);
    template<typename U>
    constexpr vec<2, T, Q>& operator+=(vec<2, U, Q> const& v);
    template<typename U>
    constexpr vec<2, T, Q>& operator-=(U scalar);
    template<typename U>
    constexpr vec<2, T, Q>& operator-=(vec<1, U, Q> const& v);
    template<typename U>
    constexpr vec<2, T, Q>& operator-=(vec<2, U, Q> const& v);
    template<typename U>
    constexpr vec<2, T, Q>& operator*=(U scalar);
    template<typename U>
    constexpr vec<2, T, Q>& operator*=(vec<1, U, Q> const& v);
    template<typename U>
    constexpr vec<2, T, Q>& operator*=(vec<2, U, Q> const& v);
    template<typename U>
    constexpr vec<2, T, Q>& operator/=(U scalar);
    template<typename U>
    constexpr vec<2, T, Q>& operator/=(vec<1, U, Q> const& v);
    template<typename U>
    constexpr vec<2, T, Q>& operator/=(vec<2, U, Q> const& v);

    // -- Increment and decrement operators --

    constexpr vec<2, T, Q>& operator++();
    constexpr vec<2, T, Q>& operator--();
    constexpr vec<2, T, Q> operator++(int);
    constexpr vec<2, T, Q> operator--(int);

    // -- Unary bit operators --

    template<typename U>
    constexpr vec<2, T, Q>& operator%=(U scalar);
    template<typename U>
    constexpr vec<2, T, Q>& operator%=(vec<1, U, Q> const& v);
    template<typename U>
    constexpr vec<2, T, Q>& operator%=(vec<2, U, Q> const& v);
    template<typename U>
    constexpr vec<2, T, Q>& operator&=(U scalar);
    template<typename U>
    constexpr vec<2, T, Q>& operator&=(vec<1, U, Q> const& v);
    template<typename U>
    constexpr vec<2, T, Q>& operator&=(vec<2, U, Q> const& v);
    template<typename U>
    constexpr vec<2, T, Q>& operator|=(U scalar);
    template<typename U>
    constexpr vec<2, T, Q>& operator|=(vec<1, U, Q> const& v);
    template<typename U>
    constexpr vec<2, T, Q>& operator|=(vec<2, U, Q> const& v);
    template<typename U>
    constexpr vec<2, T, Q>& operator^=(U scalar);
    template<typename U>
    constexpr vec<2, T, Q>& operator^=(vec<1, U, Q> const& v);
    template<typename U>
    constexpr vec<2, T, Q>& operator^=(vec<2, U, Q> const& v);
    template<typename U>
    constexpr vec<2, T, Q>& operator<<=(U scalar);
    template<typename U>
    constexpr vec<2, T, Q>& operator<<=(vec<1, U, Q> const& v);
    template<typename U>
    constexpr vec<2, T, Q>& operator<<=(vec<2, U, Q> const& v);
    template<typename U>
    constexpr vec<2, T, Q>& operator>>=(U scalar);
    template<typename U>
    constexpr vec<2, T, Q>& operator>>=(vec<1, U, Q> const& v);
    template<typename U>
    constexpr vec<2, T, Q>& operator>>=(vec<2, U, Q> const& v);
  };

  // -- Unary operators --

  template<typename T, qualifier Q>
  constexpr vec<2, T, Q> operator+(vec<2, T, Q> const& v);

  template<typename T, qualifier Q>
  constexpr vec<2, T, Q> operator-(vec<2, T, Q> const& v);

  // -- Binary operators --

  template<typename T, qualifier Q>
  constexpr vec<2, T, Q> operator+(vec<2, T, Q> const& v, T scalar);

  template<typename T, qualifier Q>
  constexpr vec<2, T, Q> operator+(vec<2, T, Q> const& v1, vec<1, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<2, T, Q> operator+(T scalar, vec<2, T, Q> const& v);

  template<typename T, qualifier Q>
  constexpr vec<2, T, Q> operator+(vec<1, T, Q> const& v1, vec<2, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<2, T, Q> operator+(vec<2, T, Q> const& v1, vec<2, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<2, T, Q> operator-(vec<2, T, Q> const& v, T scalar);

  template<typename T, qualifier Q>
  constexpr vec<2, T, Q> operator-(vec<2, T, Q> const& v1, vec<1, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<2, T, Q> operator-(T scalar, vec<2, T, Q> const& v);

  template<typename T, qualifier Q>
  constexpr vec<2, T, Q> operator-(vec<1, T, Q> const& v1, vec<2, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<2, T, Q> operator-(vec<2, T, Q> const& v1, vec<2, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<2, T, Q> operator*(vec<2, T, Q> const& v, T scalar);

  template<typename T, qualifier Q>
  constexpr vec<2, T, Q> operator*(vec<2, T, Q> const& v1, vec<1, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<2, T, Q> operator*(T scalar, vec<2, T, Q> const& v);

  template<typename T, qualifier Q>
  constexpr vec<2, T, Q> operator*(vec<1, T, Q> const& v1, vec<2, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<2, T, Q> operator*(vec<2, T, Q> const& v1, vec<2, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<2, T, Q> operator/(vec<2, T, Q> const& v, T scalar);

  template<typename T, qualifier Q>
  constexpr vec<2, T, Q> operator/(vec<2, T, Q> const& v1, vec<1, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<2, T, Q> operator/(T scalar, vec<2, T, Q> const& v);

  template<typename T, qualifier Q>
  constexpr vec<2, T, Q> operator/(vec<1, T, Q> const& v1, vec<2, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<2, T, Q> operator/(vec<2, T, Q> const& v1, vec<2, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<2, T, Q> operator%(vec<2, T, Q> const& v, T scalar);

  template<typename T, qualifier Q>
  constexpr vec<2, T, Q> operator%(vec<2, T, Q> const& v1, vec<1, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<2, T, Q> operator%(T scalar, vec<2, T, Q> const& v);

  template<typename T, qualifier Q>
  constexpr vec<2, T, Q> operator%(vec<1, T, Q> const& v1, vec<2, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<2, T, Q> operator%(vec<2, T, Q> const& v1, vec<2, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<2, T, Q> operator&(vec<2, T, Q> const& v, T scalar);

  template<typename T, qualifier Q>
  constexpr vec<2, T, Q> operator&(vec<2, T, Q> const& v1, vec<1, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<2, T, Q> operator&(T scalar, vec<2, T, Q> const& v);

  template<typename T, qualifier Q>
  constexpr vec<2, T, Q> operator&(vec<1, T, Q> const& v1, vec<2, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<2, T, Q> operator&(vec<2, T, Q> const& v1, vec<2, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<2, T, Q> operator|(vec<2, T, Q> const& v, T scalar);

  template<typename T, qualifier Q>
  constexpr vec<2, T, Q> operator|(vec<2, T, Q> const& v1, vec<1, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<2, T, Q> operator|(T scalar, vec<2, T, Q> const& v);

  template<typename T, qualifier Q>
  constexpr vec<2, T, Q> operator|(vec<1, T, Q> const& v1, vec<2, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<2, T, Q> operator|(vec<2, T, Q> const& v1, vec<2, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<2, T, Q> operator^(vec<2, T, Q> const& v, T scalar);

  template<typename T, qualifier Q>
  constexpr vec<2, T, Q> operator^(vec<2, T, Q> const& v1, vec<1, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<2, T, Q> operator^(T scalar, vec<2, T, Q> const& v);

  template<typename T, qualifier Q>
  constexpr vec<2, T, Q> operator^(vec<1, T, Q> const& v1, vec<2, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<2, T, Q> operator^(vec<2, T, Q> const& v1, vec<2, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<2, T, Q> operator<<(vec<2, T, Q> const& v, T scalar);

  template<typename T, qualifier Q>
  constexpr vec<2, T, Q> operator<<(vec<2, T, Q> const& v1, vec<1, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<2, T, Q> operator<<(T scalar, vec<2, T, Q> const& v);

  template<typename T, qualifier Q>
  constexpr vec<2, T, Q> operator<<(vec<1, T, Q> const& v1, vec<2, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<2, T, Q> operator<<(vec<2, T, Q> const& v1, vec<2, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<2, T, Q> operator>>(vec<2, T, Q> const& v, T scalar);

  template<typename T, qualifier Q>
  constexpr vec<2, T, Q> operator>>(vec<2, T, Q> const& v1, vec<1, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<2, T, Q> operator>>(T scalar, vec<2, T, Q> const& v);

  template<typename T, qualifier Q>
  constexpr vec<2, T, Q> operator>>(vec<1, T, Q> const& v1, vec<2, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<2, T, Q> operator>>(vec<2, T, Q> const& v1, vec<2, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<2, T, Q> operator~(vec<2, T, Q> const& v);

  // -- Boolean operators --

  template<typename T, qualifier Q>
  constexpr bool operator==(vec<2, T, Q> const& v1, vec<2, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr bool operator!=(vec<2, T, Q> const& v1, vec<2, T, Q> const& v2);

  template<qualifier Q>
  constexpr vec<2, bool, Q> operator&&(vec<2, bool, Q> const& v1, vec<2, bool, Q> const& v2);

  template<qualifier Q>
  constexpr vec<2, bool, Q> operator||(vec<2, bool, Q> const& v1, vec<2, bool, Q> const& v2);
}//namespace glm

namespace glm
{
  // -- Implicit basic constructors --
  template<typename T, qualifier Q>
  template<qualifier P>
  inline constexpr vec<2, T, Q>::vec(vec<2, T, P> const& v)
    : x(v.x), y(v.y)
  {}

  // -- Explicit basic constructors --

  template<typename T, qualifier Q>
  inline constexpr vec<2, T, Q>::vec(T scalar)
    : x(scalar), y(scalar)
  {}

  template<typename T, qualifier Q>
  inline constexpr vec<2, T, Q>::vec(T _x, T _y)
    : x(_x), y(_y)
  {}

  // -- Conversion scalar constructors --

  template<typename T, qualifier Q>
  template<typename U, qualifier P>
  inline constexpr vec<2, T, Q>::vec(vec<1, U, P> const& v)
    : x(static_cast<T>(v.x))
    , y(static_cast<T>(v.x))
  {}

  template<typename T, qualifier Q>
  template<typename A, typename B>
  inline constexpr vec<2, T, Q>::vec(A _x, B _y)
    : x(static_cast<T>(_x))
    , y(static_cast<T>(_y))
  {}

  template<typename T, qualifier Q>
  template<typename A, typename B>
  inline constexpr vec<2, T, Q>::vec(vec<1, A, Q> const& _x, B _y)
    : x(static_cast<T>(_x.x))
    , y(static_cast<T>(_y))
  {}

  template<typename T, qualifier Q>
  template<typename A, typename B>
  inline constexpr vec<2, T, Q>::vec(A _x, vec<1, B, Q> const& _y)
    : x(static_cast<T>(_x))
    , y(static_cast<T>(_y.x))
  {}

  template<typename T, qualifier Q>
  template<typename A, typename B>
  inline constexpr vec<2, T, Q>::vec(vec<1, A, Q> const& _x, vec<1, B, Q> const& _y)
    : x(static_cast<T>(_x.x))
    , y(static_cast<T>(_y.x))
  {}

  // -- Conversion vector constructors --

  template<typename T, qualifier Q>
  template<typename U, qualifier P>
  inline constexpr vec<2, T, Q>::vec(vec<2, U, P> const& v)
    : x(static_cast<T>(v.x))
    , y(static_cast<T>(v.y))
  {}

  template<typename T, qualifier Q>
  template<typename U, qualifier P>
  inline constexpr vec<2, T, Q>::vec(vec<3, U, P> const& v)
    : x(static_cast<T>(v.x))
    , y(static_cast<T>(v.y))
  {}

  template<typename T, qualifier Q>
  template<typename U, qualifier P>
  inline constexpr vec<2, T, Q>::vec(vec<4, U, P> const& v)
    : x(static_cast<T>(v.x))
    , y(static_cast<T>(v.y))
  {}

  // -- Component accesses --

  template<typename T, qualifier Q>
  inline constexpr T& vec<2, T, Q>::operator[](typename vec<2, T, Q>::length_type i)
  {
    assert(i >= 0 && i < this->length());
    switch (i)
    {
    default:
    case 0:
      return x;
    case 1:
      return y;
    }
  }

  template<typename T, qualifier Q>
  inline constexpr T const& vec<2, T, Q>::operator[](typename vec<2, T, Q>::length_type i) const
  {
    assert(i >= 0 && i < this->length());
    switch (i)
    {
    default:
    case 0:
      return x;
    case 1:
      return y;
    }
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<2, T, Q>& vec<2, T, Q>::operator=(vec<2, U, Q> const& v)
  {
    this->x = static_cast<T>(v.x);
    this->y = static_cast<T>(v.y);
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<2, T, Q>& vec<2, T, Q>::operator+=(U scalar)
  {
    this->x += static_cast<T>(scalar);
    this->y += static_cast<T>(scalar);
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<2, T, Q>& vec<2, T, Q>::operator+=(vec<1, U, Q> const& v)
  {
    this->x += static_cast<T>(v.x);
    this->y += static_cast<T>(v.x);
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<2, T, Q>& vec<2, T, Q>::operator+=(vec<2, U, Q> const& v)
  {
    this->x += static_cast<T>(v.x);
    this->y += static_cast<T>(v.y);
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<2, T, Q>& vec<2, T, Q>::operator-=(U scalar)
  {
    this->x -= static_cast<T>(scalar);
    this->y -= static_cast<T>(scalar);
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<2, T, Q>& vec<2, T, Q>::operator-=(vec<1, U, Q> const& v)
  {
    this->x -= static_cast<T>(v.x);
    this->y -= static_cast<T>(v.x);
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<2, T, Q>& vec<2, T, Q>::operator-=(vec<2, U, Q> const& v)
  {
    this->x -= static_cast<T>(v.x);
    this->y -= static_cast<T>(v.y);
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<2, T, Q>& vec<2, T, Q>::operator*=(U scalar)
  {
    this->x *= static_cast<T>(scalar);
    this->y *= static_cast<T>(scalar);
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<2, T, Q>& vec<2, T, Q>::operator*=(vec<1, U, Q> const& v)
  {
    this->x *= static_cast<T>(v.x);
    this->y *= static_cast<T>(v.x);
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<2, T, Q>& vec<2, T, Q>::operator*=(vec<2, U, Q> const& v)
  {
    this->x *= static_cast<T>(v.x);
    this->y *= static_cast<T>(v.y);
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<2, T, Q>& vec<2, T, Q>::operator/=(U scalar)
  {
    this->x /= static_cast<T>(scalar);
    this->y /= static_cast<T>(scalar);
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<2, T, Q>& vec<2, T, Q>::operator/=(vec<1, U, Q> const& v)
  {
    this->x /= static_cast<T>(v.x);
    this->y /= static_cast<T>(v.x);
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<2, T, Q>& vec<2, T, Q>::operator/=(vec<2, U, Q> const& v)
  {
    this->x /= static_cast<T>(v.x);
    this->y /= static_cast<T>(v.y);
    return *this;
  }

  // -- Increment and decrement operators --

  template<typename T, qualifier Q>
  inline constexpr vec<2, T, Q>& vec<2, T, Q>::operator++()
  {
    ++this->x;
    ++this->y;
    return *this;
  }

  template<typename T, qualifier Q>
  inline constexpr vec<2, T, Q>& vec<2, T, Q>::operator--()
  {
    --this->x;
    --this->y;
    return *this;
  }

  template<typename T, qualifier Q>
  inline constexpr vec<2, T, Q> vec<2, T, Q>::operator++(int)
  {
    vec<2, T, Q> Result(*this);
    ++*this;
    return Result;
  }

  template<typename T, qualifier Q>
  inline constexpr vec<2, T, Q> vec<2, T, Q>::operator--(int)
  {
    vec<2, T, Q> Result(*this);
    --*this;
    return Result;
  }

  // -- Unary bit operators --

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<2, T, Q>& vec<2, T, Q>::operator%=(U scalar)
  {
    this->x %= static_cast<T>(scalar);
    this->y %= static_cast<T>(scalar);
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<2, T, Q>& vec<2, T, Q>::operator%=(vec<1, U, Q> const& v)
  {
    this->x %= static_cast<T>(v.x);
    this->y %= static_cast<T>(v.x);
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<2, T, Q>& vec<2, T, Q>::operator%=(vec<2, U, Q> const& v)
  {
    this->x %= static_cast<T>(v.x);
    this->y %= static_cast<T>(v.y);
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<2, T, Q>& vec<2, T, Q>::operator&=(U scalar)
  {
    this->x &= static_cast<T>(scalar);
    this->y &= static_cast<T>(scalar);
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<2, T, Q>& vec<2, T, Q>::operator&=(vec<1, U, Q> const& v)
  {
    this->x &= static_cast<T>(v.x);
    this->y &= static_cast<T>(v.x);
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<2, T, Q>& vec<2, T, Q>::operator&=(vec<2, U, Q> const& v)
  {
    this->x &= static_cast<T>(v.x);
    this->y &= static_cast<T>(v.y);
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<2, T, Q>& vec<2, T, Q>::operator|=(U scalar)
  {
    this->x |= static_cast<T>(scalar);
    this->y |= static_cast<T>(scalar);
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<2, T, Q>& vec<2, T, Q>::operator|=(vec<1, U, Q> const& v)
  {
    this->x |= static_cast<T>(v.x);
    this->y |= static_cast<T>(v.x);
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<2, T, Q>& vec<2, T, Q>::operator|=(vec<2, U, Q> const& v)
  {
    this->x |= static_cast<T>(v.x);
    this->y |= static_cast<T>(v.y);
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<2, T, Q>& vec<2, T, Q>::operator^=(U scalar)
  {
    this->x ^= static_cast<T>(scalar);
    this->y ^= static_cast<T>(scalar);
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<2, T, Q>& vec<2, T, Q>::operator^=(vec<1, U, Q> const& v)
  {
    this->x ^= static_cast<T>(v.x);
    this->y ^= static_cast<T>(v.x);
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<2, T, Q>& vec<2, T, Q>::operator^=(vec<2, U, Q> const& v)
  {
    this->x ^= static_cast<T>(v.x);
    this->y ^= static_cast<T>(v.y);
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<2, T, Q>& vec<2, T, Q>::operator<<=(U scalar)
  {
    this->x <<= static_cast<T>(scalar);
    this->y <<= static_cast<T>(scalar);
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<2, T, Q>& vec<2, T, Q>::operator<<=(vec<1, U, Q> const& v)
  {
    this->x <<= static_cast<T>(v.x);
    this->y <<= static_cast<T>(v.x);
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<2, T, Q>& vec<2, T, Q>::operator<<=(vec<2, U, Q> const& v)
  {
    this->x <<= static_cast<T>(v.x);
    this->y <<= static_cast<T>(v.y);
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<2, T, Q>& vec<2, T, Q>::operator>>=(U scalar)
  {
    this->x >>= static_cast<T>(scalar);
    this->y >>= static_cast<T>(scalar);
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<2, T, Q>& vec<2, T, Q>::operator>>=(vec<1, U, Q> const& v)
  {
    this->x >>= static_cast<T>(v.x);
    this->y >>= static_cast<T>(v.x);
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<2, T, Q>& vec<2, T, Q>::operator>>=(vec<2, U, Q> const& v)
  {
    this->x >>= static_cast<T>(v.x);
    this->y >>= static_cast<T>(v.y);
    return *this;
  }

  // -- Unary arithmetic operators --

  template<typename T, qualifier Q>
  inline constexpr vec<2, T, Q> operator+(vec<2, T, Q> const& v)
  {
    return v;
  }

  template<typename T, qualifier Q>
  inline constexpr vec<2, T, Q> operator-(vec<2, T, Q> const& v)
  {
    return vec<2, T, Q>(
      -v.x,
      -v.y);
  }

  // -- Binary arithmetic operators --

  template<typename T, qualifier Q>
  inline constexpr vec<2, T, Q> operator+(vec<2, T, Q> const& v, T scalar)
  {
    return vec<2, T, Q>(
      v.x + scalar,
      v.y + scalar);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<2, T, Q> operator+(vec<2, T, Q> const& v1, vec<1, T, Q> const& v2)
  {
    return vec<2, T, Q>(
      v1.x + v2.x,
      v1.y + v2.x);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<2, T, Q> operator+(T scalar, vec<2, T, Q> const& v)
  {
    return vec<2, T, Q>(
      scalar + v.x,
      scalar + v.y);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<2, T, Q> operator+(vec<1, T, Q> const& v1, vec<2, T, Q> const& v2)
  {
    return vec<2, T, Q>(
      v1.x + v2.x,
      v1.x + v2.y);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<2, T, Q> operator+(vec<2, T, Q> const& v1, vec<2, T, Q> const& v2)
  {
    return vec<2, T, Q>(
      v1.x + v2.x,
      v1.y + v2.y);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<2, T, Q> operator-(vec<2, T, Q> const& v, T scalar)
  {
    return vec<2, T, Q>(
      v.x - scalar,
      v.y - scalar);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<2, T, Q> operator-(vec<2, T, Q> const& v1, vec<1, T, Q> const& v2)
  {
    return vec<2, T, Q>(
      v1.x - v2.x,
      v1.y - v2.x);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<2, T, Q> operator-(T scalar, vec<2, T, Q> const& v)
  {
    return vec<2, T, Q>(
      scalar - v.x,
      scalar - v.y);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<2, T, Q> operator-(vec<1, T, Q> const& v1, vec<2, T, Q> const& v2)
  {
    return vec<2, T, Q>(
      v1.x - v2.x,
      v1.x - v2.y);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<2, T, Q> operator-(vec<2, T, Q> const& v1, vec<2, T, Q> const& v2)
  {
    return vec<2, T, Q>(
      v1.x - v2.x,
      v1.y - v2.y);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<2, T, Q> operator*(vec<2, T, Q> const& v, T scalar)
  {
    return vec<2, T, Q>(
      v.x * scalar,
      v.y * scalar);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<2, T, Q> operator*(vec<2, T, Q> const& v1, vec<1, T, Q> const& v2)
  {
    return vec<2, T, Q>(
      v1.x * v2.x,
      v1.y * v2.x);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<2, T, Q> operator*(T scalar, vec<2, T, Q> const& v)
  {
    return vec<2, T, Q>(
      scalar * v.x,
      scalar * v.y);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<2, T, Q> operator*(vec<1, T, Q> const& v1, vec<2, T, Q> const& v2)
  {
    return vec<2, T, Q>(
      v1.x * v2.x,
      v1.x * v2.y);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<2, T, Q> operator*(vec<2, T, Q> const& v1, vec<2, T, Q> const& v2)
  {
    return vec<2, T, Q>(
      v1.x * v2.x,
      v1.y * v2.y);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<2, T, Q> operator/(vec<2, T, Q> const& v, T scalar)
  {
    return vec<2, T, Q>(
      v.x / scalar,
      v.y / scalar);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<2, T, Q> operator/(vec<2, T, Q> const& v1, vec<1, T, Q> const& v2)
  {
    return vec<2, T, Q>(
      v1.x / v2.x,
      v1.y / v2.x);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<2, T, Q> operator/(T scalar, vec<2, T, Q> const& v)
  {
    return vec<2, T, Q>(
      scalar / v.x,
      scalar / v.y);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<2, T, Q> operator/(vec<1, T, Q> const& v1, vec<2, T, Q> const& v2)
  {
    return vec<2, T, Q>(
      v1.x / v2.x,
      v1.x / v2.y);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<2, T, Q> operator/(vec<2, T, Q> const& v1, vec<2, T, Q> const& v2)
  {
    return vec<2, T, Q>(
      v1.x / v2.x,
      v1.y / v2.y);
  }

  // -- Binary bit operators --

  template<typename T, qualifier Q>
  inline constexpr vec<2, T, Q> operator%(vec<2, T, Q> const& v, T scalar)
  {
    return vec<2, T, Q>(
      v.x % scalar,
      v.y % scalar);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<2, T, Q> operator%(vec<2, T, Q> const& v1, vec<1, T, Q> const& v2)
  {
    return vec<2, T, Q>(
      v1.x % v2.x,
      v1.y % v2.x);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<2, T, Q> operator%(T scalar, vec<2, T, Q> const& v)
  {
    return vec<2, T, Q>(
      scalar % v.x,
      scalar % v.y);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<2, T, Q> operator%(vec<1, T, Q> const& v1, vec<2, T, Q> const& v2)
  {
    return vec<2, T, Q>(
      v1.x % v2.x,
      v1.x % v2.y);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<2, T, Q> operator%(vec<2, T, Q> const& v1, vec<2, T, Q> const& v2)
  {
    return vec<2, T, Q>(
      v1.x % v2.x,
      v1.y % v2.y);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<2, T, Q> operator&(vec<2, T, Q> const& v, T scalar)
  {
    return vec<2, T, Q>(
      v.x & scalar,
      v.y & scalar);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<2, T, Q> operator&(vec<2, T, Q> const& v1, vec<1, T, Q> const& v2)
  {
    return vec<2, T, Q>(
      v1.x & v2.x,
      v1.y & v2.x);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<2, T, Q> operator&(T scalar, vec<2, T, Q> const& v)
  {
    return vec<2, T, Q>(
      scalar & v.x,
      scalar & v.y);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<2, T, Q> operator&(vec<1, T, Q> const& v1, vec<2, T, Q> const& v2)
  {
    return vec<2, T, Q>(
      v1.x & v2.x,
      v1.x & v2.y);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<2, T, Q> operator&(vec<2, T, Q> const& v1, vec<2, T, Q> const& v2)
  {
    return vec<2, T, Q>(
      v1.x & v2.x,
      v1.y & v2.y);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<2, T, Q> operator|(vec<2, T, Q> const& v, T scalar)
  {
    return vec<2, T, Q>(
      v.x | scalar,
      v.y | scalar);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<2, T, Q> operator|(vec<2, T, Q> const& v1, vec<1, T, Q> const& v2)
  {
    return vec<2, T, Q>(
      v1.x | v2.x,
      v1.y | v2.x);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<2, T, Q> operator|(T scalar, vec<2, T, Q> const& v)
  {
    return vec<2, T, Q>(
      scalar | v.x,
      scalar | v.y);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<2, T, Q> operator|(vec<1, T, Q> const& v1, vec<2, T, Q> const& v2)
  {
    return vec<2, T, Q>(
      v1.x | v2.x,
      v1.x | v2.y);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<2, T, Q> operator|(vec<2, T, Q> const& v1, vec<2, T, Q> const& v2)
  {
    return vec<2, T, Q>(
      v1.x | v2.x,
      v1.y | v2.y);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<2, T, Q> operator^(vec<2, T, Q> const& v, T scalar)
  {
    return vec<2, T, Q>(
      v.x ^ scalar,
      v.y ^ scalar);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<2, T, Q> operator^(vec<2, T, Q> const& v1, vec<1, T, Q> const& v2)
  {
    return vec<2, T, Q>(
      v1.x ^ v2.x,
      v1.y ^ v2.x);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<2, T, Q> operator^(T scalar, vec<2, T, Q> const& v)
  {
    return vec<2, T, Q>(
      scalar ^ v.x,
      scalar ^ v.y);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<2, T, Q> operator^(vec<1, T, Q> const& v1, vec<2, T, Q> const& v2)
  {
    return vec<2, T, Q>(
      v1.x ^ v2.x,
      v1.x ^ v2.y);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<2, T, Q> operator^(vec<2, T, Q> const& v1, vec<2, T, Q> const& v2)
  {
    return vec<2, T, Q>(
      v1.x ^ v2.x,
      v1.y ^ v2.y);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<2, T, Q> operator<<(vec<2, T, Q> const& v, T scalar)
  {
    return vec<2, T, Q>(
      v.x << scalar,
      v.y << scalar);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<2, T, Q> operator<<(vec<2, T, Q> const& v1, vec<1, T, Q> const& v2)
  {
    return vec<2, T, Q>(
      v1.x << v2.x,
      v1.y << v2.x);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<2, T, Q> operator<<(T scalar, vec<2, T, Q> const& v)
  {
    return vec<2, T, Q>(
      scalar << v.x,
      scalar << v.y);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<2, T, Q> operator<<(vec<1, T, Q> const& v1, vec<2, T, Q> const& v2)
  {
    return vec<2, T, Q>(
      v1.x << v2.x,
      v1.x << v2.y);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<2, T, Q> operator<<(vec<2, T, Q> const& v1, vec<2, T, Q> const& v2)
  {
    return vec<2, T, Q>(
      v1.x << v2.x,
      v1.y << v2.y);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<2, T, Q> operator>>(vec<2, T, Q> const& v, T scalar)
  {
    return vec<2, T, Q>(
      v.x >> scalar,
      v.y >> scalar);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<2, T, Q> operator>>(vec<2, T, Q> const& v1, vec<1, T, Q> const& v2)
  {
    return vec<2, T, Q>(
      v1.x >> v2.x,
      v1.y >> v2.x);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<2, T, Q> operator>>(T scalar, vec<2, T, Q> const& v)
  {
    return vec<2, T, Q>(
      scalar >> v.x,
      scalar >> v.y);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<2, T, Q> operator>>(vec<1, T, Q> const& v1, vec<2, T, Q> const& v2)
  {
    return vec<2, T, Q>(
      v1.x >> v2.x,
      v1.x >> v2.y);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<2, T, Q> operator>>(vec<2, T, Q> const& v1, vec<2, T, Q> const& v2)
  {
    return vec<2, T, Q>(
      v1.x >> v2.x,
      v1.y >> v2.y);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<2, T, Q> operator~(vec<2, T, Q> const& v)
  {
    return vec<2, T, Q>(
      ~v.x,
      ~v.y);
  }

  // -- Boolean operators --

  template<typename T, qualifier Q>
  inline constexpr bool operator==(vec<2, T, Q> const& v1, vec<2, T, Q> const& v2)
  {
    return
      detail::compute_equal<T, std::numeric_limits<T>::is_iec559>::call(v1.x, v2.x) &&
      detail::compute_equal<T, std::numeric_limits<T>::is_iec559>::call(v1.y, v2.y);
  }

  template<typename T, qualifier Q>
  inline constexpr bool operator!=(vec<2, T, Q> const& v1, vec<2, T, Q> const& v2)
  {
    return !(v1 == v2);
  }

  template<qualifier Q>
  inline constexpr vec<2, bool, Q> operator&&(vec<2, bool, Q> const& v1, vec<2, bool, Q> const& v2)
  {
    return vec<2, bool, Q>(v1.x && v2.x, v1.y && v2.y);
  }

  template<qualifier Q>
  inline constexpr vec<2, bool, Q> operator||(vec<2, bool, Q> const& v1, vec<2, bool, Q> const& v2)
  {
    return vec<2, bool, Q>(v1.x || v2.x, v1.y || v2.y);
  }
}//namespace glm




namespace glm
{
  /// @addtogroup core_vector
  /// @{

  /// 2 components vector of boolean.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.5 Vectors</a>
  typedef vec<2, bool, defaultp>		bvec2;

  /// @}
}//namespace glm


/// @ref core
/// @file glm/ext/vector_bool2_precision.hpp




namespace glm
{
  /// @addtogroup core_vector_precision
  /// @{

  /// 2 components vector of high qualifier bool numbers.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.5 Vectors</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef vec<2, bool, highp>		highp_bvec2;

  /// 2 components vector of medium qualifier bool numbers.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.5 Vectors</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef vec<2, bool, mediump>	mediump_bvec2;

  /// 2 components vector of low qualifier bool numbers.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.5 Vectors</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef vec<2, bool, lowp>		lowp_bvec2;

  /// @}
}//namespace glm


/// @ref core
/// @file glm/ext/vector_float2.hpp




namespace glm
{
  /// @addtogroup core_vector
  /// @{

  /// 2 components vector of single-precision floating-point numbers.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.5 Vectors</a>
  typedef vec<2, float, defaultp>	vec2;

  /// @}
}//namespace glm


/// @ref core
/// @file glm/ext/vector_float2_precision.hpp




namespace glm
{
  /// @addtogroup core_vector_precision
  /// @{

  /// 2 components vector of high single-qualifier floating-point numbers.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.5 Vectors</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef vec<2, float, highp>		highp_vec2;

  /// 2 components vector of medium single-qualifier floating-point numbers.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.5 Vectors</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef vec<2, float, mediump>		mediump_vec2;

  /// 2 components vector of low single-qualifier floating-point numbers.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.5 Vectors</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef vec<2, float, lowp>			lowp_vec2;

  /// @}
}//namespace glm


/// @ref core
/// @file glm/ext/vector_double2.hpp




namespace glm
{
  /// @addtogroup core_vector
  /// @{

  /// 2 components vector of double-precision floating-point numbers.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.5 Vectors</a>
  typedef vec<2, double, defaultp>		dvec2;

  /// @}
}//namespace glm


/// @ref core
/// @file glm/ext/vector_double2_precision.hpp



namespace glm {
  namespace detail
  {
    typedef std::int8_t			int8;
    typedef std::int16_t		int16;
    typedef std::int32_t		int32;

    template<>
    struct is_int<int8>
    {
      enum test { value = ~0 };
    };

    template<>
    struct is_int<int16>
    {
      enum test { value = ~0 };
    };

    template<>
    struct is_int<int64>
    {
      enum test { value = ~0 };
    };
  }//namespace detail


    /// @addtogroup ext_scalar_int_sized
    /// @{

    /// 8 bit signed integer type.
  typedef detail::int8		int8;

  /// 16 bit signed integer type.
  typedef detail::int16		int16;

  /// 32 bit signed integer type.
  typedef detail::int32		int32;

  /// 64 bit signed integer type.
  typedef detail::int64		int64;

  /// @}
}//namespace glm

namespace glm
{
  /// @addtogroup ext_vector_int2_sized
  /// @{

  /// 8 bit signed integer vector of 2 components type.
  ///
  /// @see ext_vector_int2_sized
  typedef vec<2, int8, defaultp>		i8vec2;

  /// 16 bit signed integer vector of 2 components type.
  ///
  /// @see ext_vector_int2_sized
  typedef vec<2, int16, defaultp>		i16vec2;

  /// 32 bit signed integer vector of 2 components type.
  ///
  /// @see ext_vector_int2_sized
  typedef vec<2, int32, defaultp>		i32vec2;

  /// 64 bit signed integer vector of 2 components type.
  ///
  /// @see ext_vector_int2_sized
  typedef vec<2, int64, defaultp>		i64vec2;

  /// @}
}//namespace glm

namespace glm
{
  /// @addtogroup core_vector
  /// @{

  /// 2 components vector of unsigned integer numbers.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.5 Vectors</a>
  typedef vec<2, unsigned int, defaultp>		uvec2;

  /// @}
}//namespace glm

namespace glm {
  namespace detail
  {
    typedef std::uint8_t		uint8;
    typedef std::uint16_t		uint16;
    typedef std::uint32_t		uint32;

    template<>
    struct is_int<uint8>
    {
      enum test { value = ~0 };
    };

    template<>
    struct is_int<uint16>
    {
      enum test { value = ~0 };
    };

    template<>
    struct is_int<uint64>
    {
      enum test { value = ~0 };
    };
  }//namespace detail


    /// @addtogroup ext_scalar_uint_sized
    /// @{

    /// 8 bit unsigned integer type.
  typedef detail::uint8		uint8;

  /// 16 bit unsigned integer type.
  typedef detail::uint16		uint16;

  /// 32 bit unsigned integer type.
  typedef detail::uint32		uint32;

  /// 64 bit unsigned integer type.
  typedef detail::uint64		uint64;

  /// @}
}//namespace glm

namespace glm
{
  /// @addtogroup ext_vector_uint2_sized
  /// @{

  /// 8 bit unsigned integer vector of 2 components type.
  ///
  /// @see ext_vector_uint2_sized
  typedef vec<2, uint8, defaultp>		u8vec2;

  /// 16 bit unsigned integer vector of 2 components type.
  ///
  /// @see ext_vector_uint2_sized
  typedef vec<2, uint16, defaultp>	u16vec2;

  /// 32 bit unsigned integer vector of 2 components type.
  ///
  /// @see ext_vector_uint2_sized
  typedef vec<2, uint32, defaultp>	u32vec2;

  /// 64 bit unsigned integer vector of 2 components type.
  ///
  /// @see ext_vector_uint2_sized
  typedef vec<2, uint64, defaultp>	u64vec2;

  /// @}
}//namespace glm

namespace glm
{
  template<typename T, qualifier Q>
  struct vec<3, T, Q>
  {
    // -- Implementation detail --

    typedef T value_type;
    typedef vec<3, T, Q> type;
    typedef vec<3, bool, Q> bool_type;

    // -- Data --
    union { T x, r, s; };
    union { T y, g, t; };
    union { T z, b, p; };

    // -- Component accesses --

    /// Return the count of components of the vector
    typedef length_t length_type;
    static constexpr length_type length() { return 3; }

    constexpr T& operator[](length_type i);
    constexpr T const& operator[](length_type i) const;

    // -- Implicit basic constructors --

    constexpr vec() = default;
    constexpr vec(vec const& v) = default;
    template<qualifier P>
    constexpr vec(vec<3, T, P> const& v);

    // -- Explicit basic constructors --

    constexpr explicit vec(T scalar);
    constexpr vec(T a, T b, T c);

    // -- Conversion scalar constructors --

    template<typename U, qualifier P>
    constexpr explicit vec(vec<1, U, P> const& v);

    /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification)
    template<typename X, typename Y, typename Z>
    constexpr vec(X x, Y y, Z z);
    template<typename X, typename Y, typename Z>
    constexpr vec(vec<1, X, Q> const& _x, Y _y, Z _z);
    template<typename X, typename Y, typename Z>
    constexpr vec(X _x, vec<1, Y, Q> const& _y, Z _z);
    template<typename X, typename Y, typename Z>
    constexpr vec(vec<1, X, Q> const& _x, vec<1, Y, Q> const& _y, Z _z);
    template<typename X, typename Y, typename Z>
    constexpr vec(X _x, Y _y, vec<1, Z, Q> const& _z);
    template<typename X, typename Y, typename Z>
    constexpr vec(vec<1, X, Q> const& _x, Y _y, vec<1, Z, Q> const& _z);
    template<typename X, typename Y, typename Z>
    constexpr vec(X _x, vec<1, Y, Q> const& _y, vec<1, Z, Q> const& _z);
    template<typename X, typename Y, typename Z>
    constexpr vec(vec<1, X, Q> const& _x, vec<1, Y, Q> const& _y, vec<1, Z, Q> const& _z);

    // -- Conversion vector constructors --

    /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification)
    template<typename A, typename B, qualifier P>
    constexpr vec(vec<2, A, P> const& _xy, B _z);
    /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification)
    template<typename A, typename B, qualifier P>
    constexpr vec(vec<2, A, P> const& _xy, vec<1, B, P> const& _z);
    /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification)
    template<typename A, typename B, qualifier P>
    constexpr vec(A _x, vec<2, B, P> const& _yz);
    /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification)
    template<typename A, typename B, qualifier P>
    constexpr vec(vec<1, A, P> const& _x, vec<2, B, P> const& _yz);
    /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification)
    template<typename U, qualifier P>
    constexpr  vec(vec<4, U, P> const& v);

    /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification)
    template<typename U, qualifier P>
    constexpr  vec(vec<3, U, P> const& v);

    // -- Unary arithmetic operators --

    constexpr vec<3, T, Q>& operator=(vec<3, T, Q> const& v) = default;

    template<typename U>
    constexpr vec<3, T, Q>& operator=(vec<3, U, Q> const& v);
    template<typename U>
    constexpr vec<3, T, Q>& operator+=(U scalar);
    template<typename U>
    constexpr vec<3, T, Q>& operator+=(vec<1, U, Q> const& v);
    template<typename U>
    constexpr vec<3, T, Q>& operator+=(vec<3, U, Q> const& v);
    template<typename U>
    constexpr vec<3, T, Q>& operator-=(U scalar);
    template<typename U>
    constexpr vec<3, T, Q>& operator-=(vec<1, U, Q> const& v);
    template<typename U>
    constexpr vec<3, T, Q>& operator-=(vec<3, U, Q> const& v);
    template<typename U>
    constexpr vec<3, T, Q>& operator*=(U scalar);
    template<typename U>
    constexpr vec<3, T, Q>& operator*=(vec<1, U, Q> const& v);
    template<typename U>
    constexpr vec<3, T, Q>& operator*=(vec<3, U, Q> const& v);
    template<typename U>
    constexpr vec<3, T, Q>& operator/=(U scalar);
    template<typename U>
    constexpr vec<3, T, Q>& operator/=(vec<1, U, Q> const& v);
    template<typename U>
    constexpr vec<3, T, Q>& operator/=(vec<3, U, Q> const& v);

    // -- Increment and decrement operators --

    constexpr vec<3, T, Q>& operator++();
    constexpr vec<3, T, Q>& operator--();
    constexpr vec<3, T, Q> operator++(int);
    constexpr vec<3, T, Q> operator--(int);

    // -- Unary bit operators --

    template<typename U>
    constexpr vec<3, T, Q>& operator%=(U scalar);
    template<typename U>
    constexpr vec<3, T, Q>& operator%=(vec<1, U, Q> const& v);
    template<typename U>
    constexpr vec<3, T, Q>& operator%=(vec<3, U, Q> const& v);
    template<typename U>
    constexpr vec<3, T, Q>& operator&=(U scalar);
    template<typename U>
    constexpr vec<3, T, Q>& operator&=(vec<1, U, Q> const& v);
    template<typename U>
    constexpr vec<3, T, Q>& operator&=(vec<3, U, Q> const& v);
    template<typename U>
    constexpr vec<3, T, Q>& operator|=(U scalar);
    template<typename U>
    constexpr vec<3, T, Q>& operator|=(vec<1, U, Q> const& v);
    template<typename U>
    constexpr vec<3, T, Q>& operator|=(vec<3, U, Q> const& v);
    template<typename U>
    constexpr vec<3, T, Q>& operator^=(U scalar);
    template<typename U>
    constexpr vec<3, T, Q>& operator^=(vec<1, U, Q> const& v);
    template<typename U>
    constexpr vec<3, T, Q>& operator^=(vec<3, U, Q> const& v);
    template<typename U>
    constexpr vec<3, T, Q>& operator<<=(U scalar);
    template<typename U>
    constexpr vec<3, T, Q>& operator<<=(vec<1, U, Q> const& v);
    template<typename U>
    constexpr vec<3, T, Q>& operator<<=(vec<3, U, Q> const& v);
    template<typename U>
    constexpr vec<3, T, Q>& operator>>=(U scalar);
    template<typename U>
    constexpr vec<3, T, Q>& operator>>=(vec<1, U, Q> const& v);
    template<typename U>
    constexpr vec<3, T, Q>& operator>>=(vec<3, U, Q> const& v);
  };

  // -- Unary operators --

  template<typename T, qualifier Q>
  constexpr vec<3, T, Q> operator+(vec<3, T, Q> const& v);

  template<typename T, qualifier Q>
  constexpr vec<3, T, Q> operator-(vec<3, T, Q> const& v);

  // -- Binary operators --

  template<typename T, qualifier Q>
  constexpr vec<3, T, Q> operator+(vec<3, T, Q> const& v, T scalar);

  template<typename T, qualifier Q>
  constexpr vec<3, T, Q> operator+(vec<3, T, Q> const& v, vec<1, T, Q> const& scalar);

  template<typename T, qualifier Q>
  constexpr vec<3, T, Q> operator+(T scalar, vec<3, T, Q> const& v);

  template<typename T, qualifier Q>
  constexpr vec<3, T, Q> operator+(vec<1, T, Q> const& v1, vec<3, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<3, T, Q> operator+(vec<3, T, Q> const& v1, vec<3, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<3, T, Q> operator-(vec<3, T, Q> const& v, T scalar);

  template<typename T, qualifier Q>
  constexpr vec<3, T, Q> operator-(vec<3, T, Q> const& v1, vec<1, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<3, T, Q> operator-(T scalar, vec<3, T, Q> const& v);

  template<typename T, qualifier Q>
  constexpr vec<3, T, Q> operator-(vec<1, T, Q> const& v1, vec<3, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<3, T, Q> operator-(vec<3, T, Q> const& v1, vec<3, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<3, T, Q> operator*(vec<3, T, Q> const& v, T scalar);

  template<typename T, qualifier Q>
  constexpr vec<3, T, Q> operator*(vec<3, T, Q> const& v1, vec<1, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<3, T, Q> operator*(T scalar, vec<3, T, Q> const& v);

  template<typename T, qualifier Q>
  constexpr vec<3, T, Q> operator*(vec<1, T, Q> const& v1, vec<3, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<3, T, Q> operator*(vec<3, T, Q> const& v1, vec<3, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<3, T, Q> operator/(vec<3, T, Q> const& v, T scalar);

  template<typename T, qualifier Q>
  constexpr vec<3, T, Q> operator/(vec<3, T, Q> const& v1, vec<1, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<3, T, Q> operator/(T scalar, vec<3, T, Q> const& v);

  template<typename T, qualifier Q>
  constexpr vec<3, T, Q> operator/(vec<1, T, Q> const& v1, vec<3, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<3, T, Q> operator/(vec<3, T, Q> const& v1, vec<3, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<3, T, Q> operator%(vec<3, T, Q> const& v, T scalar);

  template<typename T, qualifier Q>
  constexpr vec<3, T, Q> operator%(vec<3, T, Q> const& v1, vec<1, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<3, T, Q> operator%(T scalar, vec<3, T, Q> const& v);

  template<typename T, qualifier Q>
  constexpr vec<3, T, Q> operator%(vec<1, T, Q> const& v1, vec<3, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<3, T, Q> operator%(vec<3, T, Q> const& v1, vec<3, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<3, T, Q> operator&(vec<3, T, Q> const& v1, T scalar);

  template<typename T, qualifier Q>
  constexpr vec<3, T, Q> operator&(vec<3, T, Q> const& v1, vec<1, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<3, T, Q> operator&(T scalar, vec<3, T, Q> const& v);

  template<typename T, qualifier Q>
  constexpr vec<3, T, Q> operator&(vec<1, T, Q> const& v1, vec<3, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<3, T, Q> operator&(vec<3, T, Q> const& v1, vec<3, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<3, T, Q> operator|(vec<3, T, Q> const& v, T scalar);

  template<typename T, qualifier Q>
  constexpr vec<3, T, Q> operator|(vec<3, T, Q> const& v1, vec<1, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<3, T, Q> operator|(T scalar, vec<3, T, Q> const& v);

  template<typename T, qualifier Q>
  constexpr vec<3, T, Q> operator|(vec<1, T, Q> const& v1, vec<3, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<3, T, Q> operator|(vec<3, T, Q> const& v1, vec<3, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<3, T, Q> operator^(vec<3, T, Q> const& v, T scalar);

  template<typename T, qualifier Q>
  constexpr vec<3, T, Q> operator^(vec<3, T, Q> const& v1, vec<1, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<3, T, Q> operator^(T scalar, vec<3, T, Q> const& v);

  template<typename T, qualifier Q>
  constexpr vec<3, T, Q> operator^(vec<1, T, Q> const& v1, vec<3, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<3, T, Q> operator^(vec<3, T, Q> const& v1, vec<3, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<3, T, Q> operator<<(vec<3, T, Q> const& v, T scalar);

  template<typename T, qualifier Q>
  constexpr vec<3, T, Q> operator<<(vec<3, T, Q> const& v1, vec<1, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<3, T, Q> operator<<(T scalar, vec<3, T, Q> const& v);

  template<typename T, qualifier Q>
  constexpr vec<3, T, Q> operator<<(vec<1, T, Q> const& v1, vec<3, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<3, T, Q> operator<<(vec<3, T, Q> const& v1, vec<3, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<3, T, Q> operator>>(vec<3, T, Q> const& v, T scalar);

  template<typename T, qualifier Q>
  constexpr vec<3, T, Q> operator>>(vec<3, T, Q> const& v1, vec<1, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<3, T, Q> operator>>(T scalar, vec<3, T, Q> const& v);

  template<typename T, qualifier Q>
  constexpr vec<3, T, Q> operator>>(vec<1, T, Q> const& v1, vec<3, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<3, T, Q> operator>>(vec<3, T, Q> const& v1, vec<3, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<3, T, Q> operator~(vec<3, T, Q> const& v);

  // -- Boolean operators --

  template<typename T, qualifier Q>
  constexpr bool operator==(vec<3, T, Q> const& v1, vec<3, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr bool operator!=(vec<3, T, Q> const& v1, vec<3, T, Q> const& v2);

  template<qualifier Q>
  constexpr vec<3, bool, Q> operator&&(vec<3, bool, Q> const& v1, vec<3, bool, Q> const& v2);

  template<qualifier Q>
  constexpr vec<3, bool, Q> operator||(vec<3, bool, Q> const& v1, vec<3, bool, Q> const& v2);
}//namespace glm

/// @ref core

namespace glm
{
  // -- Implicit basic constructors --

  template<typename T, qualifier Q>
  template<qualifier P>
  inline constexpr vec<3, T, Q>::vec(vec<3, T, P> const& v)
    : x(v.x), y(v.y), z(v.z)
  {}

  // -- Explicit basic constructors --

  template<typename T, qualifier Q>
  inline constexpr vec<3, T, Q>::vec(T scalar)
    : x(scalar), y(scalar), z(scalar)
  {}

  template <typename T, qualifier Q>
  inline constexpr vec<3, T, Q>::vec(T _x, T _y, T _z)
    : x(_x), y(_y), z(_z)
  {}

  // -- Conversion scalar constructors --

  template<typename T, qualifier Q>
  template<typename U, qualifier P>
  inline constexpr vec<3, T, Q>::vec(vec<1, U, P> const& v)
    : x(static_cast<T>(v.x))
    , y(static_cast<T>(v.x))
    , z(static_cast<T>(v.x))
  {}

  template<typename T, qualifier Q>
  template<typename X, typename Y, typename Z>
  inline constexpr vec<3, T, Q>::vec(X _x, Y _y, Z _z)
    : x(static_cast<T>(_x))
    , y(static_cast<T>(_y))
    , z(static_cast<T>(_z))
  {}

  template<typename T, qualifier Q>
  template<typename X, typename Y, typename Z>
  inline constexpr vec<3, T, Q>::vec(vec<1, X, Q> const& _x, Y _y, Z _z)
    : x(static_cast<T>(_x.x))
    , y(static_cast<T>(_y))
    , z(static_cast<T>(_z))
  {}

  template<typename T, qualifier Q>
  template<typename X, typename Y, typename Z>
  inline constexpr vec<3, T, Q>::vec(X _x, vec<1, Y, Q> const& _y, Z _z)
    : x(static_cast<T>(_x))
    , y(static_cast<T>(_y.x))
    , z(static_cast<T>(_z))
  {}

  template<typename T, qualifier Q>
  template<typename X, typename Y, typename Z>
  inline constexpr vec<3, T, Q>::vec(vec<1, X, Q> const& _x, vec<1, Y, Q> const& _y, Z _z)
    : x(static_cast<T>(_x.x))
    , y(static_cast<T>(_y.x))
    , z(static_cast<T>(_z))
  {}

  template<typename T, qualifier Q>
  template<typename X, typename Y, typename Z>
  inline constexpr vec<3, T, Q>::vec(X _x, Y _y, vec<1, Z, Q> const& _z)
    : x(static_cast<T>(_x))
    , y(static_cast<T>(_y))
    , z(static_cast<T>(_z.x))
  {}

  template<typename T, qualifier Q>
  template<typename X, typename Y, typename Z>
  inline constexpr vec<3, T, Q>::vec(vec<1, X, Q> const& _x, Y _y, vec<1, Z, Q> const& _z)
    : x(static_cast<T>(_x.x))
    , y(static_cast<T>(_y))
    , z(static_cast<T>(_z.x))
  {}

  template<typename T, qualifier Q>
  template<typename X, typename Y, typename Z>
  inline constexpr vec<3, T, Q>::vec(X _x, vec<1, Y, Q> const& _y, vec<1, Z, Q> const& _z)
    : x(static_cast<T>(_x))
    , y(static_cast<T>(_y.x))
    , z(static_cast<T>(_z.x))
  {}

  template<typename T, qualifier Q>
  template<typename X, typename Y, typename Z>
  inline constexpr vec<3, T, Q>::vec(vec<1, X, Q> const& _x, vec<1, Y, Q> const& _y, vec<1, Z, Q> const& _z)
    : x(static_cast<T>(_x.x))
    , y(static_cast<T>(_y.x))
    , z(static_cast<T>(_z.x))
  {}

  // -- Conversion vector constructors --

  template<typename T, qualifier Q>
  template<typename A, typename B, qualifier P>
  inline constexpr vec<3, T, Q>::vec(vec<2, A, P> const& _xy, B _z)
    : x(static_cast<T>(_xy.x))
    , y(static_cast<T>(_xy.y))
    , z(static_cast<T>(_z))
  {}

  template<typename T, qualifier Q>
  template<typename A, typename B, qualifier P>
  inline constexpr vec<3, T, Q>::vec(vec<2, A, P> const& _xy, vec<1, B, P> const& _z)
    : x(static_cast<T>(_xy.x))
    , y(static_cast<T>(_xy.y))
    , z(static_cast<T>(_z.x))
  {}

  template<typename T, qualifier Q>
  template<typename A, typename B, qualifier P>
  inline constexpr vec<3, T, Q>::vec(A _x, vec<2, B, P> const& _yz)
    : x(static_cast<T>(_x))
    , y(static_cast<T>(_yz.x))
    , z(static_cast<T>(_yz.y))
  {}

  template<typename T, qualifier Q>
  template<typename A, typename B, qualifier P>
  inline constexpr vec<3, T, Q>::vec(vec<1, A, P> const& _x, vec<2, B, P> const& _yz)
    : x(static_cast<T>(_x.x))
    , y(static_cast<T>(_yz.x))
    , z(static_cast<T>(_yz.y))
  {}

  template<typename T, qualifier Q>
  template<typename U, qualifier P>
  inline constexpr vec<3, T, Q>::vec(vec<3, U, P> const& v)
    : x(static_cast<T>(v.x))
    , y(static_cast<T>(v.y))
    , z(static_cast<T>(v.z))
  {}

  template<typename T, qualifier Q>
  template<typename U, qualifier P>
  inline constexpr vec<3, T, Q>::vec(vec<4, U, P> const& v)
    : x(static_cast<T>(v.x))
    , y(static_cast<T>(v.y))
    , z(static_cast<T>(v.z))
  {}

  // -- Component accesses --

  template<typename T, qualifier Q>
  inline constexpr T& vec<3, T, Q>::operator[](typename vec<3, T, Q>::length_type i)
  {
    assert(i >= 0 && i < this->length());
    switch (i)
    {
    default:
    case 0:
      return x;
    case 1:
      return y;
    case 2:
      return z;
    }
  }

  template<typename T, qualifier Q>
  inline constexpr T const& vec<3, T, Q>::operator[](typename vec<3, T, Q>::length_type i) const
  {
    assert(i >= 0 && i < this->length());
    switch (i)
    {
    default:
    case 0:
      return x;
    case 1:
      return y;
    case 2:
      return z;
    }
  }

  // -- Unary arithmetic operators --

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<3, T, Q>& vec<3, T, Q>::operator=(vec<3, U, Q> const& v)
  {
    this->x = static_cast<T>(v.x);
    this->y = static_cast<T>(v.y);
    this->z = static_cast<T>(v.z);
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<3, T, Q>& vec<3, T, Q>::operator+=(U scalar)
  {
    this->x += static_cast<T>(scalar);
    this->y += static_cast<T>(scalar);
    this->z += static_cast<T>(scalar);
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<3, T, Q>& vec<3, T, Q>::operator+=(vec<1, U, Q> const& v)
  {
    this->x += static_cast<T>(v.x);
    this->y += static_cast<T>(v.x);
    this->z += static_cast<T>(v.x);
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<3, T, Q>& vec<3, T, Q>::operator+=(vec<3, U, Q> const& v)
  {
    this->x += static_cast<T>(v.x);
    this->y += static_cast<T>(v.y);
    this->z += static_cast<T>(v.z);
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<3, T, Q>& vec<3, T, Q>::operator-=(U scalar)
  {
    this->x -= static_cast<T>(scalar);
    this->y -= static_cast<T>(scalar);
    this->z -= static_cast<T>(scalar);
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<3, T, Q>& vec<3, T, Q>::operator-=(vec<1, U, Q> const& v)
  {
    this->x -= static_cast<T>(v.x);
    this->y -= static_cast<T>(v.x);
    this->z -= static_cast<T>(v.x);
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<3, T, Q>& vec<3, T, Q>::operator-=(vec<3, U, Q> const& v)
  {
    this->x -= static_cast<T>(v.x);
    this->y -= static_cast<T>(v.y);
    this->z -= static_cast<T>(v.z);
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<3, T, Q>& vec<3, T, Q>::operator*=(U scalar)
  {
    this->x *= static_cast<T>(scalar);
    this->y *= static_cast<T>(scalar);
    this->z *= static_cast<T>(scalar);
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<3, T, Q>& vec<3, T, Q>::operator*=(vec<1, U, Q> const& v)
  {
    this->x *= static_cast<T>(v.x);
    this->y *= static_cast<T>(v.x);
    this->z *= static_cast<T>(v.x);
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<3, T, Q>& vec<3, T, Q>::operator*=(vec<3, U, Q> const& v)
  {
    this->x *= static_cast<T>(v.x);
    this->y *= static_cast<T>(v.y);
    this->z *= static_cast<T>(v.z);
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<3, T, Q>& vec<3, T, Q>::operator/=(U v)
  {
    this->x /= static_cast<T>(v);
    this->y /= static_cast<T>(v);
    this->z /= static_cast<T>(v);
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<3, T, Q>& vec<3, T, Q>::operator/=(vec<1, U, Q> const& v)
  {
    this->x /= static_cast<T>(v.x);
    this->y /= static_cast<T>(v.x);
    this->z /= static_cast<T>(v.x);
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<3, T, Q>& vec<3, T, Q>::operator/=(vec<3, U, Q> const& v)
  {
    this->x /= static_cast<T>(v.x);
    this->y /= static_cast<T>(v.y);
    this->z /= static_cast<T>(v.z);
    return *this;
  }

  // -- Increment and decrement operators --

  template<typename T, qualifier Q>
  inline constexpr vec<3, T, Q>& vec<3, T, Q>::operator++()
  {
    ++this->x;
    ++this->y;
    ++this->z;
    return *this;
  }

  template<typename T, qualifier Q>
  inline constexpr vec<3, T, Q>& vec<3, T, Q>::operator--()
  {
    --this->x;
    --this->y;
    --this->z;
    return *this;
  }

  template<typename T, qualifier Q>
  inline constexpr vec<3, T, Q> vec<3, T, Q>::operator++(int)
  {
    vec<3, T, Q> Result(*this);
    ++*this;
    return Result;
  }

  template<typename T, qualifier Q>
  inline constexpr vec<3, T, Q> vec<3, T, Q>::operator--(int)
  {
    vec<3, T, Q> Result(*this);
    --*this;
    return Result;
  }

  // -- Unary bit operators --

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<3, T, Q>& vec<3, T, Q>::operator%=(U scalar)
  {
    this->x %= scalar;
    this->y %= scalar;
    this->z %= scalar;
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<3, T, Q>& vec<3, T, Q>::operator%=(vec<1, U, Q> const& v)
  {
    this->x %= v.x;
    this->y %= v.x;
    this->z %= v.x;
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<3, T, Q>& vec<3, T, Q>::operator%=(vec<3, U, Q> const& v)
  {
    this->x %= v.x;
    this->y %= v.y;
    this->z %= v.z;
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<3, T, Q>& vec<3, T, Q>::operator&=(U scalar)
  {
    this->x &= scalar;
    this->y &= scalar;
    this->z &= scalar;
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<3, T, Q>& vec<3, T, Q>::operator&=(vec<1, U, Q> const& v)
  {
    this->x &= v.x;
    this->y &= v.x;
    this->z &= v.x;
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<3, T, Q>& vec<3, T, Q>::operator&=(vec<3, U, Q> const& v)
  {
    this->x &= v.x;
    this->y &= v.y;
    this->z &= v.z;
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<3, T, Q>& vec<3, T, Q>::operator|=(U scalar)
  {
    this->x |= scalar;
    this->y |= scalar;
    this->z |= scalar;
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<3, T, Q>& vec<3, T, Q>::operator|=(vec<1, U, Q> const& v)
  {
    this->x |= v.x;
    this->y |= v.x;
    this->z |= v.x;
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<3, T, Q>& vec<3, T, Q>::operator|=(vec<3, U, Q> const& v)
  {
    this->x |= v.x;
    this->y |= v.y;
    this->z |= v.z;
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<3, T, Q>& vec<3, T, Q>::operator^=(U scalar)
  {
    this->x ^= scalar;
    this->y ^= scalar;
    this->z ^= scalar;
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<3, T, Q>& vec<3, T, Q>::operator^=(vec<1, U, Q> const& v)
  {
    this->x ^= v.x;
    this->y ^= v.x;
    this->z ^= v.x;
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<3, T, Q>& vec<3, T, Q>::operator^=(vec<3, U, Q> const& v)
  {
    this->x ^= v.x;
    this->y ^= v.y;
    this->z ^= v.z;
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<3, T, Q>& vec<3, T, Q>::operator<<=(U scalar)
  {
    this->x <<= scalar;
    this->y <<= scalar;
    this->z <<= scalar;
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<3, T, Q>& vec<3, T, Q>::operator<<=(vec<1, U, Q> const& v)
  {
    this->x <<= static_cast<T>(v.x);
    this->y <<= static_cast<T>(v.x);
    this->z <<= static_cast<T>(v.x);
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<3, T, Q>& vec<3, T, Q>::operator<<=(vec<3, U, Q> const& v)
  {
    this->x <<= static_cast<T>(v.x);
    this->y <<= static_cast<T>(v.y);
    this->z <<= static_cast<T>(v.z);
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<3, T, Q>& vec<3, T, Q>::operator>>=(U scalar)
  {
    this->x >>= static_cast<T>(scalar);
    this->y >>= static_cast<T>(scalar);
    this->z >>= static_cast<T>(scalar);
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<3, T, Q>& vec<3, T, Q>::operator>>=(vec<1, U, Q> const& v)
  {
    this->x >>= static_cast<T>(v.x);
    this->y >>= static_cast<T>(v.x);
    this->z >>= static_cast<T>(v.x);
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<3, T, Q>& vec<3, T, Q>::operator>>=(vec<3, U, Q> const& v)
  {
    this->x >>= static_cast<T>(v.x);
    this->y >>= static_cast<T>(v.y);
    this->z >>= static_cast<T>(v.z);
    return *this;
  }

  // -- Unary arithmetic operators --

  template<typename T, qualifier Q>
  inline constexpr vec<3, T, Q> operator+(vec<3, T, Q> const& v)
  {
    return v;
  }

  template<typename T, qualifier Q>
  inline constexpr vec<3, T, Q> operator-(vec<3, T, Q> const& v)
  {
    return vec<3, T, Q>(
      -v.x,
      -v.y,
      -v.z);
  }

  // -- Binary arithmetic operators --

  template<typename T, qualifier Q>
  inline constexpr vec<3, T, Q> operator+(vec<3, T, Q> const& v, T scalar)
  {
    return vec<3, T, Q>(
      v.x + scalar,
      v.y + scalar,
      v.z + scalar);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<3, T, Q> operator+(vec<3, T, Q> const& v, vec<1, T, Q> const& scalar)
  {
    return vec<3, T, Q>(
      v.x + scalar.x,
      v.y + scalar.x,
      v.z + scalar.x);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<3, T, Q> operator+(T scalar, vec<3, T, Q> const& v)
  {
    return vec<3, T, Q>(
      scalar + v.x,
      scalar + v.y,
      scalar + v.z);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<3, T, Q> operator+(vec<1, T, Q> const& scalar, vec<3, T, Q> const& v)
  {
    return vec<3, T, Q>(
      scalar.x + v.x,
      scalar.x + v.y,
      scalar.x + v.z);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<3, T, Q> operator+(vec<3, T, Q> const& v1, vec<3, T, Q> const& v2)
  {
    return vec<3, T, Q>(
      v1.x + v2.x,
      v1.y + v2.y,
      v1.z + v2.z);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<3, T, Q> operator-(vec<3, T, Q> const& v, T scalar)
  {
    return vec<3, T, Q>(
      v.x - scalar,
      v.y - scalar,
      v.z - scalar);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<3, T, Q> operator-(vec<3, T, Q> const& v, vec<1, T, Q> const& scalar)
  {
    return vec<3, T, Q>(
      v.x - scalar.x,
      v.y - scalar.x,
      v.z - scalar.x);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<3, T, Q> operator-(T scalar, vec<3, T, Q> const& v)
  {
    return vec<3, T, Q>(
      scalar - v.x,
      scalar - v.y,
      scalar - v.z);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<3, T, Q> operator-(vec<1, T, Q> const& scalar, vec<3, T, Q> const& v)
  {
    return vec<3, T, Q>(
      scalar.x - v.x,
      scalar.x - v.y,
      scalar.x - v.z);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<3, T, Q> operator-(vec<3, T, Q> const& v1, vec<3, T, Q> const& v2)
  {
    return vec<3, T, Q>(
      v1.x - v2.x,
      v1.y - v2.y,
      v1.z - v2.z);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<3, T, Q> operator*(vec<3, T, Q> const& v, T scalar)
  {
    return vec<3, T, Q>(
      v.x * scalar,
      v.y * scalar,
      v.z * scalar);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<3, T, Q> operator*(vec<3, T, Q> const& v, vec<1, T, Q> const& scalar)
  {
    return vec<3, T, Q>(
      v.x * scalar.x,
      v.y * scalar.x,
      v.z * scalar.x);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<3, T, Q> operator*(T scalar, vec<3, T, Q> const& v)
  {
    return vec<3, T, Q>(
      scalar * v.x,
      scalar * v.y,
      scalar * v.z);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<3, T, Q> operator*(vec<1, T, Q> const& scalar, vec<3, T, Q> const& v)
  {
    return vec<3, T, Q>(
      scalar.x * v.x,
      scalar.x * v.y,
      scalar.x * v.z);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<3, T, Q> operator*(vec<3, T, Q> const& v1, vec<3, T, Q> const& v2)
  {
    return vec<3, T, Q>(
      v1.x * v2.x,
      v1.y * v2.y,
      v1.z * v2.z);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<3, T, Q> operator/(vec<3, T, Q> const& v, T scalar)
  {
    return vec<3, T, Q>(
      v.x / scalar,
      v.y / scalar,
      v.z / scalar);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<3, T, Q> operator/(vec<3, T, Q> const& v, vec<1, T, Q> const& scalar)
  {
    return vec<3, T, Q>(
      v.x / scalar.x,
      v.y / scalar.x,
      v.z / scalar.x);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<3, T, Q> operator/(T scalar, vec<3, T, Q> const& v)
  {
    return vec<3, T, Q>(
      scalar / v.x,
      scalar / v.y,
      scalar / v.z);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<3, T, Q> operator/(vec<1, T, Q> const& scalar, vec<3, T, Q> const& v)
  {
    return vec<3, T, Q>(
      scalar.x / v.x,
      scalar.x / v.y,
      scalar.x / v.z);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<3, T, Q> operator/(vec<3, T, Q> const& v1, vec<3, T, Q> const& v2)
  {
    return vec<3, T, Q>(
      v1.x / v2.x,
      v1.y / v2.y,
      v1.z / v2.z);
  }

  // -- Binary bit operators --

  template<typename T, qualifier Q>
  inline constexpr vec<3, T, Q> operator%(vec<3, T, Q> const& v, T scalar)
  {
    return vec<3, T, Q>(
      v.x % scalar,
      v.y % scalar,
      v.z % scalar);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<3, T, Q> operator%(vec<3, T, Q> const& v, vec<1, T, Q> const& scalar)
  {
    return vec<3, T, Q>(
      v.x % scalar.x,
      v.y % scalar.x,
      v.z % scalar.x);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<3, T, Q> operator%(T scalar, vec<3, T, Q> const& v)
  {
    return vec<3, T, Q>(
      scalar % v.x,
      scalar % v.y,
      scalar % v.z);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<3, T, Q> operator%(vec<1, T, Q> const& scalar, vec<3, T, Q> const& v)
  {
    return vec<3, T, Q>(
      scalar.x % v.x,
      scalar.x % v.y,
      scalar.x % v.z);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<3, T, Q> operator%(vec<3, T, Q> const& v1, vec<3, T, Q> const& v2)
  {
    return vec<3, T, Q>(
      v1.x % v2.x,
      v1.y % v2.y,
      v1.z % v2.z);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<3, T, Q> operator&(vec<3, T, Q> const& v, T scalar)
  {
    return vec<3, T, Q>(
      v.x & scalar,
      v.y & scalar,
      v.z & scalar);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<3, T, Q> operator&(vec<3, T, Q> const& v, vec<1, T, Q> const& scalar)
  {
    return vec<3, T, Q>(
      v.x & scalar.x,
      v.y & scalar.x,
      v.z & scalar.x);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<3, T, Q> operator&(T scalar, vec<3, T, Q> const& v)
  {
    return vec<3, T, Q>(
      scalar & v.x,
      scalar & v.y,
      scalar & v.z);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<3, T, Q> operator&(vec<1, T, Q> const& scalar, vec<3, T, Q> const& v)
  {
    return vec<3, T, Q>(
      scalar.x & v.x,
      scalar.x & v.y,
      scalar.x & v.z);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<3, T, Q> operator&(vec<3, T, Q> const& v1, vec<3, T, Q> const& v2)
  {
    return vec<3, T, Q>(
      v1.x & v2.x,
      v1.y & v2.y,
      v1.z & v2.z);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<3, T, Q> operator|(vec<3, T, Q> const& v, T scalar)
  {
    return vec<3, T, Q>(
      v.x | scalar,
      v.y | scalar,
      v.z | scalar);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<3, T, Q> operator|(vec<3, T, Q> const& v, vec<1, T, Q> const& scalar)
  {
    return vec<3, T, Q>(
      v.x | scalar.x,
      v.y | scalar.x,
      v.z | scalar.x);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<3, T, Q> operator|(T scalar, vec<3, T, Q> const& v)
  {
    return vec<3, T, Q>(
      scalar | v.x,
      scalar | v.y,
      scalar | v.z);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<3, T, Q> operator|(vec<1, T, Q> const& scalar, vec<3, T, Q> const& v)
  {
    return vec<3, T, Q>(
      scalar.x | v.x,
      scalar.x | v.y,
      scalar.x | v.z);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<3, T, Q> operator|(vec<3, T, Q> const& v1, vec<3, T, Q> const& v2)
  {
    return vec<3, T, Q>(
      v1.x | v2.x,
      v1.y | v2.y,
      v1.z | v2.z);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<3, T, Q> operator^(vec<3, T, Q> const& v, T scalar)
  {
    return vec<3, T, Q>(
      v.x ^ scalar,
      v.y ^ scalar,
      v.z ^ scalar);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<3, T, Q> operator^(vec<3, T, Q> const& v, vec<1, T, Q> const& scalar)
  {
    return vec<3, T, Q>(
      v.x ^ scalar.x,
      v.y ^ scalar.x,
      v.z ^ scalar.x);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<3, T, Q> operator^(T scalar, vec<3, T, Q> const& v)
  {
    return vec<3, T, Q>(
      scalar ^ v.x,
      scalar ^ v.y,
      scalar ^ v.z);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<3, T, Q> operator^(vec<1, T, Q> const& scalar, vec<3, T, Q> const& v)
  {
    return vec<3, T, Q>(
      scalar.x ^ v.x,
      scalar.x ^ v.y,
      scalar.x ^ v.z);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<3, T, Q> operator^(vec<3, T, Q> const& v1, vec<3, T, Q> const& v2)
  {
    return vec<3, T, Q>(
      v1.x ^ v2.x,
      v1.y ^ v2.y,
      v1.z ^ v2.z);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<3, T, Q> operator<<(vec<3, T, Q> const& v, T scalar)
  {
    return vec<3, T, Q>(
      v.x << scalar,
      v.y << scalar,
      v.z << scalar);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<3, T, Q> operator<<(vec<3, T, Q> const& v, vec<1, T, Q> const& scalar)
  {
    return vec<3, T, Q>(
      v.x << scalar.x,
      v.y << scalar.x,
      v.z << scalar.x);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<3, T, Q> operator<<(T scalar, vec<3, T, Q> const& v)
  {
    return vec<3, T, Q>(
      scalar << v.x,
      scalar << v.y,
      scalar << v.z);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<3, T, Q> operator<<(vec<1, T, Q> const& scalar, vec<3, T, Q> const& v)
  {
    return vec<3, T, Q>(
      scalar.x << v.x,
      scalar.x << v.y,
      scalar.x << v.z);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<3, T, Q> operator<<(vec<3, T, Q> const& v1, vec<3, T, Q> const& v2)
  {
    return vec<3, T, Q>(
      v1.x << v2.x,
      v1.y << v2.y,
      v1.z << v2.z);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<3, T, Q> operator>>(vec<3, T, Q> const& v, T scalar)
  {
    return vec<3, T, Q>(
      v.x >> scalar,
      v.y >> scalar,
      v.z >> scalar);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<3, T, Q> operator>>(vec<3, T, Q> const& v, vec<1, T, Q> const& scalar)
  {
    return vec<3, T, Q>(
      v.x >> scalar.x,
      v.y >> scalar.x,
      v.z >> scalar.x);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<3, T, Q> operator>>(T scalar, vec<3, T, Q> const& v)
  {
    return vec<3, T, Q>(
      scalar >> v.x,
      scalar >> v.y,
      scalar >> v.z);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<3, T, Q> operator>>(vec<1, T, Q> const& scalar, vec<3, T, Q> const& v)
  {
    return vec<3, T, Q>(
      scalar.x >> v.x,
      scalar.x >> v.y,
      scalar.x >> v.z);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<3, T, Q> operator>>(vec<3, T, Q> const& v1, vec<3, T, Q> const& v2)
  {
    return vec<3, T, Q>(
      v1.x >> v2.x,
      v1.y >> v2.y,
      v1.z >> v2.z);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<3, T, Q> operator~(vec<3, T, Q> const& v)
  {
    return vec<3, T, Q>(
      ~v.x,
      ~v.y,
      ~v.z);
  }

  // -- Boolean operators --

  template<typename T, qualifier Q>
  inline constexpr bool operator==(vec<3, T, Q> const& v1, vec<3, T, Q> const& v2)
  {
    return
      detail::compute_equal<T, std::numeric_limits<T>::is_iec559>::call(v1.x, v2.x) &&
      detail::compute_equal<T, std::numeric_limits<T>::is_iec559>::call(v1.y, v2.y) &&
      detail::compute_equal<T, std::numeric_limits<T>::is_iec559>::call(v1.z, v2.z);
  }

  template<typename T, qualifier Q>
  inline constexpr bool operator!=(vec<3, T, Q> const& v1, vec<3, T, Q> const& v2)
  {
    return !(v1 == v2);
  }

  template<qualifier Q>
  inline constexpr vec<3, bool, Q> operator&&(vec<3, bool, Q> const& v1, vec<3, bool, Q> const& v2)
  {
    return vec<3, bool, Q>(v1.x && v2.x, v1.y && v2.y, v1.z && v2.z);
  }

  template<qualifier Q>
  inline constexpr vec<3, bool, Q> operator||(vec<3, bool, Q> const& v1, vec<3, bool, Q> const& v2)
  {
    return vec<3, bool, Q>(v1.x || v2.x, v1.y || v2.y, v1.z || v2.z);
  }
}//namespace glm




namespace glm
{
  /// @addtogroup core_vector
  /// @{

  /// 3 components vector of boolean.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.5 Vectors</a>
  typedef vec<3, bool, defaultp>		bvec3;

  /// @}
}//namespace glm


/// @ref core
/// @file glm/ext/vector_bool3_precision.hpp




namespace glm
{
  /// @addtogroup core_vector_precision
  /// @{

  /// 3 components vector of high qualifier bool numbers.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.5 Vectors</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef vec<3, bool, highp>		highp_bvec3;

  /// 3 components vector of medium qualifier bool numbers.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.5 Vectors</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef vec<3, bool, mediump>	mediump_bvec3;

  /// 3 components vector of low qualifier bool numbers.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.5 Vectors</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef vec<3, bool, lowp>		lowp_bvec3;

  /// @}
}//namespace glm


/// @ref core
/// @file glm/ext/vector_float3.hpp




namespace glm
{
  /// @addtogroup core_vector
  /// @{

  /// 3 components vector of single-precision floating-point numbers.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.5 Vectors</a>
  typedef vec<3, float, defaultp>		vec3;

  /// @}
}//namespace glm


/// @ref core
/// @file glm/ext/vector_float3_precision.hpp




namespace glm
{
  /// @addtogroup core_vector_precision
  /// @{

  /// 3 components vector of high single-qualifier floating-point numbers.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.5 Vectors</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef vec<3, float, highp>		highp_vec3;

  /// 3 components vector of medium single-qualifier floating-point numbers.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.5 Vectors</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef vec<3, float, mediump>		mediump_vec3;

  /// 3 components vector of low single-qualifier floating-point numbers.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.5 Vectors</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef vec<3, float, lowp>			lowp_vec3;

  /// @}
}//namespace glm


/// @ref core
/// @file glm/ext/vector_double3.hpp




namespace glm
{
  /// @addtogroup core_vector
  /// @{

  /// 3 components vector of double-precision floating-point numbers.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.5 Vectors</a>
  typedef vec<3, double, defaultp>		dvec3;

  /// @}
}//namespace glm


/// @ref core
/// @file glm/ext/vector_double3_precision.hpp




namespace glm
{
  /// @addtogroup core_vector_precision
  /// @{

  /// 3 components vector of high double-qualifier floating-point numbers.
  /// There is no guarantee on the actual qualifier.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.5 Vectors</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef vec<3, double, highp>		highp_dvec3;

  /// 3 components vector of medium double-qualifier floating-point numbers.
  /// There is no guarantee on the actual qualifier.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.5 Vectors</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef vec<3, double, mediump>		mediump_dvec3;

  /// 3 components vector of low double-qualifier floating-point numbers.
  /// There is no guarantee on the actual qualifier.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.5 Vectors</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef vec<3, double, lowp>		lowp_dvec3;

  /// @}
}//namespace glm


/// @ref core
/// @file glm/ext/vector_int3.hpp




namespace glm
{
  /// @addtogroup core_vector
  /// @{

  /// 3 components vector of signed integer numbers.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.5 Vectors</a>
  typedef vec<3, int, defaultp>		ivec3;

  /// @}
}//namespace glm


/// @ref ext_vector_int3_sized
/// @file glm/ext/vector_int3_sized.hpp
///
/// @defgroup ext_vector_int3_sized GLM_EXT_vector_int3_sized
/// @ingroup ext
///
/// Exposes sized signed integer vector of 3 components type.
///
/// Include <glm/ext/vector_int3_sized.hpp> to use the features of this extension.
///
/// @see ext_scalar_int_sized
/// @see ext_vector_uint3_sized










namespace glm
{
  /// @addtogroup ext_vector_int3_sized
  /// @{

  /// 8 bit signed integer vector of 3 components type.
  ///
  /// @see ext_vector_int3_sized
  typedef vec<3, int8, defaultp>		i8vec3;

  /// 16 bit signed integer vector of 3 components type.
  ///
  /// @see ext_vector_int3_sized
  typedef vec<3, int16, defaultp>		i16vec3;

  /// 32 bit signed integer vector of 3 components type.
  ///
  /// @see ext_vector_int3_sized
  typedef vec<3, int32, defaultp>		i32vec3;

  /// 64 bit signed integer vector of 3 components type.
  ///
  /// @see ext_vector_int3_sized
  typedef vec<3, int64, defaultp>		i64vec3;

  /// @}
}//namespace glm


/// @ref core
/// @file glm/ext/vector_uint3.hpp




namespace glm
{
  /// @addtogroup core_vector
  /// @{

  /// 3 components vector of unsigned integer numbers.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.5 Vectors</a>
  typedef vec<3, unsigned int, defaultp>		uvec3;

  /// @}
}//namespace glm


/// @ref ext_vector_uint3_sized
/// @file glm/ext/vector_uint3_sized.hpp
///
/// @defgroup ext_vector_uint3_sized GLM_EXT_vector_uint3_sized
/// @ingroup ext
///
/// Exposes sized unsigned integer vector of 3 components type.
///
/// Include <glm/ext/vector_uint3_sized.hpp> to use the features of this extension.
///
/// @see ext_scalar_uint_sized
/// @see ext_vector_int3_sized










namespace glm
{
  /// @addtogroup ext_vector_uint3_sized
  /// @{

  /// 8 bit unsigned integer vector of 3 components type.
  ///
  /// @see ext_vector_uint3_sized
  typedef vec<3, uint8, defaultp>		u8vec3;

  /// 16 bit unsigned integer vector of 3 components type.
  ///
  /// @see ext_vector_uint3_sized
  typedef vec<3, uint16, defaultp>	u16vec3;

  /// 32 bit unsigned integer vector of 3 components type.
  ///
  /// @see ext_vector_uint3_sized
  typedef vec<3, uint32, defaultp>	u32vec3;

  /// 64 bit unsigned integer vector of 3 components type.
  ///
  /// @see ext_vector_uint3_sized
  typedef vec<3, uint64, defaultp>	u64vec3;

  /// @}
}//namespace glm



/// @ref core
/// @file glm/vec4.hpp



/// @ref core
/// @file glm/ext/vector_bool4.hpp




namespace glm
{
  /// @addtogroup core_vector
  /// @{

  /// 4 components vector of boolean.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.5 Vectors</a>
  typedef vec<4, bool, defaultp>		bvec4;

  /// @}
}//namespace glm


/// @ref core
/// @file glm/ext/vector_bool4_precision.hpp




namespace glm
{
  /// @addtogroup core_vector_precision
  /// @{

  /// 4 components vector of high qualifier bool numbers.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.5 Vectors</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef vec<4, bool, highp>		highp_bvec4;

  /// 4 components vector of medium qualifier bool numbers.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.5 Vectors</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef vec<4, bool, mediump>	mediump_bvec4;

  /// 4 components vector of low qualifier bool numbers.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.5 Vectors</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef vec<4, bool, lowp>		lowp_bvec4;

  /// @}
}//namespace glm


/// @ref core
/// @file glm/ext/vector_float4.hpp




namespace glm
{
  /// @addtogroup core_vector
  /// @{

  /// 4 components vector of single-precision floating-point numbers.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.5 Vectors</a>
  typedef vec<4, float, defaultp>		vec4;

  /// @}
}//namespace glm


/// @ref core
/// @file glm/ext/vector_float4_precision.hpp




namespace glm
{
  /// @addtogroup core_vector_precision
  /// @{

  /// 4 components vector of high single-qualifier floating-point numbers.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.5 Vectors</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef vec<4, float, highp>		highp_vec4;

  /// 4 components vector of medium single-qualifier floating-point numbers.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.5 Vectors</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef vec<4, float, mediump>		mediump_vec4;

  /// 4 components vector of low single-qualifier floating-point numbers.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.5 Vectors</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef vec<4, float, lowp>			lowp_vec4;

  /// @}
}//namespace glm


/// @ref core
/// @file glm/ext/vector_double4.hpp




namespace glm
{
  /// @addtogroup core_vector
  /// @{

  /// 4 components vector of double-precision floating-point numbers.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.5 Vectors</a>
  typedef vec<4, double, defaultp>		dvec4;

  /// @}
}//namespace glm


namespace glm
{
  /// @addtogroup core_vector_precision
  /// @{

  /// 4 components vector of high double-qualifier floating-point numbers.
  /// There is no guarantee on the actual qualifier.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.5 Vectors</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef vec<4, double, highp>		highp_dvec4;

  /// 4 components vector of medium double-qualifier floating-point numbers.
  /// There is no guarantee on the actual qualifier.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.5 Vectors</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef vec<4, double, mediump>		mediump_dvec4;

  /// 4 components vector of low double-qualifier floating-point numbers.
  /// There is no guarantee on the actual qualifier.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.5 Vectors</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef vec<4, double, lowp>		lowp_dvec4;

  /// @}
}//namespace glm


/// @ref core
/// @file glm/ext/vector_int4.hpp




namespace glm
{
  /// @addtogroup core_vector
  /// @{

  /// 4 components vector of signed integer numbers.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.5 Vectors</a>
  typedef vec<4, int, defaultp>		ivec4;

  /// @}
}//namespace glm


/// @ref ext_vector_int4_sized
/// @file glm/ext/vector_int4_sized.hpp
///
/// @defgroup ext_vector_int4_sized GLM_EXT_vector_int4_sized
/// @ingroup ext
///
/// Exposes sized signed integer vector of 4 components type.
///
/// Include <glm/ext/vector_int4_sized.hpp> to use the features of this extension.
///
/// @see ext_scalar_int_sized
/// @see ext_vector_uint4_sized










namespace glm
{
  /// @addtogroup ext_vector_int4_sized
  /// @{

  /// 8 bit signed integer vector of 4 components type.
  ///
  /// @see ext_vector_int4_sized
  typedef vec<4, int8, defaultp>		i8vec4;

  /// 16 bit signed integer vector of 4 components type.
  ///
  /// @see ext_vector_int4_sized
  typedef vec<4, int16, defaultp>		i16vec4;

  /// 32 bit signed integer vector of 4 components type.
  ///
  /// @see ext_vector_int4_sized
  typedef vec<4, int32, defaultp>		i32vec4;

  /// 64 bit signed integer vector of 4 components type.
  ///
  /// @see ext_vector_int4_sized
  typedef vec<4, int64, defaultp>		i64vec4;

  /// @}
}//namespace glm


/// @ref core
/// @file glm/ext/vector_uint4.hpp




namespace glm
{
  /// @addtogroup core_vector
  /// @{

  /// 4 components vector of unsigned integer numbers.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.5 Vectors</a>
  typedef vec<4, unsigned int, defaultp>		uvec4;

  /// @}
}//namespace glm


/// @ref ext_vector_uint4_sized
/// @file glm/ext/vector_uint4_sized.hpp
///
/// @defgroup ext_vector_uint4_sized GLM_EXT_vector_uint4_sized
/// @ingroup ext
///
/// Exposes sized unsigned integer vector of 4 components type.
///
/// Include <glm/ext/vector_uint4_sized.hpp> to use the features of this extension.
///
/// @see ext_scalar_uint_sized
/// @see ext_vector_int4_sized










namespace glm
{
  /// @addtogroup ext_vector_uint4_sized
  /// @{

  /// 8 bit unsigned integer vector of 4 components type.
  ///
  /// @see ext_vector_uint4_sized
  typedef vec<4, uint8, defaultp>		u8vec4;

  /// 16 bit unsigned integer vector of 4 components type.
  ///
  /// @see ext_vector_uint4_sized
  typedef vec<4, uint16, defaultp>	u16vec4;

  /// 32 bit unsigned integer vector of 4 components type.
  ///
  /// @see ext_vector_uint4_sized
  typedef vec<4, uint32, defaultp>	u32vec4;

  /// 64 bit unsigned integer vector of 4 components type.
  ///
  /// @see ext_vector_uint4_sized
  typedef vec<4, uint64, defaultp>	u64vec4;

  /// @}
}//namespace glm

namespace glm
{
  template<typename T, qualifier Q>
  struct mat<2, 2, T, Q>
  {
    typedef vec<2, T, Q> col_type;
    typedef vec<2, T, Q> row_type;
    typedef mat<2, 2, T, Q> type;
    typedef mat<2, 2, T, Q> transpose_type;
    typedef T value_type;

  private:
    col_type value[2];

  public:
    // -- Accesses --

    typedef length_t length_type;
    static constexpr length_type length() { return 2; }

    col_type& operator[](length_type i);
    constexpr col_type const& operator[](length_type i) const;

    // -- Constructors --

    constexpr mat() = default;
    template<qualifier P>
    constexpr mat(mat<2, 2, T, P> const& m);

    explicit constexpr mat(T scalar);
    constexpr mat(
      T const& x1, T const& y1,
      T const& x2, T const& y2);
    constexpr mat(
      col_type const& v1,
      col_type const& v2);

    // -- Conversions --

    template<typename U, typename V, typename M, typename N>
    constexpr mat(
      U const& x1, V const& y1,
      M const& x2, N const& y2);

    template<typename U, typename V>
    constexpr mat(
      vec<2, U, Q> const& v1,
      vec<2, V, Q> const& v2);

    // -- Matrix conversions --

    template<typename U, qualifier P>
    constexpr mat(mat<2, 2, U, P> const& m);

    constexpr mat(mat<3, 3, T, Q> const& x);
    constexpr mat(mat<4, 4, T, Q> const& x);
    constexpr mat(mat<2, 3, T, Q> const& x);
    constexpr mat(mat<3, 2, T, Q> const& x);
    constexpr mat(mat<2, 4, T, Q> const& x);
    constexpr mat(mat<4, 2, T, Q> const& x);
    constexpr mat(mat<3, 4, T, Q> const& x);
    constexpr mat(mat<4, 3, T, Q> const& x);

    // -- Unary arithmetic operators --

    template<typename U>
    mat<2, 2, T, Q>& operator=(mat<2, 2, U, Q> const& m);
    template<typename U>
    mat<2, 2, T, Q>& operator+=(U s);
    template<typename U>
    mat<2, 2, T, Q>& operator+=(mat<2, 2, U, Q> const& m);
    template<typename U>
    mat<2, 2, T, Q>& operator-=(U s);
    template<typename U>
    mat<2, 2, T, Q>& operator-=(mat<2, 2, U, Q> const& m);
    template<typename U>
    mat<2, 2, T, Q>& operator*=(U s);
    template<typename U>
    mat<2, 2, T, Q>& operator*=(mat<2, 2, U, Q> const& m);
    template<typename U>
    mat<2, 2, T, Q>& operator/=(U s);
    template<typename U>
    mat<2, 2, T, Q>& operator/=(mat<2, 2, U, Q> const& m);

    // -- Increment and decrement operators --

    mat<2, 2, T, Q>& operator++ ();
    mat<2, 2, T, Q>& operator-- ();
    mat<2, 2, T, Q> operator++(int);
    mat<2, 2, T, Q> operator--(int);
  };

  // -- Unary operators --

  template<typename T, qualifier Q>
  mat<2, 2, T, Q> operator+(mat<2, 2, T, Q> const& m);

  template<typename T, qualifier Q>
  mat<2, 2, T, Q> operator-(mat<2, 2, T, Q> const& m);

  // -- Binary operators --

  template<typename T, qualifier Q>
  mat<2, 2, T, Q> operator+(mat<2, 2, T, Q> const& m, T scalar);

  template<typename T, qualifier Q>
  mat<2, 2, T, Q> operator+(T scalar, mat<2, 2, T, Q> const& m);

  template<typename T, qualifier Q>
  mat<2, 2, T, Q> operator+(mat<2, 2, T, Q> const& m1, mat<2, 2, T, Q> const& m2);

  template<typename T, qualifier Q>
  mat<2, 2, T, Q> operator-(mat<2, 2, T, Q> const& m, T scalar);

  template<typename T, qualifier Q>
  mat<2, 2, T, Q> operator-(T scalar, mat<2, 2, T, Q> const& m);

  template<typename T, qualifier Q>
  mat<2, 2, T, Q> operator-(mat<2, 2, T, Q> const& m1, mat<2, 2, T, Q> const& m2);

  template<typename T, qualifier Q>
  mat<2, 2, T, Q> operator*(mat<2, 2, T, Q> const& m, T scalar);

  template<typename T, qualifier Q>
  mat<2, 2, T, Q> operator*(T scalar, mat<2, 2, T, Q> const& m);

  template<typename T, qualifier Q>
  typename mat<2, 2, T, Q>::col_type operator*(mat<2, 2, T, Q> const& m, typename mat<2, 2, T, Q>::row_type const& v);

  template<typename T, qualifier Q>
  typename mat<2, 2, T, Q>::row_type operator*(typename mat<2, 2, T, Q>::col_type const& v, mat<2, 2, T, Q> const& m);

  template<typename T, qualifier Q>
  mat<2, 2, T, Q> operator*(mat<2, 2, T, Q> const& m1, mat<2, 2, T, Q> const& m2);

  template<typename T, qualifier Q>
  mat<3, 2, T, Q> operator*(mat<2, 2, T, Q> const& m1, mat<3, 2, T, Q> const& m2);

  template<typename T, qualifier Q>
  mat<4, 2, T, Q> operator*(mat<2, 2, T, Q> const& m1, mat<4, 2, T, Q> const& m2);

  template<typename T, qualifier Q>
  mat<2, 2, T, Q> operator/(mat<2, 2, T, Q> const& m, T scalar);

  template<typename T, qualifier Q>
  mat<2, 2, T, Q> operator/(T scalar, mat<2, 2, T, Q> const& m);

  template<typename T, qualifier Q>
  typename mat<2, 2, T, Q>::col_type operator/(mat<2, 2, T, Q> const& m, typename mat<2, 2, T, Q>::row_type const& v);

  template<typename T, qualifier Q>
  typename mat<2, 2, T, Q>::row_type operator/(typename mat<2, 2, T, Q>::col_type const& v, mat<2, 2, T, Q> const& m);

  template<typename T, qualifier Q>
  mat<2, 2, T, Q> operator/(mat<2, 2, T, Q> const& m1, mat<2, 2, T, Q> const& m2);

  // -- Boolean operators --

  template<typename T, qualifier Q>
  bool operator==(mat<2, 2, T, Q> const& m1, mat<2, 2, T, Q> const& m2);

  template<typename T, qualifier Q>
  bool operator!=(mat<2, 2, T, Q> const& m1, mat<2, 2, T, Q> const& m2);
} //namespace glm





namespace glm
{
  // -- Constructors --















  template<typename T, qualifier Q>
  template<qualifier P>
  inline constexpr mat<2, 2, T, Q>::mat(mat<2, 2, T, P> const& m)

    : value{ m[0], m[1] }

  {




  }

  template<typename T, qualifier Q>
  inline constexpr mat<2, 2, T, Q>::mat(T scalar)

    : value{ col_type(scalar, 0), col_type(0, scalar) }

  {




  }

  template<typename T, qualifier Q>
  inline constexpr mat<2, 2, T, Q>::mat
  (
    T const& x0, T const& y0,
    T const& x1, T const& y1
  )

    : value{ col_type(x0, y0), col_type(x1, y1) }

  {




  }

  template<typename T, qualifier Q>
  inline constexpr mat<2, 2, T, Q>::mat(col_type const& v0, col_type const& v1)

    : value{ v0, v1 }

  {




  }

  // -- Conversion constructors --

  template<typename T, qualifier Q>
  template<typename X1, typename Y1, typename X2, typename Y2>
  inline constexpr mat<2, 2, T, Q>::mat
  (
    X1 const& x1, Y1 const& y1,
    X2 const& x2, Y2 const& y2
  )

    : value{ col_type(static_cast<T>(x1), value_type(y1)), col_type(static_cast<T>(x2), value_type(y2)) }

  {




  }

  template<typename T, qualifier Q>
  template<typename V1, typename V2>
  inline constexpr mat<2, 2, T, Q>::mat(vec<2, V1, Q> const& v1, vec<2, V2, Q> const& v2)

    : value{ col_type(v1), col_type(v2) }

  {




  }

  // -- mat2x2 matrix conversions --

  template<typename T, qualifier Q>
  template<typename U, qualifier P>
  inline constexpr mat<2, 2, T, Q>::mat(mat<2, 2, U, P> const& m)

    : value{ col_type(m[0]), col_type(m[1]) }

  {




  }

  template<typename T, qualifier Q>
  inline constexpr mat<2, 2, T, Q>::mat(mat<3, 3, T, Q> const& m)

    : value{ col_type(m[0]), col_type(m[1]) }

  {




  }

  template<typename T, qualifier Q>
  inline constexpr mat<2, 2, T, Q>::mat(mat<4, 4, T, Q> const& m)

    : value{ col_type(m[0]), col_type(m[1]) }

  {




  }

  template<typename T, qualifier Q>
  inline constexpr mat<2, 2, T, Q>::mat(mat<2, 3, T, Q> const& m)

    : value{ col_type(m[0]), col_type(m[1]) }

  {




  }

  template<typename T, qualifier Q>
  inline constexpr mat<2, 2, T, Q>::mat(mat<3, 2, T, Q> const& m)

    : value{ col_type(m[0]), col_type(m[1]) }

  {




  }

  template<typename T, qualifier Q>
  inline constexpr mat<2, 2, T, Q>::mat(mat<2, 4, T, Q> const& m)

    : value{ col_type(m[0]), col_type(m[1]) }

  {




  }

  template<typename T, qualifier Q>
  inline constexpr mat<2, 2, T, Q>::mat(mat<4, 2, T, Q> const& m)

    : value{ col_type(m[0]), col_type(m[1]) }

  {




  }

  template<typename T, qualifier Q>
  inline constexpr mat<2, 2, T, Q>::mat(mat<3, 4, T, Q> const& m)

    : value{ col_type(m[0]), col_type(m[1]) }

  {




  }

  template<typename T, qualifier Q>
  inline constexpr mat<2, 2, T, Q>::mat(mat<4, 3, T, Q> const& m)

    : value{ col_type(m[0]), col_type(m[1]) }

  {




  }

  // -- Accesses --

  template<typename T, qualifier Q>
  inline typename mat<2, 2, T, Q>::col_type& mat<2, 2, T, Q>::operator[](typename mat<2, 2, T, Q>::length_type i)
  {
    assert(i < this->length());
    return this->value[i];
  }

  template<typename T, qualifier Q>
  inline constexpr typename mat<2, 2, T, Q>::col_type const& mat<2, 2, T, Q>::operator[](typename mat<2, 2, T, Q>::length_type i) const
  {
    assert(i < this->length());
    return this->value[i];
  }

  // -- Unary updatable operators --

  template<typename T, qualifier Q>
  template<typename U>
  inline mat<2, 2, T, Q>& mat<2, 2, T, Q>::operator=(mat<2, 2, U, Q> const& m)
  {
    this->value[0] = m[0];
    this->value[1] = m[1];
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline mat<2, 2, T, Q>& mat<2, 2, T, Q>::operator+=(U scalar)
  {
    this->value[0] += scalar;
    this->value[1] += scalar;
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline mat<2, 2, T, Q>& mat<2, 2, T, Q>::operator+=(mat<2, 2, U, Q> const& m)
  {
    this->value[0] += m[0];
    this->value[1] += m[1];
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline mat<2, 2, T, Q>& mat<2, 2, T, Q>::operator-=(U scalar)
  {
    this->value[0] -= scalar;
    this->value[1] -= scalar;
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline mat<2, 2, T, Q>& mat<2, 2, T, Q>::operator-=(mat<2, 2, U, Q> const& m)
  {
    this->value[0] -= m[0];
    this->value[1] -= m[1];
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline mat<2, 2, T, Q>& mat<2, 2, T, Q>::operator*=(U scalar)
  {
    this->value[0] *= scalar;
    this->value[1] *= scalar;
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline mat<2, 2, T, Q>& mat<2, 2, T, Q>::operator*=(mat<2, 2, U, Q> const& m)
  {
    return (*this = *this * m);
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline mat<2, 2, T, Q>& mat<2, 2, T, Q>::operator/=(U scalar)
  {
    this->value[0] /= scalar;
    this->value[1] /= scalar;
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline mat<2, 2, T, Q>& mat<2, 2, T, Q>::operator/=(mat<2, 2, U, Q> const& m)
  {
    return *this *= inverse(m);
  }

  // -- Increment and decrement operators --

  template<typename T, qualifier Q>
  inline mat<2, 2, T, Q>& mat<2, 2, T, Q>::operator++()
  {
    ++this->value[0];
    ++this->value[1];
    return *this;
  }

  template<typename T, qualifier Q>
  inline mat<2, 2, T, Q>& mat<2, 2, T, Q>::operator--()
  {
    --this->value[0];
    --this->value[1];
    return *this;
  }

  template<typename T, qualifier Q>
  inline mat<2, 2, T, Q> mat<2, 2, T, Q>::operator++(int)
  {
    mat<2, 2, T, Q> Result(*this);
    ++*this;
    return Result;
  }

  template<typename T, qualifier Q>
  inline mat<2, 2, T, Q> mat<2, 2, T, Q>::operator--(int)
  {
    mat<2, 2, T, Q> Result(*this);
    --*this;
    return Result;
  }

  // -- Unary arithmetic operators --

  template<typename T, qualifier Q>
  inline mat<2, 2, T, Q> operator+(mat<2, 2, T, Q> const& m)
  {
    return m;
  }

  template<typename T, qualifier Q>
  inline mat<2, 2, T, Q> operator-(mat<2, 2, T, Q> const& m)
  {
    return mat<2, 2, T, Q>(
      -m[0],
      -m[1]);
  }

  // -- Binary arithmetic operators --

  template<typename T, qualifier Q>
  inline mat<2, 2, T, Q> operator+(mat<2, 2, T, Q> const& m, T scalar)
  {
    return mat<2, 2, T, Q>(
      m[0] + scalar,
      m[1] + scalar);
  }

  template<typename T, qualifier Q>
  inline mat<2, 2, T, Q> operator+(T scalar, mat<2, 2, T, Q> const& m)
  {
    return mat<2, 2, T, Q>(
      m[0] + scalar,
      m[1] + scalar);
  }

  template<typename T, qualifier Q>
  inline mat<2, 2, T, Q> operator+(mat<2, 2, T, Q> const& m1, mat<2, 2, T, Q> const& m2)
  {
    return mat<2, 2, T, Q>(
      m1[0] + m2[0],
      m1[1] + m2[1]);
  }

  template<typename T, qualifier Q>
  inline mat<2, 2, T, Q> operator-(mat<2, 2, T, Q> const& m, T scalar)
  {
    return mat<2, 2, T, Q>(
      m[0] - scalar,
      m[1] - scalar);
  }

  template<typename T, qualifier Q>
  inline mat<2, 2, T, Q> operator-(T scalar, mat<2, 2, T, Q> const& m)
  {
    return mat<2, 2, T, Q>(
      scalar - m[0],
      scalar - m[1]);
  }

  template<typename T, qualifier Q>
  inline mat<2, 2, T, Q> operator-(mat<2, 2, T, Q> const& m1, mat<2, 2, T, Q> const& m2)
  {
    return mat<2, 2, T, Q>(
      m1[0] - m2[0],
      m1[1] - m2[1]);
  }

  template<typename T, qualifier Q>
  inline mat<2, 2, T, Q> operator*(mat<2, 2, T, Q> const& m, T scalar)
  {
    return mat<2, 2, T, Q>(
      m[0] * scalar,
      m[1] * scalar);
  }

  template<typename T, qualifier Q>
  inline mat<2, 2, T, Q> operator*(T scalar, mat<2, 2, T, Q> const& m)
  {
    return mat<2, 2, T, Q>(
      m[0] * scalar,
      m[1] * scalar);
  }

  template<typename T, qualifier Q>
  inline typename mat<2, 2, T, Q>::col_type operator*
    (
      mat<2, 2, T, Q> const& m,
      typename mat<2, 2, T, Q>::row_type const& v
      )
  {
    return vec<2, T, Q>(
      m[0][0] * v.x + m[1][0] * v.y,
      m[0][1] * v.x + m[1][1] * v.y);
  }

  template<typename T, qualifier Q>
  inline typename mat<2, 2, T, Q>::row_type operator*
    (
      typename mat<2, 2, T, Q>::col_type const& v,
      mat<2, 2, T, Q> const& m
      )
  {
    return vec<2, T, Q>(
      v.x * m[0][0] + v.y * m[0][1],
      v.x * m[1][0] + v.y * m[1][1]);
  }

  template<typename T, qualifier Q>
  inline mat<2, 2, T, Q> operator*(mat<2, 2, T, Q> const& m1, mat<2, 2, T, Q> const& m2)
  {
    return mat<2, 2, T, Q>(
      m1[0][0] * m2[0][0] + m1[1][0] * m2[0][1],
      m1[0][1] * m2[0][0] + m1[1][1] * m2[0][1],
      m1[0][0] * m2[1][0] + m1[1][0] * m2[1][1],
      m1[0][1] * m2[1][0] + m1[1][1] * m2[1][1]);
  }

  template<typename T, qualifier Q>
  inline mat<3, 2, T, Q> operator*(mat<2, 2, T, Q> const& m1, mat<3, 2, T, Q> const& m2)
  {
    return mat<3, 2, T, Q>(
      m1[0][0] * m2[0][0] + m1[1][0] * m2[0][1],
      m1[0][1] * m2[0][0] + m1[1][1] * m2[0][1],
      m1[0][0] * m2[1][0] + m1[1][0] * m2[1][1],
      m1[0][1] * m2[1][0] + m1[1][1] * m2[1][1],
      m1[0][0] * m2[2][0] + m1[1][0] * m2[2][1],
      m1[0][1] * m2[2][0] + m1[1][1] * m2[2][1]);
  }

  template<typename T, qualifier Q>
  inline mat<4, 2, T, Q> operator*(mat<2, 2, T, Q> const& m1, mat<4, 2, T, Q> const& m2)
  {
    return mat<4, 2, T, Q>(
      m1[0][0] * m2[0][0] + m1[1][0] * m2[0][1],
      m1[0][1] * m2[0][0] + m1[1][1] * m2[0][1],
      m1[0][0] * m2[1][0] + m1[1][0] * m2[1][1],
      m1[0][1] * m2[1][0] + m1[1][1] * m2[1][1],
      m1[0][0] * m2[2][0] + m1[1][0] * m2[2][1],
      m1[0][1] * m2[2][0] + m1[1][1] * m2[2][1],
      m1[0][0] * m2[3][0] + m1[1][0] * m2[3][1],
      m1[0][1] * m2[3][0] + m1[1][1] * m2[3][1]);
  }

  template<typename T, qualifier Q>
  inline mat<2, 2, T, Q> operator/(mat<2, 2, T, Q> const& m, T scalar)
  {
    return mat<2, 2, T, Q>(
      m[0] / scalar,
      m[1] / scalar);
  }

  template<typename T, qualifier Q>
  inline mat<2, 2, T, Q> operator/(T scalar, mat<2, 2, T, Q> const& m)
  {
    return mat<2, 2, T, Q>(
      scalar / m[0],
      scalar / m[1]);
  }

  template<typename T, qualifier Q>
  inline typename mat<2, 2, T, Q>::col_type operator/(mat<2, 2, T, Q> const& m, typename mat<2, 2, T, Q>::row_type const& v)
  {
    return inverse(m) * v;
  }

  template<typename T, qualifier Q>
  inline typename mat<2, 2, T, Q>::row_type operator/(typename mat<2, 2, T, Q>::col_type const& v, mat<2, 2, T, Q> const& m)
  {
    return v * inverse(m);
  }

  template<typename T, qualifier Q>
  inline mat<2, 2, T, Q> operator/(mat<2, 2, T, Q> const& m1, mat<2, 2, T, Q> const& m2)
  {
    mat<2, 2, T, Q> m1_copy(m1);
    return m1_copy /= m2;
  }

  // -- Boolean operators --

  template<typename T, qualifier Q>
  inline bool operator==(mat<2, 2, T, Q> const& m1, mat<2, 2, T, Q> const& m2)
  {
    return (m1[0] == m2[0]) && (m1[1] == m2[1]);
  }

  template<typename T, qualifier Q>
  inline bool operator!=(mat<2, 2, T, Q> const& m1, mat<2, 2, T, Q> const& m2)
  {
    return (m1[0] != m2[0]) || (m1[1] != m2[1]);
  }
} //namespace glm




namespace glm
{
  /// @addtogroup core_matrix
  /// @{

  /// 2 columns of 2 components matrix of double-precision floating-point numbers.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  typedef mat<2, 2, double, defaultp>		dmat2x2;

  /// 2 columns of 2 components matrix of double-precision floating-point numbers.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  typedef mat<2, 2, double, defaultp>		dmat2;

  /// @}
}//namespace glm


/// @ref core
/// @file glm/ext/matrix_double2x2_precision.hpp




namespace glm
{
  /// @addtogroup core_matrix_precision
  /// @{

  /// 2 columns of 2 components matrix of double-precision floating-point numbers using low precision arithmetic in term of ULPs.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef mat<2, 2, double, lowp>		lowp_dmat2;

  /// 2 columns of 2 components matrix of double-precision floating-point numbers using medium precision arithmetic in term of ULPs.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef mat<2, 2, double, mediump>	mediump_dmat2;

  /// 2 columns of 2 components matrix of double-precision floating-point numbers using medium precision arithmetic in term of ULPs.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef mat<2, 2, double, highp>	highp_dmat2;

  /// 2 columns of 2 components matrix of double-precision floating-point numbers using low precision arithmetic in term of ULPs.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef mat<2, 2, double, lowp>		lowp_dmat2x2;

  /// 2 columns of 2 components matrix of double-precision floating-point numbers using medium precision arithmetic in term of ULPs.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef mat<2, 2, double, mediump>	mediump_dmat2x2;

  /// 2 columns of 2 components matrix of double-precision floating-point numbers using medium precision arithmetic in term of ULPs.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef mat<2, 2, double, highp>	highp_dmat2x2;

  /// @}
}//namespace glm


/// @ref core
/// @file glm/ext/matrix_float2x2.hpp




namespace glm
{
  /// @addtogroup core_matrix
  /// @{

  /// 2 columns of 2 components matrix of single-precision floating-point numbers.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  typedef mat<2, 2, float, defaultp>		mat2x2;

  /// 2 columns of 2 components matrix of single-precision floating-point numbers.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  typedef mat<2, 2, float, defaultp>		mat2;

  /// @}
}//namespace glm


/// @ref core
/// @file glm/ext/matrix_float2x2_precision.hpp




namespace glm
{
  /// @addtogroup core_matrix_precision
  /// @{

  /// 2 columns of 2 components matrix of single-precision floating-point numbers using low precision arithmetic in term of ULPs.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef mat<2, 2, float, lowp>		lowp_mat2;

  /// 2 columns of 2 components matrix of single-precision floating-point numbers using medium precision arithmetic in term of ULPs.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef mat<2, 2, float, mediump>	mediump_mat2;

  /// 2 columns of 2 components matrix of single-precision floating-point numbers using high precision arithmetic in term of ULPs.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef mat<2, 2, float, highp>		highp_mat2;

  /// 2 columns of 2 components matrix of single-precision floating-point numbers using low precision arithmetic in term of ULPs.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef mat<2, 2, float, lowp>		lowp_mat2x2;

  /// 2 columns of 2 components matrix of single-precision floating-point numbers using medium precision arithmetic in term of ULPs.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef mat<2, 2, float, mediump>	mediump_mat2x2;

  /// 2 columns of 2 components matrix of single-precision floating-point numbers using high precision arithmetic in term of ULPs.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef mat<2, 2, float, highp>		highp_mat2x2;

  /// @}
}//namespace glm

namespace glm
{
  template<typename T, qualifier Q>
  struct mat<2, 3, T, Q>
  {
    typedef vec<3, T, Q> col_type;
    typedef vec<2, T, Q> row_type;
    typedef mat<2, 3, T, Q> type;
    typedef mat<3, 2, T, Q> transpose_type;
    typedef T value_type;

  private:
    col_type value[2];

  public:
    // -- Accesses --

    typedef length_t length_type;
    static constexpr length_type length() { return 2; }

    col_type& operator[](length_type i);
    constexpr col_type const& operator[](length_type i) const;

    // -- Constructors --

    constexpr mat() = default;
    template<qualifier P>
    constexpr mat(mat<2, 3, T, P> const& m);

    explicit constexpr mat(T scalar);
    constexpr mat(
      T x0, T y0, T z0,
      T x1, T y1, T z1);
    constexpr mat(
      col_type const& v0,
      col_type const& v1);

    // -- Conversions --

    template<typename X1, typename Y1, typename Z1, typename X2, typename Y2, typename Z2>
    constexpr mat(
      X1 x1, Y1 y1, Z1 z1,
      X2 x2, Y2 y2, Z2 z2);

    template<typename U, typename V>
    constexpr mat(
      vec<3, U, Q> const& v1,
      vec<3, V, Q> const& v2);

    // -- Matrix conversions --

    template<typename U, qualifier P>
    constexpr mat(mat<2, 3, U, P> const& m);

    constexpr mat(mat<2, 2, T, Q> const& x);
    constexpr mat(mat<3, 3, T, Q> const& x);
    constexpr mat(mat<4, 4, T, Q> const& x);
    constexpr mat(mat<2, 4, T, Q> const& x);
    constexpr mat(mat<3, 2, T, Q> const& x);
    constexpr mat(mat<3, 4, T, Q> const& x);
    constexpr mat(mat<4, 2, T, Q> const& x);
    constexpr mat(mat<4, 3, T, Q> const& x);

    // -- Unary arithmetic operators --

    template<typename U>
    mat<2, 3, T, Q>& operator=(mat<2, 3, U, Q> const& m);
    template<typename U>
    mat<2, 3, T, Q>& operator+=(U s);
    template<typename U>
    mat<2, 3, T, Q>& operator+=(mat<2, 3, U, Q> const& m);
    template<typename U>
    mat<2, 3, T, Q>& operator-=(U s);
    template<typename U>
    mat<2, 3, T, Q>& operator-=(mat<2, 3, U, Q> const& m);
    template<typename U>
    mat<2, 3, T, Q>& operator*=(U s);
    template<typename U>
    mat<2, 3, T, Q>& operator/=(U s);

    // -- Increment and decrement operators --

    mat<2, 3, T, Q>& operator++ ();
    mat<2, 3, T, Q>& operator-- ();
    mat<2, 3, T, Q> operator++(int);
    mat<2, 3, T, Q> operator--(int);
  };

  // -- Unary operators --

  template<typename T, qualifier Q>
  mat<2, 3, T, Q> operator+(mat<2, 3, T, Q> const& m);

  template<typename T, qualifier Q>
  mat<2, 3, T, Q> operator-(mat<2, 3, T, Q> const& m);

  // -- Binary operators --

  template<typename T, qualifier Q>
  mat<2, 3, T, Q> operator+(mat<2, 3, T, Q> const& m, T scalar);

  template<typename T, qualifier Q>
  mat<2, 3, T, Q> operator+(mat<2, 3, T, Q> const& m1, mat<2, 3, T, Q> const& m2);

  template<typename T, qualifier Q>
  mat<2, 3, T, Q> operator-(mat<2, 3, T, Q> const& m, T scalar);

  template<typename T, qualifier Q>
  mat<2, 3, T, Q> operator-(mat<2, 3, T, Q> const& m1, mat<2, 3, T, Q> const& m2);

  template<typename T, qualifier Q>
  mat<2, 3, T, Q> operator*(mat<2, 3, T, Q> const& m, T scalar);

  template<typename T, qualifier Q>
  mat<2, 3, T, Q> operator*(T scalar, mat<2, 3, T, Q> const& m);

  template<typename T, qualifier Q>
  typename mat<2, 3, T, Q>::col_type operator*(mat<2, 3, T, Q> const& m, typename mat<2, 3, T, Q>::row_type const& v);

  template<typename T, qualifier Q>
  typename mat<2, 3, T, Q>::row_type operator*(typename mat<2, 3, T, Q>::col_type const& v, mat<2, 3, T, Q> const& m);

  template<typename T, qualifier Q>
  mat<2, 3, T, Q> operator*(mat<2, 3, T, Q> const& m1, mat<2, 2, T, Q> const& m2);

  template<typename T, qualifier Q>
  mat<3, 3, T, Q> operator*(mat<2, 3, T, Q> const& m1, mat<3, 2, T, Q> const& m2);

  template<typename T, qualifier Q>
  mat<4, 3, T, Q> operator*(mat<2, 3, T, Q> const& m1, mat<4, 2, T, Q> const& m2);

  template<typename T, qualifier Q>
  mat<2, 3, T, Q> operator/(mat<2, 3, T, Q> const& m, T scalar);

  template<typename T, qualifier Q>
  mat<2, 3, T, Q> operator/(T scalar, mat<2, 3, T, Q> const& m);

  // -- Boolean operators --

  template<typename T, qualifier Q>
  bool operator==(mat<2, 3, T, Q> const& m1, mat<2, 3, T, Q> const& m2);

  template<typename T, qualifier Q>
  bool operator!=(mat<2, 3, T, Q> const& m1, mat<2, 3, T, Q> const& m2);
}//namespace glm



namespace glm
{
  // -- Constructors --















  template<typename T, qualifier Q>
  template<qualifier P>
  inline constexpr mat<2, 3, T, Q>::mat(mat<2, 3, T, P> const& m)

    : value{ m.value[0], m.value[1] }

  {




  }

  template<typename T, qualifier Q>
  inline constexpr mat<2, 3, T, Q>::mat(T scalar)

    : value{ col_type(scalar, 0, 0), col_type(0, scalar, 0) }

  {




  }

  template<typename T, qualifier Q>
  inline constexpr mat<2, 3, T, Q>::mat
  (
    T x0, T y0, T z0,
    T x1, T y1, T z1
  )

    : value{ col_type(x0, y0, z0), col_type(x1, y1, z1) }

  {




  }

  template<typename T, qualifier Q>
  inline constexpr mat<2, 3, T, Q>::mat(col_type const& v0, col_type const& v1)

    : value{ col_type(v0), col_type(v1) }

  {




  }

  // -- Conversion constructors --

  template<typename T, qualifier Q>
  template<
    typename X1, typename Y1, typename Z1,
    typename X2, typename Y2, typename Z2>
  inline constexpr mat<2, 3, T, Q>::mat
  (
    X1 x1, Y1 y1, Z1 z1,
    X2 x2, Y2 y2, Z2 z2
  )

    : value{ col_type(x1, y1, z1), col_type(x2, y2, z2) }

  {




  }

  template<typename T, qualifier Q>
  template<typename V1, typename V2>
  inline constexpr mat<2, 3, T, Q>::mat(vec<3, V1, Q> const& v1, vec<3, V2, Q> const& v2)

    : value{ col_type(v1), col_type(v2) }

  {




  }

  // -- Matrix conversions --

  template<typename T, qualifier Q>
  template<typename U, qualifier P>
  inline constexpr mat<2, 3, T, Q>::mat(mat<2, 3, U, P> const& m)

    : value{ col_type(m[0]), col_type(m[1]) }

  {




  }

  template<typename T, qualifier Q>
  inline constexpr mat<2, 3, T, Q>::mat(mat<2, 2, T, Q> const& m)

    : value{ col_type(m[0], 0), col_type(m[1], 0) }

  {




  }

  template<typename T, qualifier Q>
  inline constexpr  mat<2, 3, T, Q>::mat(mat<3, 3, T, Q> const& m)

    : value{ col_type(m[0]), col_type(m[1]) }

  {




  }

  template<typename T, qualifier Q>
  inline constexpr mat<2, 3, T, Q>::mat(mat<4, 4, T, Q> const& m)

    : value{ col_type(m[0]), col_type(m[1]) }

  {




  }

  template<typename T, qualifier Q>
  inline constexpr mat<2, 3, T, Q>::mat(mat<2, 4, T, Q> const& m)

    : value{ col_type(m[0]), col_type(m[1]) }

  {




  }

  template<typename T, qualifier Q>
  inline constexpr mat<2, 3, T, Q>::mat(mat<3, 2, T, Q> const& m)

    : value{ col_type(m[0], 0), col_type(m[1], 0) }

  {




  }

  template<typename T, qualifier Q>
  inline constexpr mat<2, 3, T, Q>::mat(mat<3, 4, T, Q> const& m)

    : value{ col_type(m[0]), col_type(m[1]) }

  {




  }

  template<typename T, qualifier Q>
  inline constexpr mat<2, 3, T, Q>::mat(mat<4, 2, T, Q> const& m)

    : value{ col_type(m[0], 0), col_type(m[1], 0) }

  {




  }

  template<typename T, qualifier Q>
  inline constexpr mat<2, 3, T, Q>::mat(mat<4, 3, T, Q> const& m)

    : value{ col_type(m[0]), col_type(m[1]) }

  {




  }

  // -- Accesses --

  template<typename T, qualifier Q>
  inline typename mat<2, 3, T, Q>::col_type& mat<2, 3, T, Q>::operator[](typename mat<2, 3, T, Q>::length_type i)
  {
    assert(i < this->length());
    return this->value[i];
  }

  template<typename T, qualifier Q>
  inline constexpr typename mat<2, 3, T, Q>::col_type const& mat<2, 3, T, Q>::operator[](typename mat<2, 3, T, Q>::length_type i) const
  {
    assert(i < this->length());
    return this->value[i];
  }

  // -- Unary updatable operators --

  template<typename T, qualifier Q>
  template<typename U>
  inline mat<2, 3, T, Q>& mat<2, 3, T, Q>::operator=(mat<2, 3, U, Q> const& m)
  {
    this->value[0] = m[0];
    this->value[1] = m[1];
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline mat<2, 3, T, Q>& mat<2, 3, T, Q>::operator+=(U s)
  {
    this->value[0] += s;
    this->value[1] += s;
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline mat<2, 3, T, Q>& mat<2, 3, T, Q>::operator+=(mat<2, 3, U, Q> const& m)
  {
    this->value[0] += m[0];
    this->value[1] += m[1];
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline mat<2, 3, T, Q>& mat<2, 3, T, Q>::operator-=(U s)
  {
    this->value[0] -= s;
    this->value[1] -= s;
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline mat<2, 3, T, Q>& mat<2, 3, T, Q>::operator-=(mat<2, 3, U, Q> const& m)
  {
    this->value[0] -= m[0];
    this->value[1] -= m[1];
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline mat<2, 3, T, Q>& mat<2, 3, T, Q>::operator*=(U s)
  {
    this->value[0] *= s;
    this->value[1] *= s;
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline mat<2, 3, T, Q>& mat<2, 3, T, Q>::operator/=(U s)
  {
    this->value[0] /= s;
    this->value[1] /= s;
    return *this;
  }

  // -- Increment and decrement operators --

  template<typename T, qualifier Q>
  inline mat<2, 3, T, Q>& mat<2, 3, T, Q>::operator++()
  {
    ++this->value[0];
    ++this->value[1];
    return *this;
  }

  template<typename T, qualifier Q>
  inline mat<2, 3, T, Q>& mat<2, 3, T, Q>::operator--()
  {
    --this->value[0];
    --this->value[1];
    return *this;
  }

  template<typename T, qualifier Q>
  inline mat<2, 3, T, Q> mat<2, 3, T, Q>::operator++(int)
  {
    mat<2, 3, T, Q> Result(*this);
    ++*this;
    return Result;
  }

  template<typename T, qualifier Q>
  inline mat<2, 3, T, Q> mat<2, 3, T, Q>::operator--(int)
  {
    mat<2, 3, T, Q> Result(*this);
    --*this;
    return Result;
  }

  // -- Unary arithmetic operators --

  template<typename T, qualifier Q>
  inline mat<2, 3, T, Q> operator+(mat<2, 3, T, Q> const& m)
  {
    return m;
  }

  template<typename T, qualifier Q>
  inline mat<2, 3, T, Q> operator-(mat<2, 3, T, Q> const& m)
  {
    return mat<2, 3, T, Q>(
      -m[0],
      -m[1]);
  }

  // -- Binary arithmetic operators --

  template<typename T, qualifier Q>
  inline mat<2, 3, T, Q> operator+(mat<2, 3, T, Q> const& m, T scalar)
  {
    return mat<2, 3, T, Q>(
      m[0] + scalar,
      m[1] + scalar);
  }

  template<typename T, qualifier Q>
  inline mat<2, 3, T, Q> operator+(mat<2, 3, T, Q> const& m1, mat<2, 3, T, Q> const& m2)
  {
    return mat<2, 3, T, Q>(
      m1[0] + m2[0],
      m1[1] + m2[1]);
  }

  template<typename T, qualifier Q>
  inline mat<2, 3, T, Q> operator-(mat<2, 3, T, Q> const& m, T scalar)
  {
    return mat<2, 3, T, Q>(
      m[0] - scalar,
      m[1] - scalar);
  }

  template<typename T, qualifier Q>
  inline mat<2, 3, T, Q> operator-(mat<2, 3, T, Q> const& m1, mat<2, 3, T, Q> const& m2)
  {
    return mat<2, 3, T, Q>(
      m1[0] - m2[0],
      m1[1] - m2[1]);
  }

  template<typename T, qualifier Q>
  inline mat<2, 3, T, Q> operator*(mat<2, 3, T, Q> const& m, T scalar)
  {
    return mat<2, 3, T, Q>(
      m[0] * scalar,
      m[1] * scalar);
  }

  template<typename T, qualifier Q>
  inline mat<2, 3, T, Q> operator*(T scalar, mat<2, 3, T, Q> const& m)
  {
    return mat<2, 3, T, Q>(
      m[0] * scalar,
      m[1] * scalar);
  }

  template<typename T, qualifier Q>
  inline typename mat<2, 3, T, Q>::col_type operator*
    (
      mat<2, 3, T, Q> const& m,
      typename mat<2, 3, T, Q>::row_type const& v)
  {
    return typename mat<2, 3, T, Q>::col_type(
      m[0][0] * v.x + m[1][0] * v.y,
      m[0][1] * v.x + m[1][1] * v.y,
      m[0][2] * v.x + m[1][2] * v.y);
  }

  template<typename T, qualifier Q>
  inline typename mat<2, 3, T, Q>::row_type operator*
    (
      typename mat<2, 3, T, Q>::col_type const& v,
      mat<2, 3, T, Q> const& m)
  {
    return typename mat<2, 3, T, Q>::row_type(
      v.x * m[0][0] + v.y * m[0][1] + v.z * m[0][2],
      v.x * m[1][0] + v.y * m[1][1] + v.z * m[1][2]);
  }

  template<typename T, qualifier Q>
  inline mat<2, 3, T, Q> operator*(mat<2, 3, T, Q> const& m1, mat<2, 2, T, Q> const& m2)
  {
    return mat<2, 3, T, Q>(
      m1[0][0] * m2[0][0] + m1[1][0] * m2[0][1],
      m1[0][1] * m2[0][0] + m1[1][1] * m2[0][1],
      m1[0][2] * m2[0][0] + m1[1][2] * m2[0][1],
      m1[0][0] * m2[1][0] + m1[1][0] * m2[1][1],
      m1[0][1] * m2[1][0] + m1[1][1] * m2[1][1],
      m1[0][2] * m2[1][0] + m1[1][2] * m2[1][1]);
  }

  template<typename T, qualifier Q>
  inline mat<3, 3, T, Q> operator*(mat<2, 3, T, Q> const& m1, mat<3, 2, T, Q> const& m2)
  {
    T SrcA00 = m1[0][0];
    T SrcA01 = m1[0][1];
    T SrcA02 = m1[0][2];
    T SrcA10 = m1[1][0];
    T SrcA11 = m1[1][1];
    T SrcA12 = m1[1][2];

    T SrcB00 = m2[0][0];
    T SrcB01 = m2[0][1];
    T SrcB10 = m2[1][0];
    T SrcB11 = m2[1][1];
    T SrcB20 = m2[2][0];
    T SrcB21 = m2[2][1];

    mat<3, 3, T, Q> Result;
    Result[0][0] = SrcA00 * SrcB00 + SrcA10 * SrcB01;
    Result[0][1] = SrcA01 * SrcB00 + SrcA11 * SrcB01;
    Result[0][2] = SrcA02 * SrcB00 + SrcA12 * SrcB01;
    Result[1][0] = SrcA00 * SrcB10 + SrcA10 * SrcB11;
    Result[1][1] = SrcA01 * SrcB10 + SrcA11 * SrcB11;
    Result[1][2] = SrcA02 * SrcB10 + SrcA12 * SrcB11;
    Result[2][0] = SrcA00 * SrcB20 + SrcA10 * SrcB21;
    Result[2][1] = SrcA01 * SrcB20 + SrcA11 * SrcB21;
    Result[2][2] = SrcA02 * SrcB20 + SrcA12 * SrcB21;
    return Result;
  }

  template<typename T, qualifier Q>
  inline mat<4, 3, T, Q> operator*(mat<2, 3, T, Q> const& m1, mat<4, 2, T, Q> const& m2)
  {
    return mat<4, 3, T, Q>(
      m1[0][0] * m2[0][0] + m1[1][0] * m2[0][1],
      m1[0][1] * m2[0][0] + m1[1][1] * m2[0][1],
      m1[0][2] * m2[0][0] + m1[1][2] * m2[0][1],
      m1[0][0] * m2[1][0] + m1[1][0] * m2[1][1],
      m1[0][1] * m2[1][0] + m1[1][1] * m2[1][1],
      m1[0][2] * m2[1][0] + m1[1][2] * m2[1][1],
      m1[0][0] * m2[2][0] + m1[1][0] * m2[2][1],
      m1[0][1] * m2[2][0] + m1[1][1] * m2[2][1],
      m1[0][2] * m2[2][0] + m1[1][2] * m2[2][1],
      m1[0][0] * m2[3][0] + m1[1][0] * m2[3][1],
      m1[0][1] * m2[3][0] + m1[1][1] * m2[3][1],
      m1[0][2] * m2[3][0] + m1[1][2] * m2[3][1]);
  }

  template<typename T, qualifier Q>
  inline mat<2, 3, T, Q> operator/(mat<2, 3, T, Q> const& m, T scalar)
  {
    return mat<2, 3, T, Q>(
      m[0] / scalar,
      m[1] / scalar);
  }

  template<typename T, qualifier Q>
  inline mat<2, 3, T, Q> operator/(T scalar, mat<2, 3, T, Q> const& m)
  {
    return mat<2, 3, T, Q>(
      scalar / m[0],
      scalar / m[1]);
  }

  // -- Boolean operators --

  template<typename T, qualifier Q>
  inline bool operator==(mat<2, 3, T, Q> const& m1, mat<2, 3, T, Q> const& m2)
  {
    return (m1[0] == m2[0]) && (m1[1] == m2[1]);
  }

  template<typename T, qualifier Q>
  inline bool operator!=(mat<2, 3, T, Q> const& m1, mat<2, 3, T, Q> const& m2)
  {
    return (m1[0] != m2[0]) || (m1[1] != m2[1]);
  }
} //namespace glm




namespace glm
{
  /// @addtogroup core_matrix
  /// @{

  /// 2 columns of 3 components matrix of double-precision floating-point numbers.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  typedef mat<2, 3, double, defaultp>		dmat2x3;

  /// @}
}//namespace glm


/// @ref core
/// @file glm/ext/matrix_double2x3_precision.hpp




namespace glm
{
  /// @addtogroup core_matrix_precision
  /// @{

  /// 2 columns of 3 components matrix of double-precision floating-point numbers using low precision arithmetic in term of ULPs.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef mat<2, 3, double, lowp>		lowp_dmat2x3;

  /// 2 columns of 3 components matrix of double-precision floating-point numbers using medium precision arithmetic in term of ULPs.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef mat<2, 3, double, mediump>	mediump_dmat2x3;

  /// 2 columns of 3 components matrix of double-precision floating-point numbers using medium precision arithmetic in term of ULPs.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef mat<2, 3, double, highp>	highp_dmat2x3;

  /// @}
}//namespace glm


/// @ref core
/// @file glm/ext/matrix_float2x3.hpp




namespace glm
{
  /// @addtogroup core_matrix
  /// @{

  /// 2 columns of 3 components matrix of single-precision floating-point numbers.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  typedef mat<2, 3, float, defaultp>		mat2x3;

  /// @}
}//namespace glm


/// @ref core
/// @file glm/ext/matrix_float2x3_precision.hpp




namespace glm
{
  /// @addtogroup core_matrix_precision
  /// @{

  /// 2 columns of 3 components matrix of single-precision floating-point numbers using low precision arithmetic in term of ULPs.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef mat<2, 3, float, lowp>		lowp_mat2x3;

  /// 2 columns of 3 components matrix of single-precision floating-point numbers using medium precision arithmetic in term of ULPs.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef mat<2, 3, float, mediump>	mediump_mat2x3;

  /// 2 columns of 3 components matrix of single-precision floating-point numbers using high precision arithmetic in term of ULPs.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef mat<2, 3, float, highp>		highp_mat2x3;

  /// @}
}//namespace glm

namespace glm
{
  template<typename T, qualifier Q>
  struct mat<2, 4, T, Q>
  {
    typedef vec<4, T, Q> col_type;
    typedef vec<2, T, Q> row_type;
    typedef mat<2, 4, T, Q> type;
    typedef mat<4, 2, T, Q> transpose_type;
    typedef T value_type;

  private:
    col_type value[2];

  public:
    // -- Accesses --

    typedef length_t length_type;
    static constexpr length_type length() { return 2; }

    col_type& operator[](length_type i);
    constexpr col_type const& operator[](length_type i) const;

    // -- Constructors --

    constexpr mat() = default;
    template<qualifier P>
    constexpr mat(mat<2, 4, T, P> const& m);

    explicit constexpr mat(T scalar);
    constexpr mat(
      T x0, T y0, T z0, T w0,
      T x1, T y1, T z1, T w1);
    constexpr mat(
      col_type const& v0,
      col_type const& v1);

    // -- Conversions --

    template<
      typename X1, typename Y1, typename Z1, typename W1,
      typename X2, typename Y2, typename Z2, typename W2>
    constexpr mat(
      X1 x1, Y1 y1, Z1 z1, W1 w1,
      X2 x2, Y2 y2, Z2 z2, W2 w2);

    template<typename U, typename V>
    constexpr mat(
      vec<4, U, Q> const& v1,
      vec<4, V, Q> const& v2);

    // -- Matrix conversions --

    template<typename U, qualifier P>
    constexpr mat(mat<2, 4, U, P> const& m);

    constexpr mat(mat<2, 2, T, Q> const& x);
    constexpr mat(mat<3, 3, T, Q> const& x);
    constexpr mat(mat<4, 4, T, Q> const& x);
    constexpr mat(mat<2, 3, T, Q> const& x);
    constexpr mat(mat<3, 2, T, Q> const& x);
    constexpr mat(mat<3, 4, T, Q> const& x);
    constexpr mat(mat<4, 2, T, Q> const& x);
    constexpr mat(mat<4, 3, T, Q> const& x);

    // -- Unary arithmetic operators --

    template<typename U>
    mat<2, 4, T, Q>& operator=(mat<2, 4, U, Q> const& m);
    template<typename U>
    mat<2, 4, T, Q>& operator+=(U s);
    template<typename U>
    mat<2, 4, T, Q>& operator+=(mat<2, 4, U, Q> const& m);
    template<typename U>
    mat<2, 4, T, Q>& operator-=(U s);
    template<typename U>
    mat<2, 4, T, Q>& operator-=(mat<2, 4, U, Q> const& m);
    template<typename U>
    mat<2, 4, T, Q>& operator*=(U s);
    template<typename U>
    mat<2, 4, T, Q>& operator/=(U s);

    // -- Increment and decrement operators --

    mat<2, 4, T, Q>& operator++ ();
    mat<2, 4, T, Q>& operator-- ();
    mat<2, 4, T, Q> operator++(int);
    mat<2, 4, T, Q> operator--(int);
  };

  // -- Unary operators --

  template<typename T, qualifier Q>
  mat<2, 4, T, Q> operator+(mat<2, 4, T, Q> const& m);

  template<typename T, qualifier Q>
  mat<2, 4, T, Q> operator-(mat<2, 4, T, Q> const& m);

  // -- Binary operators --

  template<typename T, qualifier Q>
  mat<2, 4, T, Q> operator+(mat<2, 4, T, Q> const& m, T scalar);

  template<typename T, qualifier Q>
  mat<2, 4, T, Q> operator+(mat<2, 4, T, Q> const& m1, mat<2, 4, T, Q> const& m2);

  template<typename T, qualifier Q>
  mat<2, 4, T, Q> operator-(mat<2, 4, T, Q> const& m, T scalar);

  template<typename T, qualifier Q>
  mat<2, 4, T, Q> operator-(mat<2, 4, T, Q> const& m1, mat<2, 4, T, Q> const& m2);

  template<typename T, qualifier Q>
  mat<2, 4, T, Q> operator*(mat<2, 4, T, Q> const& m, T scalar);

  template<typename T, qualifier Q>
  mat<2, 4, T, Q> operator*(T scalar, mat<2, 4, T, Q> const& m);

  template<typename T, qualifier Q>
  typename mat<2, 4, T, Q>::col_type operator*(mat<2, 4, T, Q> const& m, typename mat<2, 4, T, Q>::row_type const& v);

  template<typename T, qualifier Q>
  typename mat<2, 4, T, Q>::row_type operator*(typename mat<2, 4, T, Q>::col_type const& v, mat<2, 4, T, Q> const& m);

  template<typename T, qualifier Q>
  mat<4, 4, T, Q> operator*(mat<2, 4, T, Q> const& m1, mat<4, 2, T, Q> const& m2);

  template<typename T, qualifier Q>
  mat<2, 4, T, Q> operator*(mat<2, 4, T, Q> const& m1, mat<2, 2, T, Q> const& m2);

  template<typename T, qualifier Q>
  mat<3, 4, T, Q> operator*(mat<2, 4, T, Q> const& m1, mat<3, 2, T, Q> const& m2);

  template<typename T, qualifier Q>
  mat<2, 4, T, Q> operator/(mat<2, 4, T, Q> const& m, T scalar);

  template<typename T, qualifier Q>
  mat<2, 4, T, Q> operator/(T scalar, mat<2, 4, T, Q> const& m);

  // -- Boolean operators --

  template<typename T, qualifier Q>
  bool operator==(mat<2, 4, T, Q> const& m1, mat<2, 4, T, Q> const& m2);

  template<typename T, qualifier Q>
  bool operator!=(mat<2, 4, T, Q> const& m1, mat<2, 4, T, Q> const& m2);
}//namespace glm



namespace glm
{
  // -- Constructors --















  template<typename T, qualifier Q>
  template<qualifier P>
  inline constexpr mat<2, 4, T, Q>::mat(mat<2, 4, T, P> const& m)

    : value{ m[0], m[1] }

  {




  }

  template<typename T, qualifier Q>
  inline constexpr mat<2, 4, T, Q>::mat(T s)

    : value{ col_type(s, 0, 0, 0), col_type(0, s, 0, 0) }

  {




  }

  template<typename T, qualifier Q>
  inline constexpr mat<2, 4, T, Q>::mat
  (
    T x0, T y0, T z0, T w0,
    T x1, T y1, T z1, T w1
  )

    : value{ col_type(x0, y0, z0, w0), col_type(x1, y1, z1, w1) }

  {




  }

  template<typename T, qualifier Q>
  inline constexpr mat<2, 4, T, Q>::mat(col_type const& v0, col_type const& v1)

    : value{ col_type(v0), col_type(v1) }

  {




  }

  // -- Conversion constructors --

  template<typename T, qualifier Q>
  template<
    typename X1, typename Y1, typename Z1, typename W1,
    typename X2, typename Y2, typename Z2, typename W2>
  inline constexpr mat<2, 4, T, Q>::mat
  (
    X1 x1, Y1 y1, Z1 z1, W1 w1,
    X2 x2, Y2 y2, Z2 z2, W2 w2
  )

    : value{
      col_type(x1, y1, z1, w1),
      col_type(x2, y2, z2, w2) }

  {




  }

  template<typename T, qualifier Q>
  template<typename V1, typename V2>
  inline constexpr mat<2, 4, T, Q>::mat(vec<4, V1, Q> const& v1, vec<4, V2, Q> const& v2)

    : value{ col_type(v1), col_type(v2) }

  {




  }

  // -- Matrix conversions --

  template<typename T, qualifier Q>
  template<typename U, qualifier P>
  inline constexpr mat<2, 4, T, Q>::mat(mat<2, 4, U, P> const& m)

    : value{ col_type(m[0]), col_type(m[1]) }

  {




  }

  template<typename T, qualifier Q>
  inline constexpr mat<2, 4, T, Q>::mat(mat<2, 2, T, Q> const& m)

    : value{ col_type(m[0], 0, 0), col_type(m[1], 0, 0) }

  {




  }

  template<typename T, qualifier Q>
  inline constexpr mat<2, 4, T, Q>::mat(mat<3, 3, T, Q> const& m)

    : value{ col_type(m[0], 0), col_type(m[1], 0) }

  {




  }

  template<typename T, qualifier Q>
  inline constexpr mat<2, 4, T, Q>::mat(mat<4, 4, T, Q> const& m)

    : value{ col_type(m[0]), col_type(m[1]) }

  {




  }

  template<typename T, qualifier Q>
  inline constexpr mat<2, 4, T, Q>::mat(mat<2, 3, T, Q> const& m)

    : value{ col_type(m[0], 0), col_type(m[1], 0) }

  {




  }

  template<typename T, qualifier Q>
  inline constexpr mat<2, 4, T, Q>::mat(mat<3, 2, T, Q> const& m)

    : value{ col_type(m[0], 0, 0), col_type(m[1], 0, 0) }

  {




  }

  template<typename T, qualifier Q>
  inline constexpr mat<2, 4, T, Q>::mat(mat<3, 4, T, Q> const& m)

    : value{ col_type(m[0]), col_type(m[1]) }

  {




  }

  template<typename T, qualifier Q>
  inline constexpr mat<2, 4, T, Q>::mat(mat<4, 2, T, Q> const& m)

    : value{ col_type(m[0], 0, 0), col_type(m[1], 0, 0) }

  {




  }

  template<typename T, qualifier Q>
  inline constexpr mat<2, 4, T, Q>::mat(mat<4, 3, T, Q> const& m)

    : value{ col_type(m[0], 0), col_type(m[1], 0) }

  {




  }

  // -- Accesses --

  template<typename T, qualifier Q>
  inline typename mat<2, 4, T, Q>::col_type& mat<2, 4, T, Q>::operator[](typename mat<2, 4, T, Q>::length_type i)
  {
    assert(i < this->length());
    return this->value[i];
  }

  template<typename T, qualifier Q>
  inline constexpr typename mat<2, 4, T, Q>::col_type const& mat<2, 4, T, Q>::operator[](typename mat<2, 4, T, Q>::length_type i) const
  {
    assert(i < this->length());
    return this->value[i];
  }

  // -- Unary updatable operators --

  template<typename T, qualifier Q>
  template<typename U>
  inline mat<2, 4, T, Q>& mat<2, 4, T, Q>::operator=(mat<2, 4, U, Q> const& m)
  {
    this->value[0] = m[0];
    this->value[1] = m[1];
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline mat<2, 4, T, Q>& mat<2, 4, T, Q>::operator+=(U s)
  {
    this->value[0] += s;
    this->value[1] += s;
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline mat<2, 4, T, Q>& mat<2, 4, T, Q>::operator+=(mat<2, 4, U, Q> const& m)
  {
    this->value[0] += m[0];
    this->value[1] += m[1];
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline mat<2, 4, T, Q>& mat<2, 4, T, Q>::operator-=(U s)
  {
    this->value[0] -= s;
    this->value[1] -= s;
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline mat<2, 4, T, Q>& mat<2, 4, T, Q>::operator-=(mat<2, 4, U, Q> const& m)
  {
    this->value[0] -= m[0];
    this->value[1] -= m[1];
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline mat<2, 4, T, Q>& mat<2, 4, T, Q>::operator*=(U s)
  {
    this->value[0] *= s;
    this->value[1] *= s;
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline mat<2, 4, T, Q>& mat<2, 4, T, Q>::operator/=(U s)
  {
    this->value[0] /= s;
    this->value[1] /= s;
    return *this;
  }

  // -- Increment and decrement operators --

  template<typename T, qualifier Q>
  inline mat<2, 4, T, Q>& mat<2, 4, T, Q>::operator++()
  {
    ++this->value[0];
    ++this->value[1];
    return *this;
  }

  template<typename T, qualifier Q>
  inline mat<2, 4, T, Q>& mat<2, 4, T, Q>::operator--()
  {
    --this->value[0];
    --this->value[1];
    return *this;
  }

  template<typename T, qualifier Q>
  inline mat<2, 4, T, Q> mat<2, 4, T, Q>::operator++(int)
  {
    mat<2, 4, T, Q> Result(*this);
    ++*this;
    return Result;
  }

  template<typename T, qualifier Q>
  inline mat<2, 4, T, Q> mat<2, 4, T, Q>::operator--(int)
  {
    mat<2, 4, T, Q> Result(*this);
    --*this;
    return Result;
  }

  // -- Unary arithmetic operators --

  template<typename T, qualifier Q>
  inline mat<2, 4, T, Q> operator+(mat<2, 4, T, Q> const& m)
  {
    return m;
  }

  template<typename T, qualifier Q>
  inline mat<2, 4, T, Q> operator-(mat<2, 4, T, Q> const& m)
  {
    return mat<2, 4, T, Q>(
      -m[0],
      -m[1]);
  }

  // -- Binary arithmetic operators --

  template<typename T, qualifier Q>
  inline mat<2, 4, T, Q> operator+(mat<2, 4, T, Q> const& m, T scalar)
  {
    return mat<2, 4, T, Q>(
      m[0] + scalar,
      m[1] + scalar);
  }

  template<typename T, qualifier Q>
  inline mat<2, 4, T, Q> operator+(mat<2, 4, T, Q> const& m1, mat<2, 4, T, Q> const& m2)
  {
    return mat<2, 4, T, Q>(
      m1[0] + m2[0],
      m1[1] + m2[1]);
  }

  template<typename T, qualifier Q>
  inline mat<2, 4, T, Q> operator-(mat<2, 4, T, Q> const& m, T scalar)
  {
    return mat<2, 4, T, Q>(
      m[0] - scalar,
      m[1] - scalar);
  }

  template<typename T, qualifier Q>
  inline mat<2, 4, T, Q> operator-(mat<2, 4, T, Q> const& m1, mat<2, 4, T, Q> const& m2)
  {
    return mat<2, 4, T, Q>(
      m1[0] - m2[0],
      m1[1] - m2[1]);
  }

  template<typename T, qualifier Q>
  inline mat<2, 4, T, Q> operator*(mat<2, 4, T, Q> const& m, T scalar)
  {
    return mat<2, 4, T, Q>(
      m[0] * scalar,
      m[1] * scalar);
  }

  template<typename T, qualifier Q>
  inline mat<2, 4, T, Q> operator*(T scalar, mat<2, 4, T, Q> const& m)
  {
    return mat<2, 4, T, Q>(
      m[0] * scalar,
      m[1] * scalar);
  }

  template<typename T, qualifier Q>
  inline typename mat<2, 4, T, Q>::col_type operator*(mat<2, 4, T, Q> const& m, typename mat<2, 4, T, Q>::row_type const& v)
  {
    return typename mat<2, 4, T, Q>::col_type(
      m[0][0] * v.x + m[1][0] * v.y,
      m[0][1] * v.x + m[1][1] * v.y,
      m[0][2] * v.x + m[1][2] * v.y,
      m[0][3] * v.x + m[1][3] * v.y);
  }

  template<typename T, qualifier Q>
  inline typename mat<2, 4, T, Q>::row_type operator*(typename mat<2, 4, T, Q>::col_type const& v, mat<2, 4, T, Q> const& m)
  {
    return typename mat<2, 4, T, Q>::row_type(
      v.x * m[0][0] + v.y * m[0][1] + v.z * m[0][2] + v.w * m[0][3],
      v.x * m[1][0] + v.y * m[1][1] + v.z * m[1][2] + v.w * m[1][3]);
  }

  template<typename T, qualifier Q>
  inline mat<4, 4, T, Q> operator*(mat<2, 4, T, Q> const& m1, mat<4, 2, T, Q> const& m2)
  {
    T SrcA00 = m1[0][0];
    T SrcA01 = m1[0][1];
    T SrcA02 = m1[0][2];
    T SrcA03 = m1[0][3];
    T SrcA10 = m1[1][0];
    T SrcA11 = m1[1][1];
    T SrcA12 = m1[1][2];
    T SrcA13 = m1[1][3];

    T SrcB00 = m2[0][0];
    T SrcB01 = m2[0][1];
    T SrcB10 = m2[1][0];
    T SrcB11 = m2[1][1];
    T SrcB20 = m2[2][0];
    T SrcB21 = m2[2][1];
    T SrcB30 = m2[3][0];
    T SrcB31 = m2[3][1];

    mat<4, 4, T, Q> Result;
    Result[0][0] = SrcA00 * SrcB00 + SrcA10 * SrcB01;
    Result[0][1] = SrcA01 * SrcB00 + SrcA11 * SrcB01;
    Result[0][2] = SrcA02 * SrcB00 + SrcA12 * SrcB01;
    Result[0][3] = SrcA03 * SrcB00 + SrcA13 * SrcB01;
    Result[1][0] = SrcA00 * SrcB10 + SrcA10 * SrcB11;
    Result[1][1] = SrcA01 * SrcB10 + SrcA11 * SrcB11;
    Result[1][2] = SrcA02 * SrcB10 + SrcA12 * SrcB11;
    Result[1][3] = SrcA03 * SrcB10 + SrcA13 * SrcB11;
    Result[2][0] = SrcA00 * SrcB20 + SrcA10 * SrcB21;
    Result[2][1] = SrcA01 * SrcB20 + SrcA11 * SrcB21;
    Result[2][2] = SrcA02 * SrcB20 + SrcA12 * SrcB21;
    Result[2][3] = SrcA03 * SrcB20 + SrcA13 * SrcB21;
    Result[3][0] = SrcA00 * SrcB30 + SrcA10 * SrcB31;
    Result[3][1] = SrcA01 * SrcB30 + SrcA11 * SrcB31;
    Result[3][2] = SrcA02 * SrcB30 + SrcA12 * SrcB31;
    Result[3][3] = SrcA03 * SrcB30 + SrcA13 * SrcB31;
    return Result;
  }

  template<typename T, qualifier Q>
  inline mat<2, 4, T, Q> operator*(mat<2, 4, T, Q> const& m1, mat<2, 2, T, Q> const& m2)
  {
    return mat<2, 4, T, Q>(
      m1[0][0] * m2[0][0] + m1[1][0] * m2[0][1],
      m1[0][1] * m2[0][0] + m1[1][1] * m2[0][1],
      m1[0][2] * m2[0][0] + m1[1][2] * m2[0][1],
      m1[0][3] * m2[0][0] + m1[1][3] * m2[0][1],
      m1[0][0] * m2[1][0] + m1[1][0] * m2[1][1],
      m1[0][1] * m2[1][0] + m1[1][1] * m2[1][1],
      m1[0][2] * m2[1][0] + m1[1][2] * m2[1][1],
      m1[0][3] * m2[1][0] + m1[1][3] * m2[1][1]);
  }

  template<typename T, qualifier Q>
  inline mat<3, 4, T, Q> operator*(mat<2, 4, T, Q> const& m1, mat<3, 2, T, Q> const& m2)
  {
    return mat<3, 4, T, Q>(
      m1[0][0] * m2[0][0] + m1[1][0] * m2[0][1],
      m1[0][1] * m2[0][0] + m1[1][1] * m2[0][1],
      m1[0][2] * m2[0][0] + m1[1][2] * m2[0][1],
      m1[0][3] * m2[0][0] + m1[1][3] * m2[0][1],
      m1[0][0] * m2[1][0] + m1[1][0] * m2[1][1],
      m1[0][1] * m2[1][0] + m1[1][1] * m2[1][1],
      m1[0][2] * m2[1][0] + m1[1][2] * m2[1][1],
      m1[0][3] * m2[1][0] + m1[1][3] * m2[1][1],
      m1[0][0] * m2[2][0] + m1[1][0] * m2[2][1],
      m1[0][1] * m2[2][0] + m1[1][1] * m2[2][1],
      m1[0][2] * m2[2][0] + m1[1][2] * m2[2][1],
      m1[0][3] * m2[2][0] + m1[1][3] * m2[2][1]);
  }

  template<typename T, qualifier Q>
  inline mat<2, 4, T, Q> operator/(mat<2, 4, T, Q> const& m, T scalar)
  {
    return mat<2, 4, T, Q>(
      m[0] / scalar,
      m[1] / scalar);
  }

  template<typename T, qualifier Q>
  inline mat<2, 4, T, Q> operator/(T scalar, mat<2, 4, T, Q> const& m)
  {
    return mat<2, 4, T, Q>(
      scalar / m[0],
      scalar / m[1]);
  }

  // -- Boolean operators --

  template<typename T, qualifier Q>
  inline bool operator==(mat<2, 4, T, Q> const& m1, mat<2, 4, T, Q> const& m2)
  {
    return (m1[0] == m2[0]) && (m1[1] == m2[1]);
  }

  template<typename T, qualifier Q>
  inline bool operator!=(mat<2, 4, T, Q> const& m1, mat<2, 4, T, Q> const& m2)
  {
    return (m1[0] != m2[0]) || (m1[1] != m2[1]);
  }
} //namespace glm




namespace glm
{
  /// @addtogroup core_matrix
  /// @{

  /// 2 columns of 4 components matrix of double-precision floating-point numbers.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  typedef mat<2, 4, double, defaultp>		dmat2x4;

  /// @}
}//namespace glm


/// @ref core
/// @file glm/ext/matrix_double2x4_precision.hpp




namespace glm
{
  /// @addtogroup core_matrix_precision
  /// @{

  /// 2 columns of 4 components matrix of double-precision floating-point numbers using low precision arithmetic in term of ULPs.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef mat<2, 4, double, lowp>		lowp_dmat2x4;

  /// 2 columns of 4 components matrix of double-precision floating-point numbers using medium precision arithmetic in term of ULPs.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef mat<2, 4, double, mediump>	mediump_dmat2x4;

  /// 2 columns of 4 components matrix of double-precision floating-point numbers using medium precision arithmetic in term of ULPs.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef mat<2, 4, double, highp>	highp_dmat2x4;

  /// @}
}//namespace glm


/// @ref core
/// @file glm/ext/matrix_float2x4.hpp




namespace glm
{
  /// @addtogroup core_matrix
  /// @{

  /// 2 columns of 4 components matrix of single-precision floating-point numbers.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  typedef mat<2, 4, float, defaultp>		mat2x4;

  /// @}
}//namespace glm


/// @ref core
/// @file glm/ext/matrix_float2x4_precision.hpp




namespace glm
{
  /// @addtogroup core_matrix_precision
  /// @{

  /// 2 columns of 4 components matrix of single-precision floating-point numbers using low precision arithmetic in term of ULPs.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef mat<2, 4, float, lowp>		lowp_mat2x4;

  /// 2 columns of 4 components matrix of single-precision floating-point numbers using medium precision arithmetic in term of ULPs.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef mat<2, 4, float, mediump>	mediump_mat2x4;

  /// 2 columns of 4 components matrix of single-precision floating-point numbers using high precision arithmetic in term of ULPs.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef mat<2, 4, float, highp>		highp_mat2x4;

  /// @}
}//namespace glm

namespace glm
{
  template<typename T, qualifier Q>
  struct mat<3, 2, T, Q>
  {
    typedef vec<2, T, Q> col_type;
    typedef vec<3, T, Q> row_type;
    typedef mat<3, 2, T, Q> type;
    typedef mat<2, 3, T, Q> transpose_type;
    typedef T value_type;

  private:
    col_type value[3];

  public:
    // -- Accesses --

    typedef length_t length_type;
    static constexpr length_type length() { return 3; }

    col_type& operator[](length_type i);
    constexpr col_type const& operator[](length_type i) const;

    // -- Constructors --

    constexpr mat() = default;
    template<qualifier P>
    constexpr mat(mat<3, 2, T, P> const& m);

    explicit constexpr mat(T scalar);
    constexpr mat(
      T x0, T y0,
      T x1, T y1,
      T x2, T y2);
    constexpr mat(
      col_type const& v0,
      col_type const& v1,
      col_type const& v2);

    // -- Conversions --

    template<
      typename X1, typename Y1,
      typename X2, typename Y2,
      typename X3, typename Y3>
    constexpr mat(
      X1 x1, Y1 y1,
      X2 x2, Y2 y2,
      X3 x3, Y3 y3);

    template<typename V1, typename V2, typename V3>
    constexpr mat(
      vec<2, V1, Q> const& v1,
      vec<2, V2, Q> const& v2,
      vec<2, V3, Q> const& v3);

    // -- Matrix conversions --

    template<typename U, qualifier P>
    constexpr mat(mat<3, 2, U, P> const& m);

    constexpr mat(mat<2, 2, T, Q> const& x);
    constexpr mat(mat<3, 3, T, Q> const& x);
    constexpr mat(mat<4, 4, T, Q> const& x);
    constexpr mat(mat<2, 3, T, Q> const& x);
    constexpr mat(mat<2, 4, T, Q> const& x);
    constexpr mat(mat<3, 4, T, Q> const& x);
    constexpr mat(mat<4, 2, T, Q> const& x);
    constexpr mat(mat<4, 3, T, Q> const& x);

    // -- Unary arithmetic operators --

    template<typename U>
    mat<3, 2, T, Q>& operator=(mat<3, 2, U, Q> const& m);
    template<typename U>
    mat<3, 2, T, Q>& operator+=(U s);
    template<typename U>
    mat<3, 2, T, Q>& operator+=(mat<3, 2, U, Q> const& m);
    template<typename U>
    mat<3, 2, T, Q>& operator-=(U s);
    template<typename U>
    mat<3, 2, T, Q>& operator-=(mat<3, 2, U, Q> const& m);
    template<typename U>
    mat<3, 2, T, Q>& operator*=(U s);
    template<typename U>
    mat<3, 2, T, Q>& operator/=(U s);

    // -- Increment and decrement operators --

    mat<3, 2, T, Q>& operator++ ();
    mat<3, 2, T, Q>& operator-- ();
    mat<3, 2, T, Q> operator++(int);
    mat<3, 2, T, Q> operator--(int);
  };

  // -- Unary operators --

  template<typename T, qualifier Q>
  mat<3, 2, T, Q> operator+(mat<3, 2, T, Q> const& m);

  template<typename T, qualifier Q>
  mat<3, 2, T, Q> operator-(mat<3, 2, T, Q> const& m);

  // -- Binary operators --

  template<typename T, qualifier Q>
  mat<3, 2, T, Q> operator+(mat<3, 2, T, Q> const& m, T scalar);

  template<typename T, qualifier Q>
  mat<3, 2, T, Q> operator+(mat<3, 2, T, Q> const& m1, mat<3, 2, T, Q> const& m2);

  template<typename T, qualifier Q>
  mat<3, 2, T, Q> operator-(mat<3, 2, T, Q> const& m, T scalar);

  template<typename T, qualifier Q>
  mat<3, 2, T, Q> operator-(mat<3, 2, T, Q> const& m1, mat<3, 2, T, Q> const& m2);

  template<typename T, qualifier Q>
  mat<3, 2, T, Q> operator*(mat<3, 2, T, Q> const& m, T scalar);

  template<typename T, qualifier Q>
  mat<3, 2, T, Q> operator*(T scalar, mat<3, 2, T, Q> const& m);

  template<typename T, qualifier Q>
  typename mat<3, 2, T, Q>::col_type operator*(mat<3, 2, T, Q> const& m, typename mat<3, 2, T, Q>::row_type const& v);

  template<typename T, qualifier Q>
  typename mat<3, 2, T, Q>::row_type operator*(typename mat<3, 2, T, Q>::col_type const& v, mat<3, 2, T, Q> const& m);

  template<typename T, qualifier Q>
  mat<2, 2, T, Q> operator*(mat<3, 2, T, Q> const& m1, mat<2, 3, T, Q> const& m2);

  template<typename T, qualifier Q>
  mat<3, 2, T, Q> operator*(mat<3, 2, T, Q> const& m1, mat<3, 3, T, Q> const& m2);

  template<typename T, qualifier Q>
  mat<4, 2, T, Q> operator*(mat<3, 2, T, Q> const& m1, mat<4, 3, T, Q> const& m2);

  template<typename T, qualifier Q>
  mat<3, 2, T, Q> operator/(mat<3, 2, T, Q> const& m, T scalar);

  template<typename T, qualifier Q>
  mat<3, 2, T, Q> operator/(T scalar, mat<3, 2, T, Q> const& m);

  // -- Boolean operators --

  template<typename T, qualifier Q>
  bool operator==(mat<3, 2, T, Q> const& m1, mat<3, 2, T, Q> const& m2);

  template<typename T, qualifier Q>
  bool operator!=(mat<3, 2, T, Q> const& m1, mat<3, 2, T, Q> const& m2);

}//namespace glm



namespace glm
{
  // -- Constructors --
















  template<typename T, qualifier Q>
  template<qualifier P>
  inline constexpr mat<3, 2, T, Q>::mat(mat<3, 2, T, P> const& m)

    : value{ col_type(m[0]), col_type(m[1]), col_type(m[2]) }

  {





  }

  template<typename T, qualifier Q>
  inline constexpr mat<3, 2, T, Q>::mat(T s)

    : value{ col_type(s, 0), col_type(0, s), col_type(0, 0) }

  {





  }

  template<typename T, qualifier Q>
  inline constexpr mat<3, 2, T, Q>::mat
  (
    T x0, T y0,
    T x1, T y1,
    T x2, T y2
  )

    : value{ col_type(x0, y0), col_type(x1, y1), col_type(x2, y2) }

  {





  }

  template<typename T, qualifier Q>
  inline constexpr mat<3, 2, T, Q>::mat(col_type const& v0, col_type const& v1, col_type const& v2)

    : value{ col_type(v0), col_type(v1), col_type(v2) }

  {





  }

  // -- Conversion constructors --

  template<typename T, qualifier Q>
  template<
    typename X0, typename Y0,
    typename X1, typename Y1,
    typename X2, typename Y2>
  inline constexpr mat<3, 2, T, Q>::mat
  (
    X0 x0, Y0 y0,
    X1 x1, Y1 y1,
    X2 x2, Y2 y2
  )

    : value{ col_type(x0, y0), col_type(x1, y1), col_type(x2, y2) }

  {





  }

  template<typename T, qualifier Q>
  template<typename V0, typename V1, typename V2>
  inline constexpr mat<3, 2, T, Q>::mat(vec<2, V0, Q> const& v0, vec<2, V1, Q> const& v1, vec<2, V2, Q> const& v2)

    : value{ col_type(v0), col_type(v1), col_type(v2) }

  {





  }

  // -- Matrix conversions --

  template<typename T, qualifier Q>
  template<typename U, qualifier P>
  inline constexpr mat<3, 2, T, Q>::mat(mat<3, 2, U, P> const& m)

    : value{ col_type(m[0]), col_type(m[1]), col_type(m[2]) }

  {





  }

  template<typename T, qualifier Q>
  inline constexpr mat<3, 2, T, Q>::mat(mat<2, 2, T, Q> const& m)

    : value{ col_type(m[0]), col_type(m[1]), col_type(0) }

  {





  }

  template<typename T, qualifier Q>
  inline constexpr mat<3, 2, T, Q>::mat(mat<3, 3, T, Q> const& m)

    : value{ col_type(m[0]), col_type(m[1]), col_type(m[2]) }

  {





  }

  template<typename T, qualifier Q>
  inline constexpr mat<3, 2, T, Q>::mat(mat<4, 4, T, Q> const& m)

    : value{ col_type(m[0]), col_type(m[1]), col_type(m[2]) }

  {





  }

  template<typename T, qualifier Q>
  inline constexpr mat<3, 2, T, Q>::mat(mat<2, 3, T, Q> const& m)

    : value{ col_type(m[0]), col_type(m[1]), col_type(0) }

  {





  }

  template<typename T, qualifier Q>
  inline constexpr mat<3, 2, T, Q>::mat(mat<2, 4, T, Q> const& m)

    : value{ col_type(m[0]), col_type(m[1]), col_type(0) }

  {





  }

  template<typename T, qualifier Q>
  inline constexpr mat<3, 2, T, Q>::mat(mat<3, 4, T, Q> const& m)

    : value{ col_type(m[0]), col_type(m[1]), col_type(m[2]) }

  {





  }

  template<typename T, qualifier Q>
  inline constexpr mat<3, 2, T, Q>::mat(mat<4, 2, T, Q> const& m)

    : value{ col_type(m[0]), col_type(m[1]), col_type(m[2]) }

  {





  }

  template<typename T, qualifier Q>
  inline constexpr mat<3, 2, T, Q>::mat(mat<4, 3, T, Q> const& m)

    : value{ col_type(m[0]), col_type(m[1]), col_type(m[2]) }

  {





  }

  // -- Accesses --

  template<typename T, qualifier Q>
  inline typename mat<3, 2, T, Q>::col_type& mat<3, 2, T, Q>::operator[](typename mat<3, 2, T, Q>::length_type i)
  {
    assert(i < this->length());
    return this->value[i];
  }

  template<typename T, qualifier Q>
  inline constexpr typename mat<3, 2, T, Q>::col_type const& mat<3, 2, T, Q>::operator[](typename mat<3, 2, T, Q>::length_type i) const
  {
    assert(i < this->length());
    return this->value[i];
  }

  // -- Unary updatable operators --

  template<typename T, qualifier Q>
  template<typename U>
  inline mat<3, 2, T, Q>& mat<3, 2, T, Q>::operator=(mat<3, 2, U, Q> const& m)
  {
    this->value[0] = m[0];
    this->value[1] = m[1];
    this->value[2] = m[2];
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline mat<3, 2, T, Q>& mat<3, 2, T, Q>::operator+=(U s)
  {
    this->value[0] += s;
    this->value[1] += s;
    this->value[2] += s;
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline mat<3, 2, T, Q>& mat<3, 2, T, Q>::operator+=(mat<3, 2, U, Q> const& m)
  {
    this->value[0] += m[0];
    this->value[1] += m[1];
    this->value[2] += m[2];
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline mat<3, 2, T, Q>& mat<3, 2, T, Q>::operator-=(U s)
  {
    this->value[0] -= s;
    this->value[1] -= s;
    this->value[2] -= s;
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline mat<3, 2, T, Q>& mat<3, 2, T, Q>::operator-=(mat<3, 2, U, Q> const& m)
  {
    this->value[0] -= m[0];
    this->value[1] -= m[1];
    this->value[2] -= m[2];
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline mat<3, 2, T, Q>& mat<3, 2, T, Q>::operator*=(U s)
  {
    this->value[0] *= s;
    this->value[1] *= s;
    this->value[2] *= s;
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline mat<3, 2, T, Q>& mat<3, 2, T, Q>::operator/=(U s)
  {
    this->value[0] /= s;
    this->value[1] /= s;
    this->value[2] /= s;
    return *this;
  }

  // -- Increment and decrement operators --

  template<typename T, qualifier Q>
  inline mat<3, 2, T, Q>& mat<3, 2, T, Q>::operator++()
  {
    ++this->value[0];
    ++this->value[1];
    ++this->value[2];
    return *this;
  }

  template<typename T, qualifier Q>
  inline mat<3, 2, T, Q>& mat<3, 2, T, Q>::operator--()
  {
    --this->value[0];
    --this->value[1];
    --this->value[2];
    return *this;
  }

  template<typename T, qualifier Q>
  inline mat<3, 2, T, Q> mat<3, 2, T, Q>::operator++(int)
  {
    mat<3, 2, T, Q> Result(*this);
    ++*this;
    return Result;
  }

  template<typename T, qualifier Q>
  inline mat<3, 2, T, Q> mat<3, 2, T, Q>::operator--(int)
  {
    mat<3, 2, T, Q> Result(*this);
    --*this;
    return Result;
  }

  // -- Unary arithmetic operators --

  template<typename T, qualifier Q>
  inline mat<3, 2, T, Q> operator+(mat<3, 2, T, Q> const& m)
  {
    return m;
  }

  template<typename T, qualifier Q>
  inline mat<3, 2, T, Q> operator-(mat<3, 2, T, Q> const& m)
  {
    return mat<3, 2, T, Q>(
      -m[0],
      -m[1],
      -m[2]);
  }

  // -- Binary arithmetic operators --

  template<typename T, qualifier Q>
  inline mat<3, 2, T, Q> operator+(mat<3, 2, T, Q> const& m, T scalar)
  {
    return mat<3, 2, T, Q>(
      m[0] + scalar,
      m[1] + scalar,
      m[2] + scalar);
  }

  template<typename T, qualifier Q>
  inline mat<3, 2, T, Q> operator+(mat<3, 2, T, Q> const& m1, mat<3, 2, T, Q> const& m2)
  {
    return mat<3, 2, T, Q>(
      m1[0] + m2[0],
      m1[1] + m2[1],
      m1[2] + m2[2]);
  }

  template<typename T, qualifier Q>
  inline mat<3, 2, T, Q> operator-(mat<3, 2, T, Q> const& m, T scalar)
  {
    return mat<3, 2, T, Q>(
      m[0] - scalar,
      m[1] - scalar,
      m[2] - scalar);
  }

  template<typename T, qualifier Q>
  inline mat<3, 2, T, Q> operator-(mat<3, 2, T, Q> const& m1, mat<3, 2, T, Q> const& m2)
  {
    return mat<3, 2, T, Q>(
      m1[0] - m2[0],
      m1[1] - m2[1],
      m1[2] - m2[2]);
  }

  template<typename T, qualifier Q>
  inline mat<3, 2, T, Q> operator*(mat<3, 2, T, Q> const& m, T scalar)
  {
    return mat<3, 2, T, Q>(
      m[0] * scalar,
      m[1] * scalar,
      m[2] * scalar);
  }

  template<typename T, qualifier Q>
  inline mat<3, 2, T, Q> operator*(T scalar, mat<3, 2, T, Q> const& m)
  {
    return mat<3, 2, T, Q>(
      m[0] * scalar,
      m[1] * scalar,
      m[2] * scalar);
  }

  template<typename T, qualifier Q>
  inline typename mat<3, 2, T, Q>::col_type operator*(mat<3, 2, T, Q> const& m, typename mat<3, 2, T, Q>::row_type const& v)
  {
    return typename mat<3, 2, T, Q>::col_type(
      m[0][0] * v.x + m[1][0] * v.y + m[2][0] * v.z,
      m[0][1] * v.x + m[1][1] * v.y + m[2][1] * v.z);
  }

  template<typename T, qualifier Q>
  inline typename mat<3, 2, T, Q>::row_type operator*(typename mat<3, 2, T, Q>::col_type const& v, mat<3, 2, T, Q> const& m)
  {
    return typename mat<3, 2, T, Q>::row_type(
      v.x * m[0][0] + v.y * m[0][1],
      v.x * m[1][0] + v.y * m[1][1],
      v.x * m[2][0] + v.y * m[2][1]);
  }

  template<typename T, qualifier Q>
  inline mat<2, 2, T, Q> operator*(mat<3, 2, T, Q> const& m1, mat<2, 3, T, Q> const& m2)
  {
    const T SrcA00 = m1[0][0];
    const T SrcA01 = m1[0][1];
    const T SrcA10 = m1[1][0];
    const T SrcA11 = m1[1][1];
    const T SrcA20 = m1[2][0];
    const T SrcA21 = m1[2][1];

    const T SrcB00 = m2[0][0];
    const T SrcB01 = m2[0][1];
    const T SrcB02 = m2[0][2];
    const T SrcB10 = m2[1][0];
    const T SrcB11 = m2[1][1];
    const T SrcB12 = m2[1][2];

    mat<2, 2, T, Q> Result;
    Result[0][0] = SrcA00 * SrcB00 + SrcA10 * SrcB01 + SrcA20 * SrcB02;
    Result[0][1] = SrcA01 * SrcB00 + SrcA11 * SrcB01 + SrcA21 * SrcB02;
    Result[1][0] = SrcA00 * SrcB10 + SrcA10 * SrcB11 + SrcA20 * SrcB12;
    Result[1][1] = SrcA01 * SrcB10 + SrcA11 * SrcB11 + SrcA21 * SrcB12;
    return Result;
  }

  template<typename T, qualifier Q>
  inline mat<3, 2, T, Q> operator*(mat<3, 2, T, Q> const& m1, mat<3, 3, T, Q> const& m2)
  {
    return mat<3, 2, T, Q>(
      m1[0][0] * m2[0][0] + m1[1][0] * m2[0][1] + m1[2][0] * m2[0][2],
      m1[0][1] * m2[0][0] + m1[1][1] * m2[0][1] + m1[2][1] * m2[0][2],
      m1[0][0] * m2[1][0] + m1[1][0] * m2[1][1] + m1[2][0] * m2[1][2],
      m1[0][1] * m2[1][0] + m1[1][1] * m2[1][1] + m1[2][1] * m2[1][2],
      m1[0][0] * m2[2][0] + m1[1][0] * m2[2][1] + m1[2][0] * m2[2][2],
      m1[0][1] * m2[2][0] + m1[1][1] * m2[2][1] + m1[2][1] * m2[2][2]);
  }

  template<typename T, qualifier Q>
  inline mat<4, 2, T, Q> operator*(mat<3, 2, T, Q> const& m1, mat<4, 3, T, Q> const& m2)
  {
    return mat<4, 2, T, Q>(
      m1[0][0] * m2[0][0] + m1[1][0] * m2[0][1] + m1[2][0] * m2[0][2],
      m1[0][1] * m2[0][0] + m1[1][1] * m2[0][1] + m1[2][1] * m2[0][2],
      m1[0][0] * m2[1][0] + m1[1][0] * m2[1][1] + m1[2][0] * m2[1][2],
      m1[0][1] * m2[1][0] + m1[1][1] * m2[1][1] + m1[2][1] * m2[1][2],
      m1[0][0] * m2[2][0] + m1[1][0] * m2[2][1] + m1[2][0] * m2[2][2],
      m1[0][1] * m2[2][0] + m1[1][1] * m2[2][1] + m1[2][1] * m2[2][2],
      m1[0][0] * m2[3][0] + m1[1][0] * m2[3][1] + m1[2][0] * m2[3][2],
      m1[0][1] * m2[3][0] + m1[1][1] * m2[3][1] + m1[2][1] * m2[3][2]);
  }

  template<typename T, qualifier Q>
  inline mat<3, 2, T, Q> operator/(mat<3, 2, T, Q> const& m, T scalar)
  {
    return mat<3, 2, T, Q>(
      m[0] / scalar,
      m[1] / scalar,
      m[2] / scalar);
  }

  template<typename T, qualifier Q>
  inline mat<3, 2, T, Q> operator/(T scalar, mat<3, 2, T, Q> const& m)
  {
    return mat<3, 2, T, Q>(
      scalar / m[0],
      scalar / m[1],
      scalar / m[2]);
  }

  // -- Boolean operators --

  template<typename T, qualifier Q>
  inline bool operator==(mat<3, 2, T, Q> const& m1, mat<3, 2, T, Q> const& m2)
  {
    return (m1[0] == m2[0]) && (m1[1] == m2[1]) && (m1[2] == m2[2]);
  }

  template<typename T, qualifier Q>
  inline bool operator!=(mat<3, 2, T, Q> const& m1, mat<3, 2, T, Q> const& m2)
  {
    return (m1[0] != m2[0]) || (m1[1] != m2[1]) || (m1[2] != m2[2]);
  }
} //namespace glm




namespace glm
{
  /// @addtogroup core_matrix
  /// @{

  /// 3 columns of 2 components matrix of double-precision floating-point numbers.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  typedef mat<3, 2, double, defaultp>		dmat3x2;

  /// @}
}//namespace glm


/// @ref core
/// @file glm/ext/matrix_double3x2_precision.hpp




namespace glm
{
  /// @addtogroup core_matrix_precision
  /// @{

  /// 3 columns of 2 components matrix of double-precision floating-point numbers using low precision arithmetic in term of ULPs.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef mat<3, 2, double, lowp>		lowp_dmat3x2;

  /// 3 columns of 2 components matrix of double-precision floating-point numbers using medium precision arithmetic in term of ULPs.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef mat<3, 2, double, mediump>	mediump_dmat3x2;

  /// 3 columns of 2 components matrix of double-precision floating-point numbers using medium precision arithmetic in term of ULPs.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef mat<3, 2, double, highp>	highp_dmat3x2;

  /// @}
}//namespace glm


/// @ref core
/// @file glm/ext/matrix_float3x2.hpp




namespace glm
{
  /// @addtogroup core
  /// @{

  /// 3 columns of 2 components matrix of single-precision floating-point numbers.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  typedef mat<3, 2, float, defaultp>			mat3x2;

  /// @}
}//namespace glm


/// @ref core
/// @file glm/ext/matrix_float3x2_precision.hpp




namespace glm
{
  /// @addtogroup core_matrix_precision
  /// @{

  /// 3 columns of 2 components matrix of single-precision floating-point numbers using low precision arithmetic in term of ULPs.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef mat<3, 2, float, lowp>		lowp_mat3x2;

  /// 3 columns of 2 components matrix of single-precision floating-point numbers using medium precision arithmetic in term of ULPs.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef mat<3, 2, float, mediump>	mediump_mat3x2;

  /// 3 columns of 2 components matrix of single-precision floating-point numbers using high precision arithmetic in term of ULPs.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef mat<3, 2, float, highp>		highp_mat3x2;

  /// @}
}//namespace glm

namespace glm
{
  template<typename T, qualifier Q>
  struct mat<3, 3, T, Q>
  {
    typedef vec<3, T, Q> col_type;
    typedef vec<3, T, Q> row_type;
    typedef mat<3, 3, T, Q> type;
    typedef mat<3, 3, T, Q> transpose_type;
    typedef T value_type;

  private:
    col_type value[3];

  public:
    // -- Accesses --

    typedef length_t length_type;
    static constexpr length_type length() { return 3; }

    col_type& operator[](length_type i);
    constexpr col_type const& operator[](length_type i) const;

    // -- Constructors --

    constexpr mat() = default;
    template<qualifier P>
    constexpr mat(mat<3, 3, T, P> const& m);

    explicit constexpr mat(T scalar);
    constexpr mat(
      T x0, T y0, T z0,
      T x1, T y1, T z1,
      T x2, T y2, T z2);
    constexpr mat(
      col_type const& v0,
      col_type const& v1,
      col_type const& v2);

    // -- Conversions --

    template<
      typename X1, typename Y1, typename Z1,
      typename X2, typename Y2, typename Z2,
      typename X3, typename Y3, typename Z3>
    constexpr mat(
      X1 x1, Y1 y1, Z1 z1,
      X2 x2, Y2 y2, Z2 z2,
      X3 x3, Y3 y3, Z3 z3);

    template<typename V1, typename V2, typename V3>
    constexpr mat(
      vec<3, V1, Q> const& v1,
      vec<3, V2, Q> const& v2,
      vec<3, V3, Q> const& v3);

    // -- Matrix conversions --

    template<typename U, qualifier P>
    constexpr mat(mat<3, 3, U, P> const& m);

    constexpr mat(mat<2, 2, T, Q> const& x);
    constexpr mat(mat<4, 4, T, Q> const& x);
    constexpr mat(mat<2, 3, T, Q> const& x);
    constexpr mat(mat<3, 2, T, Q> const& x);
    constexpr mat(mat<2, 4, T, Q> const& x);
    constexpr mat(mat<4, 2, T, Q> const& x);
    constexpr mat(mat<3, 4, T, Q> const& x);
    constexpr mat(mat<4, 3, T, Q> const& x);

    // -- Unary arithmetic operators --

    template<typename U>
    mat<3, 3, T, Q>& operator=(mat<3, 3, U, Q> const& m);
    template<typename U>
    mat<3, 3, T, Q>& operator+=(U s);
    template<typename U>
    mat<3, 3, T, Q>& operator+=(mat<3, 3, U, Q> const& m);
    template<typename U>
    mat<3, 3, T, Q>& operator-=(U s);
    template<typename U>
    mat<3, 3, T, Q>& operator-=(mat<3, 3, U, Q> const& m);
    template<typename U>
    mat<3, 3, T, Q>& operator*=(U s);
    template<typename U>
    mat<3, 3, T, Q>& operator*=(mat<3, 3, U, Q> const& m);
    template<typename U>
    mat<3, 3, T, Q>& operator/=(U s);
    template<typename U>
    mat<3, 3, T, Q>& operator/=(mat<3, 3, U, Q> const& m);

    // -- Increment and decrement operators --

    mat<3, 3, T, Q>& operator++();
    mat<3, 3, T, Q>& operator--();
    mat<3, 3, T, Q> operator++(int);
    mat<3, 3, T, Q> operator--(int);
  };

  // -- Unary operators --

  template<typename T, qualifier Q>
  mat<3, 3, T, Q> operator+(mat<3, 3, T, Q> const& m);

  template<typename T, qualifier Q>
  mat<3, 3, T, Q> operator-(mat<3, 3, T, Q> const& m);

  // -- Binary operators --

  template<typename T, qualifier Q>
  mat<3, 3, T, Q> operator+(mat<3, 3, T, Q> const& m, T scalar);

  template<typename T, qualifier Q>
  mat<3, 3, T, Q> operator+(T scalar, mat<3, 3, T, Q> const& m);

  template<typename T, qualifier Q>
  mat<3, 3, T, Q> operator+(mat<3, 3, T, Q> const& m1, mat<3, 3, T, Q> const& m2);

  template<typename T, qualifier Q>
  mat<3, 3, T, Q> operator-(mat<3, 3, T, Q> const& m, T scalar);

  template<typename T, qualifier Q>
  mat<3, 3, T, Q> operator-(T scalar, mat<3, 3, T, Q> const& m);

  template<typename T, qualifier Q>
  mat<3, 3, T, Q> operator-(mat<3, 3, T, Q> const& m1, mat<3, 3, T, Q> const& m2);

  template<typename T, qualifier Q>
  mat<3, 3, T, Q> operator*(mat<3, 3, T, Q> const& m, T scalar);

  template<typename T, qualifier Q>
  mat<3, 3, T, Q> operator*(T scalar, mat<3, 3, T, Q> const& m);

  template<typename T, qualifier Q>
  typename mat<3, 3, T, Q>::col_type operator*(mat<3, 3, T, Q> const& m, typename mat<3, 3, T, Q>::row_type const& v);

  template<typename T, qualifier Q>
  typename mat<3, 3, T, Q>::row_type operator*(typename mat<3, 3, T, Q>::col_type const& v, mat<3, 3, T, Q> const& m);

  template<typename T, qualifier Q>
  mat<3, 3, T, Q> operator*(mat<3, 3, T, Q> const& m1, mat<3, 3, T, Q> const& m2);

  template<typename T, qualifier Q>
  mat<2, 3, T, Q> operator*(mat<3, 3, T, Q> const& m1, mat<2, 3, T, Q> const& m2);

  template<typename T, qualifier Q>
  mat<4, 3, T, Q> operator*(mat<3, 3, T, Q> const& m1, mat<4, 3, T, Q> const& m2);

  template<typename T, qualifier Q>
  mat<3, 3, T, Q> operator/(mat<3, 3, T, Q> const& m, T scalar);

  template<typename T, qualifier Q>
  mat<3, 3, T, Q> operator/(T scalar, mat<3, 3, T, Q> const& m);

  template<typename T, qualifier Q>
  typename mat<3, 3, T, Q>::col_type operator/(mat<3, 3, T, Q> const& m, typename mat<3, 3, T, Q>::row_type const& v);

  template<typename T, qualifier Q>
  typename mat<3, 3, T, Q>::row_type operator/(typename mat<3, 3, T, Q>::col_type const& v, mat<3, 3, T, Q> const& m);

  template<typename T, qualifier Q>
  mat<3, 3, T, Q> operator/(mat<3, 3, T, Q> const& m1, mat<3, 3, T, Q> const& m2);

  // -- Boolean operators --

  template<typename T, qualifier Q>
  constexpr bool operator==(mat<3, 3, T, Q> const& m1, mat<3, 3, T, Q> const& m2);

  template<typename T, qualifier Q>
  bool operator!=(mat<3, 3, T, Q> const& m1, mat<3, 3, T, Q> const& m2);
}//namespace glm





namespace glm
{
  // -- Constructors --
















  template<typename T, qualifier Q>
  template<qualifier P>
  inline constexpr mat<3, 3, T, Q>::mat(mat<3, 3, T, P> const& m)

    : value{ col_type(m[0]), col_type(m[1]), col_type(m[2]) }

  {





  }

  template<typename T, qualifier Q>
  inline constexpr mat<3, 3, T, Q>::mat(T s)

    : value{ col_type(s, 0, 0), col_type(0, s, 0), col_type(0, 0, s) }

  {





  }

  template<typename T, qualifier Q>
  inline constexpr mat<3, 3, T, Q>::mat
  (
    T x0, T y0, T z0,
    T x1, T y1, T z1,
    T x2, T y2, T z2
  )

    : value{ col_type(x0, y0, z0), col_type(x1, y1, z1), col_type(x2, y2, z2) }

  {





  }

  template<typename T, qualifier Q>
  inline constexpr mat<3, 3, T, Q>::mat(col_type const& v0, col_type const& v1, col_type const& v2)

    : value{ col_type(v0), col_type(v1), col_type(v2) }

  {





  }

  // -- Conversion constructors --

  template<typename T, qualifier Q>
  template<
    typename X1, typename Y1, typename Z1,
    typename X2, typename Y2, typename Z2,
    typename X3, typename Y3, typename Z3>
  inline constexpr mat<3, 3, T, Q>::mat
  (
    X1 x1, Y1 y1, Z1 z1,
    X2 x2, Y2 y2, Z2 z2,
    X3 x3, Y3 y3, Z3 z3
  )

    : value{ col_type(x1, y1, z1), col_type(x2, y2, z2), col_type(x3, y3, z3) }

  {





  }

  template<typename T, qualifier Q>
  template<typename V1, typename V2, typename V3>
  inline constexpr mat<3, 3, T, Q>::mat(vec<3, V1, Q> const& v1, vec<3, V2, Q> const& v2, vec<3, V3, Q> const& v3)

    : value{ col_type(v1), col_type(v2), col_type(v3) }

  {





  }

  // -- Matrix conversions --

  template<typename T, qualifier Q>
  template<typename U, qualifier P>
  inline constexpr mat<3, 3, T, Q>::mat(mat<3, 3, U, P> const& m)

    : value{ col_type(m[0]), col_type(m[1]), col_type(m[2]) }

  {





  }

  template<typename T, qualifier Q>
  inline constexpr mat<3, 3, T, Q>::mat(mat<2, 2, T, Q> const& m)

    : value{ col_type(m[0], 0), col_type(m[1], 0), col_type(0, 0, 1) }

  {





  }

  template<typename T, qualifier Q>
  inline constexpr mat<3, 3, T, Q>::mat(mat<4, 4, T, Q> const& m)

    : value{ col_type(m[0]), col_type(m[1]), col_type(m[2]) }

  {





  }

  template<typename T, qualifier Q>
  inline constexpr mat<3, 3, T, Q>::mat(mat<2, 3, T, Q> const& m)

    : value{ col_type(m[0]), col_type(m[1]), col_type(0, 0, 1) }

  {





  }

  template<typename T, qualifier Q>
  inline constexpr mat<3, 3, T, Q>::mat(mat<3, 2, T, Q> const& m)

    : value{ col_type(m[0], 0), col_type(m[1], 0), col_type(m[2], 1) }

  {





  }

  template<typename T, qualifier Q>
  inline constexpr mat<3, 3, T, Q>::mat(mat<2, 4, T, Q> const& m)

    : value{ col_type(m[0]), col_type(m[1]), col_type(0, 0, 1) }

  {





  }

  template<typename T, qualifier Q>
  inline constexpr mat<3, 3, T, Q>::mat(mat<4, 2, T, Q> const& m)

    : value{ col_type(m[0], 0), col_type(m[1], 0), col_type(m[2], 1) }

  {





  }

  template<typename T, qualifier Q>
  inline constexpr mat<3, 3, T, Q>::mat(mat<3, 4, T, Q> const& m)

    : value{ col_type(m[0]), col_type(m[1]), col_type(m[2]) }

  {





  }

  template<typename T, qualifier Q>
  inline constexpr mat<3, 3, T, Q>::mat(mat<4, 3, T, Q> const& m)

    : value{ col_type(m[0]), col_type(m[1]), col_type(m[2]) }

  {





  }

  // -- Accesses --

  template<typename T, qualifier Q>
  inline typename mat<3, 3, T, Q>::col_type& mat<3, 3, T, Q>::operator[](typename mat<3, 3, T, Q>::length_type i)
  {
    assert(i < this->length());
    return this->value[i];
  }

  template<typename T, qualifier Q>
  inline constexpr typename mat<3, 3, T, Q>::col_type const& mat<3, 3, T, Q>::operator[](typename mat<3, 3, T, Q>::length_type i) const
  {
    assert(i < this->length());
    return this->value[i];
  }

  // -- Unary updatable operators --

  template<typename T, qualifier Q>
  template<typename U>
  inline mat<3, 3, T, Q>& mat<3, 3, T, Q>::operator=(mat<3, 3, U, Q> const& m)
  {
    this->value[0] = m[0];
    this->value[1] = m[1];
    this->value[2] = m[2];
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline mat<3, 3, T, Q>& mat<3, 3, T, Q>::operator+=(U s)
  {
    this->value[0] += s;
    this->value[1] += s;
    this->value[2] += s;
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline mat<3, 3, T, Q>& mat<3, 3, T, Q>::operator+=(mat<3, 3, U, Q> const& m)
  {
    this->value[0] += m[0];
    this->value[1] += m[1];
    this->value[2] += m[2];
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline mat<3, 3, T, Q>& mat<3, 3, T, Q>::operator-=(U s)
  {
    this->value[0] -= s;
    this->value[1] -= s;
    this->value[2] -= s;
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline mat<3, 3, T, Q>& mat<3, 3, T, Q>::operator-=(mat<3, 3, U, Q> const& m)
  {
    this->value[0] -= m[0];
    this->value[1] -= m[1];
    this->value[2] -= m[2];
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline mat<3, 3, T, Q>& mat<3, 3, T, Q>::operator*=(U s)
  {
    this->value[0] *= s;
    this->value[1] *= s;
    this->value[2] *= s;
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline mat<3, 3, T, Q>& mat<3, 3, T, Q>::operator*=(mat<3, 3, U, Q> const& m)
  {
    return (*this = *this * m);
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline mat<3, 3, T, Q>& mat<3, 3, T, Q>::operator/=(U s)
  {
    this->value[0] /= s;
    this->value[1] /= s;
    this->value[2] /= s;
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline mat<3, 3, T, Q>& mat<3, 3, T, Q>::operator/=(mat<3, 3, U, Q> const& m)
  {
    return *this *= inverse(m);
  }

  // -- Increment and decrement operators --

  template<typename T, qualifier Q>
  inline mat<3, 3, T, Q>& mat<3, 3, T, Q>::operator++()
  {
    ++this->value[0];
    ++this->value[1];
    ++this->value[2];
    return *this;
  }

  template<typename T, qualifier Q>
  inline mat<3, 3, T, Q>& mat<3, 3, T, Q>::operator--()
  {
    --this->value[0];
    --this->value[1];
    --this->value[2];
    return *this;
  }

  template<typename T, qualifier Q>
  inline mat<3, 3, T, Q> mat<3, 3, T, Q>::operator++(int)
  {
    mat<3, 3, T, Q> Result(*this);
    ++*this;
    return Result;
  }

  template<typename T, qualifier Q>
  inline mat<3, 3, T, Q> mat<3, 3, T, Q>::operator--(int)
  {
    mat<3, 3, T, Q> Result(*this);
    --*this;
    return Result;
  }

  // -- Unary arithmetic operators --

  template<typename T, qualifier Q>
  inline mat<3, 3, T, Q> operator+(mat<3, 3, T, Q> const& m)
  {
    return m;
  }

  template<typename T, qualifier Q>
  inline mat<3, 3, T, Q> operator-(mat<3, 3, T, Q> const& m)
  {
    return mat<3, 3, T, Q>(
      -m[0],
      -m[1],
      -m[2]);
  }

  // -- Binary arithmetic operators --

  template<typename T, qualifier Q>
  inline mat<3, 3, T, Q> operator+(mat<3, 3, T, Q> const& m, T scalar)
  {
    return mat<3, 3, T, Q>(
      m[0] + scalar,
      m[1] + scalar,
      m[2] + scalar);
  }

  template<typename T, qualifier Q>
  inline mat<3, 3, T, Q> operator+(T scalar, mat<3, 3, T, Q> const& m)
  {
    return mat<3, 3, T, Q>(
      m[0] + scalar,
      m[1] + scalar,
      m[2] + scalar);
  }

  template<typename T, qualifier Q>
  inline mat<3, 3, T, Q> operator+(mat<3, 3, T, Q> const& m1, mat<3, 3, T, Q> const& m2)
  {
    return mat<3, 3, T, Q>(
      m1[0] + m2[0],
      m1[1] + m2[1],
      m1[2] + m2[2]);
  }

  template<typename T, qualifier Q>
  inline mat<3, 3, T, Q> operator-(mat<3, 3, T, Q> const& m, T scalar)
  {
    return mat<3, 3, T, Q>(
      m[0] - scalar,
      m[1] - scalar,
      m[2] - scalar);
  }

  template<typename T, qualifier Q>
  inline mat<3, 3, T, Q> operator-(T scalar, mat<3, 3, T, Q> const& m)
  {
    return mat<3, 3, T, Q>(
      scalar - m[0],
      scalar - m[1],
      scalar - m[2]);
  }

  template<typename T, qualifier Q>
  inline mat<3, 3, T, Q> operator-(mat<3, 3, T, Q> const& m1, mat<3, 3, T, Q> const& m2)
  {
    return mat<3, 3, T, Q>(
      m1[0] - m2[0],
      m1[1] - m2[1],
      m1[2] - m2[2]);
  }

  template<typename T, qualifier Q>
  inline mat<3, 3, T, Q> operator*(mat<3, 3, T, Q> const& m, T scalar)
  {
    return mat<3, 3, T, Q>(
      m[0] * scalar,
      m[1] * scalar,
      m[2] * scalar);
  }

  template<typename T, qualifier Q>
  inline mat<3, 3, T, Q> operator*(T scalar, mat<3, 3, T, Q> const& m)
  {
    return mat<3, 3, T, Q>(
      m[0] * scalar,
      m[1] * scalar,
      m[2] * scalar);
  }

  template<typename T, qualifier Q>
  inline typename mat<3, 3, T, Q>::col_type operator*(mat<3, 3, T, Q> const& m, typename mat<3, 3, T, Q>::row_type const& v)
  {
    return typename mat<3, 3, T, Q>::col_type(
      m[0][0] * v.x + m[1][0] * v.y + m[2][0] * v.z,
      m[0][1] * v.x + m[1][1] * v.y + m[2][1] * v.z,
      m[0][2] * v.x + m[1][2] * v.y + m[2][2] * v.z);
  }

  template<typename T, qualifier Q>
  inline typename mat<3, 3, T, Q>::row_type operator*(typename mat<3, 3, T, Q>::col_type const& v, mat<3, 3, T, Q> const& m)
  {
    return typename mat<3, 3, T, Q>::row_type(
      m[0][0] * v.x + m[0][1] * v.y + m[0][2] * v.z,
      m[1][0] * v.x + m[1][1] * v.y + m[1][2] * v.z,
      m[2][0] * v.x + m[2][1] * v.y + m[2][2] * v.z);
  }

  template<typename T, qualifier Q>
  inline mat<3, 3, T, Q> operator*(mat<3, 3, T, Q> const& m1, mat<3, 3, T, Q> const& m2)
  {
    T const SrcA00 = m1[0][0];
    T const SrcA01 = m1[0][1];
    T const SrcA02 = m1[0][2];
    T const SrcA10 = m1[1][0];
    T const SrcA11 = m1[1][1];
    T const SrcA12 = m1[1][2];
    T const SrcA20 = m1[2][0];
    T const SrcA21 = m1[2][1];
    T const SrcA22 = m1[2][2];

    T const SrcB00 = m2[0][0];
    T const SrcB01 = m2[0][1];
    T const SrcB02 = m2[0][2];
    T const SrcB10 = m2[1][0];
    T const SrcB11 = m2[1][1];
    T const SrcB12 = m2[1][2];
    T const SrcB20 = m2[2][0];
    T const SrcB21 = m2[2][1];
    T const SrcB22 = m2[2][2];

    mat<3, 3, T, Q> Result;
    Result[0][0] = SrcA00 * SrcB00 + SrcA10 * SrcB01 + SrcA20 * SrcB02;
    Result[0][1] = SrcA01 * SrcB00 + SrcA11 * SrcB01 + SrcA21 * SrcB02;
    Result[0][2] = SrcA02 * SrcB00 + SrcA12 * SrcB01 + SrcA22 * SrcB02;
    Result[1][0] = SrcA00 * SrcB10 + SrcA10 * SrcB11 + SrcA20 * SrcB12;
    Result[1][1] = SrcA01 * SrcB10 + SrcA11 * SrcB11 + SrcA21 * SrcB12;
    Result[1][2] = SrcA02 * SrcB10 + SrcA12 * SrcB11 + SrcA22 * SrcB12;
    Result[2][0] = SrcA00 * SrcB20 + SrcA10 * SrcB21 + SrcA20 * SrcB22;
    Result[2][1] = SrcA01 * SrcB20 + SrcA11 * SrcB21 + SrcA21 * SrcB22;
    Result[2][2] = SrcA02 * SrcB20 + SrcA12 * SrcB21 + SrcA22 * SrcB22;
    return Result;
  }

  template<typename T, qualifier Q>
  inline mat<2, 3, T, Q> operator*(mat<3, 3, T, Q> const& m1, mat<2, 3, T, Q> const& m2)
  {
    return mat<2, 3, T, Q>(
      m1[0][0] * m2[0][0] + m1[1][0] * m2[0][1] + m1[2][0] * m2[0][2],
      m1[0][1] * m2[0][0] + m1[1][1] * m2[0][1] + m1[2][1] * m2[0][2],
      m1[0][2] * m2[0][0] + m1[1][2] * m2[0][1] + m1[2][2] * m2[0][2],
      m1[0][0] * m2[1][0] + m1[1][0] * m2[1][1] + m1[2][0] * m2[1][2],
      m1[0][1] * m2[1][0] + m1[1][1] * m2[1][1] + m1[2][1] * m2[1][2],
      m1[0][2] * m2[1][0] + m1[1][2] * m2[1][1] + m1[2][2] * m2[1][2]);
  }

  template<typename T, qualifier Q>
  inline mat<4, 3, T, Q> operator*(mat<3, 3, T, Q> const& m1, mat<4, 3, T, Q> const& m2)
  {
    return mat<4, 3, T, Q>(
      m1[0][0] * m2[0][0] + m1[1][0] * m2[0][1] + m1[2][0] * m2[0][2],
      m1[0][1] * m2[0][0] + m1[1][1] * m2[0][1] + m1[2][1] * m2[0][2],
      m1[0][2] * m2[0][0] + m1[1][2] * m2[0][1] + m1[2][2] * m2[0][2],
      m1[0][0] * m2[1][0] + m1[1][0] * m2[1][1] + m1[2][0] * m2[1][2],
      m1[0][1] * m2[1][0] + m1[1][1] * m2[1][1] + m1[2][1] * m2[1][2],
      m1[0][2] * m2[1][0] + m1[1][2] * m2[1][1] + m1[2][2] * m2[1][2],
      m1[0][0] * m2[2][0] + m1[1][0] * m2[2][1] + m1[2][0] * m2[2][2],
      m1[0][1] * m2[2][0] + m1[1][1] * m2[2][1] + m1[2][1] * m2[2][2],
      m1[0][2] * m2[2][0] + m1[1][2] * m2[2][1] + m1[2][2] * m2[2][2],
      m1[0][0] * m2[3][0] + m1[1][0] * m2[3][1] + m1[2][0] * m2[3][2],
      m1[0][1] * m2[3][0] + m1[1][1] * m2[3][1] + m1[2][1] * m2[3][2],
      m1[0][2] * m2[3][0] + m1[1][2] * m2[3][1] + m1[2][2] * m2[3][2]);
  }

  template<typename T, qualifier Q>
  inline mat<3, 3, T, Q> operator/(mat<3, 3, T, Q> const& m, T scalar)
  {
    return mat<3, 3, T, Q>(
      m[0] / scalar,
      m[1] / scalar,
      m[2] / scalar);
  }

  template<typename T, qualifier Q>
  inline mat<3, 3, T, Q> operator/(T scalar, mat<3, 3, T, Q> const& m)
  {
    return mat<3, 3, T, Q>(
      scalar / m[0],
      scalar / m[1],
      scalar / m[2]);
  }

  template<typename T, qualifier Q>
  inline typename mat<3, 3, T, Q>::col_type operator/(mat<3, 3, T, Q> const& m, typename mat<3, 3, T, Q>::row_type const& v)
  {
    return  inverse(m) * v;
  }

  template<typename T, qualifier Q>
  inline typename mat<3, 3, T, Q>::row_type operator/(typename mat<3, 3, T, Q>::col_type const& v, mat<3, 3, T, Q> const& m)
  {
    return v * inverse(m);
  }

  template<typename T, qualifier Q>
  inline mat<3, 3, T, Q> operator/(mat<3, 3, T, Q> const& m1, mat<3, 3, T, Q> const& m2)
  {
    mat<3, 3, T, Q> m1_copy(m1);
    return m1_copy /= m2;
  }

  // -- Boolean operators --

  template<typename T, qualifier Q>
  inline constexpr bool operator==(mat<3, 3, T, Q> const& m1, mat<3, 3, T, Q> const& m2)
  {
    return (m1[0] == m2[0]) && (m1[1] == m2[1]) && (m1[2] == m2[2]);
  }

  template<typename T, qualifier Q>
  inline bool operator!=(mat<3, 3, T, Q> const& m1, mat<3, 3, T, Q> const& m2)
  {
    return (m1[0] != m2[0]) || (m1[1] != m2[1]) || (m1[2] != m2[2]);
  }
} //namespace glm




namespace glm
{
  /// @addtogroup core_matrix
  /// @{

  /// 3 columns of 3 components matrix of double-precision floating-point numbers.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  typedef mat<3, 3, double, defaultp>		dmat3x3;

  /// 3 columns of 3 components matrix of double-precision floating-point numbers.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  typedef mat<3, 3, double, defaultp>		dmat3;

  /// @}
}//namespace glm


/// @ref core
/// @file glm/ext/matrix_double3x3_precision.hpp




namespace glm
{
  /// @addtogroup core_matrix_precision
  /// @{

  /// 3 columns of 3 components matrix of double-precision floating-point numbers using low precision arithmetic in term of ULPs.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef mat<3, 3, double, lowp>		lowp_dmat3;

  /// 3 columns of 3 components matrix of double-precision floating-point numbers using medium precision arithmetic in term of ULPs.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef mat<3, 3, double, mediump>	mediump_dmat3;

  /// 3 columns of 3 components matrix of double-precision floating-point numbers using medium precision arithmetic in term of ULPs.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef mat<3, 3, double, highp>	highp_dmat3;

  /// 3 columns of 3 components matrix of double-precision floating-point numbers using low precision arithmetic in term of ULPs.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef mat<3, 3, double, lowp>		lowp_dmat3x3;

  /// 3 columns of 3 components matrix of double-precision floating-point numbers using medium precision arithmetic in term of ULPs.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef mat<3, 3, double, mediump>	mediump_dmat3x3;

  /// 3 columns of 3 components matrix of double-precision floating-point numbers using medium precision arithmetic in term of ULPs.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef mat<3, 3, double, highp>	highp_dmat3x3;

  /// @}
}//namespace glm


/// @ref core
/// @file glm/ext/matrix_float3x3.hpp




namespace glm
{
  /// @addtogroup core_matrix
  /// @{

  /// 3 columns of 3 components matrix of single-precision floating-point numbers.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  typedef mat<3, 3, float, defaultp>			mat3x3;

  /// 3 columns of 3 components matrix of single-precision floating-point numbers.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  typedef mat<3, 3, float, defaultp>			mat3;

  /// @}
}//namespace glm


/// @ref core
/// @file glm/ext/matrix_float3x3_precision.hpp




namespace glm
{
  /// @addtogroup core_matrix_precision
  /// @{

  /// 3 columns of 3 components matrix of single-precision floating-point numbers using low precision arithmetic in term of ULPs.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef mat<3, 3, float, lowp>		lowp_mat3;

  /// 3 columns of 3 components matrix of single-precision floating-point numbers using medium precision arithmetic in term of ULPs.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef mat<3, 3, float, mediump>	mediump_mat3;

  /// 3 columns of 3 components matrix of single-precision floating-point numbers using high precision arithmetic in term of ULPs.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef mat<3, 3, float, highp>		highp_mat3;

  /// 3 columns of 3 components matrix of single-precision floating-point numbers using low precision arithmetic in term of ULPs.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef mat<3, 3, float, lowp>		lowp_mat3x3;

  /// 3 columns of 3 components matrix of single-precision floating-point numbers using medium precision arithmetic in term of ULPs.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef mat<3, 3, float, mediump>	mediump_mat3x3;

  /// 3 columns of 3 components matrix of single-precision floating-point numbers using high precision arithmetic in term of ULPs.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef mat<3, 3, float, highp>		highp_mat3x3;

  /// @}
}//namespace glm

namespace glm
{
  template<typename T, qualifier Q>
  struct mat<3, 4, T, Q>
  {
    typedef vec<4, T, Q> col_type;
    typedef vec<3, T, Q> row_type;
    typedef mat<3, 4, T, Q> type;
    typedef mat<4, 3, T, Q> transpose_type;
    typedef T value_type;

  private:
    col_type value[3];

  public:
    // -- Accesses --

    typedef length_t length_type;
    static constexpr length_type length() { return 3; }

    col_type& operator[](length_type i);
    constexpr col_type const& operator[](length_type i) const;

    // -- Constructors --

    constexpr mat() = default;
    template<qualifier P>
    constexpr mat(mat<3, 4, T, P> const& m);

    explicit constexpr mat(T scalar);
    constexpr mat(
      T x0, T y0, T z0, T w0,
      T x1, T y1, T z1, T w1,
      T x2, T y2, T z2, T w2);
    constexpr mat(
      col_type const& v0,
      col_type const& v1,
      col_type const& v2);

    // -- Conversions --

    template<
      typename X1, typename Y1, typename Z1, typename W1,
      typename X2, typename Y2, typename Z2, typename W2,
      typename X3, typename Y3, typename Z3, typename W3>
    constexpr mat(
      X1 x1, Y1 y1, Z1 z1, W1 w1,
      X2 x2, Y2 y2, Z2 z2, W2 w2,
      X3 x3, Y3 y3, Z3 z3, W3 w3);

    template<typename V1, typename V2, typename V3>
    constexpr mat(
      vec<4, V1, Q> const& v1,
      vec<4, V2, Q> const& v2,
      vec<4, V3, Q> const& v3);

    // -- Matrix conversions --

    template<typename U, qualifier P>
    constexpr mat(mat<3, 4, U, P> const& m);

    constexpr mat(mat<2, 2, T, Q> const& x);
    constexpr mat(mat<3, 3, T, Q> const& x);
    constexpr mat(mat<4, 4, T, Q> const& x);
    constexpr mat(mat<2, 3, T, Q> const& x);
    constexpr mat(mat<3, 2, T, Q> const& x);
    constexpr mat(mat<2, 4, T, Q> const& x);
    constexpr mat(mat<4, 2, T, Q> const& x);
    constexpr mat(mat<4, 3, T, Q> const& x);

    // -- Unary arithmetic operators --

    template<typename U>
    mat<3, 4, T, Q>& operator=(mat<3, 4, U, Q> const& m);
    template<typename U>
    mat<3, 4, T, Q>& operator+=(U s);
    template<typename U>
    mat<3, 4, T, Q>& operator+=(mat<3, 4, U, Q> const& m);
    template<typename U>
    mat<3, 4, T, Q>& operator-=(U s);
    template<typename U>
    mat<3, 4, T, Q>& operator-=(mat<3, 4, U, Q> const& m);
    template<typename U>
    mat<3, 4, T, Q>& operator*=(U s);
    template<typename U>
    mat<3, 4, T, Q>& operator/=(U s);

    // -- Increment and decrement operators --

    mat<3, 4, T, Q>& operator++();
    mat<3, 4, T, Q>& operator--();
    mat<3, 4, T, Q> operator++(int);
    mat<3, 4, T, Q> operator--(int);
  };

  // -- Unary operators --

  template<typename T, qualifier Q>
  mat<3, 4, T, Q> operator+(mat<3, 4, T, Q> const& m);

  template<typename T, qualifier Q>
  mat<3, 4, T, Q> operator-(mat<3, 4, T, Q> const& m);

  // -- Binary operators --

  template<typename T, qualifier Q>
  mat<3, 4, T, Q> operator+(mat<3, 4, T, Q> const& m, T scalar);

  template<typename T, qualifier Q>
  mat<3, 4, T, Q> operator+(mat<3, 4, T, Q> const& m1, mat<3, 4, T, Q> const& m2);

  template<typename T, qualifier Q>
  mat<3, 4, T, Q> operator-(mat<3, 4, T, Q> const& m, T scalar);

  template<typename T, qualifier Q>
  mat<3, 4, T, Q> operator-(mat<3, 4, T, Q> const& m1, mat<3, 4, T, Q> const& m2);

  template<typename T, qualifier Q>
  mat<3, 4, T, Q> operator*(mat<3, 4, T, Q> const& m, T scalar);

  template<typename T, qualifier Q>
  mat<3, 4, T, Q> operator*(T scalar, mat<3, 4, T, Q> const& m);

  template<typename T, qualifier Q>
  typename mat<3, 4, T, Q>::col_type operator*(mat<3, 4, T, Q> const& m, typename mat<3, 4, T, Q>::row_type const& v);

  template<typename T, qualifier Q>
  typename mat<3, 4, T, Q>::row_type operator*(typename mat<3, 4, T, Q>::col_type const& v, mat<3, 4, T, Q> const& m);

  template<typename T, qualifier Q>
  mat<4, 4, T, Q> operator*(mat<3, 4, T, Q> const& m1, mat<4, 3, T, Q> const& m2);

  template<typename T, qualifier Q>
  mat<2, 4, T, Q> operator*(mat<3, 4, T, Q> const& m1, mat<2, 3, T, Q> const& m2);

  template<typename T, qualifier Q>
  mat<3, 4, T, Q> operator*(mat<3, 4, T, Q> const& m1, mat<3, 3, T, Q> const& m2);

  template<typename T, qualifier Q>
  mat<3, 4, T, Q> operator/(mat<3, 4, T, Q> const& m, T scalar);

  template<typename T, qualifier Q>
  mat<3, 4, T, Q> operator/(T scalar, mat<3, 4, T, Q> const& m);

  // -- Boolean operators --

  template<typename T, qualifier Q>
  bool operator==(mat<3, 4, T, Q> const& m1, mat<3, 4, T, Q> const& m2);

  template<typename T, qualifier Q>
  bool operator!=(mat<3, 4, T, Q> const& m1, mat<3, 4, T, Q> const& m2);
}//namespace glm



namespace glm
{
  // -- Constructors --
















  template<typename T, qualifier Q>
  template<qualifier P>
  inline constexpr mat<3, 4, T, Q>::mat(mat<3, 4, T, P> const& m)

    : value{ col_type(m[0]), col_type(m[1]), col_type(m[2]) }

  {





  }

  template<typename T, qualifier Q>
  inline constexpr mat<3, 4, T, Q>::mat(T s)

    : value{ col_type(s, 0, 0, 0), col_type(0, s, 0, 0), col_type(0, 0, s, 0) }

  {





  }

  template<typename T, qualifier Q>
  inline constexpr mat<3, 4, T, Q>::mat
  (
    T x0, T y0, T z0, T w0,
    T x1, T y1, T z1, T w1,
    T x2, T y2, T z2, T w2
  )

    : value{
      col_type(x0, y0, z0, w0),
      col_type(x1, y1, z1, w1),
      col_type(x2, y2, z2, w2) }

  {





  }

  template<typename T, qualifier Q>
  inline constexpr mat<3, 4, T, Q>::mat(col_type const& v0, col_type const& v1, col_type const& v2)

    : value{ col_type(v0), col_type(v1), col_type(v2) }

  {





  }

  // -- Conversion constructors --

  template<typename T, qualifier Q>
  template<
    typename X0, typename Y0, typename Z0, typename W0,
    typename X1, typename Y1, typename Z1, typename W1,
    typename X2, typename Y2, typename Z2, typename W2>
  inline constexpr mat<3, 4, T, Q>::mat
  (
    X0 x0, Y0 y0, Z0 z0, W0 w0,
    X1 x1, Y1 y1, Z1 z1, W1 w1,
    X2 x2, Y2 y2, Z2 z2, W2 w2
  )

    : value{
      col_type(x0, y0, z0, w0),
      col_type(x1, y1, z1, w1),
      col_type(x2, y2, z2, w2) }

  {





  }

  template<typename T, qualifier Q>
  template<typename V1, typename V2, typename V3>
  inline constexpr mat<3, 4, T, Q>::mat(vec<4, V1, Q> const& v0, vec<4, V2, Q> const& v1, vec<4, V3, Q> const& v2)

    : value{ col_type(v0), col_type(v1), col_type(v2) }

  {





  }

  // -- Matrix conversions --

  template<typename T, qualifier Q>
  template<typename U, qualifier P>
  inline constexpr mat<3, 4, T, Q>::mat(mat<3, 4, U, P> const& m)

    : value{ col_type(m[0]), col_type(m[1]), col_type(m[2]) }

  {





  }

  template<typename T, qualifier Q>
  inline constexpr mat<3, 4, T, Q>::mat(mat<2, 2, T, Q> const& m)

    : value{ col_type(m[0], 0, 0), col_type(m[1], 0, 0), col_type(0, 0, 1, 0) }

  {





  }

  template<typename T, qualifier Q>
  inline constexpr mat<3, 4, T, Q>::mat(mat<3, 3, T, Q> const& m)

    : value{ col_type(m[0], 0), col_type(m[1], 0), col_type(m[2], 0) }

  {





  }

  template<typename T, qualifier Q>
  inline constexpr mat<3, 4, T, Q>::mat(mat<4, 4, T, Q> const& m)

    : value{ col_type(m[0]), col_type(m[1]), col_type(m[2]) }

  {





  }

  template<typename T, qualifier Q>
  inline constexpr mat<3, 4, T, Q>::mat(mat<2, 3, T, Q> const& m)

    : value{ col_type(m[0], 0), col_type(m[1], 0), col_type(0, 0, 1, 0) }

  {





  }

  template<typename T, qualifier Q>
  inline constexpr mat<3, 4, T, Q>::mat(mat<3, 2, T, Q> const& m)

    : value{ col_type(m[0], 0, 0), col_type(m[1], 0, 0), col_type(m[2], 1, 0) }

  {





  }

  template<typename T, qualifier Q>
  inline constexpr mat<3, 4, T, Q>::mat(mat<2, 4, T, Q> const& m)

    : value{ col_type(m[0]), col_type(m[1]), col_type(0, 0, 1, 0) }

  {





  }

  template<typename T, qualifier Q>
  inline constexpr mat<3, 4, T, Q>::mat(mat<4, 2, T, Q> const& m)

    : value{ col_type(m[0], 0, 0), col_type(m[1], 0, 0), col_type(m[2], 1, 0) }

  {





  }

  template<typename T, qualifier Q>
  inline constexpr mat<3, 4, T, Q>::mat(mat<4, 3, T, Q> const& m)

    : value{ col_type(m[0], 0), col_type(m[1], 0), col_type(m[2], 0) }

  {





  }

  // -- Accesses --

  template<typename T, qualifier Q>
  inline typename mat<3, 4, T, Q>::col_type& mat<3, 4, T, Q>::operator[](typename mat<3, 4, T, Q>::length_type i)
  {
    assert(i < this->length());
    return this->value[i];
  }

  template<typename T, qualifier Q>
  inline constexpr typename mat<3, 4, T, Q>::col_type const& mat<3, 4, T, Q>::operator[](typename mat<3, 4, T, Q>::length_type i) const
  {
    assert(i < this->length());
    return this->value[i];
  }

  // -- Unary updatable operators --

  template<typename T, qualifier Q>
  template<typename U>
  inline mat<3, 4, T, Q>& mat<3, 4, T, Q>::operator=(mat<3, 4, U, Q> const& m)
  {
    this->value[0] = m[0];
    this->value[1] = m[1];
    this->value[2] = m[2];
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline mat<3, 4, T, Q>& mat<3, 4, T, Q>::operator+=(U s)
  {
    this->value[0] += s;
    this->value[1] += s;
    this->value[2] += s;
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline mat<3, 4, T, Q>& mat<3, 4, T, Q>::operator+=(mat<3, 4, U, Q> const& m)
  {
    this->value[0] += m[0];
    this->value[1] += m[1];
    this->value[2] += m[2];
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline mat<3, 4, T, Q>& mat<3, 4, T, Q>::operator-=(U s)
  {
    this->value[0] -= s;
    this->value[1] -= s;
    this->value[2] -= s;
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline mat<3, 4, T, Q>& mat<3, 4, T, Q>::operator-=(mat<3, 4, U, Q> const& m)
  {
    this->value[0] -= m[0];
    this->value[1] -= m[1];
    this->value[2] -= m[2];
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline mat<3, 4, T, Q>& mat<3, 4, T, Q>::operator*=(U s)
  {
    this->value[0] *= s;
    this->value[1] *= s;
    this->value[2] *= s;
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline mat<3, 4, T, Q>& mat<3, 4, T, Q>::operator/=(U s)
  {
    this->value[0] /= s;
    this->value[1] /= s;
    this->value[2] /= s;
    return *this;
  }

  // -- Increment and decrement operators --

  template<typename T, qualifier Q>
  inline mat<3, 4, T, Q>& mat<3, 4, T, Q>::operator++()
  {
    ++this->value[0];
    ++this->value[1];
    ++this->value[2];
    return *this;
  }

  template<typename T, qualifier Q>
  inline mat<3, 4, T, Q>& mat<3, 4, T, Q>::operator--()
  {
    --this->value[0];
    --this->value[1];
    --this->value[2];
    return *this;
  }

  template<typename T, qualifier Q>
  inline mat<3, 4, T, Q> mat<3, 4, T, Q>::operator++(int)
  {
    mat<3, 4, T, Q> Result(*this);
    ++*this;
    return Result;
  }

  template<typename T, qualifier Q>
  inline mat<3, 4, T, Q> mat<3, 4, T, Q>::operator--(int)
  {
    mat<3, 4, T, Q> Result(*this);
    --*this;
    return Result;
  }

  // -- Unary arithmetic operators --

  template<typename T, qualifier Q>
  inline mat<3, 4, T, Q> operator+(mat<3, 4, T, Q> const& m)
  {
    return m;
  }

  template<typename T, qualifier Q>
  inline mat<3, 4, T, Q> operator-(mat<3, 4, T, Q> const& m)
  {
    return mat<3, 4, T, Q>(
      -m[0],
      -m[1],
      -m[2]);
  }

  // -- Binary arithmetic operators --

  template<typename T, qualifier Q>
  inline mat<3, 4, T, Q> operator+(mat<3, 4, T, Q> const& m, T scalar)
  {
    return mat<3, 4, T, Q>(
      m[0] + scalar,
      m[1] + scalar,
      m[2] + scalar);
  }

  template<typename T, qualifier Q>
  inline mat<3, 4, T, Q> operator+(mat<3, 4, T, Q> const& m1, mat<3, 4, T, Q> const& m2)
  {
    return mat<3, 4, T, Q>(
      m1[0] + m2[0],
      m1[1] + m2[1],
      m1[2] + m2[2]);
  }

  template<typename T, qualifier Q>
  inline mat<3, 4, T, Q> operator-(mat<3, 4, T, Q> const& m, T scalar)
  {
    return mat<3, 4, T, Q>(
      m[0] - scalar,
      m[1] - scalar,
      m[2] - scalar);
  }

  template<typename T, qualifier Q>
  inline mat<3, 4, T, Q> operator-(mat<3, 4, T, Q> const& m1, mat<3, 4, T, Q> const& m2)
  {
    return mat<3, 4, T, Q>(
      m1[0] - m2[0],
      m1[1] - m2[1],
      m1[2] - m2[2]);
  }

  template<typename T, qualifier Q>
  inline mat<3, 4, T, Q> operator*(mat<3, 4, T, Q> const& m, T scalar)
  {
    return mat<3, 4, T, Q>(
      m[0] * scalar,
      m[1] * scalar,
      m[2] * scalar);
  }

  template<typename T, qualifier Q>
  inline mat<3, 4, T, Q> operator*(T scalar, mat<3, 4, T, Q> const& m)
  {
    return mat<3, 4, T, Q>(
      m[0] * scalar,
      m[1] * scalar,
      m[2] * scalar);
  }

  template<typename T, qualifier Q>
  inline typename mat<3, 4, T, Q>::col_type operator*
    (
      mat<3, 4, T, Q> const& m,
      typename mat<3, 4, T, Q>::row_type const& v
      )
  {
    return typename mat<3, 4, T, Q>::col_type(
      m[0][0] * v.x + m[1][0] * v.y + m[2][0] * v.z,
      m[0][1] * v.x + m[1][1] * v.y + m[2][1] * v.z,
      m[0][2] * v.x + m[1][2] * v.y + m[2][2] * v.z,
      m[0][3] * v.x + m[1][3] * v.y + m[2][3] * v.z);
  }

  template<typename T, qualifier Q>
  inline typename mat<3, 4, T, Q>::row_type operator*
    (
      typename mat<3, 4, T, Q>::col_type const& v,
      mat<3, 4, T, Q> const& m
      )
  {
    return typename mat<3, 4, T, Q>::row_type(
      v.x * m[0][0] + v.y * m[0][1] + v.z * m[0][2] + v.w * m[0][3],
      v.x * m[1][0] + v.y * m[1][1] + v.z * m[1][2] + v.w * m[1][3],
      v.x * m[2][0] + v.y * m[2][1] + v.z * m[2][2] + v.w * m[2][3]);
  }

  template<typename T, qualifier Q>
  inline mat<4, 4, T, Q> operator*(mat<3, 4, T, Q> const& m1, mat<4, 3, T, Q> const& m2)
  {
    const T SrcA00 = m1[0][0];
    const T SrcA01 = m1[0][1];
    const T SrcA02 = m1[0][2];
    const T SrcA03 = m1[0][3];
    const T SrcA10 = m1[1][0];
    const T SrcA11 = m1[1][1];
    const T SrcA12 = m1[1][2];
    const T SrcA13 = m1[1][3];
    const T SrcA20 = m1[2][0];
    const T SrcA21 = m1[2][1];
    const T SrcA22 = m1[2][2];
    const T SrcA23 = m1[2][3];

    const T SrcB00 = m2[0][0];
    const T SrcB01 = m2[0][1];
    const T SrcB02 = m2[0][2];
    const T SrcB10 = m2[1][0];
    const T SrcB11 = m2[1][1];
    const T SrcB12 = m2[1][2];
    const T SrcB20 = m2[2][0];
    const T SrcB21 = m2[2][1];
    const T SrcB22 = m2[2][2];
    const T SrcB30 = m2[3][0];
    const T SrcB31 = m2[3][1];
    const T SrcB32 = m2[3][2];

    mat<4, 4, T, Q> Result;
    Result[0][0] = SrcA00 * SrcB00 + SrcA10 * SrcB01 + SrcA20 * SrcB02;
    Result[0][1] = SrcA01 * SrcB00 + SrcA11 * SrcB01 + SrcA21 * SrcB02;
    Result[0][2] = SrcA02 * SrcB00 + SrcA12 * SrcB01 + SrcA22 * SrcB02;
    Result[0][3] = SrcA03 * SrcB00 + SrcA13 * SrcB01 + SrcA23 * SrcB02;
    Result[1][0] = SrcA00 * SrcB10 + SrcA10 * SrcB11 + SrcA20 * SrcB12;
    Result[1][1] = SrcA01 * SrcB10 + SrcA11 * SrcB11 + SrcA21 * SrcB12;
    Result[1][2] = SrcA02 * SrcB10 + SrcA12 * SrcB11 + SrcA22 * SrcB12;
    Result[1][3] = SrcA03 * SrcB10 + SrcA13 * SrcB11 + SrcA23 * SrcB12;
    Result[2][0] = SrcA00 * SrcB20 + SrcA10 * SrcB21 + SrcA20 * SrcB22;
    Result[2][1] = SrcA01 * SrcB20 + SrcA11 * SrcB21 + SrcA21 * SrcB22;
    Result[2][2] = SrcA02 * SrcB20 + SrcA12 * SrcB21 + SrcA22 * SrcB22;
    Result[2][3] = SrcA03 * SrcB20 + SrcA13 * SrcB21 + SrcA23 * SrcB22;
    Result[3][0] = SrcA00 * SrcB30 + SrcA10 * SrcB31 + SrcA20 * SrcB32;
    Result[3][1] = SrcA01 * SrcB30 + SrcA11 * SrcB31 + SrcA21 * SrcB32;
    Result[3][2] = SrcA02 * SrcB30 + SrcA12 * SrcB31 + SrcA22 * SrcB32;
    Result[3][3] = SrcA03 * SrcB30 + SrcA13 * SrcB31 + SrcA23 * SrcB32;
    return Result;
  }

  template<typename T, qualifier Q>
  inline mat<2, 4, T, Q> operator*(mat<3, 4, T, Q> const& m1, mat<2, 3, T, Q> const& m2)
  {
    return mat<2, 4, T, Q>(
      m1[0][0] * m2[0][0] + m1[1][0] * m2[0][1] + m1[2][0] * m2[0][2],
      m1[0][1] * m2[0][0] + m1[1][1] * m2[0][1] + m1[2][1] * m2[0][2],
      m1[0][2] * m2[0][0] + m1[1][2] * m2[0][1] + m1[2][2] * m2[0][2],
      m1[0][3] * m2[0][0] + m1[1][3] * m2[0][1] + m1[2][3] * m2[0][2],
      m1[0][0] * m2[1][0] + m1[1][0] * m2[1][1] + m1[2][0] * m2[1][2],
      m1[0][1] * m2[1][0] + m1[1][1] * m2[1][1] + m1[2][1] * m2[1][2],
      m1[0][2] * m2[1][0] + m1[1][2] * m2[1][1] + m1[2][2] * m2[1][2],
      m1[0][3] * m2[1][0] + m1[1][3] * m2[1][1] + m1[2][3] * m2[1][2]);
  }

  template<typename T, qualifier Q>
  inline mat<3, 4, T, Q> operator*(mat<3, 4, T, Q> const& m1, mat<3, 3, T, Q> const& m2)
  {
    return mat<3, 4, T, Q>(
      m1[0][0] * m2[0][0] + m1[1][0] * m2[0][1] + m1[2][0] * m2[0][2],
      m1[0][1] * m2[0][0] + m1[1][1] * m2[0][1] + m1[2][1] * m2[0][2],
      m1[0][2] * m2[0][0] + m1[1][2] * m2[0][1] + m1[2][2] * m2[0][2],
      m1[0][3] * m2[0][0] + m1[1][3] * m2[0][1] + m1[2][3] * m2[0][2],
      m1[0][0] * m2[1][0] + m1[1][0] * m2[1][1] + m1[2][0] * m2[1][2],
      m1[0][1] * m2[1][0] + m1[1][1] * m2[1][1] + m1[2][1] * m2[1][2],
      m1[0][2] * m2[1][0] + m1[1][2] * m2[1][1] + m1[2][2] * m2[1][2],
      m1[0][3] * m2[1][0] + m1[1][3] * m2[1][1] + m1[2][3] * m2[1][2],
      m1[0][0] * m2[2][0] + m1[1][0] * m2[2][1] + m1[2][0] * m2[2][2],
      m1[0][1] * m2[2][0] + m1[1][1] * m2[2][1] + m1[2][1] * m2[2][2],
      m1[0][2] * m2[2][0] + m1[1][2] * m2[2][1] + m1[2][2] * m2[2][2],
      m1[0][3] * m2[2][0] + m1[1][3] * m2[2][1] + m1[2][3] * m2[2][2]);
  }

  template<typename T, qualifier Q>
  inline mat<3, 4, T, Q> operator/(mat<3, 4, T, Q> const& m, T scalar)
  {
    return mat<3, 4, T, Q>(
      m[0] / scalar,
      m[1] / scalar,
      m[2] / scalar);
  }

  template<typename T, qualifier Q>
  inline mat<3, 4, T, Q> operator/(T scalar, mat<3, 4, T, Q> const& m)
  {
    return mat<3, 4, T, Q>(
      scalar / m[0],
      scalar / m[1],
      scalar / m[2]);
  }

  // -- Boolean operators --

  template<typename T, qualifier Q>
  inline bool operator==(mat<3, 4, T, Q> const& m1, mat<3, 4, T, Q> const& m2)
  {
    return (m1[0] == m2[0]) && (m1[1] == m2[1]) && (m1[2] == m2[2]);
  }

  template<typename T, qualifier Q>
  inline bool operator!=(mat<3, 4, T, Q> const& m1, mat<3, 4, T, Q> const& m2)
  {
    return (m1[0] != m2[0]) || (m1[1] != m2[1]) || (m1[2] != m2[2]);
  }
} //namespace glm




namespace glm
{
  /// @addtogroup core_matrix
  /// @{

  /// 3 columns of 4 components matrix of double-precision floating-point numbers.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  typedef mat<3, 4, double, defaultp>		dmat3x4;

  /// @}
}//namespace glm


/// @ref core
/// @file glm/ext/matrix_double3x4_precision.hpp




namespace glm
{
  /// @addtogroup core_matrix_precision
  /// @{

  /// 3 columns of 4 components matrix of double-precision floating-point numbers using low precision arithmetic in term of ULPs.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef mat<3, 4, double, lowp>		lowp_dmat3x4;

  /// 3 columns of 4 components matrix of double-precision floating-point numbers using medium precision arithmetic in term of ULPs.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef mat<3, 4, double, mediump>	mediump_dmat3x4;

  /// 3 columns of 4 components matrix of double-precision floating-point numbers using medium precision arithmetic in term of ULPs.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef mat<3, 4, double, highp>	highp_dmat3x4;

  /// @}
}//namespace glm


/// @ref core
/// @file glm/ext/matrix_float3x4.hpp




namespace glm
{
  /// @addtogroup core_matrix
  /// @{

  /// 3 columns of 4 components matrix of single-precision floating-point numbers.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  typedef mat<3, 4, float, defaultp>			mat3x4;

  /// @}
}//namespace glm


/// @ref core
/// @file glm/ext/matrix_float3x4_precision.hpp




namespace glm
{
  /// @addtogroup core_matrix_precision
  /// @{

  /// 3 columns of 4 components matrix of single-precision floating-point numbers using low precision arithmetic in term of ULPs.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef mat<3, 4, float, lowp>		lowp_mat3x4;

  /// 3 columns of 4 components matrix of single-precision floating-point numbers using medium precision arithmetic in term of ULPs.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef mat<3, 4, float, mediump>	mediump_mat3x4;

  /// 3 columns of 4 components matrix of single-precision floating-point numbers using high precision arithmetic in term of ULPs.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef mat<3, 4, float, highp>		highp_mat3x4;

  /// @}
}//namespace glm

namespace glm
{
  template<typename T, qualifier Q>
  struct mat<4, 2, T, Q>
  {
    typedef vec<2, T, Q> col_type;
    typedef vec<4, T, Q> row_type;
    typedef mat<4, 2, T, Q> type;
    typedef mat<2, 4, T, Q> transpose_type;
    typedef T value_type;

  private:
    col_type value[4];

  public:
    // -- Accesses --

    typedef length_t length_type;
    static constexpr length_type length() { return 4; }

    col_type& operator[](length_type i);
    constexpr col_type const& operator[](length_type i) const;

    // -- Constructors --

    constexpr mat() = default;
    template<qualifier P>
    constexpr mat(mat<4, 2, T, P> const& m);

    explicit constexpr mat(T scalar);
    constexpr mat(
      T x0, T y0,
      T x1, T y1,
      T x2, T y2,
      T x3, T y3);
    constexpr mat(
      col_type const& v0,
      col_type const& v1,
      col_type const& v2,
      col_type const& v3);

    // -- Conversions --

    template<
      typename X0, typename Y0,
      typename X1, typename Y1,
      typename X2, typename Y2,
      typename X3, typename Y3>
    constexpr mat(
      X0 x0, Y0 y0,
      X1 x1, Y1 y1,
      X2 x2, Y2 y2,
      X3 x3, Y3 y3);

    template<typename V1, typename V2, typename V3, typename V4>
    constexpr mat(
      vec<2, V1, Q> const& v1,
      vec<2, V2, Q> const& v2,
      vec<2, V3, Q> const& v3,
      vec<2, V4, Q> const& v4);

    // -- Matrix conversions --

    template<typename U, qualifier P>
    constexpr mat(mat<4, 2, U, P> const& m);

    constexpr mat(mat<2, 2, T, Q> const& x);
    constexpr mat(mat<3, 3, T, Q> const& x);
    constexpr mat(mat<4, 4, T, Q> const& x);
    constexpr mat(mat<2, 3, T, Q> const& x);
    constexpr mat(mat<3, 2, T, Q> const& x);
    constexpr mat(mat<2, 4, T, Q> const& x);
    constexpr mat(mat<4, 3, T, Q> const& x);
    constexpr mat(mat<3, 4, T, Q> const& x);

    // -- Unary arithmetic operators --

    template<typename U>
    mat<4, 2, T, Q>& operator=(mat<4, 2, U, Q> const& m);
    template<typename U>
    mat<4, 2, T, Q>& operator+=(U s);
    template<typename U>
    mat<4, 2, T, Q>& operator+=(mat<4, 2, U, Q> const& m);
    template<typename U>
    mat<4, 2, T, Q>& operator-=(U s);
    template<typename U>
    mat<4, 2, T, Q>& operator-=(mat<4, 2, U, Q> const& m);
    template<typename U>
    mat<4, 2, T, Q>& operator*=(U s);
    template<typename U>
    mat<4, 2, T, Q>& operator/=(U s);

    // -- Increment and decrement operators --

    mat<4, 2, T, Q>& operator++ ();
    mat<4, 2, T, Q>& operator-- ();
    mat<4, 2, T, Q> operator++(int);
    mat<4, 2, T, Q> operator--(int);
  };

  // -- Unary operators --

  template<typename T, qualifier Q>
  mat<4, 2, T, Q> operator+(mat<4, 2, T, Q> const& m);

  template<typename T, qualifier Q>
  mat<4, 2, T, Q> operator-(mat<4, 2, T, Q> const& m);

  // -- Binary operators --

  template<typename T, qualifier Q>
  mat<4, 2, T, Q> operator+(mat<4, 2, T, Q> const& m, T scalar);

  template<typename T, qualifier Q>
  mat<4, 2, T, Q> operator+(mat<4, 2, T, Q> const& m1, mat<4, 2, T, Q> const& m2);

  template<typename T, qualifier Q>
  mat<4, 2, T, Q> operator-(mat<4, 2, T, Q> const& m, T scalar);

  template<typename T, qualifier Q>
  mat<4, 2, T, Q> operator-(mat<4, 2, T, Q> const& m1, mat<4, 2, T, Q> const& m2);

  template<typename T, qualifier Q>
  mat<4, 2, T, Q> operator*(mat<4, 2, T, Q> const& m, T scalar);

  template<typename T, qualifier Q>
  mat<4, 2, T, Q> operator*(T scalar, mat<4, 2, T, Q> const& m);

  template<typename T, qualifier Q>
  typename mat<4, 2, T, Q>::col_type operator*(mat<4, 2, T, Q> const& m, typename mat<4, 2, T, Q>::row_type const& v);

  template<typename T, qualifier Q>
  typename mat<4, 2, T, Q>::row_type operator*(typename mat<4, 2, T, Q>::col_type const& v, mat<4, 2, T, Q> const& m);

  template<typename T, qualifier Q>
  mat<2, 2, T, Q> operator*(mat<4, 2, T, Q> const& m1, mat<2, 4, T, Q> const& m2);

  template<typename T, qualifier Q>
  mat<3, 2, T, Q> operator*(mat<4, 2, T, Q> const& m1, mat<3, 4, T, Q> const& m2);

  template<typename T, qualifier Q>
  mat<4, 2, T, Q> operator*(mat<4, 2, T, Q> const& m1, mat<4, 4, T, Q> const& m2);

  template<typename T, qualifier Q>
  mat<4, 2, T, Q> operator/(mat<4, 2, T, Q> const& m, T scalar);

  template<typename T, qualifier Q>
  mat<4, 2, T, Q> operator/(T scalar, mat<4, 2, T, Q> const& m);

  // -- Boolean operators --

  template<typename T, qualifier Q>
  bool operator==(mat<4, 2, T, Q> const& m1, mat<4, 2, T, Q> const& m2);

  template<typename T, qualifier Q>
  bool operator!=(mat<4, 2, T, Q> const& m1, mat<4, 2, T, Q> const& m2);
}//namespace glm



namespace glm
{
  // -- Constructors --

















  template<typename T, qualifier Q>
  template<qualifier P>
  inline constexpr mat<4, 2, T, Q>::mat(mat<4, 2, T, P> const& m)

    : value{ col_type(m[0]), col_type(m[1]), col_type(m[2]), col_type(m[3]) }

  {






  }

  template<typename T, qualifier Q>
  inline constexpr mat<4, 2, T, Q>::mat(T s)

    : value{ col_type(s, 0), col_type(0, s), col_type(0, 0), col_type(0, 0) }

  {






  }

  template<typename T, qualifier Q>
  inline constexpr mat<4, 2, T, Q>::mat
  (
    T x0, T y0,
    T x1, T y1,
    T x2, T y2,
    T x3, T y3
  )

    : value{ col_type(x0, y0), col_type(x1, y1), col_type(x2, y2), col_type(x3, y3) }

  {






  }

  template<typename T, qualifier Q>
  inline constexpr mat<4, 2, T, Q>::mat(col_type const& v0, col_type const& v1, col_type const& v2, col_type const& v3)

    : value{ col_type(v0), col_type(v1), col_type(v2), col_type(v3) }

  {






  }

  // -- Conversion constructors --

  template<typename T, qualifier Q>
  template<
    typename X0, typename Y0,
    typename X1, typename Y1,
    typename X2, typename Y2,
    typename X3, typename Y3>
  inline constexpr mat<4, 2, T, Q>::mat
  (
    X0 x0, Y0 y0,
    X1 x1, Y1 y1,
    X2 x2, Y2 y2,
    X3 x3, Y3 y3
  )

    : value{ col_type(x0, y0), col_type(x1, y1), col_type(x2, y2), col_type(x3, y3) }

  {






  }

  template<typename T, qualifier Q>
  template<typename V0, typename V1, typename V2, typename V3>
  inline constexpr mat<4, 2, T, Q>::mat(vec<2, V0, Q> const& v0, vec<2, V1, Q> const& v1, vec<2, V2, Q> const& v2, vec<2, V3, Q> const& v3)

    : value{ col_type(v0), col_type(v1), col_type(v2), col_type(v3) }

  {






  }

  // -- Conversion --

  template<typename T, qualifier Q>
  template<typename U, qualifier P>
  inline constexpr mat<4, 2, T, Q>::mat(mat<4, 2, U, P> const& m)

    : value{ col_type(m[0]), col_type(m[1]), col_type(m[2]), col_type(m[3]) }

  {






  }

  template<typename T, qualifier Q>
  inline constexpr mat<4, 2, T, Q>::mat(mat<2, 2, T, Q> const& m)

    : value{ col_type(m[0]), col_type(m[1]), col_type(0), col_type(0) }

  {






  }

  template<typename T, qualifier Q>
  inline constexpr mat<4, 2, T, Q>::mat(mat<3, 3, T, Q> const& m)

    : value{ col_type(m[0]), col_type(m[1]), col_type(m[2]), col_type(0) }

  {






  }

  template<typename T, qualifier Q>
  inline constexpr mat<4, 2, T, Q>::mat(mat<4, 4, T, Q> const& m)

    : value{ col_type(m[0]), col_type(m[1]), col_type(m[2]), col_type(m[3]) }

  {






  }

  template<typename T, qualifier Q>
  inline constexpr mat<4, 2, T, Q>::mat(mat<2, 3, T, Q> const& m)

    : value{ col_type(m[0]), col_type(m[1]), col_type(0), col_type(0) }

  {






  }

  template<typename T, qualifier Q>
  inline constexpr mat<4, 2, T, Q>::mat(mat<3, 2, T, Q> const& m)

    : value{ col_type(m[0]), col_type(m[1]), col_type(m[2]), col_type(0) }

  {






  }

  template<typename T, qualifier Q>
  inline constexpr mat<4, 2, T, Q>::mat(mat<2, 4, T, Q> const& m)

    : value{ col_type(m[0]), col_type(m[1]), col_type(0), col_type(0) }

  {






  }

  template<typename T, qualifier Q>
  inline constexpr mat<4, 2, T, Q>::mat(mat<4, 3, T, Q> const& m)

    : value{ col_type(m[0]), col_type(m[1]), col_type(m[2]), col_type(m[3]) }

  {






  }

  template<typename T, qualifier Q>
  inline constexpr mat<4, 2, T, Q>::mat(mat<3, 4, T, Q> const& m)

    : value{ col_type(m[0]), col_type(m[1]), col_type(m[2]), col_type(0) }

  {






  }

  // -- Accesses --

  template<typename T, qualifier Q>
  inline typename mat<4, 2, T, Q>::col_type& mat<4, 2, T, Q>::operator[](typename mat<4, 2, T, Q>::length_type i)
  {
    assert(i < this->length());
    return this->value[i];
  }

  template<typename T, qualifier Q>
  inline constexpr typename mat<4, 2, T, Q>::col_type const& mat<4, 2, T, Q>::operator[](typename mat<4, 2, T, Q>::length_type i) const
  {
    assert(i < this->length());
    return this->value[i];
  }

  // -- Unary updatable operators --

  template<typename T, qualifier Q>
  template<typename U>
  inline mat<4, 2, T, Q>& mat<4, 2, T, Q>::operator=(mat<4, 2, U, Q> const& m)
  {
    this->value[0] = m[0];
    this->value[1] = m[1];
    this->value[2] = m[2];
    this->value[3] = m[3];
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline mat<4, 2, T, Q>& mat<4, 2, T, Q>::operator+=(U s)
  {
    this->value[0] += s;
    this->value[1] += s;
    this->value[2] += s;
    this->value[3] += s;
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline mat<4, 2, T, Q>& mat<4, 2, T, Q>::operator+=(mat<4, 2, U, Q> const& m)
  {
    this->value[0] += m[0];
    this->value[1] += m[1];
    this->value[2] += m[2];
    this->value[3] += m[3];
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline mat<4, 2, T, Q>& mat<4, 2, T, Q>::operator-=(U s)
  {
    this->value[0] -= s;
    this->value[1] -= s;
    this->value[2] -= s;
    this->value[3] -= s;
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline mat<4, 2, T, Q>& mat<4, 2, T, Q>::operator-=(mat<4, 2, U, Q> const& m)
  {
    this->value[0] -= m[0];
    this->value[1] -= m[1];
    this->value[2] -= m[2];
    this->value[3] -= m[3];
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline mat<4, 2, T, Q>& mat<4, 2, T, Q>::operator*=(U s)
  {
    this->value[0] *= s;
    this->value[1] *= s;
    this->value[2] *= s;
    this->value[3] *= s;
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline mat<4, 2, T, Q>& mat<4, 2, T, Q>::operator/=(U s)
  {
    this->value[0] /= s;
    this->value[1] /= s;
    this->value[2] /= s;
    this->value[3] /= s;
    return *this;
  }

  // -- Increment and decrement operators --

  template<typename T, qualifier Q>
  inline mat<4, 2, T, Q>& mat<4, 2, T, Q>::operator++()
  {
    ++this->value[0];
    ++this->value[1];
    ++this->value[2];
    ++this->value[3];
    return *this;
  }

  template<typename T, qualifier Q>
  inline mat<4, 2, T, Q>& mat<4, 2, T, Q>::operator--()
  {
    --this->value[0];
    --this->value[1];
    --this->value[2];
    --this->value[3];
    return *this;
  }

  template<typename T, qualifier Q>
  inline mat<4, 2, T, Q> mat<4, 2, T, Q>::operator++(int)
  {
    mat<4, 2, T, Q> Result(*this);
    ++*this;
    return Result;
  }

  template<typename T, qualifier Q>
  inline mat<4, 2, T, Q> mat<4, 2, T, Q>::operator--(int)
  {
    mat<4, 2, T, Q> Result(*this);
    --*this;
    return Result;
  }

  // -- Unary arithmetic operators --

  template<typename T, qualifier Q>
  inline mat<4, 2, T, Q> operator+(mat<4, 2, T, Q> const& m)
  {
    return m;
  }

  template<typename T, qualifier Q>
  inline mat<4, 2, T, Q> operator-(mat<4, 2, T, Q> const& m)
  {
    return mat<4, 2, T, Q>(
      -m[0],
      -m[1],
      -m[2],
      -m[3]);
  }

  // -- Binary arithmetic operators --

  template<typename T, qualifier Q>
  inline mat<4, 2, T, Q> operator+(mat<4, 2, T, Q> const& m, T scalar)
  {
    return mat<4, 2, T, Q>(
      m[0] + scalar,
      m[1] + scalar,
      m[2] + scalar,
      m[3] + scalar);
  }

  template<typename T, qualifier Q>
  inline mat<4, 2, T, Q> operator+(mat<4, 2, T, Q> const& m1, mat<4, 2, T, Q> const& m2)
  {
    return mat<4, 2, T, Q>(
      m1[0] + m2[0],
      m1[1] + m2[1],
      m1[2] + m2[2],
      m1[3] + m2[3]);
  }

  template<typename T, qualifier Q>
  inline mat<4, 2, T, Q> operator-(mat<4, 2, T, Q> const& m, T scalar)
  {
    return mat<4, 2, T, Q>(
      m[0] - scalar,
      m[1] - scalar,
      m[2] - scalar,
      m[3] - scalar);
  }

  template<typename T, qualifier Q>
  inline mat<4, 2, T, Q> operator-(mat<4, 2, T, Q> const& m1, mat<4, 2, T, Q> const& m2)
  {
    return mat<4, 2, T, Q>(
      m1[0] - m2[0],
      m1[1] - m2[1],
      m1[2] - m2[2],
      m1[3] - m2[3]);
  }

  template<typename T, qualifier Q>
  inline mat<4, 2, T, Q> operator*(mat<4, 2, T, Q> const& m, T scalar)
  {
    return mat<4, 2, T, Q>(
      m[0] * scalar,
      m[1] * scalar,
      m[2] * scalar,
      m[3] * scalar);
  }

  template<typename T, qualifier Q>
  inline mat<4, 2, T, Q> operator*(T scalar, mat<4, 2, T, Q> const& m)
  {
    return mat<4, 2, T, Q>(
      m[0] * scalar,
      m[1] * scalar,
      m[2] * scalar,
      m[3] * scalar);
  }

  template<typename T, qualifier Q>
  inline typename mat<4, 2, T, Q>::col_type operator*(mat<4, 2, T, Q> const& m, typename mat<4, 2, T, Q>::row_type const& v)
  {
    return typename mat<4, 2, T, Q>::col_type(
      m[0][0] * v.x + m[1][0] * v.y + m[2][0] * v.z + m[3][0] * v.w,
      m[0][1] * v.x + m[1][1] * v.y + m[2][1] * v.z + m[3][1] * v.w);
  }

  template<typename T, qualifier Q>
  inline typename mat<4, 2, T, Q>::row_type operator*(typename mat<4, 2, T, Q>::col_type const& v, mat<4, 2, T, Q> const& m)
  {
    return typename mat<4, 2, T, Q>::row_type(
      v.x * m[0][0] + v.y * m[0][1],
      v.x * m[1][0] + v.y * m[1][1],
      v.x * m[2][0] + v.y * m[2][1],
      v.x * m[3][0] + v.y * m[3][1]);
  }

  template<typename T, qualifier Q>
  inline mat<2, 2, T, Q> operator*(mat<4, 2, T, Q> const& m1, mat<2, 4, T, Q> const& m2)
  {
    T const SrcA00 = m1[0][0];
    T const SrcA01 = m1[0][1];
    T const SrcA10 = m1[1][0];
    T const SrcA11 = m1[1][1];
    T const SrcA20 = m1[2][0];
    T const SrcA21 = m1[2][1];
    T const SrcA30 = m1[3][0];
    T const SrcA31 = m1[3][1];

    T const SrcB00 = m2[0][0];
    T const SrcB01 = m2[0][1];
    T const SrcB02 = m2[0][2];
    T const SrcB03 = m2[0][3];
    T const SrcB10 = m2[1][0];
    T const SrcB11 = m2[1][1];
    T const SrcB12 = m2[1][2];
    T const SrcB13 = m2[1][3];

    mat<2, 2, T, Q> Result;
    Result[0][0] = SrcA00 * SrcB00 + SrcA10 * SrcB01 + SrcA20 * SrcB02 + SrcA30 * SrcB03;
    Result[0][1] = SrcA01 * SrcB00 + SrcA11 * SrcB01 + SrcA21 * SrcB02 + SrcA31 * SrcB03;
    Result[1][0] = SrcA00 * SrcB10 + SrcA10 * SrcB11 + SrcA20 * SrcB12 + SrcA30 * SrcB13;
    Result[1][1] = SrcA01 * SrcB10 + SrcA11 * SrcB11 + SrcA21 * SrcB12 + SrcA31 * SrcB13;
    return Result;
  }

  template<typename T, qualifier Q>
  inline mat<3, 2, T, Q> operator*(mat<4, 2, T, Q> const& m1, mat<3, 4, T, Q> const& m2)
  {
    return mat<3, 2, T, Q>(
      m1[0][0] * m2[0][0] + m1[1][0] * m2[0][1] + m1[2][0] * m2[0][2] + m1[3][0] * m2[0][3],
      m1[0][1] * m2[0][0] + m1[1][1] * m2[0][1] + m1[2][1] * m2[0][2] + m1[3][1] * m2[0][3],
      m1[0][0] * m2[1][0] + m1[1][0] * m2[1][1] + m1[2][0] * m2[1][2] + m1[3][0] * m2[1][3],
      m1[0][1] * m2[1][0] + m1[1][1] * m2[1][1] + m1[2][1] * m2[1][2] + m1[3][1] * m2[1][3],
      m1[0][0] * m2[2][0] + m1[1][0] * m2[2][1] + m1[2][0] * m2[2][2] + m1[3][0] * m2[2][3],
      m1[0][1] * m2[2][0] + m1[1][1] * m2[2][1] + m1[2][1] * m2[2][2] + m1[3][1] * m2[2][3]);
  }

  template<typename T, qualifier Q>
  inline mat<4, 2, T, Q> operator*(mat<4, 2, T, Q> const& m1, mat<4, 4, T, Q> const& m2)
  {
    return mat<4, 2, T, Q>(
      m1[0][0] * m2[0][0] + m1[1][0] * m2[0][1] + m1[2][0] * m2[0][2] + m1[3][0] * m2[0][3],
      m1[0][1] * m2[0][0] + m1[1][1] * m2[0][1] + m1[2][1] * m2[0][2] + m1[3][1] * m2[0][3],
      m1[0][0] * m2[1][0] + m1[1][0] * m2[1][1] + m1[2][0] * m2[1][2] + m1[3][0] * m2[1][3],
      m1[0][1] * m2[1][0] + m1[1][1] * m2[1][1] + m1[2][1] * m2[1][2] + m1[3][1] * m2[1][3],
      m1[0][0] * m2[2][0] + m1[1][0] * m2[2][1] + m1[2][0] * m2[2][2] + m1[3][0] * m2[2][3],
      m1[0][1] * m2[2][0] + m1[1][1] * m2[2][1] + m1[2][1] * m2[2][2] + m1[3][1] * m2[2][3],
      m1[0][0] * m2[3][0] + m1[1][0] * m2[3][1] + m1[2][0] * m2[3][2] + m1[3][0] * m2[3][3],
      m1[0][1] * m2[3][0] + m1[1][1] * m2[3][1] + m1[2][1] * m2[3][2] + m1[3][1] * m2[3][3]);
  }

  template<typename T, qualifier Q>
  inline mat<4, 2, T, Q> operator/(mat<4, 2, T, Q> const& m, T scalar)
  {
    return mat<4, 2, T, Q>(
      m[0] / scalar,
      m[1] / scalar,
      m[2] / scalar,
      m[3] / scalar);
  }

  template<typename T, qualifier Q>
  inline mat<4, 2, T, Q> operator/(T scalar, mat<4, 2, T, Q> const& m)
  {
    return mat<4, 2, T, Q>(
      scalar / m[0],
      scalar / m[1],
      scalar / m[2],
      scalar / m[3]);
  }

  // -- Boolean operators --

  template<typename T, qualifier Q>
  inline bool operator==(mat<4, 2, T, Q> const& m1, mat<4, 2, T, Q> const& m2)
  {
    return (m1[0] == m2[0]) && (m1[1] == m2[1]) && (m1[2] == m2[2]) && (m1[3] == m2[3]);
  }

  template<typename T, qualifier Q>
  inline bool operator!=(mat<4, 2, T, Q> const& m1, mat<4, 2, T, Q> const& m2)
  {
    return (m1[0] != m2[0]) || (m1[1] != m2[1]) || (m1[2] != m2[2]) || (m1[3] != m2[3]);
  }
} //namespace glm




namespace glm
{
  /// @addtogroup core_matrix
  /// @{

  /// 4 columns of 2 components matrix of double-precision floating-point numbers.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  typedef mat<4, 2, double, defaultp>		dmat4x2;

  /// @}
}//namespace glm


/// @ref core
/// @file glm/ext/matrix_double4x2_precision.hpp




namespace glm
{
  /// @addtogroup core_matrix_precision
  /// @{

  /// 4 columns of 2 components matrix of double-precision floating-point numbers using low precision arithmetic in term of ULPs.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef mat<4, 2, double, lowp>		lowp_dmat4x2;

  /// 4 columns of 2 components matrix of double-precision floating-point numbers using medium precision arithmetic in term of ULPs.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef mat<4, 2, double, mediump>	mediump_dmat4x2;

  /// 4 columns of 2 components matrix of double-precision floating-point numbers using medium precision arithmetic in term of ULPs.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef mat<4, 2, double, highp>	highp_dmat4x2;

  /// @}
}//namespace glm


/// @ref core
/// @file glm/ext/matrix_float4x2.hpp




namespace glm
{
  /// @addtogroup core_matrix
  /// @{

  /// 4 columns of 2 components matrix of single-precision floating-point numbers.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  typedef mat<4, 2, float, defaultp>			mat4x2;

  /// @}
}//namespace glm


/// @ref core
/// @file glm/ext/matrix_float2x2_precision.hpp




namespace glm
{
  /// @addtogroup core_matrix_precision
  /// @{

  /// 4 columns of 2 components matrix of single-precision floating-point numbers using low precision arithmetic in term of ULPs.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef mat<4, 2, float, lowp>		lowp_mat4x2;

  /// 4 columns of 2 components matrix of single-precision floating-point numbers using medium precision arithmetic in term of ULPs.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef mat<4, 2, float, mediump>	mediump_mat4x2;

  /// 4 columns of 2 components matrix of single-precision floating-point numbers using high precision arithmetic in term of ULPs.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef mat<4, 2, float, highp>		highp_mat4x2;

  /// @}
}//namespace glm

namespace glm
{
  template<typename T, qualifier Q>
  struct mat<4, 3, T, Q>
  {
    typedef vec<3, T, Q> col_type;
    typedef vec<4, T, Q> row_type;
    typedef mat<4, 3, T, Q> type;
    typedef mat<3, 4, T, Q> transpose_type;
    typedef T value_type;

  private:
    col_type value[4];

  public:
    // -- Accesses --

    typedef length_t length_type;
    static constexpr length_type length() { return 4; }

    col_type& operator[](length_type i);
    constexpr col_type const& operator[](length_type i) const;

    // -- Constructors --

    constexpr mat() = default;
    template<qualifier P>
    constexpr mat(mat<4, 3, T, P> const& m);

    explicit constexpr mat(T const& x);
    constexpr mat(
      T const& x0, T const& y0, T const& z0,
      T const& x1, T const& y1, T const& z1,
      T const& x2, T const& y2, T const& z2,
      T const& x3, T const& y3, T const& z3);
    constexpr mat(
      col_type const& v0,
      col_type const& v1,
      col_type const& v2,
      col_type const& v3);

    // -- Conversions --

    template<
      typename X1, typename Y1, typename Z1,
      typename X2, typename Y2, typename Z2,
      typename X3, typename Y3, typename Z3,
      typename X4, typename Y4, typename Z4>
    constexpr mat(
      X1 const& x1, Y1 const& y1, Z1 const& z1,
      X2 const& x2, Y2 const& y2, Z2 const& z2,
      X3 const& x3, Y3 const& y3, Z3 const& z3,
      X4 const& x4, Y4 const& y4, Z4 const& z4);

    template<typename V1, typename V2, typename V3, typename V4>
    constexpr mat(
      vec<3, V1, Q> const& v1,
      vec<3, V2, Q> const& v2,
      vec<3, V3, Q> const& v3,
      vec<3, V4, Q> const& v4);

    // -- Matrix conversions --

    template<typename U, qualifier P>
    constexpr mat(mat<4, 3, U, P> const& m);

    constexpr mat(mat<2, 2, T, Q> const& x);
    constexpr mat(mat<3, 3, T, Q> const& x);
    constexpr mat(mat<4, 4, T, Q> const& x);
    constexpr mat(mat<2, 3, T, Q> const& x);
    constexpr mat(mat<3, 2, T, Q> const& x);
    constexpr mat(mat<2, 4, T, Q> const& x);
    constexpr mat(mat<4, 2, T, Q> const& x);
    constexpr mat(mat<3, 4, T, Q> const& x);

    // -- Unary arithmetic operators --

    template<typename U>
    mat<4, 3, T, Q>& operator=(mat<4, 3, U, Q> const& m);
    template<typename U>
    mat<4, 3, T, Q>& operator+=(U s);
    template<typename U>
    mat<4, 3, T, Q>& operator+=(mat<4, 3, U, Q> const& m);
    template<typename U>
    mat<4, 3, T, Q>& operator-=(U s);
    template<typename U>
    mat<4, 3, T, Q>& operator-=(mat<4, 3, U, Q> const& m);
    template<typename U>
    mat<4, 3, T, Q>& operator*=(U s);
    template<typename U>
    mat<4, 3, T, Q>& operator/=(U s);

    // -- Increment and decrement operators --

    mat<4, 3, T, Q>& operator++();
    mat<4, 3, T, Q>& operator--();
    mat<4, 3, T, Q> operator++(int);
    mat<4, 3, T, Q> operator--(int);
  };

  // -- Unary operators --

  template<typename T, qualifier Q>
  mat<4, 3, T, Q> operator+(mat<4, 3, T, Q> const& m);

  template<typename T, qualifier Q>
  mat<4, 3, T, Q> operator-(mat<4, 3, T, Q> const& m);

  // -- Binary operators --

  template<typename T, qualifier Q>
  mat<4, 3, T, Q> operator+(mat<4, 3, T, Q> const& m, T const& s);

  template<typename T, qualifier Q>
  mat<4, 3, T, Q> operator+(mat<4, 3, T, Q> const& m1, mat<4, 3, T, Q> const& m2);

  template<typename T, qualifier Q>
  mat<4, 3, T, Q> operator-(mat<4, 3, T, Q> const& m, T const& s);

  template<typename T, qualifier Q>
  mat<4, 3, T, Q> operator-(mat<4, 3, T, Q> const& m1, mat<4, 3, T, Q> const& m2);

  template<typename T, qualifier Q>
  mat<4, 3, T, Q> operator*(mat<4, 3, T, Q> const& m, T const& s);

  template<typename T, qualifier Q>
  mat<4, 3, T, Q> operator*(T const& s, mat<4, 3, T, Q> const& m);

  template<typename T, qualifier Q>
  typename mat<4, 3, T, Q>::col_type operator*(mat<4, 3, T, Q> const& m, typename mat<4, 3, T, Q>::row_type const& v);

  template<typename T, qualifier Q>
  typename mat<4, 3, T, Q>::row_type operator*(typename mat<4, 3, T, Q>::col_type const& v, mat<4, 3, T, Q> const& m);

  template<typename T, qualifier Q>
  mat<2, 3, T, Q> operator*(mat<4, 3, T, Q> const& m1, mat<2, 4, T, Q> const& m2);

  template<typename T, qualifier Q>
  mat<3, 3, T, Q> operator*(mat<4, 3, T, Q> const& m1, mat<3, 4, T, Q> const& m2);

  template<typename T, qualifier Q>
  mat<4, 3, T, Q> operator*(mat<4, 3, T, Q> const& m1, mat<4, 4, T, Q> const& m2);

  template<typename T, qualifier Q>
  mat<4, 3, T, Q> operator/(mat<4, 3, T, Q> const& m, T const& s);

  template<typename T, qualifier Q>
  mat<4, 3, T, Q> operator/(T const& s, mat<4, 3, T, Q> const& m);

  // -- Boolean operators --

  template<typename T, qualifier Q>
  bool operator==(mat<4, 3, T, Q> const& m1, mat<4, 3, T, Q> const& m2);

  template<typename T, qualifier Q>
  bool operator!=(mat<4, 3, T, Q> const& m1, mat<4, 3, T, Q> const& m2);
}//namespace glm



namespace glm
{
  // -- Constructors --

















  template<typename T, qualifier Q>
  template<qualifier P>
  inline constexpr mat<4, 3, T, Q>::mat(mat<4, 3, T, P> const& m)

    : value{ col_type(m[0]), col_type(m[1]), col_type(m[2]), col_type(m[3]) }

  {






  }

  template<typename T, qualifier Q>
  inline constexpr mat<4, 3, T, Q>::mat(T const& s)

    : value{ col_type(s, 0, 0), col_type(0, s, 0), col_type(0, 0, s), col_type(0, 0, 0) }

  {






  }

  template<typename T, qualifier Q>
  inline constexpr mat<4, 3, T, Q>::mat
  (
    T const& x0, T const& y0, T const& z0,
    T const& x1, T const& y1, T const& z1,
    T const& x2, T const& y2, T const& z2,
    T const& x3, T const& y3, T const& z3
  )

    : value{ col_type(x0, y0, z0), col_type(x1, y1, z1), col_type(x2, y2, z2), col_type(x3, y3, z3) }

  {






  }

  template<typename T, qualifier Q>
  inline constexpr mat<4, 3, T, Q>::mat(col_type const& v0, col_type const& v1, col_type const& v2, col_type const& v3)

    : value{ col_type(v0), col_type(v1), col_type(v2), col_type(v3) }

  {






  }

  // -- Conversion constructors --

  template<typename T, qualifier Q>
  template<
    typename X0, typename Y0, typename Z0,
    typename X1, typename Y1, typename Z1,
    typename X2, typename Y2, typename Z2,
    typename X3, typename Y3, typename Z3>
  inline constexpr mat<4, 3, T, Q>::mat
  (
    X0 const& x0, Y0 const& y0, Z0 const& z0,
    X1 const& x1, Y1 const& y1, Z1 const& z1,
    X2 const& x2, Y2 const& y2, Z2 const& z2,
    X3 const& x3, Y3 const& y3, Z3 const& z3
  )

    : value{ col_type(x0, y0, z0), col_type(x1, y1, z1), col_type(x2, y2, z2), col_type(x3, y3, z3) }

  {






  }

  template<typename T, qualifier Q>
  template<typename V1, typename V2, typename V3, typename V4>
  inline constexpr mat<4, 3, T, Q>::mat(vec<3, V1, Q> const& v1, vec<3, V2, Q> const& v2, vec<3, V3, Q> const& v3, vec<3, V4, Q> const& v4)

    : value{ col_type(v1), col_type(v2), col_type(v3), col_type(v4) }

  {






  }

  // -- Matrix conversions --

  template<typename T, qualifier Q>
  template<typename U, qualifier P>
  inline constexpr mat<4, 3, T, Q>::mat(mat<4, 3, U, P> const& m)

    : value{ col_type(m[0]), col_type(m[1]), col_type(m[2]), col_type(m[3]) }

  {






  }

  template<typename T, qualifier Q>
  inline constexpr mat<4, 3, T, Q>::mat(mat<2, 2, T, Q> const& m)

    : value{ col_type(m[0], 0), col_type(m[1], 0), col_type(0, 0, 1), col_type(0) }

  {






  }

  template<typename T, qualifier Q>
  inline constexpr mat<4, 3, T, Q>::mat(mat<3, 3, T, Q> const& m)

    : value{ col_type(m[0]), col_type(m[1]), col_type(m[2]), col_type(0) }

  {






  }

  template<typename T, qualifier Q>
  inline constexpr mat<4, 3, T, Q>::mat(mat<4, 4, T, Q> const& m)

    : value{ col_type(m[0]), col_type(m[1]), col_type(m[2]), col_type(m[3]) }

  {






  }

  template<typename T, qualifier Q>
  inline constexpr mat<4, 3, T, Q>::mat(mat<2, 3, T, Q> const& m)

    : value{ col_type(m[0]), col_type(m[1]), col_type(0, 0, 1), col_type(0) }

  {






  }

  template<typename T, qualifier Q>
  inline constexpr mat<4, 3, T, Q>::mat(mat<3, 2, T, Q> const& m)

    : value{ col_type(m[0], 0), col_type(m[1], 0), col_type(m[2], 1), col_type(0) }

  {






  }

  template<typename T, qualifier Q>
  inline constexpr mat<4, 3, T, Q>::mat(mat<2, 4, T, Q> const& m)

    : value{ col_type(m[0]), col_type(m[1]), col_type(0, 0, 1), col_type(0) }

  {






  }

  template<typename T, qualifier Q>
  inline constexpr mat<4, 3, T, Q>::mat(mat<4, 2, T, Q> const& m)

    : value{ col_type(m[0], 0), col_type(m[1], 0), col_type(m[2], 1), col_type(m[3], 0) }

  {






  }

  template<typename T, qualifier Q>
  inline constexpr mat<4, 3, T, Q>::mat(mat<3, 4, T, Q> const& m)

    : value{ col_type(m[0]), col_type(m[1]), col_type(m[2]), col_type(0) }

  {






  }

  // -- Accesses --

  template<typename T, qualifier Q>
  inline typename mat<4, 3, T, Q>::col_type& mat<4, 3, T, Q>::operator[](typename mat<4, 3, T, Q>::length_type i)
  {
    assert(i < this->length());
    return this->value[i];
  }

  template<typename T, qualifier Q>
  inline constexpr typename mat<4, 3, T, Q>::col_type const& mat<4, 3, T, Q>::operator[](typename mat<4, 3, T, Q>::length_type i) const
  {
    assert(i < this->length());
    return this->value[i];
  }

  // -- Unary updatable operators --

  template<typename T, qualifier Q>
  template<typename U>
  inline mat<4, 3, T, Q>& mat<4, 3, T, Q>::operator=(mat<4, 3, U, Q> const& m)
  {
    this->value[0] = m[0];
    this->value[1] = m[1];
    this->value[2] = m[2];
    this->value[3] = m[3];
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline mat<4, 3, T, Q>& mat<4, 3, T, Q>::operator+=(U s)
  {
    this->value[0] += s;
    this->value[1] += s;
    this->value[2] += s;
    this->value[3] += s;
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline mat<4, 3, T, Q>& mat<4, 3, T, Q>::operator+=(mat<4, 3, U, Q> const& m)
  {
    this->value[0] += m[0];
    this->value[1] += m[1];
    this->value[2] += m[2];
    this->value[3] += m[3];
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline mat<4, 3, T, Q>& mat<4, 3, T, Q>::operator-=(U s)
  {
    this->value[0] -= s;
    this->value[1] -= s;
    this->value[2] -= s;
    this->value[3] -= s;
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline mat<4, 3, T, Q>& mat<4, 3, T, Q>::operator-=(mat<4, 3, U, Q> const& m)
  {
    this->value[0] -= m[0];
    this->value[1] -= m[1];
    this->value[2] -= m[2];
    this->value[3] -= m[3];
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline mat<4, 3, T, Q>& mat<4, 3, T, Q>::operator*=(U s)
  {
    this->value[0] *= s;
    this->value[1] *= s;
    this->value[2] *= s;
    this->value[3] *= s;
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline mat<4, 3, T, Q>& mat<4, 3, T, Q>::operator/=(U s)
  {
    this->value[0] /= s;
    this->value[1] /= s;
    this->value[2] /= s;
    this->value[3] /= s;
    return *this;
  }

  // -- Increment and decrement operators --

  template<typename T, qualifier Q>
  inline mat<4, 3, T, Q>& mat<4, 3, T, Q>::operator++()
  {
    ++this->value[0];
    ++this->value[1];
    ++this->value[2];
    ++this->value[3];
    return *this;
  }

  template<typename T, qualifier Q>
  inline mat<4, 3, T, Q>& mat<4, 3, T, Q>::operator--()
  {
    --this->value[0];
    --this->value[1];
    --this->value[2];
    --this->value[3];
    return *this;
  }

  template<typename T, qualifier Q>
  inline mat<4, 3, T, Q> mat<4, 3, T, Q>::operator++(int)
  {
    mat<4, 3, T, Q> Result(*this);
    ++*this;
    return Result;
  }

  template<typename T, qualifier Q>
  inline mat<4, 3, T, Q> mat<4, 3, T, Q>::operator--(int)
  {
    mat<4, 3, T, Q> Result(*this);
    --*this;
    return Result;
  }

  // -- Unary arithmetic operators --

  template<typename T, qualifier Q>
  inline mat<4, 3, T, Q> operator+(mat<4, 3, T, Q> const& m)
  {
    return m;
  }

  template<typename T, qualifier Q>
  inline mat<4, 3, T, Q> operator-(mat<4, 3, T, Q> const& m)
  {
    return mat<4, 3, T, Q>(
      -m[0],
      -m[1],
      -m[2],
      -m[3]);
  }

  // -- Binary arithmetic operators --

  template<typename T, qualifier Q>
  inline mat<4, 3, T, Q> operator+(mat<4, 3, T, Q> const& m, T const& s)
  {
    return mat<4, 3, T, Q>(
      m[0] + s,
      m[1] + s,
      m[2] + s,
      m[3] + s);
  }

  template<typename T, qualifier Q>
  inline mat<4, 3, T, Q> operator+(mat<4, 3, T, Q> const& m1, mat<4, 3, T, Q> const& m2)
  {
    return mat<4, 3, T, Q>(
      m1[0] + m2[0],
      m1[1] + m2[1],
      m1[2] + m2[2],
      m1[3] + m2[3]);
  }

  template<typename T, qualifier Q>
  inline mat<4, 3, T, Q> operator-(mat<4, 3, T, Q> const& m, T const& s)
  {
    return mat<4, 3, T, Q>(
      m[0] - s,
      m[1] - s,
      m[2] - s,
      m[3] - s);
  }

  template<typename T, qualifier Q>
  inline mat<4, 3, T, Q> operator-(mat<4, 3, T, Q> const& m1, mat<4, 3, T, Q> const& m2)
  {
    return mat<4, 3, T, Q>(
      m1[0] - m2[0],
      m1[1] - m2[1],
      m1[2] - m2[2],
      m1[3] - m2[3]);
  }

  template<typename T, qualifier Q>
  inline mat<4, 3, T, Q> operator*(mat<4, 3, T, Q> const& m, T const& s)
  {
    return mat<4, 3, T, Q>(
      m[0] * s,
      m[1] * s,
      m[2] * s,
      m[3] * s);
  }

  template<typename T, qualifier Q>
  inline mat<4, 3, T, Q> operator*(T const& s, mat<4, 3, T, Q> const& m)
  {
    return mat<4, 3, T, Q>(
      m[0] * s,
      m[1] * s,
      m[2] * s,
      m[3] * s);
  }

  template<typename T, qualifier Q>
  inline typename mat<4, 3, T, Q>::col_type operator*
    (
      mat<4, 3, T, Q> const& m,
      typename mat<4, 3, T, Q>::row_type const& v)
  {
    return typename mat<4, 3, T, Q>::col_type(
      m[0][0] * v.x + m[1][0] * v.y + m[2][0] * v.z + m[3][0] * v.w,
      m[0][1] * v.x + m[1][1] * v.y + m[2][1] * v.z + m[3][1] * v.w,
      m[0][2] * v.x + m[1][2] * v.y + m[2][2] * v.z + m[3][2] * v.w);
  }

  template<typename T, qualifier Q>
  inline typename mat<4, 3, T, Q>::row_type operator*
    (
      typename mat<4, 3, T, Q>::col_type const& v,
      mat<4, 3, T, Q> const& m)
  {
    return typename mat<4, 3, T, Q>::row_type(
      v.x * m[0][0] + v.y * m[0][1] + v.z * m[0][2],
      v.x * m[1][0] + v.y * m[1][1] + v.z * m[1][2],
      v.x * m[2][0] + v.y * m[2][1] + v.z * m[2][2],
      v.x * m[3][0] + v.y * m[3][1] + v.z * m[3][2]);
  }

  template<typename T, qualifier Q>
  inline mat<2, 3, T, Q> operator*(mat<4, 3, T, Q> const& m1, mat<2, 4, T, Q> const& m2)
  {
    return mat<2, 3, T, Q>(
      m1[0][0] * m2[0][0] + m1[1][0] * m2[0][1] + m1[2][0] * m2[0][2] + m1[3][0] * m2[0][3],
      m1[0][1] * m2[0][0] + m1[1][1] * m2[0][1] + m1[2][1] * m2[0][2] + m1[3][1] * m2[0][3],
      m1[0][2] * m2[0][0] + m1[1][2] * m2[0][1] + m1[2][2] * m2[0][2] + m1[3][2] * m2[0][3],
      m1[0][0] * m2[1][0] + m1[1][0] * m2[1][1] + m1[2][0] * m2[1][2] + m1[3][0] * m2[1][3],
      m1[0][1] * m2[1][0] + m1[1][1] * m2[1][1] + m1[2][1] * m2[1][2] + m1[3][1] * m2[1][3],
      m1[0][2] * m2[1][0] + m1[1][2] * m2[1][1] + m1[2][2] * m2[1][2] + m1[3][2] * m2[1][3]);
  }

  template<typename T, qualifier Q>
  inline mat<3, 3, T, Q> operator*(mat<4, 3, T, Q> const& m1, mat<3, 4, T, Q> const& m2)
  {
    T const SrcA00 = m1[0][0];
    T const SrcA01 = m1[0][1];
    T const SrcA02 = m1[0][2];
    T const SrcA10 = m1[1][0];
    T const SrcA11 = m1[1][1];
    T const SrcA12 = m1[1][2];
    T const SrcA20 = m1[2][0];
    T const SrcA21 = m1[2][1];
    T const SrcA22 = m1[2][2];
    T const SrcA30 = m1[3][0];
    T const SrcA31 = m1[3][1];
    T const SrcA32 = m1[3][2];

    T const SrcB00 = m2[0][0];
    T const SrcB01 = m2[0][1];
    T const SrcB02 = m2[0][2];
    T const SrcB03 = m2[0][3];
    T const SrcB10 = m2[1][0];
    T const SrcB11 = m2[1][1];
    T const SrcB12 = m2[1][2];
    T const SrcB13 = m2[1][3];
    T const SrcB20 = m2[2][0];
    T const SrcB21 = m2[2][1];
    T const SrcB22 = m2[2][2];
    T const SrcB23 = m2[2][3];

    mat<3, 3, T, Q> Result;
    Result[0][0] = SrcA00 * SrcB00 + SrcA10 * SrcB01 + SrcA20 * SrcB02 + SrcA30 * SrcB03;
    Result[0][1] = SrcA01 * SrcB00 + SrcA11 * SrcB01 + SrcA21 * SrcB02 + SrcA31 * SrcB03;
    Result[0][2] = SrcA02 * SrcB00 + SrcA12 * SrcB01 + SrcA22 * SrcB02 + SrcA32 * SrcB03;
    Result[1][0] = SrcA00 * SrcB10 + SrcA10 * SrcB11 + SrcA20 * SrcB12 + SrcA30 * SrcB13;
    Result[1][1] = SrcA01 * SrcB10 + SrcA11 * SrcB11 + SrcA21 * SrcB12 + SrcA31 * SrcB13;
    Result[1][2] = SrcA02 * SrcB10 + SrcA12 * SrcB11 + SrcA22 * SrcB12 + SrcA32 * SrcB13;
    Result[2][0] = SrcA00 * SrcB20 + SrcA10 * SrcB21 + SrcA20 * SrcB22 + SrcA30 * SrcB23;
    Result[2][1] = SrcA01 * SrcB20 + SrcA11 * SrcB21 + SrcA21 * SrcB22 + SrcA31 * SrcB23;
    Result[2][2] = SrcA02 * SrcB20 + SrcA12 * SrcB21 + SrcA22 * SrcB22 + SrcA32 * SrcB23;
    return Result;
  }

  template<typename T, qualifier Q>
  inline mat<4, 3, T, Q> operator*(mat<4, 3, T, Q> const& m1, mat<4, 4, T, Q> const& m2)
  {
    return mat<4, 3, T, Q>(
      m1[0][0] * m2[0][0] + m1[1][0] * m2[0][1] + m1[2][0] * m2[0][2] + m1[3][0] * m2[0][3],
      m1[0][1] * m2[0][0] + m1[1][1] * m2[0][1] + m1[2][1] * m2[0][2] + m1[3][1] * m2[0][3],
      m1[0][2] * m2[0][0] + m1[1][2] * m2[0][1] + m1[2][2] * m2[0][2] + m1[3][2] * m2[0][3],
      m1[0][0] * m2[1][0] + m1[1][0] * m2[1][1] + m1[2][0] * m2[1][2] + m1[3][0] * m2[1][3],
      m1[0][1] * m2[1][0] + m1[1][1] * m2[1][1] + m1[2][1] * m2[1][2] + m1[3][1] * m2[1][3],
      m1[0][2] * m2[1][0] + m1[1][2] * m2[1][1] + m1[2][2] * m2[1][2] + m1[3][2] * m2[1][3],
      m1[0][0] * m2[2][0] + m1[1][0] * m2[2][1] + m1[2][0] * m2[2][2] + m1[3][0] * m2[2][3],
      m1[0][1] * m2[2][0] + m1[1][1] * m2[2][1] + m1[2][1] * m2[2][2] + m1[3][1] * m2[2][3],
      m1[0][2] * m2[2][0] + m1[1][2] * m2[2][1] + m1[2][2] * m2[2][2] + m1[3][2] * m2[2][3],
      m1[0][0] * m2[3][0] + m1[1][0] * m2[3][1] + m1[2][0] * m2[3][2] + m1[3][0] * m2[3][3],
      m1[0][1] * m2[3][0] + m1[1][1] * m2[3][1] + m1[2][1] * m2[3][2] + m1[3][1] * m2[3][3],
      m1[0][2] * m2[3][0] + m1[1][2] * m2[3][1] + m1[2][2] * m2[3][2] + m1[3][2] * m2[3][3]);
  }

  template<typename T, qualifier Q>
  inline mat<4, 3, T, Q> operator/(mat<4, 3, T, Q> const& m, T const& s)
  {
    return mat<4, 3, T, Q>(
      m[0] / s,
      m[1] / s,
      m[2] / s,
      m[3] / s);
  }

  template<typename T, qualifier Q>
  inline mat<4, 3, T, Q> operator/(T const& s, mat<4, 3, T, Q> const& m)
  {
    return mat<4, 3, T, Q>(
      s / m[0],
      s / m[1],
      s / m[2],
      s / m[3]);
  }

  // -- Boolean operators --

  template<typename T, qualifier Q>
  inline bool operator==(mat<4, 3, T, Q> const& m1, mat<4, 3, T, Q> const& m2)
  {
    return (m1[0] == m2[0]) && (m1[1] == m2[1]) && (m1[2] == m2[2]) && (m1[3] == m2[3]);
  }

  template<typename T, qualifier Q>
  inline bool operator!=(mat<4, 3, T, Q> const& m1, mat<4, 3, T, Q> const& m2)
  {
    return (m1[0] != m2[0]) || (m1[1] != m2[1]) || (m1[2] != m2[2]) || (m1[3] != m2[3]);
  }
} //namespace glm




namespace glm
{
  /// @addtogroup core_matrix
  /// @{

  /// 4 columns of 3 components matrix of double-precision floating-point numbers.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  typedef mat<4, 3, double, defaultp>		dmat4x3;

  /// @}
}//namespace glm


/// @ref core
/// @file glm/ext/matrix_double4x3_precision.hpp




namespace glm
{
  /// @addtogroup core_matrix_precision
  /// @{

  /// 4 columns of 3 components matrix of double-precision floating-point numbers using low precision arithmetic in term of ULPs.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef mat<4, 3, double, lowp>		lowp_dmat4x3;

  /// 4 columns of 3 components matrix of double-precision floating-point numbers using medium precision arithmetic in term of ULPs.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef mat<4, 3, double, mediump>	mediump_dmat4x3;

  /// 4 columns of 3 components matrix of double-precision floating-point numbers using medium precision arithmetic in term of ULPs.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef mat<4, 3, double, highp>	highp_dmat4x3;

  /// @}
}//namespace glm


/// @ref core
/// @file glm/ext/matrix_float4x3.hpp




namespace glm
{
  /// @addtogroup core_matrix
  /// @{

  /// 4 columns of 3 components matrix of single-precision floating-point numbers.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  typedef mat<4, 3, float, defaultp>			mat4x3;

  /// @}
}//namespace glm


/// @ref core
/// @file glm/ext/matrix_float4x3_precision.hpp




namespace glm
{
  /// @addtogroup core_matrix_precision
  /// @{

  /// 4 columns of 3 components matrix of single-precision floating-point numbers using low precision arithmetic in term of ULPs.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef mat<4, 3, float, lowp>		lowp_mat4x3;

  /// 4 columns of 3 components matrix of single-precision floating-point numbers using medium precision arithmetic in term of ULPs.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef mat<4, 3, float, mediump>	mediump_mat4x3;

  /// 4 columns of 3 components matrix of single-precision floating-point numbers using high precision arithmetic in term of ULPs.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef mat<4, 3, float, highp>		highp_mat4x3;

  /// @}
}//namespace glm




namespace glm {
  namespace detail
  {
    template<length_t C, length_t R, typename T, qualifier Q>
    struct outerProduct_trait {};

    template<typename T, qualifier Q>
    struct outerProduct_trait<2, 2, T, Q>
    {
      typedef mat<2, 2, T, Q> type;
    };

    template<typename T, qualifier Q>
    struct outerProduct_trait<2, 3, T, Q>
    {
      typedef mat<3, 2, T, Q> type;
    };

    template<typename T, qualifier Q>
    struct outerProduct_trait<2, 4, T, Q>
    {
      typedef mat<4, 2, T, Q> type;
    };

    template<typename T, qualifier Q>
    struct outerProduct_trait<3, 2, T, Q>
    {
      typedef mat<2, 3, T, Q> type;
    };

    template<typename T, qualifier Q>
    struct outerProduct_trait<3, 3, T, Q>
    {
      typedef mat<3, 3, T, Q> type;
    };

    template<typename T, qualifier Q>
    struct outerProduct_trait<3, 4, T, Q>
    {
      typedef mat<4, 3, T, Q> type;
    };

    template<typename T, qualifier Q>
    struct outerProduct_trait<4, 2, T, Q>
    {
      typedef mat<2, 4, T, Q> type;
    };

    template<typename T, qualifier Q>
    struct outerProduct_trait<4, 3, T, Q>
    {
      typedef mat<3, 4, T, Q> type;
    };

    template<typename T, qualifier Q>
    struct outerProduct_trait<4, 4, T, Q>
    {
      typedef mat<4, 4, T, Q> type;
    };
  }//namespace detail

     /// @addtogroup core_func_matrix
     /// @{

     /// Multiply matrix x by matrix y component-wise, i.e.,
     /// result[i][j] is the scalar product of x[i][j] and y[i][j].
     ///
     /// @tparam C Integer between 1 and 4 included that qualify the number a column
     /// @tparam R Integer between 1 and 4 included that qualify the number a row
     /// @tparam T Floating-point or signed integer scalar types
     /// @tparam Q Value from qualifier enum
     ///
     /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/matrixCompMult.xml">GLSL matrixCompMult man page</a>
     /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.6 Matrix Functions</a>
  template<length_t C, length_t R, typename T, qualifier Q>
  mat<C, R, T, Q> matrixCompMult(mat<C, R, T, Q> const& x, mat<C, R, T, Q> const& y);

  /// Treats the first parameter c as a column vector
  /// and the second parameter r as a row vector
  /// and does a linear algebraic matrix multiply c * r.
  ///
  /// @tparam C Integer between 1 and 4 included that qualify the number a column
  /// @tparam R Integer between 1 and 4 included that qualify the number a row
  /// @tparam T Floating-point or signed integer scalar types
  /// @tparam Q Value from qualifier enum
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/outerProduct.xml">GLSL outerProduct man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.6 Matrix Functions</a>
  template<length_t C, length_t R, typename T, qualifier Q>
  typename detail::outerProduct_trait<C, R, T, Q>::type outerProduct(vec<C, T, Q> const& c, vec<R, T, Q> const& r);

  /// Returns the transposed matrix of x
  ///
  /// @tparam C Integer between 1 and 4 included that qualify the number a column
  /// @tparam R Integer between 1 and 4 included that qualify the number a row
  /// @tparam T Floating-point or signed integer scalar types
  /// @tparam Q Value from qualifier enum
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/transpose.xml">GLSL transpose man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.6 Matrix Functions</a>
  template<length_t C, length_t R, typename T, qualifier Q>
  typename mat<C, R, T, Q>::transpose_type transpose(mat<C, R, T, Q> const& x);

  /// Return the determinant of a squared matrix.
  ///
  /// @tparam C Integer between 1 and 4 included that qualify the number a column
  /// @tparam R Integer between 1 and 4 included that qualify the number a row
  /// @tparam T Floating-point or signed integer scalar types
  /// @tparam Q Value from qualifier enum
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/determinant.xml">GLSL determinant man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.6 Matrix Functions</a>
  template<length_t C, length_t R, typename T, qualifier Q>
  T determinant(mat<C, R, T, Q> const& m);

  /// Return the inverse of a squared matrix.
  ///
  /// @tparam C Integer between 1 and 4 included that qualify the number a column
  /// @tparam R Integer between 1 and 4 included that qualify the number a row
  /// @tparam T Floating-point or signed integer scalar types
  /// @tparam Q Value from qualifier enum
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/inverse.xml">GLSL inverse man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.6 Matrix Functions</a>
  template<length_t C, length_t R, typename T, qualifier Q>
  mat<C, R, T, Q> inverse(mat<C, R, T, Q> const& m);

  /// @}
}//namespace glm



/// @ref core
/// @file glm/geometric.hpp
///
/// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.5 Geometric Functions</a>
///
/// @defgroup core_func_geometric Geometric functions
/// @ingroup core
///
/// These operate on vectors as vectors, not component-wise.
///
/// Include <glm/geometric.hpp> to use these core features.





namespace glm
{
  /// @addtogroup core_func_geometric
  /// @{

  /// Returns the length of x, i.e., sqrt(x * x).
  ///
  /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector.
  /// @tparam T Floating-point scalar types.
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/length.xml">GLSL length man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.5 Geometric Functions</a>
  template<length_t L, typename T, qualifier Q>
  T length(vec<L, T, Q> const& x);

  /// Returns the distance betwwen p0 and p1, i.e., length(p0 - p1).
  ///
  /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector.
  /// @tparam T Floating-point scalar types.
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/distance.xml">GLSL distance man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.5 Geometric Functions</a>
  template<length_t L, typename T, qualifier Q>
  T distance(vec<L, T, Q> const& p0, vec<L, T, Q> const& p1);

  /// Returns the dot product of x and y, i.e., result = x * y.
  ///
  /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector.
  /// @tparam T Floating-point scalar types.
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/dot.xml">GLSL dot man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.5 Geometric Functions</a>
  template<length_t L, typename T, qualifier Q>
  T dot(vec<L, T, Q> const& x, vec<L, T, Q> const& y);

  /// Returns the cross product of x and y.
  ///
  /// @tparam T Floating-point scalar types.
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/cross.xml">GLSL cross man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.5 Geometric Functions</a>
  template<typename T, qualifier Q>
  vec<3, T, Q> cross(vec<3, T, Q> const& x, vec<3, T, Q> const& y);

  /// Returns a vector in the same direction as x but with length of 1.
  /// According to issue 10 GLSL 1.10 specification, if length(x) == 0 then result is undefined and generate an error.
  ///
  /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector.
  /// @tparam T Floating-point scalar types.
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/normalize.xml">GLSL normalize man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.5 Geometric Functions</a>
  template<length_t L, typename T, qualifier Q>
  vec<L, T, Q> normalize(vec<L, T, Q> const& x);

  /// If dot(Nref, I) < 0.0, return N, otherwise, return -N.
  ///
  /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector.
  /// @tparam T Floating-point scalar types.
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/faceforward.xml">GLSL faceforward man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.5 Geometric Functions</a>
  template<length_t L, typename T, qualifier Q>
  vec<L, T, Q> faceforward(
    vec<L, T, Q> const& N,
    vec<L, T, Q> const& I,
    vec<L, T, Q> const& Nref);

  /// For the incident vector I and surface orientation N,
  /// returns the reflection direction : result = I - 2.0 * dot(N, I) * N.
  ///
  /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector.
  /// @tparam T Floating-point scalar types.
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/reflect.xml">GLSL reflect man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.5 Geometric Functions</a>
  template<length_t L, typename T, qualifier Q>
  vec<L, T, Q> reflect(
    vec<L, T, Q> const& I,
    vec<L, T, Q> const& N);

  /// For the incident vector I and surface normal N,
  /// and the ratio of indices of refraction eta,
  /// return the refraction vector.
  ///
  /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector.
  /// @tparam T Floating-point scalar types.
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/refract.xml">GLSL refract man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.5 Geometric Functions</a>
  template<length_t L, typename T, qualifier Q>
  vec<L, T, Q> refract(
    vec<L, T, Q> const& I,
    vec<L, T, Q> const& N,
    T eta);

  /// @}
}//namespace glm

namespace glm
{
  template<typename T, qualifier Q>
  struct vec<1, T, Q>
  {
    // -- Implementation detail --

    typedef T value_type;
    typedef vec<1, T, Q> type;
    typedef vec<1, bool, Q> bool_type;

    // -- Data --
    union { T x, r, s; };

    /// Return the count of components of the vector
    typedef length_t length_type;
    static constexpr length_type length() { return 1; }

    constexpr T& operator[](length_type i);
    constexpr T const& operator[](length_type i) const;

    // -- Implicit basic constructors --

    constexpr vec() = default;
    constexpr vec(vec const& v) = default;
    template<qualifier P>
    constexpr vec(vec<1, T, P> const& v);

    // -- Explicit basic constructors --

    constexpr explicit vec(T scalar);

    // -- Conversion vector constructors --

    /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification)
    template<typename U, qualifier P>
    constexpr  vec(vec<2, U, P> const& v);
    /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification)
    template<typename U, qualifier P>
    constexpr  vec(vec<3, U, P> const& v);
    /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification)
    template<typename U, qualifier P>
    constexpr  vec(vec<4, U, P> const& v);

    /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification)
    template<typename U, qualifier P>
    constexpr  vec(vec<1, U, P> const& v);

    // -- Swizzle constructors --
/*
#		if GLM_CONFIG_SWIZZLE == GLM_SWIZZLE_OPERATOR
      template<int E0>
      GLM_FUNC_DECL GLM_CONSTEXPR vec(detail::_swizzle<1, T, Q, E0, -1,-2,-3> const& that)
      {
        *this = that();
      }
#		endif//GLM_CONFIG_SWIZZLE == GLM_SWIZZLE_OPERATOR
*/
// -- Unary arithmetic operators --

    constexpr vec<1, T, Q>& operator=(vec const& v) = default;

    template<typename U>
    constexpr vec<1, T, Q>& operator=(vec<1, U, Q> const& v);
    template<typename U>
    constexpr vec<1, T, Q>& operator+=(U scalar);
    template<typename U>
    constexpr vec<1, T, Q>& operator+=(vec<1, U, Q> const& v);
    template<typename U>
    constexpr vec<1, T, Q>& operator-=(U scalar);
    template<typename U>
    constexpr vec<1, T, Q>& operator-=(vec<1, U, Q> const& v);
    template<typename U>
    constexpr vec<1, T, Q>& operator*=(U scalar);
    template<typename U>
    constexpr vec<1, T, Q>& operator*=(vec<1, U, Q> const& v);
    template<typename U>
    constexpr vec<1, T, Q>& operator/=(U scalar);
    template<typename U>
    constexpr vec<1, T, Q>& operator/=(vec<1, U, Q> const& v);

    // -- Increment and decrement operators --

    constexpr vec<1, T, Q>& operator++();
    constexpr vec<1, T, Q>& operator--();
    constexpr vec<1, T, Q> operator++(int);
    constexpr vec<1, T, Q> operator--(int);

    // -- Unary bit operators --

    template<typename U>
    constexpr vec<1, T, Q>& operator%=(U scalar);
    template<typename U>
    constexpr vec<1, T, Q>& operator%=(vec<1, U, Q> const& v);
    template<typename U>
    constexpr vec<1, T, Q>& operator&=(U scalar);
    template<typename U>
    constexpr vec<1, T, Q>& operator&=(vec<1, U, Q> const& v);
    template<typename U>
    constexpr vec<1, T, Q>& operator|=(U scalar);
    template<typename U>
    constexpr vec<1, T, Q>& operator|=(vec<1, U, Q> const& v);
    template<typename U>
    constexpr vec<1, T, Q>& operator^=(U scalar);
    template<typename U>
    constexpr vec<1, T, Q>& operator^=(vec<1, U, Q> const& v);
    template<typename U>
    constexpr vec<1, T, Q>& operator<<=(U scalar);
    template<typename U>
    constexpr vec<1, T, Q>& operator<<=(vec<1, U, Q> const& v);
    template<typename U>
    constexpr vec<1, T, Q>& operator>>=(U scalar);
    template<typename U>
    constexpr vec<1, T, Q>& operator>>=(vec<1, U, Q> const& v);
  };

  // -- Unary operators --

  template<typename T, qualifier Q>
  constexpr vec<1, T, Q> operator+(vec<1, T, Q> const& v);

  template<typename T, qualifier Q>
  constexpr vec<1, T, Q> operator-(vec<1, T, Q> const& v);

  // -- Binary operators --

  template<typename T, qualifier Q>
  constexpr vec<1, T, Q> operator+(vec<1, T, Q> const& v, T scalar);

  template<typename T, qualifier Q>
  constexpr vec<1, T, Q> operator+(T scalar, vec<1, T, Q> const& v);

  template<typename T, qualifier Q>
  constexpr vec<1, T, Q> operator+(vec<1, T, Q> const& v1, vec<1, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<1, T, Q> operator-(vec<1, T, Q> const& v, T scalar);

  template<typename T, qualifier Q>
  constexpr vec<1, T, Q> operator-(T scalar, vec<1, T, Q> const& v);

  template<typename T, qualifier Q>
  constexpr vec<1, T, Q> operator-(vec<1, T, Q> const& v1, vec<1, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<1, T, Q> operator*(vec<1, T, Q> const& v, T scalar);

  template<typename T, qualifier Q>
  constexpr vec<1, T, Q> operator*(T scalar, vec<1, T, Q> const& v);

  template<typename T, qualifier Q>
  constexpr vec<1, T, Q> operator*(vec<1, T, Q> const& v1, vec<1, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<1, T, Q> operator/(vec<1, T, Q> const& v, T scalar);

  template<typename T, qualifier Q>
  constexpr vec<1, T, Q> operator/(T scalar, vec<1, T, Q> const& v);

  template<typename T, qualifier Q>
  constexpr vec<1, T, Q> operator/(vec<1, T, Q> const& v1, vec<1, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<1, T, Q> operator%(vec<1, T, Q> const& v, T scalar);

  template<typename T, qualifier Q>
  constexpr vec<1, T, Q> operator%(T scalar, vec<1, T, Q> const& v);

  template<typename T, qualifier Q>
  constexpr vec<1, T, Q> operator%(vec<1, T, Q> const& v1, vec<1, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<1, T, Q> operator&(vec<1, T, Q> const& v, T scalar);

  template<typename T, qualifier Q>
  constexpr vec<1, T, Q> operator&(T scalar, vec<1, T, Q> const& v);

  template<typename T, qualifier Q>
  constexpr vec<1, T, Q> operator&(vec<1, T, Q> const& v1, vec<1, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<1, T, Q> operator|(vec<1, T, Q> const& v, T scalar);

  template<typename T, qualifier Q>
  constexpr vec<1, T, Q> operator|(T scalar, vec<1, T, Q> const& v);

  template<typename T, qualifier Q>
  constexpr vec<1, T, Q> operator|(vec<1, T, Q> const& v1, vec<1, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<1, T, Q> operator^(vec<1, T, Q> const& v, T scalar);

  template<typename T, qualifier Q>
  constexpr vec<1, T, Q> operator^(T scalar, vec<1, T, Q> const& v);

  template<typename T, qualifier Q>
  constexpr vec<1, T, Q> operator^(vec<1, T, Q> const& v1, vec<1, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<1, T, Q> operator<<(vec<1, T, Q> const& v, T scalar);

  template<typename T, qualifier Q>
  constexpr vec<1, T, Q> operator<<(T scalar, vec<1, T, Q> const& v);

  template<typename T, qualifier Q>
  constexpr vec<1, T, Q> operator<<(vec<1, T, Q> const& v1, vec<1, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<1, T, Q> operator>>(vec<1, T, Q> const& v, T scalar);

  template<typename T, qualifier Q>
  constexpr vec<1, T, Q> operator>>(T scalar, vec<1, T, Q> const& v);

  template<typename T, qualifier Q>
  constexpr vec<1, T, Q> operator>>(vec<1, T, Q> const& v1, vec<1, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr vec<1, T, Q> operator~(vec<1, T, Q> const& v);

  // -- Boolean operators --

  template<typename T, qualifier Q>
  constexpr bool operator==(vec<1, T, Q> const& v1, vec<1, T, Q> const& v2);

  template<typename T, qualifier Q>
  constexpr bool operator!=(vec<1, T, Q> const& v1, vec<1, T, Q> const& v2);

  template<qualifier Q>
  constexpr vec<1, bool, Q> operator&&(vec<1, bool, Q> const& v1, vec<1, bool, Q> const& v2);

  template<qualifier Q>
  constexpr vec<1, bool, Q> operator||(vec<1, bool, Q> const& v1, vec<1, bool, Q> const& v2);
}//namespace glm



/// @ref core



namespace glm
{
  // -- Implicit basic constructors --















  template<typename T, qualifier Q>
  template<qualifier P>
  inline constexpr vec<1, T, Q>::vec(vec<1, T, P> const& v)
    : x(v.x)
  {}

  // -- Explicit basic constructors --

  template<typename T, qualifier Q>
  inline constexpr vec<1, T, Q>::vec(T scalar)
    : x(scalar)
  {}

  // -- Conversion vector constructors --

  template<typename T, qualifier Q>
  template<typename U, qualifier P>
  inline constexpr vec<1, T, Q>::vec(vec<1, U, P> const& v)
    : x(static_cast<T>(v.x))
  {}

  template<typename T, qualifier Q>
  template<typename U, qualifier P>
  inline constexpr vec<1, T, Q>::vec(vec<2, U, P> const& v)
    : x(static_cast<T>(v.x))
  {}

  template<typename T, qualifier Q>
  template<typename U, qualifier P>
  inline constexpr vec<1, T, Q>::vec(vec<3, U, P> const& v)
    : x(static_cast<T>(v.x))
  {}

  template<typename T, qualifier Q>
  template<typename U, qualifier P>
  inline constexpr vec<1, T, Q>::vec(vec<4, U, P> const& v)
    : x(static_cast<T>(v.x))
  {}

  // -- Component accesses --

  template<typename T, qualifier Q>
  inline constexpr T& vec<1, T, Q>::operator[](typename vec<1, T, Q>::length_type)
  {
    return x;
  }

  template<typename T, qualifier Q>
  inline constexpr T const& vec<1, T, Q>::operator[](typename vec<1, T, Q>::length_type) const
  {
    return x;
  }

  // -- Unary arithmetic operators --










  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<1, T, Q>& vec<1, T, Q>::operator=(vec<1, U, Q> const& v)
  {
    this->x = static_cast<T>(v.x);
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<1, T, Q>& vec<1, T, Q>::operator+=(U scalar)
  {
    this->x += static_cast<T>(scalar);
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<1, T, Q>& vec<1, T, Q>::operator+=(vec<1, U, Q> const& v)
  {
    this->x += static_cast<T>(v.x);
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<1, T, Q>& vec<1, T, Q>::operator-=(U scalar)
  {
    this->x -= static_cast<T>(scalar);
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<1, T, Q>& vec<1, T, Q>::operator-=(vec<1, U, Q> const& v)
  {
    this->x -= static_cast<T>(v.x);
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<1, T, Q>& vec<1, T, Q>::operator*=(U scalar)
  {
    this->x *= static_cast<T>(scalar);
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<1, T, Q>& vec<1, T, Q>::operator*=(vec<1, U, Q> const& v)
  {
    this->x *= static_cast<T>(v.x);
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<1, T, Q>& vec<1, T, Q>::operator/=(U scalar)
  {
    this->x /= static_cast<T>(scalar);
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<1, T, Q>& vec<1, T, Q>::operator/=(vec<1, U, Q> const& v)
  {
    this->x /= static_cast<T>(v.x);
    return *this;
  }

  // -- Increment and decrement operators --

  template<typename T, qualifier Q>
  inline constexpr vec<1, T, Q>& vec<1, T, Q>::operator++()
  {
    ++this->x;
    return *this;
  }

  template<typename T, qualifier Q>
  inline constexpr vec<1, T, Q>& vec<1, T, Q>::operator--()
  {
    --this->x;
    return *this;
  }

  template<typename T, qualifier Q>
  inline constexpr vec<1, T, Q> vec<1, T, Q>::operator++(int)
  {
    vec<1, T, Q> Result(*this);
    ++*this;
    return Result;
  }

  template<typename T, qualifier Q>
  inline constexpr vec<1, T, Q> vec<1, T, Q>::operator--(int)
  {
    vec<1, T, Q> Result(*this);
    --*this;
    return Result;
  }

  // -- Unary bit operators --

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<1, T, Q>& vec<1, T, Q>::operator%=(U scalar)
  {
    this->x %= static_cast<T>(scalar);
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<1, T, Q>& vec<1, T, Q>::operator%=(vec<1, U, Q> const& v)
  {
    this->x %= static_cast<T>(v.x);
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<1, T, Q>& vec<1, T, Q>::operator&=(U scalar)
  {
    this->x &= static_cast<T>(scalar);
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<1, T, Q>& vec<1, T, Q>::operator&=(vec<1, U, Q> const& v)
  {
    this->x &= static_cast<T>(v.x);
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<1, T, Q>& vec<1, T, Q>::operator|=(U scalar)
  {
    this->x |= static_cast<T>(scalar);
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<1, T, Q>& vec<1, T, Q>::operator|=(vec<1, U, Q> const& v)
  {
    this->x |= U(v.x);
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<1, T, Q>& vec<1, T, Q>::operator^=(U scalar)
  {
    this->x ^= static_cast<T>(scalar);
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<1, T, Q>& vec<1, T, Q>::operator^=(vec<1, U, Q> const& v)
  {
    this->x ^= static_cast<T>(v.x);
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<1, T, Q>& vec<1, T, Q>::operator<<=(U scalar)
  {
    this->x <<= static_cast<T>(scalar);
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<1, T, Q>& vec<1, T, Q>::operator<<=(vec<1, U, Q> const& v)
  {
    this->x <<= static_cast<T>(v.x);
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<1, T, Q>& vec<1, T, Q>::operator>>=(U scalar)
  {
    this->x >>= static_cast<T>(scalar);
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr vec<1, T, Q>& vec<1, T, Q>::operator>>=(vec<1, U, Q> const& v)
  {
    this->x >>= static_cast<T>(v.x);
    return *this;
  }

  // -- Unary constant operators --

  template<typename T, qualifier Q>
  inline constexpr vec<1, T, Q> operator+(vec<1, T, Q> const& v)
  {
    return v;
  }

  template<typename T, qualifier Q>
  inline constexpr vec<1, T, Q> operator-(vec<1, T, Q> const& v)
  {
    return vec<1, T, Q>(
      -v.x);
  }

  // -- Binary arithmetic operators --

  template<typename T, qualifier Q>
  inline constexpr vec<1, T, Q> operator+(vec<1, T, Q> const& v, T scalar)
  {
    return vec<1, T, Q>(
      v.x + scalar);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<1, T, Q> operator+(T scalar, vec<1, T, Q> const& v)
  {
    return vec<1, T, Q>(
      scalar + v.x);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<1, T, Q> operator+(vec<1, T, Q> const& v1, vec<1, T, Q> const& v2)
  {
    return vec<1, T, Q>(
      v1.x + v2.x);
  }

  //operator-
  template<typename T, qualifier Q>
  inline constexpr vec<1, T, Q> operator-(vec<1, T, Q> const& v, T scalar)
  {
    return vec<1, T, Q>(
      v.x - scalar);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<1, T, Q> operator-(T scalar, vec<1, T, Q> const& v)
  {
    return vec<1, T, Q>(
      scalar - v.x);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<1, T, Q> operator-(vec<1, T, Q> const& v1, vec<1, T, Q> const& v2)
  {
    return vec<1, T, Q>(
      v1.x - v2.x);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<1, T, Q> operator*(vec<1, T, Q> const& v, T scalar)
  {
    return vec<1, T, Q>(
      v.x * scalar);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<1, T, Q> operator*(T scalar, vec<1, T, Q> const& v)
  {
    return vec<1, T, Q>(
      scalar * v.x);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<1, T, Q> operator*(vec<1, T, Q> const& v1, vec<1, T, Q> const& v2)
  {
    return vec<1, T, Q>(
      v1.x * v2.x);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<1, T, Q> operator/(vec<1, T, Q> const& v, T scalar)
  {
    return vec<1, T, Q>(
      v.x / scalar);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<1, T, Q> operator/(T scalar, vec<1, T, Q> const& v)
  {
    return vec<1, T, Q>(
      scalar / v.x);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<1, T, Q> operator/(vec<1, T, Q> const& v1, vec<1, T, Q> const& v2)
  {
    return vec<1, T, Q>(
      v1.x / v2.x);
  }

  // -- Binary bit operators --

  template<typename T, qualifier Q>
  inline constexpr vec<1, T, Q> operator%(vec<1, T, Q> const& v, T scalar)
  {
    return vec<1, T, Q>(
      v.x % scalar);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<1, T, Q> operator%(T scalar, vec<1, T, Q> const& v)
  {
    return vec<1, T, Q>(
      scalar % v.x);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<1, T, Q> operator%(vec<1, T, Q> const& v1, vec<1, T, Q> const& v2)
  {
    return vec<1, T, Q>(
      v1.x % v2.x);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<1, T, Q> operator&(vec<1, T, Q> const& v, T scalar)
  {
    return vec<1, T, Q>(
      v.x & scalar);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<1, T, Q> operator&(T scalar, vec<1, T, Q> const& v)
  {
    return vec<1, T, Q>(
      scalar & v.x);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<1, T, Q> operator&(vec<1, T, Q> const& v1, vec<1, T, Q> const& v2)
  {
    return vec<1, T, Q>(
      v1.x & v2.x);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<1, T, Q> operator|(vec<1, T, Q> const& v, T scalar)
  {
    return vec<1, T, Q>(
      v.x | scalar);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<1, T, Q> operator|(T scalar, vec<1, T, Q> const& v)
  {
    return vec<1, T, Q>(
      scalar | v.x);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<1, T, Q> operator|(vec<1, T, Q> const& v1, vec<1, T, Q> const& v2)
  {
    return vec<1, T, Q>(
      v1.x | v2.x);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<1, T, Q> operator^(vec<1, T, Q> const& v, T scalar)
  {
    return vec<1, T, Q>(
      v.x ^ scalar);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<1, T, Q> operator^(T scalar, vec<1, T, Q> const& v)
  {
    return vec<1, T, Q>(
      scalar ^ v.x);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<1, T, Q> operator^(vec<1, T, Q> const& v1, vec<1, T, Q> const& v2)
  {
    return vec<1, T, Q>(
      v1.x ^ v2.x);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<1, T, Q> operator<<(vec<1, T, Q> const& v, T scalar)
  {
    return vec<1, T, Q>(
      static_cast<T>(v.x << scalar));
  }

  template<typename T, qualifier Q>
  inline constexpr vec<1, T, Q> operator<<(T scalar, vec<1, T, Q> const& v)
  {
    return vec<1, T, Q>(
      static_cast<T>(scalar << v.x));
  }

  template<typename T, qualifier Q>
  inline constexpr vec<1, T, Q> operator<<(vec<1, T, Q> const& v1, vec<1, T, Q> const& v2)
  {
    return vec<1, T, Q>(
      static_cast<T>(v1.x << v2.x));
  }

  template<typename T, qualifier Q>
  inline constexpr vec<1, T, Q> operator>>(vec<1, T, Q> const& v, T scalar)
  {
    return vec<1, T, Q>(
      static_cast<T>(v.x >> scalar));
  }

  template<typename T, qualifier Q>
  inline constexpr vec<1, T, Q> operator>>(T scalar, vec<1, T, Q> const& v)
  {
    return vec<1, T, Q>(
      static_cast<T>(scalar >> v.x));
  }

  template<typename T, qualifier Q>
  inline constexpr vec<1, T, Q> operator>>(vec<1, T, Q> const& v1, vec<1, T, Q> const& v2)
  {
    return vec<1, T, Q>(
      static_cast<T>(v1.x >> v2.x));
  }

  template<typename T, qualifier Q>
  inline constexpr vec<1, T, Q> operator~(vec<1, T, Q> const& v)
  {
    return vec<1, T, Q>(
      ~v.x);
  }

  // -- Boolean operators --

  template<typename T, qualifier Q>
  inline constexpr bool operator==(vec<1, T, Q> const& v1, vec<1, T, Q> const& v2)
  {
    return detail::compute_equal<T, std::numeric_limits<T>::is_iec559>::call(v1.x, v2.x);
  }

  template<typename T, qualifier Q>
  inline constexpr bool operator!=(vec<1, T, Q> const& v1, vec<1, T, Q> const& v2)
  {
    return !(v1 == v2);
  }

  template<qualifier Q>
  inline constexpr vec<1, bool, Q> operator&&(vec<1, bool, Q> const& v1, vec<1, bool, Q> const& v2)
  {
    return vec<1, bool, Q>(v1.x && v2.x);
  }

  template<qualifier Q>
  inline constexpr vec<1, bool, Q> operator||(vec<1, bool, Q> const& v1, vec<1, bool, Q> const& v2)
  {
    return vec<1, bool, Q>(v1.x || v2.x);
  }
}//namespace glm

namespace glm
{
  /// @addtogroup core_func_exponential
  /// @{

  /// Returns 'base' raised to the power 'exponent'.
  ///
  /// @param base Floating point value. pow function is defined for input values of 'base' defined in the range (inf-, inf+) in the limit of the type qualifier.
  /// @param exponent Floating point value representing the 'exponent'.
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/pow.xml">GLSL pow man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.2 Exponential Functions</a>
  template<length_t L, typename T, qualifier Q>
  vec<L, T, Q> pow(vec<L, T, Q> const& base, vec<L, T, Q> const& exponent);

  /// Returns the natural exponentiation of x, i.e., e^x.
  ///
  /// @param v exp function is defined for input values of v defined in the range (inf-, inf+) in the limit of the type qualifier.
  /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector.
  /// @tparam T Floating-point scalar types.
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/exp.xml">GLSL exp man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.2 Exponential Functions</a>
  template<length_t L, typename T, qualifier Q>
  vec<L, T, Q> exp(vec<L, T, Q> const& v);

  /// Returns the natural logarithm of v, i.e.,
  /// returns the value y which satisfies the equation x = e^y.
  /// Results are undefined if v <= 0.
  ///
  /// @param v log function is defined for input values of v defined in the range (0, inf+) in the limit of the type qualifier.
  /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector.
  /// @tparam T Floating-point scalar types.
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/log.xml">GLSL log man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.2 Exponential Functions</a>
  template<length_t L, typename T, qualifier Q>
  vec<L, T, Q> log(vec<L, T, Q> const& v);

  /// Returns 2 raised to the v power.
  ///
  /// @param v exp2 function is defined for input values of v defined in the range (inf-, inf+) in the limit of the type qualifier.
  /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector.
  /// @tparam T Floating-point scalar types.
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/exp2.xml">GLSL exp2 man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.2 Exponential Functions</a>
  template<length_t L, typename T, qualifier Q>
  vec<L, T, Q> exp2(vec<L, T, Q> const& v);

  /// Returns the base 2 log of x, i.e., returns the value y,
  /// which satisfies the equation x = 2 ^ y.
  ///
  /// @param v log2 function is defined for input values of v defined in the range (0, inf+) in the limit of the type qualifier.
  /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector.
  /// @tparam T Floating-point scalar types.
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/log2.xml">GLSL log2 man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.2 Exponential Functions</a>
  template<length_t L, typename T, qualifier Q>
  vec<L, T, Q> log2(vec<L, T, Q> const& v);

  /// Returns the positive square root of v.
  ///
  /// @param v sqrt function is defined for input values of v defined in the range [0, inf+) in the limit of the type qualifier.
  /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector.
  /// @tparam T Floating-point scalar types.
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/sqrt.xml">GLSL sqrt man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.2 Exponential Functions</a>
  template<length_t L, typename T, qualifier Q>
  vec<L, T, Q> sqrt(vec<L, T, Q> const& v);

  /// Returns the reciprocal of the positive square root of v.
  ///
  /// @param v inversesqrt function is defined for input values of v defined in the range [0, inf+) in the limit of the type qualifier.
  /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector.
  /// @tparam T Floating-point scalar types.
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/inversesqrt.xml">GLSL inversesqrt man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.2 Exponential Functions</a>
  template<length_t L, typename T, qualifier Q>
  vec<L, T, Q> inversesqrt(vec<L, T, Q> const& v);

  /// @}
}//namespace glm

namespace glm
{
  /// @addtogroup core_func_vector_relational
  /// @{

  /// Returns the component-wise comparison result of x < y.
  ///
  /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector.
  /// @tparam T A floating-point or integer scalar type.
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/lessThan.xml">GLSL lessThan man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.7 Vector Relational Functions</a>
  template<length_t L, typename T, qualifier Q>
  constexpr vec<L, bool, Q> lessThan(vec<L, T, Q> const& x, vec<L, T, Q> const& y);

  /// Returns the component-wise comparison of result x <= y.
  ///
  /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector.
  /// @tparam T A floating-point or integer scalar type.
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/lessThanEqual.xml">GLSL lessThanEqual man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.7 Vector Relational Functions</a>
  template<length_t L, typename T, qualifier Q>
  constexpr vec<L, bool, Q> lessThanEqual(vec<L, T, Q> const& x, vec<L, T, Q> const& y);

  /// Returns the component-wise comparison of result x > y.
  ///
  /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector.
  /// @tparam T A floating-point or integer scalar type.
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/greaterThan.xml">GLSL greaterThan man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.7 Vector Relational Functions</a>
  template<length_t L, typename T, qualifier Q>
  constexpr vec<L, bool, Q> greaterThan(vec<L, T, Q> const& x, vec<L, T, Q> const& y);

  /// Returns the component-wise comparison of result x >= y.
  ///
  /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector.
  /// @tparam T A floating-point or integer scalar type.
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/greaterThanEqual.xml">GLSL greaterThanEqual man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.7 Vector Relational Functions</a>
  template<length_t L, typename T, qualifier Q>
  constexpr vec<L, bool, Q> greaterThanEqual(vec<L, T, Q> const& x, vec<L, T, Q> const& y);

  /// Returns the component-wise comparison of result x == y.
  ///
  /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector.
  /// @tparam T A floating-point, integer or bool scalar type.
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/equal.xml">GLSL equal man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.7 Vector Relational Functions</a>
  template<length_t L, typename T, qualifier Q>
  constexpr vec<L, bool, Q> equal(vec<L, T, Q> const& x, vec<L, T, Q> const& y);

  /// Returns the component-wise comparison of result x != y.
  ///
  /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector.
  /// @tparam T A floating-point, integer or bool scalar type.
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/notEqual.xml">GLSL notEqual man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.7 Vector Relational Functions</a>
  template<length_t L, typename T, qualifier Q>
  constexpr vec<L, bool, Q> notEqual(vec<L, T, Q> const& x, vec<L, T, Q> const& y);

  /// Returns true if any component of x is true.
  ///
  /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector.
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/any.xml">GLSL any man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.7 Vector Relational Functions</a>
  template<length_t L, qualifier Q>
  constexpr bool any(vec<L, bool, Q> const& v);

  /// Returns true if all components of x are true.
  ///
  /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector.
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/all.xml">GLSL all man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.7 Vector Relational Functions</a>
  template<length_t L, qualifier Q>
  constexpr bool all(vec<L, bool, Q> const& v);

  /// Returns the component-wise logical complement of x.
  /// /!\ Because of language incompatibilities between C++ and GLSL, GLM defines the function not but not_ instead.
  ///
  /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector.
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/not.xml">GLSL not man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.7 Vector Relational Functions</a>
  template<length_t L, qualifier Q>
  constexpr vec<L, bool, Q> not_(vec<L, bool, Q> const& v);

  /// @}
}//namespace glm


namespace glm
{
  template<length_t L, typename T, qualifier Q>
  inline constexpr vec<L, bool, Q> lessThan(vec<L, T, Q> const& x, vec<L, T, Q> const& y)
  {
    vec<L, bool, Q> Result(true);
    for (length_t i = 0; i < L; ++i)
      Result[i] = x[i] < y[i];
    return Result;
  }

  template<length_t L, typename T, qualifier Q>
  inline constexpr vec<L, bool, Q> lessThanEqual(vec<L, T, Q> const& x, vec<L, T, Q> const& y)
  {
    vec<L, bool, Q> Result(true);
    for (length_t i = 0; i < L; ++i)
      Result[i] = x[i] <= y[i];
    return Result;
  }

  template<length_t L, typename T, qualifier Q>
  inline constexpr vec<L, bool, Q> greaterThan(vec<L, T, Q> const& x, vec<L, T, Q> const& y)
  {
    vec<L, bool, Q> Result(true);
    for (length_t i = 0; i < L; ++i)
      Result[i] = x[i] > y[i];
    return Result;
  }

  template<length_t L, typename T, qualifier Q>
  inline constexpr vec<L, bool, Q> greaterThanEqual(vec<L, T, Q> const& x, vec<L, T, Q> const& y)
  {
    vec<L, bool, Q> Result(true);
    for (length_t i = 0; i < L; ++i)
      Result[i] = x[i] >= y[i];
    return Result;
  }

  template<length_t L, typename T, qualifier Q>
  inline constexpr vec<L, bool, Q> equal(vec<L, T, Q> const& x, vec<L, T, Q> const& y)
  {
    vec<L, bool, Q> Result(true);
    for (length_t i = 0; i < L; ++i)
      Result[i] = x[i] == y[i];
    return Result;
  }

  template<length_t L, typename T, qualifier Q>
  inline constexpr vec<L, bool, Q> notEqual(vec<L, T, Q> const& x, vec<L, T, Q> const& y)
  {
    vec<L, bool, Q> Result(true);
    for (length_t i = 0; i < L; ++i)
      Result[i] = x[i] != y[i];
    return Result;
  }

  template<length_t L, qualifier Q>
  inline constexpr bool any(vec<L, bool, Q> const& v)
  {
    bool Result = false;
    for (length_t i = 0; i < L; ++i)
      Result = Result || v[i];
    return Result;
  }

  template<length_t L, qualifier Q>
  inline constexpr bool all(vec<L, bool, Q> const& v)
  {
    bool Result = true;
    for (length_t i = 0; i < L; ++i)
      Result = Result && v[i];
    return Result;
  }

  template<length_t L, qualifier Q>
  inline constexpr vec<L, bool, Q> not_(vec<L, bool, Q> const& v)
  {
    vec<L, bool, Q> Result(true);
    for (length_t i = 0; i < L; ++i)
      Result[i] = !v[i];
    return Result;
  }
}//namespace glm









namespace glm {
  namespace detail
  {
    template<template<length_t L, typename T, qualifier Q> class vec, length_t L, typename R, typename T, qualifier Q>
    struct functor1 {};

    template<template<length_t L, typename T, qualifier Q> class vec, typename R, typename T, qualifier Q>
    struct functor1<vec, 1, R, T, Q>
    {
      inline constexpr static vec<1, R, Q> call(R(*Func) (T x), vec<1, T, Q> const& v)
      {
        return vec<1, R, Q>(Func(v.x));
      }
    };

    template<template<length_t L, typename T, qualifier Q> class vec, typename R, typename T, qualifier Q>
    struct functor1<vec, 2, R, T, Q>
    {
      inline constexpr static vec<2, R, Q> call(R(*Func) (T x), vec<2, T, Q> const& v)
      {
        return vec<2, R, Q>(Func(v.x), Func(v.y));
      }
    };

    template<template<length_t L, typename T, qualifier Q> class vec, typename R, typename T, qualifier Q>
    struct functor1<vec, 3, R, T, Q>
    {
      inline constexpr static vec<3, R, Q> call(R(*Func) (T x), vec<3, T, Q> const& v)
      {
        return vec<3, R, Q>(Func(v.x), Func(v.y), Func(v.z));
      }
    };

    template<template<length_t L, typename T, qualifier Q> class vec, typename R, typename T, qualifier Q>
    struct functor1<vec, 4, R, T, Q>
    {
      inline constexpr static vec<4, R, Q> call(R(*Func) (T x), vec<4, T, Q> const& v)
      {
        return vec<4, R, Q>(Func(v.x), Func(v.y), Func(v.z), Func(v.w));
      }
    };

    template<template<length_t L, typename T, qualifier Q> class vec, length_t L, typename T, qualifier Q>
    struct functor2 {};

    template<template<length_t L, typename T, qualifier Q> class vec, typename T, qualifier Q>
    struct functor2<vec, 1, T, Q>
    {
      inline static vec<1, T, Q> call(T(*Func) (T x, T y), vec<1, T, Q> const& a, vec<1, T, Q> const& b)
      {
        return vec<1, T, Q>(Func(a.x, b.x));
      }
    };

    template<template<length_t L, typename T, qualifier Q> class vec, typename T, qualifier Q>
    struct functor2<vec, 2, T, Q>
    {
      inline static vec<2, T, Q> call(T(*Func) (T x, T y), vec<2, T, Q> const& a, vec<2, T, Q> const& b)
      {
        return vec<2, T, Q>(Func(a.x, b.x), Func(a.y, b.y));
      }
    };

    template<template<length_t L, typename T, qualifier Q> class vec, typename T, qualifier Q>
    struct functor2<vec, 3, T, Q>
    {
      inline static vec<3, T, Q> call(T(*Func) (T x, T y), vec<3, T, Q> const& a, vec<3, T, Q> const& b)
      {
        return vec<3, T, Q>(Func(a.x, b.x), Func(a.y, b.y), Func(a.z, b.z));
      }
    };

    template<template<length_t L, typename T, qualifier Q> class vec, typename T, qualifier Q>
    struct functor2<vec, 4, T, Q>
    {
      inline static vec<4, T, Q> call(T(*Func) (T x, T y), vec<4, T, Q> const& a, vec<4, T, Q> const& b)
      {
        return vec<4, T, Q>(Func(a.x, b.x), Func(a.y, b.y), Func(a.z, b.z), Func(a.w, b.w));
      }
    };

    template<template<length_t L, typename T, qualifier Q> class vec, length_t L, typename T, qualifier Q>
    struct functor2_vec_sca {};

    template<template<length_t L, typename T, qualifier Q> class vec, typename T, qualifier Q>
    struct functor2_vec_sca<vec, 1, T, Q>
    {
      inline static vec<1, T, Q> call(T(*Func) (T x, T y), vec<1, T, Q> const& a, T b)
      {
        return vec<1, T, Q>(Func(a.x, b));
      }
    };

    template<template<length_t L, typename T, qualifier Q> class vec, typename T, qualifier Q>
    struct functor2_vec_sca<vec, 2, T, Q>
    {
      inline static vec<2, T, Q> call(T(*Func) (T x, T y), vec<2, T, Q> const& a, T b)
      {
        return vec<2, T, Q>(Func(a.x, b), Func(a.y, b));
      }
    };

    template<template<length_t L, typename T, qualifier Q> class vec, typename T, qualifier Q>
    struct functor2_vec_sca<vec, 3, T, Q>
    {
      inline static vec<3, T, Q> call(T(*Func) (T x, T y), vec<3, T, Q> const& a, T b)
      {
        return vec<3, T, Q>(Func(a.x, b), Func(a.y, b), Func(a.z, b));
      }
    };

    template<template<length_t L, typename T, qualifier Q> class vec, typename T, qualifier Q>
    struct functor2_vec_sca<vec, 4, T, Q>
    {
      inline static vec<4, T, Q> call(T(*Func) (T x, T y), vec<4, T, Q> const& a, T b)
      {
        return vec<4, T, Q>(Func(a.x, b), Func(a.y, b), Func(a.z, b), Func(a.w, b));
      }
    };

    template<length_t L, typename T, qualifier Q>
    struct functor2_vec_int {};

    template<typename T, qualifier Q>
    struct functor2_vec_int<1, T, Q>
    {
      inline static vec<1, int, Q> call(int (*Func) (T x, int y), vec<1, T, Q> const& a, vec<1, int, Q> const& b)
      {
        return vec<1, int, Q>(Func(a.x, b.x));
      }
    };

    template<typename T, qualifier Q>
    struct functor2_vec_int<2, T, Q>
    {
      inline static vec<2, int, Q> call(int (*Func) (T x, int y), vec<2, T, Q> const& a, vec<2, int, Q> const& b)
      {
        return vec<2, int, Q>(Func(a.x, b.x), Func(a.y, b.y));
      }
    };

    template<typename T, qualifier Q>
    struct functor2_vec_int<3, T, Q>
    {
      inline static vec<3, int, Q> call(int (*Func) (T x, int y), vec<3, T, Q> const& a, vec<3, int, Q> const& b)
      {
        return vec<3, int, Q>(Func(a.x, b.x), Func(a.y, b.y), Func(a.z, b.z));
      }
    };

    template<typename T, qualifier Q>
    struct functor2_vec_int<4, T, Q>
    {
      inline static vec<4, int, Q> call(int (*Func) (T x, int y), vec<4, T, Q> const& a, vec<4, int, Q> const& b)
      {
        return vec<4, int, Q>(Func(a.x, b.x), Func(a.y, b.y), Func(a.z, b.z), Func(a.w, b.w));
      }
    };
  }//namespace detail
}//namespace glm

namespace glm {
  namespace detail
  {

    using std::log2;








    template<length_t L, typename T, qualifier Q, bool isFloat, bool Aligned>
    struct compute_log2
    {
      inline static vec<L, T, Q> call(vec<L, T, Q> const& v)
      {
        static_assert(std::numeric_limits<T>::is_iec559, "'log2' only accept floating-point inputs. Include <glm/gtc/integer.hpp> for integer inputs.");

        return detail::functor1<vec, L, T, T, Q>::call(log2, v);
      }
    };

    template<length_t L, typename T, qualifier Q, bool Aligned>
    struct compute_sqrt
    {
      inline static vec<L, T, Q> call(vec<L, T, Q> const& x)
      {
        return detail::functor1<vec, L, T, T, Q>::call(std::sqrt, x);
      }
    };

    template<length_t L, typename T, qualifier Q, bool Aligned>
    struct compute_inversesqrt
    {
      inline static vec<L, T, Q> call(vec<L, T, Q> const& x)
      {
        return static_cast<T>(1) / sqrt(x);
      }
    };

    template<length_t L, bool Aligned>
    struct compute_inversesqrt<L, float, lowp, Aligned>
    {
      inline static vec<L, float, lowp> call(vec<L, float, lowp> const& x)
      {
        vec<L, float, lowp> tmp(x);
        vec<L, float, lowp> xhalf(tmp * 0.5f);
        vec<L, uint, lowp>* p = reinterpret_cast<vec<L, uint, lowp>*>(const_cast<vec<L, float, lowp>*>(&x));
        vec<L, uint, lowp> i = vec<L, uint, lowp>(0x5f375a86) - (*p >> vec<L, uint, lowp>(1));
        vec<L, float, lowp>* ptmp = reinterpret_cast<vec<L, float, lowp>*>(&i);
        tmp = *ptmp;
        tmp = tmp * (1.5f - xhalf * tmp * tmp);
        return tmp;
      }
    };
  }//namespace detail

    // pow
  using std::pow;
  template<length_t L, typename T, qualifier Q>
  inline vec<L, T, Q> pow(vec<L, T, Q> const& base, vec<L, T, Q> const& exponent)
  {
    return detail::functor2<vec, L, T, Q>::call(pow, base, exponent);
  }

  // exp
  using std::exp;
  template<length_t L, typename T, qualifier Q>
  inline vec<L, T, Q> exp(vec<L, T, Q> const& x)
  {
    return detail::functor1<vec, L, T, T, Q>::call(exp, x);
  }

  // log
  using std::log;
  template<length_t L, typename T, qualifier Q>
  inline vec<L, T, Q> log(vec<L, T, Q> const& x)
  {
    return detail::functor1<vec, L, T, T, Q>::call(log, x);
  }


  using std::exp2;











  template<length_t L, typename T, qualifier Q>
  inline vec<L, T, Q> exp2(vec<L, T, Q> const& x)
  {
    return detail::functor1<vec, L, T, T, Q>::call(exp2, x);
  }

  // log2, ln2 = 0.69314718055994530941723212145818f
  template<typename genType>
  inline genType log2(genType x)
  {
    return log2(vec<1, genType>(x)).x;
  }

  template<length_t L, typename T, qualifier Q>
  inline vec<L, T, Q> log2(vec<L, T, Q> const& x)
  {
    return detail::compute_log2<L, T, Q, std::numeric_limits<T>::is_iec559, detail::is_aligned<Q>::value>::call(x);
  }

  // sqrt
  using std::sqrt;
  template<length_t L, typename T, qualifier Q>
  inline vec<L, T, Q> sqrt(vec<L, T, Q> const& x)
  {
    static_assert(std::numeric_limits<T>::is_iec559, "'sqrt' only accept floating-point inputs");
    return detail::compute_sqrt<L, T, Q, detail::is_aligned<Q>::value>::call(x);
  }

  // inversesqrt
  template<typename genType>
  inline genType inversesqrt(genType x)
  {
    return static_cast<genType>(1) / sqrt(x);
  }

  template<length_t L, typename T, qualifier Q>
  inline vec<L, T, Q> inversesqrt(vec<L, T, Q> const& x)
  {
    static_assert(std::numeric_limits<T>::is_iec559, "'inversesqrt' only accept floating-point inputs");
    return detail::compute_inversesqrt<L, T, Q, detail::is_aligned<Q>::value>::call(x);
  }
}//namespace glm

namespace glm
{
  /// @addtogroup core_func_common
  /// @{

  /// Returns x if x >= 0; otherwise, it returns -x.
  ///
  /// @tparam genType floating-point or signed integer; scalar or vector types.
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/abs.xml">GLSL abs man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.3 Common Functions</a>
  template<typename genType>
  constexpr genType abs(genType x);

  /// Returns x if x >= 0; otherwise, it returns -x.
  ///
  /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector
  /// @tparam T Floating-point or signed integer scalar types
  /// @tparam Q Value from qualifier enum
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/abs.xml">GLSL abs man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.3 Common Functions</a>
  template<length_t L, typename T, qualifier Q>
  constexpr vec<L, T, Q> abs(vec<L, T, Q> const& x);

  /// Returns 1.0 if x > 0, 0.0 if x == 0, or -1.0 if x < 0.
  ///
  /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector
  /// @tparam T Floating-point scalar types
  /// @tparam Q Value from qualifier enum
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/sign.xml">GLSL sign man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.3 Common Functions</a>
  template<length_t L, typename T, qualifier Q>
  vec<L, T, Q> sign(vec<L, T, Q> const& x);

  /// Returns a value equal to the nearest integer that is less then or equal to x.
  ///
  /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector
  /// @tparam T Floating-point scalar types
  /// @tparam Q Value from qualifier enum
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/floor.xml">GLSL floor man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.3 Common Functions</a>
  template<length_t L, typename T, qualifier Q>
  vec<L, T, Q> floor(vec<L, T, Q> const& x);

  /// Returns a value equal to the nearest integer to x
  /// whose absolute value is not larger than the absolute value of x.
  ///
  /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector
  /// @tparam T Floating-point scalar types
  /// @tparam Q Value from qualifier enum
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/trunc.xml">GLSL trunc man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.3 Common Functions</a>
  template<length_t L, typename T, qualifier Q>
  vec<L, T, Q> trunc(vec<L, T, Q> const& x);

  /// Returns a value equal to the nearest integer to x.
  /// The fraction 0.5 will round in a direction chosen by the
  /// implementation, presumably the direction that is fastest.
  /// This includes the possibility that round(x) returns the
  /// same value as roundEven(x) for all values of x.
  ///
  /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector
  /// @tparam T Floating-point scalar types
  /// @tparam Q Value from qualifier enum
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/round.xml">GLSL round man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.3 Common Functions</a>
  template<length_t L, typename T, qualifier Q>
  vec<L, T, Q> round(vec<L, T, Q> const& x);

  /// Returns a value equal to the nearest integer to x.
  /// A fractional part of 0.5 will round toward the nearest even
  /// integer. (Both 3.5 and 4.5 for x will return 4.0.)
  ///
  /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector
  /// @tparam T Floating-point scalar types
  /// @tparam Q Value from qualifier enum
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/roundEven.xml">GLSL roundEven man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.3 Common Functions</a>
  /// @see <a href="http://developer.amd.com/documentation/articles/pages/New-Round-to-Even-Technique.aspx">New round to even technique</a>
  template<length_t L, typename T, qualifier Q>
  vec<L, T, Q> roundEven(vec<L, T, Q> const& x);

  /// Returns a value equal to the nearest integer
  /// that is greater than or equal to x.
  ///
  /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector
  /// @tparam T Floating-point scalar types
  /// @tparam Q Value from qualifier enum
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/ceil.xml">GLSL ceil man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.3 Common Functions</a>
  template<length_t L, typename T, qualifier Q>
  vec<L, T, Q> ceil(vec<L, T, Q> const& x);

  /// Return x - floor(x).
  ///
  /// @tparam genType Floating-point scalar or vector types.
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/fract.xml">GLSL fract man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.3 Common Functions</a>
  template<typename genType>
  genType fract(genType x);

  /// Return x - floor(x).
  ///
  /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector
  /// @tparam T Floating-point scalar types
  /// @tparam Q Value from qualifier enum
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/fract.xml">GLSL fract man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.3 Common Functions</a>
  template<length_t L, typename T, qualifier Q>
  vec<L, T, Q> fract(vec<L, T, Q> const& x);

  template<typename genType>
  genType mod(genType x, genType y);

  template<length_t L, typename T, qualifier Q>
  vec<L, T, Q> mod(vec<L, T, Q> const& x, T y);

  /// Modulus. Returns x - y * floor(x / y)
  /// for each component in x using the floating point value y.
  ///
  /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector
  /// @tparam T Floating-point scalar types, include glm/gtc/integer for integer scalar types support
  /// @tparam Q Value from qualifier enum
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/mod.xml">GLSL mod man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.3 Common Functions</a>
  template<length_t L, typename T, qualifier Q>
  vec<L, T, Q> mod(vec<L, T, Q> const& x, vec<L, T, Q> const& y);

  /// Returns the fractional part of x and sets i to the integer
  /// part (as a whole number floating point value). Both the
  /// return value and the output parameter will have the same
  /// sign as x.
  ///
  /// @tparam genType Floating-point scalar or vector types.
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/modf.xml">GLSL modf man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.3 Common Functions</a>
  template<typename genType>
  genType modf(genType x, genType& i);

  /// Returns y if y < x; otherwise, it returns x.
  ///
  /// @tparam genType Floating-point or integer; scalar or vector types.
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/min.xml">GLSL min man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.3 Common Functions</a>
  template<typename genType>
  constexpr genType min(genType x, genType y);

  /// Returns y if y < x; otherwise, it returns x.
  ///
  /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector
  /// @tparam T Floating-point or integer scalar types
  /// @tparam Q Value from qualifier enum
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/min.xml">GLSL min man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.3 Common Functions</a>
  template<length_t L, typename T, qualifier Q>
  constexpr vec<L, T, Q> min(vec<L, T, Q> const& x, T y);

  /// Returns y if y < x; otherwise, it returns x.
  ///
  /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector
  /// @tparam T Floating-point or integer scalar types
  /// @tparam Q Value from qualifier enum
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/min.xml">GLSL min man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.3 Common Functions</a>
  template<length_t L, typename T, qualifier Q>
  constexpr vec<L, T, Q> min(vec<L, T, Q> const& x, vec<L, T, Q> const& y);

  /// Returns y if x < y; otherwise, it returns x.
  ///
  /// @tparam genType Floating-point or integer; scalar or vector types.
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/max.xml">GLSL max man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.3 Common Functions</a>
  template<typename genType>
  constexpr genType max(genType x, genType y);

  /// Returns y if x < y; otherwise, it returns x.
  ///
  /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector
  /// @tparam T Floating-point or integer scalar types
  /// @tparam Q Value from qualifier enum
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/max.xml">GLSL max man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.3 Common Functions</a>
  template<length_t L, typename T, qualifier Q>
  constexpr vec<L, T, Q> max(vec<L, T, Q> const& x, T y);

  /// Returns y if x < y; otherwise, it returns x.
  ///
  /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector
  /// @tparam T Floating-point or integer scalar types
  /// @tparam Q Value from qualifier enum
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/max.xml">GLSL max man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.3 Common Functions</a>
  template<length_t L, typename T, qualifier Q>
  constexpr vec<L, T, Q> max(vec<L, T, Q> const& x, vec<L, T, Q> const& y);

  /// Returns min(max(x, minVal), maxVal) for each component in x
  /// using the floating-point values minVal and maxVal.
  ///
  /// @tparam genType Floating-point or integer; scalar or vector types.
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/clamp.xml">GLSL clamp man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.3 Common Functions</a>
  template<typename genType>
  constexpr genType clamp(genType x, genType minVal, genType maxVal);

  /// Returns min(max(x, minVal), maxVal) for each component in x
  /// using the floating-point values minVal and maxVal.
  ///
  /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector
  /// @tparam T Floating-point or integer scalar types
  /// @tparam Q Value from qualifier enum
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/clamp.xml">GLSL clamp man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.3 Common Functions</a>
  template<length_t L, typename T, qualifier Q>
  constexpr vec<L, T, Q> clamp(vec<L, T, Q> const& x, T minVal, T maxVal);

  /// Returns min(max(x, minVal), maxVal) for each component in x
  /// using the floating-point values minVal and maxVal.
  ///
  /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector
  /// @tparam T Floating-point or integer scalar types
  /// @tparam Q Value from qualifier enum
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/clamp.xml">GLSL clamp man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.3 Common Functions</a>
  template<length_t L, typename T, qualifier Q>
  constexpr vec<L, T, Q> clamp(vec<L, T, Q> const& x, vec<L, T, Q> const& minVal, vec<L, T, Q> const& maxVal);

  /// If genTypeU is a floating scalar or vector:
  /// Returns x * (1.0 - a) + y * a, i.e., the linear blend of
  /// x and y using the floating-point value a.
  /// The value for a is not restricted to the range [0, 1].
  ///
  /// If genTypeU is a boolean scalar or vector:
  /// Selects which vector each returned component comes
  /// from. For a component of 'a' that is false, the
  /// corresponding component of 'x' is returned. For a
  /// component of 'a' that is true, the corresponding
  /// component of 'y' is returned. Components of 'x' and 'y' that
  /// are not selected are allowed to be invalid floating point
  /// values and will have no effect on the results. Thus, this
  /// provides different functionality than
  /// genType mix(genType x, genType y, genType(a))
  /// where a is a Boolean vector.
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/mix.xml">GLSL mix man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.3 Common Functions</a>
  ///
  /// @param[in]  x Value to interpolate.
  /// @param[in]  y Value to interpolate.
  /// @param[in]  a Interpolant.
  ///
  /// @tparam	genTypeT Floating point scalar or vector.
  /// @tparam genTypeU Floating point or boolean scalar or vector. It can't be a vector if it is the length of genTypeT.
  ///
  /// @code
  /// #include <glm/glm.hpp>
  /// ...
  /// float a;
  /// bool b;
  /// glm::dvec3 e;
  /// glm::dvec3 f;
  /// glm::vec4 g;
  /// glm::vec4 h;
  /// ...
  /// glm::vec4 r = glm::mix(g, h, a); // Interpolate with a floating-point scalar two vectors.
  /// glm::vec4 s = glm::mix(g, h, b); // Returns g or h;
  /// glm::dvec3 t = glm::mix(e, f, a); // Types of the third parameter is not required to match with the first and the second.
  /// glm::vec4 u = glm::mix(g, h, r); // Interpolations can be perform per component with a vector for the last parameter.
  /// @endcode
  template<typename genTypeT, typename genTypeU>
  genTypeT mix(genTypeT x, genTypeT y, genTypeU a);

  template<length_t L, typename T, typename U, qualifier Q>
  vec<L, T, Q> mix(vec<L, T, Q> const& x, vec<L, T, Q> const& y, vec<L, U, Q> const& a);

  template<length_t L, typename T, typename U, qualifier Q>
  vec<L, T, Q> mix(vec<L, T, Q> const& x, vec<L, T, Q> const& y, U a);

  /// Returns 0.0 if x < edge, otherwise it returns 1.0 for each component of a genType.
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/step.xml">GLSL step man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.3 Common Functions</a>
  template<typename genType>
  genType step(genType edge, genType x);

  /// Returns 0.0 if x < edge, otherwise it returns 1.0.
  ///
  /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector
  /// @tparam T Floating-point scalar types
  /// @tparam Q Value from qualifier enum
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/step.xml">GLSL step man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.3 Common Functions</a>
  template<length_t L, typename T, qualifier Q>
  vec<L, T, Q> step(T edge, vec<L, T, Q> const& x);

  /// Returns 0.0 if x < edge, otherwise it returns 1.0.
  ///
  /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector
  /// @tparam T Floating-point scalar types
  /// @tparam Q Value from qualifier enum
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/step.xml">GLSL step man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.3 Common Functions</a>
  template<length_t L, typename T, qualifier Q>
  vec<L, T, Q> step(vec<L, T, Q> const& edge, vec<L, T, Q> const& x);

  /// Returns 0.0 if x <= edge0 and 1.0 if x >= edge1 and
  /// performs smooth Hermite interpolation between 0 and 1
  /// when edge0 < x < edge1. This is useful in cases where
  /// you would want a threshold function with a smooth
  /// transition. This is equivalent to:
  /// genType t;
  /// t = clamp ((x - edge0) / (edge1 - edge0), 0, 1);
  /// return t * t * (3 - 2 * t);
  /// Results are undefined if edge0 >= edge1.
  ///
  /// @tparam genType Floating-point scalar or vector types.
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/smoothstep.xml">GLSL smoothstep man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.3 Common Functions</a>
  template<typename genType>
  genType smoothstep(genType edge0, genType edge1, genType x);

  template<length_t L, typename T, qualifier Q>
  vec<L, T, Q> smoothstep(T edge0, T edge1, vec<L, T, Q> const& x);

  template<length_t L, typename T, qualifier Q>
  vec<L, T, Q> smoothstep(vec<L, T, Q> const& edge0, vec<L, T, Q> const& edge1, vec<L, T, Q> const& x);

  /// Returns true if x holds a NaN (not a number)
  /// representation in the underlying implementation's set of
  /// floating point representations. Returns false otherwise,
  /// including for implementations with no NaN
  /// representations.
  ///
  /// /!\ When using compiler fast math, this function may fail.
  ///
  /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector
  /// @tparam T Floating-point scalar types
  /// @tparam Q Value from qualifier enum
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/isnan.xml">GLSL isnan man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.3 Common Functions</a>
  template<length_t L, typename T, qualifier Q>
  vec<L, bool, Q> isnan(vec<L, T, Q> const& x);

  /// Returns true if x holds a positive infinity or negative
  /// infinity representation in the underlying implementation's
  /// set of floating point representations. Returns false
  /// otherwise, including for implementations with no infinity
  /// representations.
  ///
  /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector
  /// @tparam T Floating-point scalar types
  /// @tparam Q Value from qualifier enum
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/isinf.xml">GLSL isinf man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.3 Common Functions</a>
  template<length_t L, typename T, qualifier Q>
  vec<L, bool, Q> isinf(vec<L, T, Q> const& x);

  /// Returns a signed integer value representing
  /// the encoding of a floating-point value. The floating-point
  /// value's bit-level representation is preserved.
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/floatBitsToInt.xml">GLSL floatBitsToInt man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.3 Common Functions</a>
  int floatBitsToInt(float const& v);

  /// Returns a signed integer value representing
  /// the encoding of a floating-point value. The floatingpoint
  /// value's bit-level representation is preserved.
  ///
  /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector
  /// @tparam Q Value from qualifier enum
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/floatBitsToInt.xml">GLSL floatBitsToInt man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.3 Common Functions</a>
  template<length_t L, qualifier Q>
  vec<L, int, Q> floatBitsToInt(vec<L, float, Q> const& v);

  /// Returns a unsigned integer value representing
  /// the encoding of a floating-point value. The floatingpoint
  /// value's bit-level representation is preserved.
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/floatBitsToUint.xml">GLSL floatBitsToUint man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.3 Common Functions</a>
  uint floatBitsToUint(float const& v);

  /// Returns a unsigned integer value representing
  /// the encoding of a floating-point value. The floatingpoint
  /// value's bit-level representation is preserved.
  ///
  /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector
  /// @tparam Q Value from qualifier enum
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/floatBitsToUint.xml">GLSL floatBitsToUint man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.3 Common Functions</a>
  template<length_t L, qualifier Q>
  vec<L, uint, Q> floatBitsToUint(vec<L, float, Q> const& v);

  /// Returns a floating-point value corresponding to a signed
  /// integer encoding of a floating-point value.
  /// If an inf or NaN is passed in, it will not signal, and the
  /// resulting floating point value is unspecified. Otherwise,
  /// the bit-level representation is preserved.
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/intBitsToFloat.xml">GLSL intBitsToFloat man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.3 Common Functions</a>
  float intBitsToFloat(int const& v);

  /// Returns a floating-point value corresponding to a signed
  /// integer encoding of a floating-point value.
  /// If an inf or NaN is passed in, it will not signal, and the
  /// resulting floating point value is unspecified. Otherwise,
  /// the bit-level representation is preserved.
  ///
  /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector
  /// @tparam Q Value from qualifier enum
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/intBitsToFloat.xml">GLSL intBitsToFloat man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.3 Common Functions</a>
  template<length_t L, qualifier Q>
  vec<L, float, Q> intBitsToFloat(vec<L, int, Q> const& v);

  /// Returns a floating-point value corresponding to a
  /// unsigned integer encoding of a floating-point value.
  /// If an inf or NaN is passed in, it will not signal, and the
  /// resulting floating point value is unspecified. Otherwise,
  /// the bit-level representation is preserved.
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/uintBitsToFloat.xml">GLSL uintBitsToFloat man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.3 Common Functions</a>
  float uintBitsToFloat(uint const& v);

  /// Returns a floating-point value corresponding to a
  /// unsigned integer encoding of a floating-point value.
  /// If an inf or NaN is passed in, it will not signal, and the
  /// resulting floating point value is unspecified. Otherwise,
  /// the bit-level representation is preserved.
  ///
  /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector
  /// @tparam Q Value from qualifier enum
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/uintBitsToFloat.xml">GLSL uintBitsToFloat man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.3 Common Functions</a>
  template<length_t L, qualifier Q>
  vec<L, float, Q> uintBitsToFloat(vec<L, uint, Q> const& v);

  /// Computes and returns a * b + c.
  ///
  /// @tparam genType Floating-point scalar or vector types.
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/fma.xml">GLSL fma man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.3 Common Functions</a>
  template<typename genType>
  genType fma(genType const& a, genType const& b, genType const& c);

  /// Splits x into a floating-point significand in the range
  /// [0.5, 1.0) and an integral exponent of two, such that:
  /// x = significand * exp(2, exponent)
  ///
  /// The significand is returned by the function and the
  /// exponent is returned in the parameter exp. For a
  /// floating-point value of zero, the significant and exponent
  /// are both zero. For a floating-point value that is an
  /// infinity or is not a number, the results are undefined.
  ///
  /// @tparam genType Floating-point scalar or vector types.
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/frexp.xml">GLSL frexp man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.3 Common Functions</a>
  template<typename genType>
  genType frexp(genType x, int& exp);

  template<length_t L, typename T, qualifier Q>
  vec<L, T, Q> frexp(vec<L, T, Q> const& v, vec<L, int, Q>& exp);

  /// Builds a floating-point number from x and the
  /// corresponding integral exponent of two in exp, returning:
  /// significand * exp(2, exponent)
  ///
  /// If this product is too large to be represented in the
  /// floating-point type, the result is undefined.
  ///
  /// @tparam genType Floating-point scalar or vector types.
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/ldexp.xml">GLSL ldexp man page</a>;
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.3 Common Functions</a>
  template<typename genType>
  genType ldexp(genType const& x, int const& exp);

  template<length_t L, typename T, qualifier Q>
  vec<L, T, Q> ldexp(vec<L, T, Q> const& v, vec<L, int, Q> const& exp);

  /// @}
}//namespace glm

namespace glm {
  namespace detail
  {
    template<typename genFIType, bool /*signed*/>
    struct compute_abs
    {};

    template<typename genFIType>
    struct compute_abs<genFIType, true>
    {
      inline constexpr static genFIType call(genFIType x)
      {
        static_assert(std::numeric_limits<genFIType>::is_iec559 || std::numeric_limits<genFIType>::is_signed, "'abs' only accept floating-point and integer scalar or vector inputs");

        return x >= genFIType(0) ? x : -x;
        // TODO, perf comp with: *(((int *) &x) + 1) &= 0x7fffffff;
      }
    };


    template<typename genFIType>
    struct compute_abs<genFIType, false>
    {
      inline constexpr static genFIType call(genFIType x)
      {
        static_assert((!std::numeric_limits<genFIType>::is_signed && std::numeric_limits<genFIType>::is_integer), "'abs' only accept floating-point and integer scalar or vector inputs");
        return x;
      }
    };
  }//namespace detail
}//namespace glm

namespace glm
{
  // min
  template<typename genType>
  inline constexpr genType min(genType x, genType y)
  {
    static_assert(std::numeric_limits<genType>::is_iec559 || std::numeric_limits<genType>::is_integer, "'min' only accept floating-point or integer inputs");
    return (y < x) ? y : x;
  }

  // max
  template<typename genType>
  inline constexpr genType max(genType x, genType y)
  {
    static_assert(std::numeric_limits<genType>::is_iec559 || std::numeric_limits<genType>::is_integer, "'max' only accept floating-point or integer inputs");

    return (x < y) ? y : x;
  }

  // abs
  template<>
  inline constexpr int abs(int x)
  {
    int const y = x >> (sizeof(int) * 8 - 1);
    return (x ^ y) - y;
  }

  using ::std::round;
  using ::std::trunc;

}//namespace glm

namespace glm {
  namespace detail
  {
    template<length_t L, typename T, qualifier Q, bool Aligned>
    struct compute_abs_vector
    {
      inline constexpr static vec<L, T, Q> call(vec<L, T, Q> const& x)
      {
        return detail::functor1<vec, L, T, T, Q>::call(abs, x);
      }
    };

    template<length_t L, typename T, typename U, qualifier Q, bool Aligned>
    struct compute_mix_vector
    {
      inline static vec<L, T, Q> call(vec<L, T, Q> const& x, vec<L, T, Q> const& y, vec<L, U, Q> const& a)
      {
        static_assert(std::numeric_limits<U>::is_iec559 || 0, "'mix' only accept floating-point inputs for the interpolator a");

        return vec<L, T, Q>(vec<L, U, Q>(x) * (static_cast<U>(1) - a) + vec<L, U, Q>(y) * a);
      }
    };

    template<length_t L, typename T, qualifier Q, bool Aligned>
    struct compute_mix_vector<L, T, bool, Q, Aligned>
    {
      inline static vec<L, T, Q> call(vec<L, T, Q> const& x, vec<L, T, Q> const& y, vec<L, bool, Q> const& a)
      {
        vec<L, T, Q> Result;
        for (length_t i = 0; i < x.length(); ++i)
          Result[i] = a[i] ? y[i] : x[i];
        return Result;
      }
    };

    template<length_t L, typename T, typename U, qualifier Q, bool Aligned>
    struct compute_mix_scalar
    {
      inline static vec<L, T, Q> call(vec<L, T, Q> const& x, vec<L, T, Q> const& y, U const& a)
      {
        static_assert(std::numeric_limits<U>::is_iec559 || 0, "'mix' only accept floating-point inputs for the interpolator a");

        return vec<L, T, Q>(vec<L, U, Q>(x) * (static_cast<U>(1) - a) + vec<L, U, Q>(y) * a);
      }
    };

    template<length_t L, typename T, qualifier Q, bool Aligned>
    struct compute_mix_scalar<L, T, bool, Q, Aligned>
    {
      inline static vec<L, T, Q> call(vec<L, T, Q> const& x, vec<L, T, Q> const& y, bool const& a)
      {
        return a ? y : x;
      }
    };

    template<typename T, typename U>
    struct compute_mix
    {
      inline static T call(T const& x, T const& y, U const& a)
      {
        static_assert(std::numeric_limits<U>::is_iec559 || 0, "'mix' only accept floating-point inputs for the interpolator a");

        return static_cast<T>(static_cast<U>(x) * (static_cast<U>(1) - a) + static_cast<U>(y) * a);
      }
    };

    template<typename T>
    struct compute_mix<T, bool>
    {
      inline static T call(T const& x, T const& y, bool const& a)
      {
        return a ? y : x;
      }
    };

    template<length_t L, typename T, qualifier Q, bool isFloat, bool Aligned>
    struct compute_sign
    {
      inline static vec<L, T, Q> call(vec<L, T, Q> const& x)
      {
        return vec<L, T, Q>(glm::lessThan(vec<L, T, Q>(0), x)) - vec<L, T, Q>(glm::lessThan(x, vec<L, T, Q>(0)));
      }
    };


    template<length_t L, typename T, qualifier Q, bool Aligned>
    struct compute_sign<L, T, Q, false, Aligned>
    {
      inline static vec<L, T, Q> call(vec<L, T, Q> const& x)
      {
        T const Shift(static_cast<T>(sizeof(T) * 8 - 1));
        vec<L, T, Q> const y(vec<L, typename detail::make_unsigned<T>::type, Q>(-x) >> typename detail::make_unsigned<T>::type(Shift));

        return (x >> Shift) | y;
      }
    };


    template<length_t L, typename T, qualifier Q, bool Aligned>
    struct compute_floor
    {
      inline static vec<L, T, Q> call(vec<L, T, Q> const& x)
      {
        return detail::functor1<vec, L, T, T, Q>::call(std::floor, x);
      }
    };

    template<length_t L, typename T, qualifier Q, bool Aligned>
    struct compute_ceil
    {
      inline static vec<L, T, Q> call(vec<L, T, Q> const& x)
      {
        return detail::functor1<vec, L, T, T, Q>::call(std::ceil, x);
      }
    };

    template<length_t L, typename T, qualifier Q, bool Aligned>
    struct compute_fract
    {
      inline static vec<L, T, Q> call(vec<L, T, Q> const& x)
      {
        return x - floor(x);
      }
    };

    template<length_t L, typename T, qualifier Q, bool Aligned>
    struct compute_trunc
    {
      inline static vec<L, T, Q> call(vec<L, T, Q> const& x)
      {
        return detail::functor1<vec, L, T, T, Q>::call(trunc, x);
      }
    };

    template<length_t L, typename T, qualifier Q, bool Aligned>
    struct compute_round
    {
      inline static vec<L, T, Q> call(vec<L, T, Q> const& x)
      {
        return detail::functor1<vec, L, T, T, Q>::call(round, x);
      }
    };

    template<length_t L, typename T, qualifier Q, bool Aligned>
    struct compute_mod
    {
      inline static vec<L, T, Q> call(vec<L, T, Q> const& a, vec<L, T, Q> const& b)
      {
        static_assert(std::numeric_limits<T>::is_iec559, "'mod' only accept floating-point inputs. Include <glm/gtc/integer.hpp> for integer inputs.");
        return a - b * floor(a / b);
      }
    };

    template<length_t L, typename T, qualifier Q, bool Aligned>
    struct compute_min_vector
    {
      inline static vec<L, T, Q> call(vec<L, T, Q> const& x, vec<L, T, Q> const& y)
      {
        return detail::functor2<vec, L, T, Q>::call(min, x, y);
      }
    };

    template<length_t L, typename T, qualifier Q, bool Aligned>
    struct compute_max_vector
    {
      inline static vec<L, T, Q> call(vec<L, T, Q> const& x, vec<L, T, Q> const& y)
      {
        return detail::functor2<vec, L, T, Q>::call(max, x, y);
      }
    };

    template<length_t L, typename T, qualifier Q, bool Aligned>
    struct compute_clamp_vector
    {
      inline static vec<L, T, Q> call(vec<L, T, Q> const& x, vec<L, T, Q> const& minVal, vec<L, T, Q> const& maxVal)
      {
        return min(max(x, minVal), maxVal);
      }
    };

    template<length_t L, typename T, qualifier Q, bool Aligned>
    struct compute_step_vector
    {
      inline static vec<L, T, Q> call(vec<L, T, Q> const& edge, vec<L, T, Q> const& x)
      {
        return mix(vec<L, T, Q>(1), vec<L, T, Q>(0), glm::lessThan(x, edge));
      }
    };

    template<length_t L, typename T, qualifier Q, bool Aligned>
    struct compute_smoothstep_vector
    {
      inline static vec<L, T, Q> call(vec<L, T, Q> const& edge0, vec<L, T, Q> const& edge1, vec<L, T, Q> const& x)
      {
        static_assert(std::numeric_limits<T>::is_iec559 || 0, "'smoothstep' only accept floating-point inputs");
        vec<L, T, Q> const tmp(clamp((x - edge0) / (edge1 - edge0), static_cast<T>(0), static_cast<T>(1)));
        return tmp * tmp * (static_cast<T>(3) - static_cast<T>(2) * tmp);
      }
    };
  }//namespace detail

  template<typename genFIType>
  inline constexpr genFIType abs(genFIType x)
  {
    return detail::compute_abs<genFIType, std::numeric_limits<genFIType>::is_signed>::call(x);
  }

  template<length_t L, typename T, qualifier Q>
  inline constexpr vec<L, T, Q> abs(vec<L, T, Q> const& x)
  {
    return detail::compute_abs_vector<L, T, Q, detail::is_aligned<Q>::value>::call(x);
  }

  // sign
  // fast and works for any type
  template<typename genFIType>
  inline genFIType sign(genFIType x)
  {
    static_assert(std::numeric_limits<genFIType>::is_iec559 || (std::numeric_limits<genFIType>::is_signed && std::numeric_limits<genFIType>::is_integer), "'sign' only accept signed inputs");

    return detail::compute_sign<1, genFIType, defaultp,
      std::numeric_limits<genFIType>::is_iec559, detail::is_aligned<highp>::value>::call(vec<1, genFIType>(x)).x;
  }

  template<length_t L, typename T, qualifier Q>
  inline vec<L, T, Q> sign(vec<L, T, Q> const& x)
  {
    static_assert(std::numeric_limits<T>::is_iec559 || (std::numeric_limits<T>::is_signed && std::numeric_limits<T>::is_integer), "'sign' only accept signed inputs");

    return detail::compute_sign<L, T, Q, std::numeric_limits<T>::is_iec559, detail::is_aligned<Q>::value>::call(x);
  }

  // floor
  using ::std::floor;
  template<length_t L, typename T, qualifier Q>
  inline vec<L, T, Q> floor(vec<L, T, Q> const& x)
  {
    static_assert(std::numeric_limits<T>::is_iec559, "'floor' only accept floating-point inputs.");
    return detail::compute_floor<L, T, Q, detail::is_aligned<Q>::value>::call(x);
  }

  template<length_t L, typename T, qualifier Q>
  inline vec<L, T, Q> trunc(vec<L, T, Q> const& x)
  {
    static_assert(std::numeric_limits<T>::is_iec559, "'trunc' only accept floating-point inputs");
    return detail::compute_trunc<L, T, Q, detail::is_aligned<Q>::value>::call(x);
  }

  template<length_t L, typename T, qualifier Q>
  inline vec<L, T, Q> round(vec<L, T, Q> const& x)
  {
    static_assert(std::numeric_limits<T>::is_iec559, "'round' only accept floating-point inputs");
    return detail::compute_round<L, T, Q, detail::is_aligned<Q>::value>::call(x);
  }

  /*
    // roundEven
    template<typename genType>
    GLM_FUNC_QUALIFIER genType roundEven(genType const& x)
    {
      GLM_STATIC_ASSERT(std::numeric_limits<genType>::is_iec559, "'roundEven' only accept floating-point inputs");

      return genType(int(x + genType(int(x) % 2)));
    }
  */

  // roundEven
  template<typename genType>
  inline genType roundEven(genType x)
  {
    static_assert(std::numeric_limits<genType>::is_iec559, "'roundEven' only accept floating-point inputs");

    int Integer = static_cast<int>(x);
    genType IntegerPart = static_cast<genType>(Integer);
    genType FractionalPart = fract(x);

    if (FractionalPart > static_cast<genType>(0.5) || FractionalPart < static_cast<genType>(0.5))
    {
      return round(x);
    }
    else if ((Integer % 2) == 0)
    {
      return IntegerPart;
    }
    else if (x <= static_cast<genType>(0)) // Work around...
    {
      return IntegerPart - static_cast<genType>(1);
    }
    else
    {
      return IntegerPart + static_cast<genType>(1);
    }
    //else // Bug on MinGW 4.5.2
    //{
    //	return mix(IntegerPart + genType(-1), IntegerPart + genType(1), x <= genType(0));
    //}
  }

  template<length_t L, typename T, qualifier Q>
  inline vec<L, T, Q> roundEven(vec<L, T, Q> const& x)
  {
    static_assert(std::numeric_limits<T>::is_iec559, "'roundEven' only accept floating-point inputs");
    return detail::functor1<vec, L, T, T, Q>::call(roundEven, x);
  }

  // ceil
  using ::std::ceil;
  template<length_t L, typename T, qualifier Q>
  inline vec<L, T, Q> ceil(vec<L, T, Q> const& x)
  {
    static_assert(std::numeric_limits<T>::is_iec559, "'ceil' only accept floating-point inputs");
    return detail::compute_ceil<L, T, Q, detail::is_aligned<Q>::value>::call(x);
  }

  // fract
  template<typename genType>
  inline genType fract(genType x)
  {
    return fract(vec<1, genType>(x)).x;
  }

  template<length_t L, typename T, qualifier Q>
  inline vec<L, T, Q> fract(vec<L, T, Q> const& x)
  {
    static_assert(std::numeric_limits<T>::is_iec559, "'fract' only accept floating-point inputs");
    return detail::compute_fract<L, T, Q, detail::is_aligned<Q>::value>::call(x);
  }

  // mod
  template<typename genType>
  inline genType mod(genType x, genType y)
  {
    return mod(vec<1, genType, defaultp>(x), y).x;
  }

  template<length_t L, typename T, qualifier Q>
  inline vec<L, T, Q> mod(vec<L, T, Q> const& x, T y)
  {
    return detail::compute_mod<L, T, Q, detail::is_aligned<Q>::value>::call(x, vec<L, T, Q>(y));
  }

  template<length_t L, typename T, qualifier Q>
  inline vec<L, T, Q> mod(vec<L, T, Q> const& x, vec<L, T, Q> const& y)
  {
    return detail::compute_mod<L, T, Q, detail::is_aligned<Q>::value>::call(x, y);
  }

  // modf
  template<typename genType>
  inline genType modf(genType x, genType& i)
  {
    static_assert(std::numeric_limits<genType>::is_iec559, "'modf' only accept floating-point inputs");
    return std::modf(x, &i);
  }

  template<typename T, qualifier Q>
  inline vec<1, T, Q> modf(vec<1, T, Q> const& x, vec<1, T, Q>& i)
  {
    return vec<1, T, Q>(
      modf(x.x, i.x));
  }

  template<typename T, qualifier Q>
  inline vec<2, T, Q> modf(vec<2, T, Q> const& x, vec<2, T, Q>& i)
  {
    return vec<2, T, Q>(
      modf(x.x, i.x),
      modf(x.y, i.y));
  }

  template<typename T, qualifier Q>
  inline vec<3, T, Q> modf(vec<3, T, Q> const& x, vec<3, T, Q>& i)
  {
    return vec<3, T, Q>(
      modf(x.x, i.x),
      modf(x.y, i.y),
      modf(x.z, i.z));
  }

  template<typename T, qualifier Q>
  inline vec<4, T, Q> modf(vec<4, T, Q> const& x, vec<4, T, Q>& i)
  {
    return vec<4, T, Q>(
      modf(x.x, i.x),
      modf(x.y, i.y),
      modf(x.z, i.z),
      modf(x.w, i.w));
  }

  // min
  template<length_t L, typename T, qualifier Q>
  inline constexpr vec<L, T, Q> min(vec<L, T, Q> const& a, T b)
  {
    static_assert(std::numeric_limits<T>::is_iec559 || std::numeric_limits<T>::is_integer, "'min' only accept floating-point or integer inputs");
    return detail::compute_min_vector<L, T, Q, detail::is_aligned<Q>::value>::call(a, vec<L, T, Q>(b));
  }

  template<length_t L, typename T, qualifier Q>
  inline constexpr vec<L, T, Q> min(vec<L, T, Q> const& a, vec<L, T, Q> const& b)
  {
    return detail::compute_min_vector<L, T, Q, detail::is_aligned<Q>::value>::call(a, b);
  }

  // max
  template<length_t L, typename T, qualifier Q>
  inline constexpr vec<L, T, Q> max(vec<L, T, Q> const& a, T b)
  {
    static_assert(std::numeric_limits<T>::is_iec559 || std::numeric_limits<T>::is_integer, "'max' only accept floating-point or integer inputs");
    return detail::compute_max_vector<L, T, Q, detail::is_aligned<Q>::value>::call(a, vec<L, T, Q>(b));
  }

  template<length_t L, typename T, qualifier Q>
  inline constexpr vec<L, T, Q> max(vec<L, T, Q> const& a, vec<L, T, Q> const& b)
  {
    return detail::compute_max_vector<L, T, Q, detail::is_aligned<Q>::value>::call(a, b);
  }

  // clamp
  template<typename genType>
  inline constexpr genType clamp(genType x, genType minVal, genType maxVal)
  {
    static_assert(std::numeric_limits<genType>::is_iec559 || std::numeric_limits<genType>::is_integer, "'clamp' only accept floating-point or integer inputs");
    return min(max(x, minVal), maxVal);
  }

  template<length_t L, typename T, qualifier Q>
  inline constexpr vec<L, T, Q> clamp(vec<L, T, Q> const& x, T minVal, T maxVal)
  {
    static_assert(std::numeric_limits<T>::is_iec559 || std::numeric_limits<T>::is_integer, "'clamp' only accept floating-point or integer inputs");
    return detail::compute_clamp_vector<L, T, Q, detail::is_aligned<Q>::value>::call(x, vec<L, T, Q>(minVal), vec<L, T, Q>(maxVal));
  }

  template<length_t L, typename T, qualifier Q>
  inline constexpr vec<L, T, Q> clamp(vec<L, T, Q> const& x, vec<L, T, Q> const& minVal, vec<L, T, Q> const& maxVal)
  {
    static_assert(std::numeric_limits<T>::is_iec559 || std::numeric_limits<T>::is_integer, "'clamp' only accept floating-point or integer inputs");
    return detail::compute_clamp_vector<L, T, Q, detail::is_aligned<Q>::value>::call(x, minVal, maxVal);
  }

  template<typename genTypeT, typename genTypeU>
  inline genTypeT mix(genTypeT x, genTypeT y, genTypeU a)
  {
    return detail::compute_mix<genTypeT, genTypeU>::call(x, y, a);
  }

  template<length_t L, typename T, typename U, qualifier Q>
  inline vec<L, T, Q> mix(vec<L, T, Q> const& x, vec<L, T, Q> const& y, U a)
  {
    return detail::compute_mix_scalar<L, T, U, Q, detail::is_aligned<Q>::value>::call(x, y, a);
  }

  template<length_t L, typename T, typename U, qualifier Q>
  inline vec<L, T, Q> mix(vec<L, T, Q> const& x, vec<L, T, Q> const& y, vec<L, U, Q> const& a)
  {
    return detail::compute_mix_vector<L, T, U, Q, detail::is_aligned<Q>::value>::call(x, y, a);
  }

  // step
  template<typename genType>
  inline genType step(genType edge, genType x)
  {
    return mix(static_cast<genType>(1), static_cast<genType>(0), x < edge);
  }

  template<length_t L, typename T, qualifier Q>
  inline vec<L, T, Q> step(T edge, vec<L, T, Q> const& x)
  {
    return detail::compute_step_vector<L, T, Q, detail::is_aligned<Q>::value>::call(vec<L, T, Q>(edge), x);
  }

  template<length_t L, typename T, qualifier Q>
  inline vec<L, T, Q> step(vec<L, T, Q> const& edge, vec<L, T, Q> const& x)
  {
    return detail::compute_step_vector<L, T, Q, detail::is_aligned<Q>::value>::call(edge, x);
  }

  // smoothstep
  template<typename genType>
  inline genType smoothstep(genType edge0, genType edge1, genType x)
  {
    static_assert(std::numeric_limits<genType>::is_iec559 || 0, "'smoothstep' only accept floating-point inputs");

    genType const tmp(clamp((x - edge0) / (edge1 - edge0), genType(0), genType(1)));
    return tmp * tmp * (genType(3) - genType(2) * tmp);
  }

  template<length_t L, typename T, qualifier Q>
  inline vec<L, T, Q> smoothstep(T edge0, T edge1, vec<L, T, Q> const& x)
  {
    return detail::compute_smoothstep_vector<L, T, Q, detail::is_aligned<Q>::value>::call(vec<L, T, Q>(edge0), vec<L, T, Q>(edge1), x);
  }

  template<length_t L, typename T, qualifier Q>
  inline vec<L, T, Q> smoothstep(vec<L, T, Q> const& edge0, vec<L, T, Q> const& edge1, vec<L, T, Q> const& x)
  {
    return detail::compute_smoothstep_vector<L, T, Q, detail::is_aligned<Q>::value>::call(edge0, edge1, x);
  }


  using std::isnan;


  template<length_t L, typename T, qualifier Q>
  inline vec<L, bool, Q> isnan(vec<L, T, Q> const& v)
  {
    static_assert(std::numeric_limits<T>::is_iec559, "'isnan' only accept floating-point inputs");

    vec<L, bool, Q> Result;
    for (length_t l = 0; l < v.length(); ++l)
      Result[l] = glm::isnan(v[l]);
    return Result;
  }


  using std::isinf;



  template<length_t L, typename T, qualifier Q>
  inline vec<L, bool, Q> isinf(vec<L, T, Q> const& v)
  {
    static_assert(std::numeric_limits<T>::is_iec559, "'isinf' only accept floating-point inputs");

    vec<L, bool, Q> Result;
    for (length_t l = 0; l < v.length(); ++l)
      Result[l] = glm::isinf(v[l]);
    return Result;
  }

  inline int floatBitsToInt(float const& v)
  {
    union
    {
      float in;
      int out;
    } u;

    u.in = v;

    return u.out;
  }

  template<length_t L, qualifier Q>
  inline vec<L, int, Q> floatBitsToInt(vec<L, float, Q> const& v)
  {
    return reinterpret_cast<vec<L, int, Q>&>(const_cast<vec<L, float, Q>&>(v));
  }

  inline uint floatBitsToUint(float const& v)
  {
    union
    {
      float in;
      uint out;
    } u;

    u.in = v;

    return u.out;
  }

  template<length_t L, qualifier Q>
  inline vec<L, uint, Q> floatBitsToUint(vec<L, float, Q> const& v)
  {
    return reinterpret_cast<vec<L, uint, Q>&>(const_cast<vec<L, float, Q>&>(v));
  }

  inline float intBitsToFloat(int const& v)
  {
    union
    {
      int in;
      float out;
    } u;

    u.in = v;

    return u.out;
  }

  template<length_t L, qualifier Q>
  inline vec<L, float, Q> intBitsToFloat(vec<L, int, Q> const& v)
  {
    return reinterpret_cast<vec<L, float, Q>&>(const_cast<vec<L, int, Q>&>(v));
  }

  inline float uintBitsToFloat(uint const& v)
  {
    union
    {
      uint in;
      float out;
    } u;

    u.in = v;

    return u.out;
  }

  template<length_t L, qualifier Q>
  inline vec<L, float, Q> uintBitsToFloat(vec<L, uint, Q> const& v)
  {
    return reinterpret_cast<vec<L, float, Q>&>(const_cast<vec<L, uint, Q>&>(v));
  }


  using std::fma;








  template<typename genType>
  inline genType frexp(genType x, int& exp)
  {
    static_assert(std::numeric_limits<genType>::is_iec559, "'frexp' only accept floating-point inputs");

    return std::frexp(x, &exp);
  }

  template<length_t L, typename T, qualifier Q>
  inline vec<L, T, Q> frexp(vec<L, T, Q> const& v, vec<L, int, Q>& exp)
  {
    static_assert(std::numeric_limits<T>::is_iec559, "'frexp' only accept floating-point inputs");

    vec<L, T, Q> Result;
    for (length_t l = 0; l < v.length(); ++l)
      Result[l] = std::frexp(v[l], &exp[l]);
    return Result;
  }

  template<typename genType>
  inline genType ldexp(genType const& x, int const& exp)
  {
    static_assert(std::numeric_limits<genType>::is_iec559, "'ldexp' only accept floating-point inputs");

    return std::ldexp(x, exp);
  }

  template<length_t L, typename T, qualifier Q>
  inline vec<L, T, Q> ldexp(vec<L, T, Q> const& v, vec<L, int, Q> const& exp)
  {
    static_assert(std::numeric_limits<T>::is_iec559, "'ldexp' only accept floating-point inputs");

    vec<L, T, Q> Result;
    for (length_t l = 0; l < v.length(); ++l)
      Result[l] = std::ldexp(v[l], exp[l]);
    return Result;
  }
}//namespace glm








namespace glm {
  namespace detail
  {
    template<length_t L, typename T, qualifier Q, bool Aligned>
    struct compute_length
    {
      inline static T call(vec<L, T, Q> const& v)
      {
        return sqrt(dot(v, v));
      }
    };

    template<length_t L, typename T, qualifier Q, bool Aligned>
    struct compute_distance
    {
      inline static T call(vec<L, T, Q> const& p0, vec<L, T, Q> const& p1)
      {
        return length(p1 - p0);
      }
    };

    template<typename V, typename T, bool Aligned>
    struct compute_dot {};

    template<typename T, qualifier Q, bool Aligned>
    struct compute_dot<vec<1, T, Q>, T, Aligned>
    {
      inline static T call(vec<1, T, Q> const& a, vec<1, T, Q> const& b)
      {
        return a.x * b.x;
      }
    };

    template<typename T, qualifier Q, bool Aligned>
    struct compute_dot<vec<2, T, Q>, T, Aligned>
    {
      inline static T call(vec<2, T, Q> const& a, vec<2, T, Q> const& b)
      {
        vec<2, T, Q> tmp(a * b);
        return tmp.x + tmp.y;
      }
    };

    template<typename T, qualifier Q, bool Aligned>
    struct compute_dot<vec<3, T, Q>, T, Aligned>
    {
      inline static T call(vec<3, T, Q> const& a, vec<3, T, Q> const& b)
      {
        vec<3, T, Q> tmp(a * b);
        return tmp.x + tmp.y + tmp.z;
      }
    };

    template<typename T, qualifier Q, bool Aligned>
    struct compute_dot<vec<4, T, Q>, T, Aligned>
    {
      inline static T call(vec<4, T, Q> const& a, vec<4, T, Q> const& b)
      {
        vec<4, T, Q> tmp(a * b);
        return (tmp.x + tmp.y) + (tmp.z + tmp.w);
      }
    };

    template<typename T, qualifier Q, bool Aligned>
    struct compute_cross
    {
      inline static vec<3, T, Q> call(vec<3, T, Q> const& x, vec<3, T, Q> const& y)
      {
        static_assert(std::numeric_limits<T>::is_iec559, "'cross' accepts only floating-point inputs");

        return vec<3, T, Q>(
          x.y * y.z - y.y * x.z,
          x.z * y.x - y.z * x.x,
          x.x * y.y - y.x * x.y);
      }
    };

    template<length_t L, typename T, qualifier Q, bool Aligned>
    struct compute_normalize
    {
      inline static vec<L, T, Q> call(vec<L, T, Q> const& v)
      {
        static_assert(std::numeric_limits<T>::is_iec559, "'normalize' accepts only floating-point inputs");

        return v * inversesqrt(dot(v, v));
      }
    };

    template<length_t L, typename T, qualifier Q, bool Aligned>
    struct compute_faceforward
    {
      inline static vec<L, T, Q> call(vec<L, T, Q> const& N, vec<L, T, Q> const& I, vec<L, T, Q> const& Nref)
      {
        static_assert(std::numeric_limits<T>::is_iec559, "'normalize' accepts only floating-point inputs");

        return dot(Nref, I) < static_cast<T>(0) ? N : -N;
      }
    };

    template<length_t L, typename T, qualifier Q, bool Aligned>
    struct compute_reflect
    {
      inline static vec<L, T, Q> call(vec<L, T, Q> const& I, vec<L, T, Q> const& N)
      {
        return I - N * dot(N, I) * static_cast<T>(2);
      }
    };

    template<length_t L, typename T, qualifier Q, bool Aligned>
    struct compute_refract
    {
      inline static vec<L, T, Q> call(vec<L, T, Q> const& I, vec<L, T, Q> const& N, T eta)
      {
        T const dotValue(dot(N, I));
        T const k(static_cast<T>(1) - eta * eta * (static_cast<T>(1) - dotValue * dotValue));
        vec<L, T, Q> const Result =
          (k >= static_cast<T>(0)) ? (eta * I - (eta * dotValue + std::sqrt(k)) * N) : vec<L, T, Q>(0);
        return Result;
      }
    };
  }//namespace detail

    // length
  template<typename genType>
  inline genType length(genType x)
  {
    static_assert(std::numeric_limits<genType>::is_iec559, "'length' accepts only floating-point inputs");

    return abs(x);
  }

  template<length_t L, typename T, qualifier Q>
  inline T length(vec<L, T, Q> const& v)
  {
    static_assert(std::numeric_limits<T>::is_iec559, "'length' accepts only floating-point inputs");

    return detail::compute_length<L, T, Q, detail::is_aligned<Q>::value>::call(v);
  }

  // distance
  template<typename genType>
  inline genType distance(genType const& p0, genType const& p1)
  {
    static_assert(std::numeric_limits<genType>::is_iec559, "'distance' accepts only floating-point inputs");

    return length(p1 - p0);
  }

  template<length_t L, typename T, qualifier Q>
  inline T distance(vec<L, T, Q> const& p0, vec<L, T, Q> const& p1)
  {
    return detail::compute_distance<L, T, Q, detail::is_aligned<Q>::value>::call(p0, p1);
  }

  // dot
  template<typename T>
  inline T dot(T x, T y)
  {
    static_assert(std::numeric_limits<T>::is_iec559, "'dot' accepts only floating-point inputs");
    return x * y;
  }

  template<length_t L, typename T, qualifier Q>
  inline T dot(vec<L, T, Q> const& x, vec<L, T, Q> const& y)
  {
    static_assert(std::numeric_limits<T>::is_iec559, "'dot' accepts only floating-point inputs");
    return detail::compute_dot<vec<L, T, Q>, T, detail::is_aligned<Q>::value>::call(x, y);
  }

  // cross
  template<typename T, qualifier Q>
  inline vec<3, T, Q> cross(vec<3, T, Q> const& x, vec<3, T, Q> const& y)
  {
    return detail::compute_cross<T, Q, detail::is_aligned<Q>::value>::call(x, y);
  }
  /*
    // normalize
    template<typename genType>
    GLM_FUNC_QUALIFIER genType normalize(genType const& x)
    {
      GLM_STATIC_ASSERT(std::numeric_limits<genType>::is_iec559, "'normalize' accepts only floating-point inputs");

      return x < genType(0) ? genType(-1) : genType(1);
    }
  */
  template<length_t L, typename T, qualifier Q>
  inline vec<L, T, Q> normalize(vec<L, T, Q> const& x)
  {
    static_assert(std::numeric_limits<T>::is_iec559, "'normalize' accepts only floating-point inputs");

    return detail::compute_normalize<L, T, Q, detail::is_aligned<Q>::value>::call(x);
  }

  // faceforward
  template<typename genType>
  inline genType faceforward(genType const& N, genType const& I, genType const& Nref)
  {
    return dot(Nref, I) < static_cast<genType>(0) ? N : -N;
  }

  template<length_t L, typename T, qualifier Q>
  inline vec<L, T, Q> faceforward(vec<L, T, Q> const& N, vec<L, T, Q> const& I, vec<L, T, Q> const& Nref)
  {
    return detail::compute_faceforward<L, T, Q, detail::is_aligned<Q>::value>::call(N, I, Nref);
  }

  // reflect
  template<typename genType>
  inline genType reflect(genType const& I, genType const& N)
  {
    return I - N * dot(N, I) * genType(2);
  }

  template<length_t L, typename T, qualifier Q>
  inline vec<L, T, Q> reflect(vec<L, T, Q> const& I, vec<L, T, Q> const& N)
  {
    return detail::compute_reflect<L, T, Q, detail::is_aligned<Q>::value>::call(I, N);
  }

  // refract
  template<typename genType>
  inline genType refract(genType const& I, genType const& N, genType eta)
  {
    static_assert(std::numeric_limits<genType>::is_iec559, "'refract' accepts only floating-point inputs");
    genType const dotValue(dot(N, I));
    genType const k(static_cast<genType>(1) - eta * eta * (static_cast<genType>(1) - dotValue * dotValue));
    return (eta * I - (eta * dotValue + sqrt(k)) * N) * static_cast<genType>(k >= static_cast<genType>(0));
  }

  template<length_t L, typename T, qualifier Q>
  inline vec<L, T, Q> refract(vec<L, T, Q> const& I, vec<L, T, Q> const& N, T eta)
  {
    static_assert(std::numeric_limits<T>::is_iec559, "'refract' accepts only floating-point inputs");
    return detail::compute_refract<L, T, Q, detail::is_aligned<Q>::value>::call(I, N, eta);
  }
}//namespace glm

namespace glm {
  namespace detail
  {
    template<length_t C, length_t R, typename T, qualifier Q, bool Aligned>
    struct compute_matrixCompMult
    {
      inline static mat<C, R, T, Q> call(mat<C, R, T, Q> const& x, mat<C, R, T, Q> const& y)
      {
        mat<C, R, T, Q> Result;
        for (length_t i = 0; i < Result.length(); ++i)
          Result[i] = x[i] * y[i];
        return Result;
      }
    };

    template<length_t C, length_t R, typename T, qualifier Q, bool Aligned>
    struct compute_transpose {};

    template<typename T, qualifier Q, bool Aligned>
    struct compute_transpose<2, 2, T, Q, Aligned>
    {
      inline static mat<2, 2, T, Q> call(mat<2, 2, T, Q> const& m)
      {
        mat<2, 2, T, Q> Result;
        Result[0][0] = m[0][0];
        Result[0][1] = m[1][0];
        Result[1][0] = m[0][1];
        Result[1][1] = m[1][1];
        return Result;
      }
    };

    template<typename T, qualifier Q, bool Aligned>
    struct compute_transpose<2, 3, T, Q, Aligned>
    {
      inline static mat<3, 2, T, Q> call(mat<2, 3, T, Q> const& m)
      {
        mat<3, 2, T, Q> Result;
        Result[0][0] = m[0][0];
        Result[0][1] = m[1][0];
        Result[1][0] = m[0][1];
        Result[1][1] = m[1][1];
        Result[2][0] = m[0][2];
        Result[2][1] = m[1][2];
        return Result;
      }
    };

    template<typename T, qualifier Q, bool Aligned>
    struct compute_transpose<2, 4, T, Q, Aligned>
    {
      inline static mat<4, 2, T, Q> call(mat<2, 4, T, Q> const& m)
      {
        mat<4, 2, T, Q> Result;
        Result[0][0] = m[0][0];
        Result[0][1] = m[1][0];
        Result[1][0] = m[0][1];
        Result[1][1] = m[1][1];
        Result[2][0] = m[0][2];
        Result[2][1] = m[1][2];
        Result[3][0] = m[0][3];
        Result[3][1] = m[1][3];
        return Result;
      }
    };

    template<typename T, qualifier Q, bool Aligned>
    struct compute_transpose<3, 2, T, Q, Aligned>
    {
      inline static mat<2, 3, T, Q> call(mat<3, 2, T, Q> const& m)
      {
        mat<2, 3, T, Q> Result;
        Result[0][0] = m[0][0];
        Result[0][1] = m[1][0];
        Result[0][2] = m[2][0];
        Result[1][0] = m[0][1];
        Result[1][1] = m[1][1];
        Result[1][2] = m[2][1];
        return Result;
      }
    };

    template<typename T, qualifier Q, bool Aligned>
    struct compute_transpose<3, 3, T, Q, Aligned>
    {
      inline static mat<3, 3, T, Q> call(mat<3, 3, T, Q> const& m)
      {
        mat<3, 3, T, Q> Result;
        Result[0][0] = m[0][0];
        Result[0][1] = m[1][0];
        Result[0][2] = m[2][0];

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

        Result[2][0] = m[0][2];
        Result[2][1] = m[1][2];
        Result[2][2] = m[2][2];
        return Result;
      }
    };

    template<typename T, qualifier Q, bool Aligned>
    struct compute_transpose<3, 4, T, Q, Aligned>
    {
      inline static mat<4, 3, T, Q> call(mat<3, 4, T, Q> const& m)
      {
        mat<4, 3, T, Q> Result;
        Result[0][0] = m[0][0];
        Result[0][1] = m[1][0];
        Result[0][2] = m[2][0];
        Result[1][0] = m[0][1];
        Result[1][1] = m[1][1];
        Result[1][2] = m[2][1];
        Result[2][0] = m[0][2];
        Result[2][1] = m[1][2];
        Result[2][2] = m[2][2];
        Result[3][0] = m[0][3];
        Result[3][1] = m[1][3];
        Result[3][2] = m[2][3];
        return Result;
      }
    };

    template<typename T, qualifier Q, bool Aligned>
    struct compute_transpose<4, 2, T, Q, Aligned>
    {
      inline static mat<2, 4, T, Q> call(mat<4, 2, T, Q> const& m)
      {
        mat<2, 4, T, Q> Result;
        Result[0][0] = m[0][0];
        Result[0][1] = m[1][0];
        Result[0][2] = m[2][0];
        Result[0][3] = m[3][0];
        Result[1][0] = m[0][1];
        Result[1][1] = m[1][1];
        Result[1][2] = m[2][1];
        Result[1][3] = m[3][1];
        return Result;
      }
    };

    template<typename T, qualifier Q, bool Aligned>
    struct compute_transpose<4, 3, T, Q, Aligned>
    {
      inline static mat<3, 4, T, Q> call(mat<4, 3, T, Q> const& m)
      {
        mat<3, 4, T, Q> Result;
        Result[0][0] = m[0][0];
        Result[0][1] = m[1][0];
        Result[0][2] = m[2][0];
        Result[0][3] = m[3][0];
        Result[1][0] = m[0][1];
        Result[1][1] = m[1][1];
        Result[1][2] = m[2][1];
        Result[1][3] = m[3][1];
        Result[2][0] = m[0][2];
        Result[2][1] = m[1][2];
        Result[2][2] = m[2][2];
        Result[2][3] = m[3][2];
        return Result;
      }
    };

    template<typename T, qualifier Q, bool Aligned>
    struct compute_transpose<4, 4, T, Q, Aligned>
    {
      inline static mat<4, 4, T, Q> call(mat<4, 4, T, Q> const& m)
      {
        mat<4, 4, T, Q> Result;
        Result[0][0] = m[0][0];
        Result[0][1] = m[1][0];
        Result[0][2] = m[2][0];
        Result[0][3] = m[3][0];

        Result[1][0] = m[0][1];
        Result[1][1] = m[1][1];
        Result[1][2] = m[2][1];
        Result[1][3] = m[3][1];

        Result[2][0] = m[0][2];
        Result[2][1] = m[1][2];
        Result[2][2] = m[2][2];
        Result[2][3] = m[3][2];

        Result[3][0] = m[0][3];
        Result[3][1] = m[1][3];
        Result[3][2] = m[2][3];
        Result[3][3] = m[3][3];
        return Result;
      }
    };

    template<length_t C, length_t R, typename T, qualifier Q, bool Aligned>
    struct compute_determinant {};

    template<typename T, qualifier Q, bool Aligned>
    struct compute_determinant<2, 2, T, Q, Aligned>
    {
      inline static T call(mat<2, 2, T, Q> const& m)
      {
        return m[0][0] * m[1][1] - m[1][0] * m[0][1];
      }
    };

    template<typename T, qualifier Q, bool Aligned>
    struct compute_determinant<3, 3, T, Q, Aligned>
    {
      inline static T call(mat<3, 3, T, Q> const& m)
      {
        return
          +m[0][0] * (m[1][1] * m[2][2] - m[2][1] * m[1][2])
          - m[1][0] * (m[0][1] * m[2][2] - m[2][1] * m[0][2])
          + m[2][0] * (m[0][1] * m[1][2] - m[1][1] * m[0][2]);
      }
    };

    template<typename T, qualifier Q, bool Aligned>
    struct compute_determinant<4, 4, T, Q, Aligned>
    {
      inline static T call(mat<4, 4, T, Q> const& m)
      {
        T SubFactor00 = m[2][2] * m[3][3] - m[3][2] * m[2][3];
        T SubFactor01 = m[2][1] * m[3][3] - m[3][1] * m[2][3];
        T SubFactor02 = m[2][1] * m[3][2] - m[3][1] * m[2][2];
        T SubFactor03 = m[2][0] * m[3][3] - m[3][0] * m[2][3];
        T SubFactor04 = m[2][0] * m[3][2] - m[3][0] * m[2][2];
        T SubFactor05 = m[2][0] * m[3][1] - m[3][0] * m[2][1];

        vec<4, T, Q> DetCof(
          +(m[1][1] * SubFactor00 - m[1][2] * SubFactor01 + m[1][3] * SubFactor02),
          -(m[1][0] * SubFactor00 - m[1][2] * SubFactor03 + m[1][3] * SubFactor04),
          +(m[1][0] * SubFactor01 - m[1][1] * SubFactor03 + m[1][3] * SubFactor05),
          -(m[1][0] * SubFactor02 - m[1][1] * SubFactor04 + m[1][2] * SubFactor05));

        return
          m[0][0] * DetCof[0] + m[0][1] * DetCof[1] +
          m[0][2] * DetCof[2] + m[0][3] * DetCof[3];
      }
    };

    template<length_t C, length_t R, typename T, qualifier Q, bool Aligned>
    struct compute_inverse {};

    template<typename T, qualifier Q, bool Aligned>
    struct compute_inverse<2, 2, T, Q, Aligned>
    {
      inline static mat<2, 2, T, Q> call(mat<2, 2, T, Q> const& m)
      {
        T OneOverDeterminant = static_cast<T>(1) / (
          +m[0][0] * m[1][1]
          - m[1][0] * m[0][1]);

        mat<2, 2, T, Q> Inverse(
          +m[1][1] * OneOverDeterminant,
          -m[0][1] * OneOverDeterminant,
          -m[1][0] * OneOverDeterminant,
          +m[0][0] * OneOverDeterminant);

        return Inverse;
      }
    };

    template<typename T, qualifier Q, bool Aligned>
    struct compute_inverse<3, 3, T, Q, Aligned>
    {
      inline static mat<3, 3, T, Q> call(mat<3, 3, T, Q> const& m)
      {
        T OneOverDeterminant = static_cast<T>(1) / (
          +m[0][0] * (m[1][1] * m[2][2] - m[2][1] * m[1][2])
          - m[1][0] * (m[0][1] * m[2][2] - m[2][1] * m[0][2])
          + m[2][0] * (m[0][1] * m[1][2] - m[1][1] * m[0][2]));

        mat<3, 3, T, Q> Inverse;
        Inverse[0][0] = +(m[1][1] * m[2][2] - m[2][1] * m[1][2]) * OneOverDeterminant;
        Inverse[1][0] = -(m[1][0] * m[2][2] - m[2][0] * m[1][2]) * OneOverDeterminant;
        Inverse[2][0] = +(m[1][0] * m[2][1] - m[2][0] * m[1][1]) * OneOverDeterminant;
        Inverse[0][1] = -(m[0][1] * m[2][2] - m[2][1] * m[0][2]) * OneOverDeterminant;
        Inverse[1][1] = +(m[0][0] * m[2][2] - m[2][0] * m[0][2]) * OneOverDeterminant;
        Inverse[2][1] = -(m[0][0] * m[2][1] - m[2][0] * m[0][1]) * OneOverDeterminant;
        Inverse[0][2] = +(m[0][1] * m[1][2] - m[1][1] * m[0][2]) * OneOverDeterminant;
        Inverse[1][2] = -(m[0][0] * m[1][2] - m[1][0] * m[0][2]) * OneOverDeterminant;
        Inverse[2][2] = +(m[0][0] * m[1][1] - m[1][0] * m[0][1]) * OneOverDeterminant;

        return Inverse;
      }
    };

    template<typename T, qualifier Q, bool Aligned>
    struct compute_inverse<4, 4, T, Q, Aligned>
    {
      inline static mat<4, 4, T, Q> call(mat<4, 4, T, Q> const& m)
      {
        T Coef00 = m[2][2] * m[3][3] - m[3][2] * m[2][3];
        T Coef02 = m[1][2] * m[3][3] - m[3][2] * m[1][3];
        T Coef03 = m[1][2] * m[2][3] - m[2][2] * m[1][3];

        T Coef04 = m[2][1] * m[3][3] - m[3][1] * m[2][3];
        T Coef06 = m[1][1] * m[3][3] - m[3][1] * m[1][3];
        T Coef07 = m[1][1] * m[2][3] - m[2][1] * m[1][3];

        T Coef08 = m[2][1] * m[3][2] - m[3][1] * m[2][2];
        T Coef10 = m[1][1] * m[3][2] - m[3][1] * m[1][2];
        T Coef11 = m[1][1] * m[2][2] - m[2][1] * m[1][2];

        T Coef12 = m[2][0] * m[3][3] - m[3][0] * m[2][3];
        T Coef14 = m[1][0] * m[3][3] - m[3][0] * m[1][3];
        T Coef15 = m[1][0] * m[2][3] - m[2][0] * m[1][3];

        T Coef16 = m[2][0] * m[3][2] - m[3][0] * m[2][2];
        T Coef18 = m[1][0] * m[3][2] - m[3][0] * m[1][2];
        T Coef19 = m[1][0] * m[2][2] - m[2][0] * m[1][2];

        T Coef20 = m[2][0] * m[3][1] - m[3][0] * m[2][1];
        T Coef22 = m[1][0] * m[3][1] - m[3][0] * m[1][1];
        T Coef23 = m[1][0] * m[2][1] - m[2][0] * m[1][1];

        vec<4, T, Q> Fac0(Coef00, Coef00, Coef02, Coef03);
        vec<4, T, Q> Fac1(Coef04, Coef04, Coef06, Coef07);
        vec<4, T, Q> Fac2(Coef08, Coef08, Coef10, Coef11);
        vec<4, T, Q> Fac3(Coef12, Coef12, Coef14, Coef15);
        vec<4, T, Q> Fac4(Coef16, Coef16, Coef18, Coef19);
        vec<4, T, Q> Fac5(Coef20, Coef20, Coef22, Coef23);

        vec<4, T, Q> Vec0(m[1][0], m[0][0], m[0][0], m[0][0]);
        vec<4, T, Q> Vec1(m[1][1], m[0][1], m[0][1], m[0][1]);
        vec<4, T, Q> Vec2(m[1][2], m[0][2], m[0][2], m[0][2]);
        vec<4, T, Q> Vec3(m[1][3], m[0][3], m[0][3], m[0][3]);

        vec<4, T, Q> Inv0(Vec1 * Fac0 - Vec2 * Fac1 + Vec3 * Fac2);
        vec<4, T, Q> Inv1(Vec0 * Fac0 - Vec2 * Fac3 + Vec3 * Fac4);
        vec<4, T, Q> Inv2(Vec0 * Fac1 - Vec1 * Fac3 + Vec3 * Fac5);
        vec<4, T, Q> Inv3(Vec0 * Fac2 - Vec1 * Fac4 + Vec2 * Fac5);

        vec<4, T, Q> SignA(+1, -1, +1, -1);
        vec<4, T, Q> SignB(-1, +1, -1, +1);
        mat<4, 4, T, Q> Inverse(Inv0 * SignA, Inv1 * SignB, Inv2 * SignA, Inv3 * SignB);

        vec<4, T, Q> Row0(Inverse[0][0], Inverse[1][0], Inverse[2][0], Inverse[3][0]);

        vec<4, T, Q> Dot0(m[0] * Row0);
        T Dot1 = (Dot0.x + Dot0.y) + (Dot0.z + Dot0.w);

        T OneOverDeterminant = static_cast<T>(1) / Dot1;

        return Inverse * OneOverDeterminant;
      }
    };
  }//namespace detail

  template<length_t C, length_t R, typename T, qualifier Q>
  inline mat<C, R, T, Q> matrixCompMult(mat<C, R, T, Q> const& x, mat<C, R, T, Q> const& y)
  {
    static_assert(std::numeric_limits<T>::is_iec559 || 0, "'matrixCompMult' only accept floating-point inputs");
    return detail::compute_matrixCompMult<C, R, T, Q, detail::is_aligned<Q>::value>::call(x, y);
  }

  template<length_t DA, length_t DB, typename T, qualifier Q>
  inline typename detail::outerProduct_trait<DA, DB, T, Q>::type outerProduct(vec<DA, T, Q> const& c, vec<DB, T, Q> const& r)
  {
    static_assert(std::numeric_limits<T>::is_iec559 || 0, "'outerProduct' only accept floating-point inputs");

    typename detail::outerProduct_trait<DA, DB, T, Q>::type m;
    for (length_t i = 0; i < m.length(); ++i)
      m[i] = c * r[i];
    return m;
  }

  template<length_t C, length_t R, typename T, qualifier Q>
  inline typename mat<C, R, T, Q>::transpose_type transpose(mat<C, R, T, Q> const& m)
  {
    static_assert(std::numeric_limits<T>::is_iec559 || 0, "'transpose' only accept floating-point inputs");
    return detail::compute_transpose<C, R, T, Q, detail::is_aligned<Q>::value>::call(m);
  }

  template<length_t C, length_t R, typename T, qualifier Q>
  inline T determinant(mat<C, R, T, Q> const& m)
  {
    static_assert(std::numeric_limits<T>::is_iec559 || 0, "'determinant' only accept floating-point inputs");
    return detail::compute_determinant<C, R, T, Q, detail::is_aligned<Q>::value>::call(m);
  }

  template<length_t C, length_t R, typename T, qualifier Q>
  inline mat<C, R, T, Q> inverse(mat<C, R, T, Q> const& m)
  {
    static_assert(std::numeric_limits<T>::is_iec559 || 0, "'inverse' only accept floating-point inputs");
    return detail::compute_inverse<C, R, T, Q, detail::is_aligned<Q>::value>::call(m);
  }
}//namespace glm

namespace glm
{
  template<typename T, qualifier Q>
  template<qualifier P>
  inline constexpr mat<4, 4, T, Q>::mat(mat<4, 4, T, P> const& m)

    : value{ col_type(m[0]), col_type(m[1]), col_type(m[2]), col_type(m[3]) }
  {
  }

  template<typename T, qualifier Q>
  inline constexpr mat<4, 4, T, Q>::mat(T const& s)

    : value{ col_type(s, 0, 0, 0), col_type(0, s, 0, 0), col_type(0, 0, s, 0), col_type(0, 0, 0, s) }

  {
  }

  template<typename T, qualifier Q>
  inline constexpr mat<4, 4, T, Q>::mat
  (
    T const& x0, T const& y0, T const& z0, T const& w0,
    T const& x1, T const& y1, T const& z1, T const& w1,
    T const& x2, T const& y2, T const& z2, T const& w2,
    T const& x3, T const& y3, T const& z3, T const& w3
  )

    : value{
      col_type(x0, y0, z0, w0),
      col_type(x1, y1, z1, w1),
      col_type(x2, y2, z2, w2),
      col_type(x3, y3, z3, w3) }

  {
  }

  template<typename T, qualifier Q>
  inline constexpr mat<4, 4, T, Q>::mat(col_type const& v0, col_type const& v1, col_type const& v2, col_type const& v3)

    : value{ col_type(v0), col_type(v1), col_type(v2), col_type(v3) }

  {
  }

  template<typename T, qualifier Q>
  template<typename U, qualifier P>
  inline constexpr mat<4, 4, T, Q>::mat(mat<4, 4, U, P> const& m)

    : value{ col_type(m[0]), col_type(m[1]), col_type(m[2]), col_type(m[3]) }

  {
  }

  // -- Conversions --

  template<typename T, qualifier Q>
  template<
    typename X1, typename Y1, typename Z1, typename W1,
    typename X2, typename Y2, typename Z2, typename W2,
    typename X3, typename Y3, typename Z3, typename W3,
    typename X4, typename Y4, typename Z4, typename W4>
  inline constexpr mat<4, 4, T, Q>::mat
  (
    X1 const& x1, Y1 const& y1, Z1 const& z1, W1 const& w1,
    X2 const& x2, Y2 const& y2, Z2 const& z2, W2 const& w2,
    X3 const& x3, Y3 const& y3, Z3 const& z3, W3 const& w3,
    X4 const& x4, Y4 const& y4, Z4 const& z4, W4 const& w4
  )

    : value{ col_type(x1, y1, z1, w1), col_type(x2, y2, z2, w2), col_type(x3, y3, z3, w3), col_type(x4, y4, z4, w4) }

  {
    static_assert(std::numeric_limits<X1>::is_iec559 || std::numeric_limits<X1>::is_integer || 0, "*mat4x4 constructor only takes float and integer types, 1st parameter type invalid.");
    static_assert(std::numeric_limits<Y1>::is_iec559 || std::numeric_limits<Y1>::is_integer || 0, "*mat4x4 constructor only takes float and integer types, 2nd parameter type invalid.");
    static_assert(std::numeric_limits<Z1>::is_iec559 || std::numeric_limits<Z1>::is_integer || 0, "*mat4x4 constructor only takes float and integer types, 3rd parameter type invalid.");
    static_assert(std::numeric_limits<W1>::is_iec559 || std::numeric_limits<W1>::is_integer || 0, "*mat4x4 constructor only takes float and integer types, 4th parameter type invalid.");

    static_assert(std::numeric_limits<X2>::is_iec559 || std::numeric_limits<X2>::is_integer || 0, "*mat4x4 constructor only takes float and integer types, 5th parameter type invalid.");
    static_assert(std::numeric_limits<Y2>::is_iec559 || std::numeric_limits<Y2>::is_integer || 0, "*mat4x4 constructor only takes float and integer types, 6th parameter type invalid.");
    static_assert(std::numeric_limits<Z2>::is_iec559 || std::numeric_limits<Z2>::is_integer || 0, "*mat4x4 constructor only takes float and integer types, 7th parameter type invalid.");
    static_assert(std::numeric_limits<W2>::is_iec559 || std::numeric_limits<W2>::is_integer || 0, "*mat4x4 constructor only takes float and integer types, 8th parameter type invalid.");

    static_assert(std::numeric_limits<X3>::is_iec559 || std::numeric_limits<X3>::is_integer || 0, "*mat4x4 constructor only takes float and integer types, 9th parameter type invalid.");
    static_assert(std::numeric_limits<Y3>::is_iec559 || std::numeric_limits<Y3>::is_integer || 0, "*mat4x4 constructor only takes float and integer types, 10th parameter type invalid.");
    static_assert(std::numeric_limits<Z3>::is_iec559 || std::numeric_limits<Z3>::is_integer || 0, "*mat4x4 constructor only takes float and integer types, 11th parameter type invalid.");
    static_assert(std::numeric_limits<W3>::is_iec559 || std::numeric_limits<W3>::is_integer || 0, "*mat4x4 constructor only takes float and integer types, 12th parameter type invalid.");

    static_assert(std::numeric_limits<X4>::is_iec559 || std::numeric_limits<X4>::is_integer || 0, "*mat4x4 constructor only takes float and integer types, 13th parameter type invalid.");
    static_assert(std::numeric_limits<Y4>::is_iec559 || std::numeric_limits<Y4>::is_integer || 0, "*mat4x4 constructor only takes float and integer types, 14th parameter type invalid.");
    static_assert(std::numeric_limits<Z4>::is_iec559 || std::numeric_limits<Z4>::is_integer || 0, "*mat4x4 constructor only takes float and integer types, 15th parameter type invalid.");
    static_assert(std::numeric_limits<W4>::is_iec559 || std::numeric_limits<W4>::is_integer || 0, "*mat4x4 constructor only takes float and integer types, 16th parameter type invalid.");
  }

  template<typename T, qualifier Q>
  template<typename V1, typename V2, typename V3, typename V4>
  inline constexpr mat<4, 4, T, Q>::mat(vec<4, V1, Q> const& v1, vec<4, V2, Q> const& v2, vec<4, V3, Q> const& v3, vec<4, V4, Q> const& v4)

    : value{ col_type(v1), col_type(v2), col_type(v3), col_type(v4) }

  {
    static_assert(std::numeric_limits<V1>::is_iec559 || std::numeric_limits<V1>::is_integer || 0, "*mat4x4 constructor only takes float and integer types, 1st parameter type invalid.");
    static_assert(std::numeric_limits<V2>::is_iec559 || std::numeric_limits<V2>::is_integer || 0, "*mat4x4 constructor only takes float and integer types, 2nd parameter type invalid.");
    static_assert(std::numeric_limits<V3>::is_iec559 || std::numeric_limits<V3>::is_integer || 0, "*mat4x4 constructor only takes float and integer types, 3rd parameter type invalid.");
    static_assert(std::numeric_limits<V4>::is_iec559 || std::numeric_limits<V4>::is_integer || 0, "*mat4x4 constructor only takes float and integer types, 4th parameter type invalid.");
  }

  // -- Matrix conversions --

  template<typename T, qualifier Q>
  inline constexpr mat<4, 4, T, Q>::mat(mat<2, 2, T, Q> const& m)

    : value{ col_type(m[0], 0, 0), col_type(m[1], 0, 0), col_type(0, 0, 1, 0), col_type(0, 0, 0, 1) }

  {
  }

  template<typename T, qualifier Q>
  inline constexpr mat<4, 4, T, Q>::mat(mat<3, 3, T, Q> const& m)

    : value{ col_type(m[0], 0), col_type(m[1], 0), col_type(m[2], 0), col_type(0, 0, 0, 1) }

  {
  }

  template<typename T, qualifier Q>
  inline constexpr mat<4, 4, T, Q>::mat(mat<2, 3, T, Q> const& m)

    : value{ col_type(m[0], 0), col_type(m[1], 0), col_type(0, 0, 1, 0), col_type(0, 0, 0, 1) }

  {
  }

  template<typename T, qualifier Q>
  inline constexpr mat<4, 4, T, Q>::mat(mat<3, 2, T, Q> const& m)

    : value{ col_type(m[0], 0, 0), col_type(m[1], 0, 0), col_type(m[2], 1, 0), col_type(0, 0, 0, 1) }

  {
  }

  template<typename T, qualifier Q>
  inline constexpr mat<4, 4, T, Q>::mat(mat<2, 4, T, Q> const& m)

    : value{ col_type(m[0]), col_type(m[1]), col_type(0, 0, 1, 0), col_type(0, 0, 0, 1) }

  {
  }

  template<typename T, qualifier Q>
  inline constexpr mat<4, 4, T, Q>::mat(mat<4, 2, T, Q> const& m)

    : value{ col_type(m[0], 0, 0), col_type(m[1], 0, 0), col_type(0, 0, 1, 0), col_type(0, 0, 0, 1) }

  {
  }

  template<typename T, qualifier Q>
  inline constexpr mat<4, 4, T, Q>::mat(mat<3, 4, T, Q> const& m)

    : value{ col_type(m[0]), col_type(m[1]), col_type(m[2]), col_type(0, 0, 0, 1) }

  {
  }

  template<typename T, qualifier Q>
  inline constexpr mat<4, 4, T, Q>::mat(mat<4, 3, T, Q> const& m)

    : value{ col_type(m[0], 0), col_type(m[1], 0), col_type(m[2], 0), col_type(m[3], 1) }

  {
  }

  // -- Accesses --

  template<typename T, qualifier Q>
  inline typename mat<4, 4, T, Q>::col_type& mat<4, 4, T, Q>::operator[](typename mat<4, 4, T, Q>::length_type i)
  {
    assert(i < this->length());
    return this->value[i];
  }

  template<typename T, qualifier Q>
  inline constexpr typename mat<4, 4, T, Q>::col_type const& mat<4, 4, T, Q>::operator[](typename mat<4, 4, T, Q>::length_type i) const
  {
    assert(i < this->length());
    return this->value[i];
  }

  // -- Unary arithmetic operators --

  template<typename T, qualifier Q>
  template<typename U>
  inline mat<4, 4, T, Q>& mat<4, 4, T, Q>::operator=(mat<4, 4, U, Q> const& m)
  {
    //memcpy could be faster
    //memcpy(&this->value, &m.value, 16 * sizeof(valType));
    this->value[0] = m[0];
    this->value[1] = m[1];
    this->value[2] = m[2];
    this->value[3] = m[3];
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline mat<4, 4, T, Q>& mat<4, 4, T, Q>::operator+=(U s)
  {
    this->value[0] += s;
    this->value[1] += s;
    this->value[2] += s;
    this->value[3] += s;
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline mat<4, 4, T, Q>& mat<4, 4, T, Q>::operator+=(mat<4, 4, U, Q> const& m)
  {
    this->value[0] += m[0];
    this->value[1] += m[1];
    this->value[2] += m[2];
    this->value[3] += m[3];
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline mat<4, 4, T, Q>& mat<4, 4, T, Q>::operator-=(U s)
  {
    this->value[0] -= s;
    this->value[1] -= s;
    this->value[2] -= s;
    this->value[3] -= s;
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline mat<4, 4, T, Q>& mat<4, 4, T, Q>::operator-=(mat<4, 4, U, Q> const& m)
  {
    this->value[0] -= m[0];
    this->value[1] -= m[1];
    this->value[2] -= m[2];
    this->value[3] -= m[3];
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline mat<4, 4, T, Q>& mat<4, 4, T, Q>::operator*=(U s)
  {
    this->value[0] *= s;
    this->value[1] *= s;
    this->value[2] *= s;
    this->value[3] *= s;
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline mat<4, 4, T, Q>& mat<4, 4, T, Q>::operator*=(mat<4, 4, U, Q> const& m)
  {
    return (*this = *this * m);
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline mat<4, 4, T, Q>& mat<4, 4, T, Q>::operator/=(U s)
  {
    this->value[0] /= s;
    this->value[1] /= s;
    this->value[2] /= s;
    this->value[3] /= s;
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline mat<4, 4, T, Q>& mat<4, 4, T, Q>::operator/=(mat<4, 4, U, Q> const& m)
  {
    return *this *= inverse(m);
  }

  // -- Increment and decrement operators --

  template<typename T, qualifier Q>
  inline mat<4, 4, T, Q>& mat<4, 4, T, Q>::operator++()
  {
    ++this->value[0];
    ++this->value[1];
    ++this->value[2];
    ++this->value[3];
    return *this;
  }

  template<typename T, qualifier Q>
  inline mat<4, 4, T, Q>& mat<4, 4, T, Q>::operator--()
  {
    --this->value[0];
    --this->value[1];
    --this->value[2];
    --this->value[3];
    return *this;
  }

  template<typename T, qualifier Q>
  inline mat<4, 4, T, Q> mat<4, 4, T, Q>::operator++(int)
  {
    mat<4, 4, T, Q> Result(*this);
    ++*this;
    return Result;
  }

  template<typename T, qualifier Q>
  inline mat<4, 4, T, Q> mat<4, 4, T, Q>::operator--(int)
  {
    mat<4, 4, T, Q> Result(*this);
    --*this;
    return Result;
  }

  // -- Unary constant operators --

  template<typename T, qualifier Q>
  inline mat<4, 4, T, Q> operator+(mat<4, 4, T, Q> const& m)
  {
    return m;
  }

  template<typename T, qualifier Q>
  inline mat<4, 4, T, Q> operator-(mat<4, 4, T, Q> const& m)
  {
    return mat<4, 4, T, Q>(
      -m[0],
      -m[1],
      -m[2],
      -m[3]);
  }

  // -- Binary arithmetic operators --

  template<typename T, qualifier Q>
  inline mat<4, 4, T, Q> operator+(mat<4, 4, T, Q> const& m, T const& s)
  {
    return mat<4, 4, T, Q>(
      m[0] + s,
      m[1] + s,
      m[2] + s,
      m[3] + s);
  }

  template<typename T, qualifier Q>
  inline mat<4, 4, T, Q> operator+(T const& s, mat<4, 4, T, Q> const& m)
  {
    return mat<4, 4, T, Q>(
      m[0] + s,
      m[1] + s,
      m[2] + s,
      m[3] + s);
  }

  template<typename T, qualifier Q>
  inline mat<4, 4, T, Q> operator+(mat<4, 4, T, Q> const& m1, mat<4, 4, T, Q> const& m2)
  {
    return mat<4, 4, T, Q>(
      m1[0] + m2[0],
      m1[1] + m2[1],
      m1[2] + m2[2],
      m1[3] + m2[3]);
  }

  template<typename T, qualifier Q>
  inline mat<4, 4, T, Q> operator-(mat<4, 4, T, Q> const& m, T const& s)
  {
    return mat<4, 4, T, Q>(
      m[0] - s,
      m[1] - s,
      m[2] - s,
      m[3] - s);
  }

  template<typename T, qualifier Q>
  inline mat<4, 4, T, Q> operator-(T const& s, mat<4, 4, T, Q> const& m)
  {
    return mat<4, 4, T, Q>(
      s - m[0],
      s - m[1],
      s - m[2],
      s - m[3]);
  }

  template<typename T, qualifier Q>
  inline mat<4, 4, T, Q> operator-(mat<4, 4, T, Q> const& m1, mat<4, 4, T, Q> const& m2)
  {
    return mat<4, 4, T, Q>(
      m1[0] - m2[0],
      m1[1] - m2[1],
      m1[2] - m2[2],
      m1[3] - m2[3]);
  }

  template<typename T, qualifier Q>
  inline mat<4, 4, T, Q> operator*(mat<4, 4, T, Q> const& m, T const& s)
  {
    return mat<4, 4, T, Q>(
      m[0] * s,
      m[1] * s,
      m[2] * s,
      m[3] * s);
  }

  template<typename T, qualifier Q>
  inline mat<4, 4, T, Q> operator*(T const& s, mat<4, 4, T, Q> const& m)
  {
    return mat<4, 4, T, Q>(
      m[0] * s,
      m[1] * s,
      m[2] * s,
      m[3] * s);
  }

  template<typename T, qualifier Q>
  inline typename mat<4, 4, T, Q>::col_type operator*
    (
      mat<4, 4, T, Q> const& m,
      typename mat<4, 4, T, Q>::row_type const& v
      )
  {
    /*
        __m128 v0 = _mm_shuffle_ps(v.data, v.data, _MM_SHUFFLE(0, 0, 0, 0));
        __m128 v1 = _mm_shuffle_ps(v.data, v.data, _MM_SHUFFLE(1, 1, 1, 1));
        __m128 v2 = _mm_shuffle_ps(v.data, v.data, _MM_SHUFFLE(2, 2, 2, 2));
        __m128 v3 = _mm_shuffle_ps(v.data, v.data, _MM_SHUFFLE(3, 3, 3, 3));

        __m128 m0 = _mm_mul_ps(m[0].data, v0);
        __m128 m1 = _mm_mul_ps(m[1].data, v1);
        __m128 a0 = _mm_add_ps(m0, m1);

        __m128 m2 = _mm_mul_ps(m[2].data, v2);
        __m128 m3 = _mm_mul_ps(m[3].data, v3);
        __m128 a1 = _mm_add_ps(m2, m3);

        __m128 a2 = _mm_add_ps(a0, a1);

        return typename mat<4, 4, T, Q>::col_type(a2);
    */

    typename mat<4, 4, T, Q>::col_type const Mov0(v[0]);
    typename mat<4, 4, T, Q>::col_type const Mov1(v[1]);
    typename mat<4, 4, T, Q>::col_type const Mul0 = m[0] * Mov0;
    typename mat<4, 4, T, Q>::col_type const Mul1 = m[1] * Mov1;
    typename mat<4, 4, T, Q>::col_type const Add0 = Mul0 + Mul1;
    typename mat<4, 4, T, Q>::col_type const Mov2(v[2]);
    typename mat<4, 4, T, Q>::col_type const Mov3(v[3]);
    typename mat<4, 4, T, Q>::col_type const Mul2 = m[2] * Mov2;
    typename mat<4, 4, T, Q>::col_type const Mul3 = m[3] * Mov3;
    typename mat<4, 4, T, Q>::col_type const Add1 = Mul2 + Mul3;
    typename mat<4, 4, T, Q>::col_type const Add2 = Add0 + Add1;
    return Add2;

    /*
        return typename mat<4, 4, T, Q>::col_type(
          m[0][0] * v[0] + m[1][0] * v[1] + m[2][0] * v[2] + m[3][0] * v[3],
          m[0][1] * v[0] + m[1][1] * v[1] + m[2][1] * v[2] + m[3][1] * v[3],
          m[0][2] * v[0] + m[1][2] * v[1] + m[2][2] * v[2] + m[3][2] * v[3],
          m[0][3] * v[0] + m[1][3] * v[1] + m[2][3] * v[2] + m[3][3] * v[3]);
    */
  }

  template<typename T, qualifier Q>
  inline typename mat<4, 4, T, Q>::row_type operator*
    (
      typename mat<4, 4, T, Q>::col_type const& v,
      mat<4, 4, T, Q> const& m
      )
  {
    return typename mat<4, 4, T, Q>::row_type(
      m[0][0] * v[0] + m[0][1] * v[1] + m[0][2] * v[2] + m[0][3] * v[3],
      m[1][0] * v[0] + m[1][1] * v[1] + m[1][2] * v[2] + m[1][3] * v[3],
      m[2][0] * v[0] + m[2][1] * v[1] + m[2][2] * v[2] + m[2][3] * v[3],
      m[3][0] * v[0] + m[3][1] * v[1] + m[3][2] * v[2] + m[3][3] * v[3]);
  }

  template<typename T, qualifier Q>
  inline mat<2, 4, T, Q> operator*(mat<4, 4, T, Q> const& m1, mat<2, 4, T, Q> const& m2)
  {
    return mat<2, 4, T, Q>(
      m1[0][0] * m2[0][0] + m1[1][0] * m2[0][1] + m1[2][0] * m2[0][2] + m1[3][0] * m2[0][3],
      m1[0][1] * m2[0][0] + m1[1][1] * m2[0][1] + m1[2][1] * m2[0][2] + m1[3][1] * m2[0][3],
      m1[0][2] * m2[0][0] + m1[1][2] * m2[0][1] + m1[2][2] * m2[0][2] + m1[3][2] * m2[0][3],
      m1[0][3] * m2[0][0] + m1[1][3] * m2[0][1] + m1[2][3] * m2[0][2] + m1[3][3] * m2[0][3],
      m1[0][0] * m2[1][0] + m1[1][0] * m2[1][1] + m1[2][0] * m2[1][2] + m1[3][0] * m2[1][3],
      m1[0][1] * m2[1][0] + m1[1][1] * m2[1][1] + m1[2][1] * m2[1][2] + m1[3][1] * m2[1][3],
      m1[0][2] * m2[1][0] + m1[1][2] * m2[1][1] + m1[2][2] * m2[1][2] + m1[3][2] * m2[1][3],
      m1[0][3] * m2[1][0] + m1[1][3] * m2[1][1] + m1[2][3] * m2[1][2] + m1[3][3] * m2[1][3]);
  }

  template<typename T, qualifier Q>
  inline mat<3, 4, T, Q> operator*(mat<4, 4, T, Q> const& m1, mat<3, 4, T, Q> const& m2)
  {
    return mat<3, 4, T, Q>(
      m1[0][0] * m2[0][0] + m1[1][0] * m2[0][1] + m1[2][0] * m2[0][2] + m1[3][0] * m2[0][3],
      m1[0][1] * m2[0][0] + m1[1][1] * m2[0][1] + m1[2][1] * m2[0][2] + m1[3][1] * m2[0][3],
      m1[0][2] * m2[0][0] + m1[1][2] * m2[0][1] + m1[2][2] * m2[0][2] + m1[3][2] * m2[0][3],
      m1[0][3] * m2[0][0] + m1[1][3] * m2[0][1] + m1[2][3] * m2[0][2] + m1[3][3] * m2[0][3],
      m1[0][0] * m2[1][0] + m1[1][0] * m2[1][1] + m1[2][0] * m2[1][2] + m1[3][0] * m2[1][3],
      m1[0][1] * m2[1][0] + m1[1][1] * m2[1][1] + m1[2][1] * m2[1][2] + m1[3][1] * m2[1][3],
      m1[0][2] * m2[1][0] + m1[1][2] * m2[1][1] + m1[2][2] * m2[1][2] + m1[3][2] * m2[1][3],
      m1[0][3] * m2[1][0] + m1[1][3] * m2[1][1] + m1[2][3] * m2[1][2] + m1[3][3] * m2[1][3],
      m1[0][0] * m2[2][0] + m1[1][0] * m2[2][1] + m1[2][0] * m2[2][2] + m1[3][0] * m2[2][3],
      m1[0][1] * m2[2][0] + m1[1][1] * m2[2][1] + m1[2][1] * m2[2][2] + m1[3][1] * m2[2][3],
      m1[0][2] * m2[2][0] + m1[1][2] * m2[2][1] + m1[2][2] * m2[2][2] + m1[3][2] * m2[2][3],
      m1[0][3] * m2[2][0] + m1[1][3] * m2[2][1] + m1[2][3] * m2[2][2] + m1[3][3] * m2[2][3]);
  }

  template<typename T, qualifier Q>
  inline mat<4, 4, T, Q> operator*(mat<4, 4, T, Q> const& m1, mat<4, 4, T, Q> const& m2)
  {
    typename mat<4, 4, T, Q>::col_type const SrcA0 = m1[0];
    typename mat<4, 4, T, Q>::col_type const SrcA1 = m1[1];
    typename mat<4, 4, T, Q>::col_type const SrcA2 = m1[2];
    typename mat<4, 4, T, Q>::col_type const SrcA3 = m1[3];

    typename mat<4, 4, T, Q>::col_type const SrcB0 = m2[0];
    typename mat<4, 4, T, Q>::col_type const SrcB1 = m2[1];
    typename mat<4, 4, T, Q>::col_type const SrcB2 = m2[2];
    typename mat<4, 4, T, Q>::col_type const SrcB3 = m2[3];

    mat<4, 4, T, Q> Result;
    Result[0] = SrcA0 * SrcB0[0] + SrcA1 * SrcB0[1] + SrcA2 * SrcB0[2] + SrcA3 * SrcB0[3];
    Result[1] = SrcA0 * SrcB1[0] + SrcA1 * SrcB1[1] + SrcA2 * SrcB1[2] + SrcA3 * SrcB1[3];
    Result[2] = SrcA0 * SrcB2[0] + SrcA1 * SrcB2[1] + SrcA2 * SrcB2[2] + SrcA3 * SrcB2[3];
    Result[3] = SrcA0 * SrcB3[0] + SrcA1 * SrcB3[1] + SrcA2 * SrcB3[2] + SrcA3 * SrcB3[3];
    return Result;
  }

  template<typename T, qualifier Q>
  inline mat<4, 4, T, Q> operator/(mat<4, 4, T, Q> const& m, T const& s)
  {
    return mat<4, 4, T, Q>(
      m[0] / s,
      m[1] / s,
      m[2] / s,
      m[3] / s);
  }

  template<typename T, qualifier Q>
  inline mat<4, 4, T, Q> operator/(T const& s, mat<4, 4, T, Q> const& m)
  {
    return mat<4, 4, T, Q>(
      s / m[0],
      s / m[1],
      s / m[2],
      s / m[3]);
  }

  template<typename T, qualifier Q>
  inline typename mat<4, 4, T, Q>::col_type operator/(mat<4, 4, T, Q> const& m, typename mat<4, 4, T, Q>::row_type const& v)
  {
    return inverse(m) * v;
  }

  template<typename T, qualifier Q>
  inline typename mat<4, 4, T, Q>::row_type operator/(typename mat<4, 4, T, Q>::col_type const& v, mat<4, 4, T, Q> const& m)
  {
    return v * inverse(m);
  }

  template<typename T, qualifier Q>
  inline mat<4, 4, T, Q> operator/(mat<4, 4, T, Q> const& m1, mat<4, 4, T, Q> const& m2)
  {
    mat<4, 4, T, Q> m1_copy(m1);
    return m1_copy /= m2;
  }

  // -- Boolean operators --

  template<typename T, qualifier Q>
  inline bool operator==(mat<4, 4, T, Q> const& m1, mat<4, 4, T, Q> const& m2)
  {
    return (m1[0] == m2[0]) && (m1[1] == m2[1]) && (m1[2] == m2[2]) && (m1[3] == m2[3]);
  }

  template<typename T, qualifier Q>
  inline bool operator!=(mat<4, 4, T, Q> const& m1, mat<4, 4, T, Q> const& m2)
  {
    return (m1[0] != m2[0]) || (m1[1] != m2[1]) || (m1[2] != m2[2]) || (m1[3] != m2[3]);
  }
}//namespace glm








namespace glm
{
  /// @addtogroup core_matrix
  /// @{

  /// 4 columns of 4 components matrix of double-precision floating-point numbers.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  typedef mat<4, 4, double, defaultp>		dmat4x4;

  /// 4 columns of 4 components matrix of double-precision floating-point numbers.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  typedef mat<4, 4, double, defaultp>		dmat4;

  /// @}
}//namespace glm


/// @ref core
/// @file glm/ext/matrix_double4x4_precision.hpp




namespace glm
{
  /// @addtogroup core_matrix_precision
  /// @{

  /// 4 columns of 4 components matrix of double-precision floating-point numbers using low precision arithmetic in term of ULPs.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef mat<4, 4, double, lowp>		lowp_dmat4;

  /// 4 columns of 4 components matrix of double-precision floating-point numbers using medium precision arithmetic in term of ULPs.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef mat<4, 4, double, mediump>	mediump_dmat4;

  /// 4 columns of 4 components matrix of double-precision floating-point numbers using medium precision arithmetic in term of ULPs.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef mat<4, 4, double, highp>	highp_dmat4;

  /// 4 columns of 4 components matrix of double-precision floating-point numbers using low precision arithmetic in term of ULPs.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef mat<4, 4, double, lowp>		lowp_dmat4x4;

  /// 4 columns of 4 components matrix of double-precision floating-point numbers using medium precision arithmetic in term of ULPs.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef mat<4, 4, double, mediump>	mediump_dmat4x4;

  /// 4 columns of 4 components matrix of double-precision floating-point numbers using medium precision arithmetic in term of ULPs.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef mat<4, 4, double, highp>	highp_dmat4x4;

  /// @}
}//namespace glm


/// @ref core
/// @file glm/ext/matrix_float4x4.hpp




namespace glm
{
  /// @ingroup core_matrix
  /// @{

  /// 4 columns of 4 components matrix of single-precision floating-point numbers.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  typedef mat<4, 4, float, defaultp>			mat4x4;

  /// 4 columns of 4 components matrix of single-precision floating-point numbers.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  typedef mat<4, 4, float, defaultp>			mat4;

  /// @}
}//namespace glm


/// @ref core
/// @file glm/ext/matrix_float4x4_precision.hpp




namespace glm
{
  /// @addtogroup core_matrix_precision
  /// @{

  /// 4 columns of 4 components matrix of single-precision floating-point numbers using low precision arithmetic in term of ULPs.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef mat<4, 4, float, lowp>		lowp_mat4;

  /// 4 columns of 4 components matrix of single-precision floating-point numbers using medium precision arithmetic in term of ULPs.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef mat<4, 4, float, mediump>	mediump_mat4;

  /// 4 columns of 4 components matrix of single-precision floating-point numbers using high precision arithmetic in term of ULPs.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef mat<4, 4, float, highp>		highp_mat4;

  /// 4 columns of 4 components matrix of single-precision floating-point numbers using low precision arithmetic in term of ULPs.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef mat<4, 4, float, lowp>		lowp_mat4x4;

  /// 4 columns of 4 components matrix of single-precision floating-point numbers using medium precision arithmetic in term of ULPs.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef mat<4, 4, float, mediump>	mediump_mat4x4;

  /// 4 columns of 4 components matrix of single-precision floating-point numbers using high precision arithmetic in term of ULPs.
  ///
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.1.6 Matrices</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier</a>
  typedef mat<4, 4, float, highp>		highp_mat4x4;

  /// @}
}//namespace glm







/// @ref ext_matrix_projection
/// @file glm/ext/matrix_projection.hpp
///
/// @defgroup ext_matrix_projection GLM_EXT_matrix_projection
/// @ingroup ext
///
/// Functions that generate common projection transformation matrices.
///
/// The matrices generated by this extension use standard OpenGL fixed-function
/// conventions. For example, the lookAt function generates a transform from world
/// space into the specific eye space that the projective matrix functions
/// (perspective, ortho, etc) are designed to expect. The OpenGL compatibility
/// specifications defines the particular layout of this eye space.
///
/// Include <glm/ext/matrix_projection.hpp> to use the features of this extension.
///
/// @see ext_matrix_transform
/// @see ext_matrix_clip_space



// Dependencies

/// @ref gtc_constants
/// @file glm/gtc/constants.hpp
///
/// @see core (dependence)
///
/// @defgroup gtc_constants GLM_GTC_constants
/// @ingroup gtc
///
/// Include <glm/gtc/constants.hpp> to use the features of this extension.
///
/// Provide a list of constants and precomputed useful values.



// Dependencies

/// @ref ext_scalar_constants
/// @file glm/ext/scalar_constants.hpp
///
/// @defgroup ext_scalar_constants GLM_EXT_scalar_constants
/// @ingroup ext
///
/// Provides a list of constants and precomputed useful values.
///
/// Include <glm/ext/scalar_constants.hpp> to use the features of this extension.


namespace glm
{
  /// @addtogroup ext_scalar_constants
  /// @{

  /// Return the epsilon constant for floating point types.
  template<typename genType>
  constexpr genType epsilon();

  /// Return the pi constant for floating point types.
  template<typename genType>
  constexpr genType pi();

  /// Return the value of cos(1 / 2) for floating point types.
  template<typename genType>
  constexpr genType cos_one_over_two();

  /// @}
} //namespace glm

namespace glm
{
  template<typename genType>
  inline constexpr genType epsilon()
  {
    static_assert(std::numeric_limits<genType>::is_iec559, "'epsilon' only accepts floating-point inputs");
    return std::numeric_limits<genType>::epsilon();
  }

  template<typename genType>
  inline constexpr genType pi()
  {
    static_assert(std::numeric_limits<genType>::is_iec559, "'pi' only accepts floating-point inputs");
    return static_cast<genType>(3.14159265358979323846264338327950288);
  }

  template<typename genType>
  inline constexpr genType cos_one_over_two()
  {
    return genType(0.877582561890372716130286068203503191);
  }
} //namespace glm

namespace glm
{
  /// @addtogroup gtc_constants
  /// @{

  /// Return 0.
  /// @see gtc_constants
  template<typename genType>
  constexpr genType zero();

  /// Return 1.
  /// @see gtc_constants
  template<typename genType>
  constexpr genType one();

  /// Return pi * 2.
  /// @see gtc_constants
  template<typename genType>
  constexpr genType two_pi();

  /// Return square root of pi.
  /// @see gtc_constants
  template<typename genType>
  constexpr genType root_pi();

  /// Return pi / 2.
  /// @see gtc_constants
  template<typename genType>
  constexpr genType half_pi();

  /// Return pi / 2 * 3.
  /// @see gtc_constants
  template<typename genType>
  constexpr genType three_over_two_pi();

  /// Return pi / 4.
  /// @see gtc_constants
  template<typename genType>
  constexpr genType quarter_pi();

  /// Return 1 / pi.
  /// @see gtc_constants
  template<typename genType>
  constexpr genType one_over_pi();

  /// Return 1 / (pi * 2).
  /// @see gtc_constants
  template<typename genType>
  constexpr genType one_over_two_pi();

  /// Return 2 / pi.
  /// @see gtc_constants
  template<typename genType>
  constexpr genType two_over_pi();

  /// Return 4 / pi.
  /// @see gtc_constants
  template<typename genType>
  constexpr genType four_over_pi();

  /// Return 2 / sqrt(pi).
  /// @see gtc_constants
  template<typename genType>
  constexpr genType two_over_root_pi();

  /// Return 1 / sqrt(2).
  /// @see gtc_constants
  template<typename genType>
  constexpr genType one_over_root_two();

  /// Return sqrt(pi / 2).
  /// @see gtc_constants
  template<typename genType>
  constexpr genType root_half_pi();

  /// Return sqrt(2 * pi).
  /// @see gtc_constants
  template<typename genType>
  constexpr genType root_two_pi();

  /// Return sqrt(ln(4)).
  /// @see gtc_constants
  template<typename genType>
  constexpr genType root_ln_four();

  /// Return e constant.
  /// @see gtc_constants
  template<typename genType>
  constexpr genType e();

  /// Return Euler's constant.
  /// @see gtc_constants
  template<typename genType>
  constexpr genType euler();

  /// Return sqrt(2).
  /// @see gtc_constants
  template<typename genType>
  constexpr genType root_two();

  /// Return sqrt(3).
  /// @see gtc_constants
  template<typename genType>
  constexpr genType root_three();

  /// Return sqrt(5).
  /// @see gtc_constants
  template<typename genType>
  constexpr genType root_five();

  /// Return ln(2).
  /// @see gtc_constants
  template<typename genType>
  constexpr genType ln_two();

  /// Return ln(10).
  /// @see gtc_constants
  template<typename genType>
  constexpr genType ln_ten();

  /// Return ln(ln(2)).
  /// @see gtc_constants
  template<typename genType>
  constexpr genType ln_ln_two();

  /// Return 1 / 3.
  /// @see gtc_constants
  template<typename genType>
  constexpr genType third();

  /// Return 2 / 3.
  /// @see gtc_constants
  template<typename genType>
  constexpr genType two_thirds();

  /// Return the golden ratio constant.
  /// @see gtc_constants
  template<typename genType>
  constexpr genType golden_ratio();

  /// @}
} //namespace glm


/// @ref gtc_constants

namespace glm
{
  template<typename genType>
  inline constexpr genType zero()
  {
    return genType(0);
  }

  template<typename genType>
  inline constexpr genType one()
  {
    return genType(1);
  }

  template<typename genType>
  inline constexpr genType two_pi()
  {
    return genType(6.28318530717958647692528676655900576);
  }

  template<typename genType>
  inline constexpr genType root_pi()
  {
    return genType(1.772453850905516027);
  }

  template<typename genType>
  inline constexpr genType half_pi()
  {
    return genType(1.57079632679489661923132169163975144);
  }

  template<typename genType>
  inline constexpr genType three_over_two_pi()
  {
    return genType(4.71238898038468985769396507491925432);
  }

  template<typename genType>
  inline constexpr genType quarter_pi()
  {
    return genType(0.785398163397448309615660845819875721);
  }

  template<typename genType>
  inline constexpr genType one_over_pi()
  {
    return genType(0.318309886183790671537767526745028724);
  }

  template<typename genType>
  inline constexpr genType one_over_two_pi()
  {
    return genType(0.159154943091895335768883763372514362);
  }

  template<typename genType>
  inline constexpr genType two_over_pi()
  {
    return genType(0.636619772367581343075535053490057448);
  }

  template<typename genType>
  inline constexpr genType four_over_pi()
  {
    return genType(1.273239544735162686151070106980114898);
  }

  template<typename genType>
  inline constexpr genType two_over_root_pi()
  {
    return genType(1.12837916709551257389615890312154517);
  }

  template<typename genType>
  inline constexpr genType one_over_root_two()
  {
    return genType(0.707106781186547524400844362104849039);
  }

  template<typename genType>
  inline constexpr genType root_half_pi()
  {
    return genType(1.253314137315500251);
  }

  template<typename genType>
  inline constexpr genType root_two_pi()
  {
    return genType(2.506628274631000502);
  }

  template<typename genType>
  inline constexpr genType root_ln_four()
  {
    return genType(1.17741002251547469);
  }

  template<typename genType>
  inline constexpr genType e()
  {
    return genType(2.71828182845904523536);
  }

  template<typename genType>
  inline constexpr genType euler()
  {
    return genType(0.577215664901532860606);
  }

  template<typename genType>
  inline constexpr genType root_two()
  {
    return genType(1.41421356237309504880168872420969808);
  }

  template<typename genType>
  inline constexpr genType root_three()
  {
    return genType(1.73205080756887729352744634150587236);
  }

  template<typename genType>
  inline constexpr genType root_five()
  {
    return genType(2.23606797749978969640917366873127623);
  }

  template<typename genType>
  inline constexpr genType ln_two()
  {
    return genType(0.693147180559945309417232121458176568);
  }

  template<typename genType>
  inline constexpr genType ln_ten()
  {
    return genType(2.30258509299404568401799145468436421);
  }

  template<typename genType>
  inline constexpr genType ln_ln_two()
  {
    return genType(-0.3665129205816643);
  }

  template<typename genType>
  inline constexpr genType third()
  {
    return genType(0.3333333333333333333333333333333333333333);
  }

  template<typename genType>
  inline constexpr genType two_thirds()
  {
    return genType(0.666666666666666666666666666666666666667);
  }

  template<typename genType>
  inline constexpr genType golden_ratio()
  {
    return genType(1.61803398874989484820458683436563811);
  }

} //namespace glm



namespace glm
{
  /// @addtogroup core_func_trigonometric
  /// @{

  /// Converts degrees to radians and returns the result.
  ///
  /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector
  /// @tparam T Floating-point scalar types
  /// @tparam Q Value from qualifier enum
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/radians.xml">GLSL radians man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.1 Angle and Trigonometry Functions</a>
  template<length_t L, typename T, qualifier Q>
  constexpr vec<L, T, Q> radians(vec<L, T, Q> const& degrees);

  /// Converts radians to degrees and returns the result.
  ///
  /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector
  /// @tparam T Floating-point scalar types
  /// @tparam Q Value from qualifier enum
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/degrees.xml">GLSL degrees man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.1 Angle and Trigonometry Functions</a>
  template<length_t L, typename T, qualifier Q>
  constexpr vec<L, T, Q> degrees(vec<L, T, Q> const& radians);

  /// The standard trigonometric sine function.
  /// The values returned by this function will range from [-1, 1].
  ///
  /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector
  /// @tparam T Floating-point scalar types
  /// @tparam Q Value from qualifier enum
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/sin.xml">GLSL sin man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.1 Angle and Trigonometry Functions</a>
  template<length_t L, typename T, qualifier Q>
  vec<L, T, Q> sin(vec<L, T, Q> const& angle);

  /// The standard trigonometric cosine function.
  /// The values returned by this function will range from [-1, 1].
  ///
  /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector
  /// @tparam T Floating-point scalar types
  /// @tparam Q Value from qualifier enum
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/cos.xml">GLSL cos man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.1 Angle and Trigonometry Functions</a>
  template<length_t L, typename T, qualifier Q>
  vec<L, T, Q> cos(vec<L, T, Q> const& angle);

  /// The standard trigonometric tangent function.
  ///
  /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector
  /// @tparam T Floating-point scalar types
  /// @tparam Q Value from qualifier enum
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/tan.xml">GLSL tan man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.1 Angle and Trigonometry Functions</a>
  template<length_t L, typename T, qualifier Q>
  vec<L, T, Q> tan(vec<L, T, Q> const& angle);

  /// Arc sine. Returns an angle whose sine is x.
  /// The range of values returned by this function is [-PI/2, PI/2].
  /// Results are undefined if |x| > 1.
  ///
  /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector
  /// @tparam T Floating-point scalar types
  /// @tparam Q Value from qualifier enum
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/asin.xml">GLSL asin man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.1 Angle and Trigonometry Functions</a>
  template<length_t L, typename T, qualifier Q>
  vec<L, T, Q> asin(vec<L, T, Q> const& x);

  /// Arc cosine. Returns an angle whose sine is x.
  /// The range of values returned by this function is [0, PI].
  /// Results are undefined if |x| > 1.
  ///
  /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector
  /// @tparam T Floating-point scalar types
  /// @tparam Q Value from qualifier enum
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/acos.xml">GLSL acos man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.1 Angle and Trigonometry Functions</a>
  template<length_t L, typename T, qualifier Q>
  vec<L, T, Q> acos(vec<L, T, Q> const& x);

  /// Arc tangent. Returns an angle whose tangent is y/x.
  /// The signs of x and y are used to determine what
  /// quadrant the angle is in. The range of values returned
  /// by this function is [-PI, PI]. Results are undefined
  /// if x and y are both 0.
  ///
  /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector
  /// @tparam T Floating-point scalar types
  /// @tparam Q Value from qualifier enum
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/atan.xml">GLSL atan man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.1 Angle and Trigonometry Functions</a>
  template<length_t L, typename T, qualifier Q>
  vec<L, T, Q> atan(vec<L, T, Q> const& y, vec<L, T, Q> const& x);

  /// Arc tangent. Returns an angle whose tangent is y_over_x.
  /// The range of values returned by this function is [-PI/2, PI/2].
  ///
  /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector
  /// @tparam T Floating-point scalar types
  /// @tparam Q Value from qualifier enum
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/atan.xml">GLSL atan man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.1 Angle and Trigonometry Functions</a>
  template<length_t L, typename T, qualifier Q>
  vec<L, T, Q> atan(vec<L, T, Q> const& y_over_x);

  /// Returns the hyperbolic sine function, (exp(x) - exp(-x)) / 2
  ///
  /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector
  /// @tparam T Floating-point scalar types
  /// @tparam Q Value from qualifier enum
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/sinh.xml">GLSL sinh man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.1 Angle and Trigonometry Functions</a>
  template<length_t L, typename T, qualifier Q>
  vec<L, T, Q> sinh(vec<L, T, Q> const& angle);

  /// Returns the hyperbolic cosine function, (exp(x) + exp(-x)) / 2
  ///
  /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector
  /// @tparam T Floating-point scalar types
  /// @tparam Q Value from qualifier enum
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/cosh.xml">GLSL cosh man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.1 Angle and Trigonometry Functions</a>
  template<length_t L, typename T, qualifier Q>
  vec<L, T, Q> cosh(vec<L, T, Q> const& angle);

  /// Returns the hyperbolic tangent function, sinh(angle) / cosh(angle)
  ///
  /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector
  /// @tparam T Floating-point scalar types
  /// @tparam Q Value from qualifier enum
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/tanh.xml">GLSL tanh man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.1 Angle and Trigonometry Functions</a>
  template<length_t L, typename T, qualifier Q>
  vec<L, T, Q> tanh(vec<L, T, Q> const& angle);

  /// Arc hyperbolic sine; returns the inverse of sinh.
  ///
  /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector
  /// @tparam T Floating-point scalar types
  /// @tparam Q Value from qualifier enum
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/asinh.xml">GLSL asinh man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.1 Angle and Trigonometry Functions</a>
  template<length_t L, typename T, qualifier Q>
  vec<L, T, Q> asinh(vec<L, T, Q> const& x);

  /// Arc hyperbolic cosine; returns the non-negative inverse
  /// of cosh. Results are undefined if x < 1.
  ///
  /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector
  /// @tparam T Floating-point scalar types
  /// @tparam Q Value from qualifier enum
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/acosh.xml">GLSL acosh man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.1 Angle and Trigonometry Functions</a>
  template<length_t L, typename T, qualifier Q>
  vec<L, T, Q> acosh(vec<L, T, Q> const& x);

  /// Arc hyperbolic tangent; returns the inverse of tanh.
  /// Results are undefined if abs(x) >= 1.
  ///
  /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector
  /// @tparam T Floating-point scalar types
  /// @tparam Q Value from qualifier enum
  ///
  /// @see <a href="http://www.opengl.org/sdk/docs/manglsl/xhtml/atanh.xml">GLSL atanh man page</a>
  /// @see <a href="http://www.opengl.org/registry/doc/GLSLangSpec.4.20.8.pdf">GLSL 4.20.8 specification, section 8.1 Angle and Trigonometry Functions</a>
  template<length_t L, typename T, qualifier Q>
  vec<L, T, Q> atanh(vec<L, T, Q> const& x);

  /// @}
}//namespace glm

namespace glm
{
  // radians
  template<typename genType>
  inline constexpr genType radians(genType degrees)
  {
    static_assert(std::numeric_limits<genType>::is_iec559, "'radians' only accept floating-point input");

    return degrees * static_cast<genType>(0.01745329251994329576923690768489);
  }

  template<length_t L, typename T, qualifier Q>
  inline constexpr vec<L, T, Q> radians(vec<L, T, Q> const& v)
  {
    return detail::functor1<vec, L, T, T, Q>::call(radians, v);
  }

  // degrees
  template<typename genType>
  inline constexpr genType degrees(genType radians)
  {
    static_assert(std::numeric_limits<genType>::is_iec559, "'degrees' only accept floating-point input");

    return radians * static_cast<genType>(57.295779513082320876798154814105);
  }

  template<length_t L, typename T, qualifier Q>
  inline constexpr vec<L, T, Q> degrees(vec<L, T, Q> const& v)
  {
    return detail::functor1<vec, L, T, T, Q>::call(degrees, v);
  }

  // sin
  using ::std::sin;

  template<length_t L, typename T, qualifier Q>
  inline vec<L, T, Q> sin(vec<L, T, Q> const& v)
  {
    return detail::functor1<vec, L, T, T, Q>::call(sin, v);
  }

  // cos
  using std::cos;

  template<length_t L, typename T, qualifier Q>
  inline vec<L, T, Q> cos(vec<L, T, Q> const& v)
  {
    return detail::functor1<vec, L, T, T, Q>::call(cos, v);
  }

  // tan
  using std::tan;

  template<length_t L, typename T, qualifier Q>
  inline vec<L, T, Q> tan(vec<L, T, Q> const& v)
  {
    return detail::functor1<vec, L, T, T, Q>::call(tan, v);
  }

  // asin
  using std::asin;

  template<length_t L, typename T, qualifier Q>
  inline vec<L, T, Q> asin(vec<L, T, Q> const& v)
  {
    return detail::functor1<vec, L, T, T, Q>::call(asin, v);
  }

  // acos
  using std::acos;

  template<length_t L, typename T, qualifier Q>
  inline vec<L, T, Q> acos(vec<L, T, Q> const& v)
  {
    return detail::functor1<vec, L, T, T, Q>::call(acos, v);
  }

  // atan
  template<typename genType>
  inline genType atan(genType y, genType x)
  {
    static_assert(std::numeric_limits<genType>::is_iec559, "'atan' only accept floating-point input");

    return ::std::atan2(y, x);
  }

  template<length_t L, typename T, qualifier Q>
  inline vec<L, T, Q> atan(vec<L, T, Q> const& a, vec<L, T, Q> const& b)
  {
    return detail::functor2<vec, L, T, Q>::call(::std::atan2, a, b);
  }

  using std::atan;

  template<length_t L, typename T, qualifier Q>
  inline vec<L, T, Q> atan(vec<L, T, Q> const& v)
  {
    return detail::functor1<vec, L, T, T, Q>::call(atan, v);
  }

  // sinh
  using std::sinh;

  template<length_t L, typename T, qualifier Q>
  inline vec<L, T, Q> sinh(vec<L, T, Q> const& v)
  {
    return detail::functor1<vec, L, T, T, Q>::call(sinh, v);
  }

  // cosh
  using std::cosh;

  template<length_t L, typename T, qualifier Q>
  inline vec<L, T, Q> cosh(vec<L, T, Q> const& v)
  {
    return detail::functor1<vec, L, T, T, Q>::call(cosh, v);
  }

  // tanh
  using std::tanh;

  template<length_t L, typename T, qualifier Q>
  inline vec<L, T, Q> tanh(vec<L, T, Q> const& v)
  {
    return detail::functor1<vec, L, T, T, Q>::call(tanh, v);
  }

  // asinh

  using std::asinh;










  template<length_t L, typename T, qualifier Q>
  inline vec<L, T, Q> asinh(vec<L, T, Q> const& v)
  {
    return detail::functor1<vec, L, T, T, Q>::call(asinh, v);
  }

  // acosh

  using std::acosh;












  template<length_t L, typename T, qualifier Q>
  inline vec<L, T, Q> acosh(vec<L, T, Q> const& v)
  {
    return detail::functor1<vec, L, T, T, Q>::call(acosh, v);
  }

  // atanh

  using std::atanh;












  template<length_t L, typename T, qualifier Q>
  inline vec<L, T, Q> atanh(vec<L, T, Q> const& v)
  {
    return detail::functor1<vec, L, T, T, Q>::call(atanh, v);
  }
}//namespace glm













namespace glm
{
  /// @addtogroup ext_matrix_projection
  /// @{

  /// Map the specified object coordinates (obj.x, obj.y, obj.z) into window coordinates.
  /// The near and far clip planes correspond to z normalized device coordinates of 0 and +1 respectively. (Direct3D clip volume definition)
  ///
  /// @param obj Specify the object coordinates.
  /// @param model Specifies the current modelview matrix
  /// @param proj Specifies the current projection matrix
  /// @param viewport Specifies the current viewport
  /// @return Return the computed window coordinates.
  /// @tparam T Native type used for the computation. Currently supported: half (not recommended), float or double.
  /// @tparam U Currently supported: Floating-point types and integer types.
  ///
  /// @see <a href="https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/gluProject.xml">gluProject man page</a>
  template<typename T, typename U, qualifier Q>
  vec<3, T, Q> projectZO(
    vec<3, T, Q> const& obj, mat<4, 4, T, Q> const& model, mat<4, 4, T, Q> const& proj, vec<4, U, Q> const& viewport);

  /// Map the specified object coordinates (obj.x, obj.y, obj.z) into window coordinates.
  /// The near and far clip planes correspond to z normalized device coordinates of -1 and +1 respectively. (OpenGL clip volume definition)
  ///
  /// @param obj Specify the object coordinates.
  /// @param model Specifies the current modelview matrix
  /// @param proj Specifies the current projection matrix
  /// @param viewport Specifies the current viewport
  /// @return Return the computed window coordinates.
  /// @tparam T Native type used for the computation. Currently supported: half (not recommended), float or double.
  /// @tparam U Currently supported: Floating-point types and integer types.
  ///
  /// @see <a href="https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/gluProject.xml">gluProject man page</a>
  template<typename T, typename U, qualifier Q>
  vec<3, T, Q> projectNO(
    vec<3, T, Q> const& obj, mat<4, 4, T, Q> const& model, mat<4, 4, T, Q> const& proj, vec<4, U, Q> const& viewport);

  /// Map the specified object coordinates (obj.x, obj.y, obj.z) into window coordinates using default near and far clip planes definition.
  /// To change default near and far clip planes definition use GLM_FORCE_DEPTH_ZERO_TO_ONE.
  ///
  /// @param obj Specify the object coordinates.
  /// @param model Specifies the current modelview matrix
  /// @param proj Specifies the current projection matrix
  /// @param viewport Specifies the current viewport
  /// @return Return the computed window coordinates.
  /// @tparam T Native type used for the computation. Currently supported: half (not recommended), float or double.
  /// @tparam U Currently supported: Floating-point types and integer types.
  ///
  /// @see <a href="https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/gluProject.xml">gluProject man page</a>
  template<typename T, typename U, qualifier Q>
  vec<3, T, Q> project(
    vec<3, T, Q> const& obj, mat<4, 4, T, Q> const& model, mat<4, 4, T, Q> const& proj, vec<4, U, Q> const& viewport);

  /// Map the specified window coordinates (win.x, win.y, win.z) into object coordinates.
  /// The near and far clip planes correspond to z normalized device coordinates of 0 and +1 respectively. (Direct3D clip volume definition)
  ///
  /// @param win Specify the window coordinates to be mapped.
  /// @param model Specifies the modelview matrix
  /// @param proj Specifies the projection matrix
  /// @param viewport Specifies the viewport
  /// @return Returns the computed object coordinates.
  /// @tparam T Native type used for the computation. Currently supported: half (not recommended), float or double.
  /// @tparam U Currently supported: Floating-point types and integer types.
  ///
  /// @see <a href="https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/gluUnProject.xml">gluUnProject man page</a>
  template<typename T, typename U, qualifier Q>
  vec<3, T, Q> unProjectZO(
    vec<3, T, Q> const& win, mat<4, 4, T, Q> const& model, mat<4, 4, T, Q> const& proj, vec<4, U, Q> const& viewport);

  /// Map the specified window coordinates (win.x, win.y, win.z) into object coordinates.
  /// The near and far clip planes correspond to z normalized device coordinates of -1 and +1 respectively. (OpenGL clip volume definition)
  ///
  /// @param win Specify the window coordinates to be mapped.
  /// @param model Specifies the modelview matrix
  /// @param proj Specifies the projection matrix
  /// @param viewport Specifies the viewport
  /// @return Returns the computed object coordinates.
  /// @tparam T Native type used for the computation. Currently supported: half (not recommended), float or double.
  /// @tparam U Currently supported: Floating-point types and integer types.
  ///
  /// @see <a href="https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/gluUnProject.xml">gluUnProject man page</a>
  template<typename T, typename U, qualifier Q>
  vec<3, T, Q> unProjectNO(
    vec<3, T, Q> const& win, mat<4, 4, T, Q> const& model, mat<4, 4, T, Q> const& proj, vec<4, U, Q> const& viewport);

  /// Map the specified window coordinates (win.x, win.y, win.z) into object coordinates using default near and far clip planes definition.
  /// To change default near and far clip planes definition use GLM_FORCE_DEPTH_ZERO_TO_ONE.
  ///
  /// @param win Specify the window coordinates to be mapped.
  /// @param model Specifies the modelview matrix
  /// @param proj Specifies the projection matrix
  /// @param viewport Specifies the viewport
  /// @return Returns the computed object coordinates.
  /// @tparam T Native type used for the computation. Currently supported: half (not recommended), float or double.
  /// @tparam U Currently supported: Floating-point types and integer types.
  ///
  /// @see <a href="https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/gluUnProject.xml">gluUnProject man page</a>
  template<typename T, typename U, qualifier Q>
  vec<3, T, Q> unProject(
    vec<3, T, Q> const& win, mat<4, 4, T, Q> const& model, mat<4, 4, T, Q> const& proj, vec<4, U, Q> const& viewport);

  /// Define a picking region
  ///
  /// @param center Specify the center of a picking region in window coordinates.
  /// @param delta Specify the width and height, respectively, of the picking region in window coordinates.
  /// @param viewport Rendering viewport
  /// @tparam T Native type used for the computation. Currently supported: half (not recommended), float or double.
  /// @tparam U Currently supported: Floating-point types and integer types.
  ///
  /// @see <a href="https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/gluPickMatrix.xml">gluPickMatrix man page</a>
  template<typename T, qualifier Q, typename U>
  mat<4, 4, T, Q> pickMatrix(
    vec<2, T, Q> const& center, vec<2, T, Q> const& delta, vec<4, U, Q> const& viewport);

  /// @}
}//namespace glm


namespace glm
{
  template<typename T, typename U, qualifier Q>
  inline vec<3, T, Q> projectZO(vec<3, T, Q> const& obj, mat<4, 4, T, Q> const& model, mat<4, 4, T, Q> const& proj, vec<4, U, Q> const& viewport)
  {
    vec<4, T, Q> tmp = vec<4, T, Q>(obj, static_cast<T>(1));
    tmp = model * tmp;
    tmp = proj * tmp;

    tmp /= tmp.w;
    tmp.x = tmp.x * static_cast<T>(0.5) + static_cast<T>(0.5);
    tmp.y = tmp.y * static_cast<T>(0.5) + static_cast<T>(0.5);

    tmp[0] = tmp[0] * T(viewport[2]) + T(viewport[0]);
    tmp[1] = tmp[1] * T(viewport[3]) + T(viewport[1]);

    return vec<3, T, Q>(tmp);
  }

  template<typename T, typename U, qualifier Q>
  inline vec<3, T, Q> projectNO(vec<3, T, Q> const& obj, mat<4, 4, T, Q> const& model, mat<4, 4, T, Q> const& proj, vec<4, U, Q> const& viewport)
  {
    vec<4, T, Q> tmp = vec<4, T, Q>(obj, static_cast<T>(1));
    tmp = model * tmp;
    tmp = proj * tmp;

    tmp /= tmp.w;
    tmp = tmp * static_cast<T>(0.5) + static_cast<T>(0.5);
    tmp[0] = tmp[0] * T(viewport[2]) + T(viewport[0]);
    tmp[1] = tmp[1] * T(viewport[3]) + T(viewport[1]);

    return vec<3, T, Q>(tmp);
  }

  template<typename T, typename U, qualifier Q>
  inline vec<3, T, Q> project(vec<3, T, Q> const& obj, mat<4, 4, T, Q> const& model, mat<4, 4, T, Q> const& proj, vec<4, U, Q> const& viewport)
  {



    return projectNO(obj, model, proj, viewport);

  }

  template<typename T, typename U, qualifier Q>
  inline vec<3, T, Q> unProjectZO(vec<3, T, Q> const& win, mat<4, 4, T, Q> const& model, mat<4, 4, T, Q> const& proj, vec<4, U, Q> const& viewport)
  {
    mat<4, 4, T, Q> Inverse = inverse(proj * model);

    vec<4, T, Q> tmp = vec<4, T, Q>(win, T(1));
    tmp.x = (tmp.x - T(viewport[0])) / T(viewport[2]);
    tmp.y = (tmp.y - T(viewport[1])) / T(viewport[3]);
    tmp.x = tmp.x * static_cast<T>(2) - static_cast<T>(1);
    tmp.y = tmp.y * static_cast<T>(2) - static_cast<T>(1);

    vec<4, T, Q> obj = Inverse * tmp;
    obj /= obj.w;

    return vec<3, T, Q>(obj);
  }

  template<typename T, typename U, qualifier Q>
  inline vec<3, T, Q> unProjectNO(vec<3, T, Q> const& win, mat<4, 4, T, Q> const& model, mat<4, 4, T, Q> const& proj, vec<4, U, Q> const& viewport)
  {
    mat<4, 4, T, Q> Inverse = inverse(proj * model);

    vec<4, T, Q> tmp = vec<4, T, Q>(win, T(1));
    tmp.x = (tmp.x - T(viewport[0])) / T(viewport[2]);
    tmp.y = (tmp.y - T(viewport[1])) / T(viewport[3]);
    tmp = tmp * static_cast<T>(2) - static_cast<T>(1);

    vec<4, T, Q> obj = Inverse * tmp;
    obj /= obj.w;

    return vec<3, T, Q>(obj);
  }

  template<typename T, typename U, qualifier Q>
  inline vec<3, T, Q> unProject(vec<3, T, Q> const& win, mat<4, 4, T, Q> const& model, mat<4, 4, T, Q> const& proj, vec<4, U, Q> const& viewport)
  {



    return unProjectNO(win, model, proj, viewport);

  }

  template<typename T, qualifier Q, typename U>
  inline mat<4, 4, T, Q> pickMatrix(vec<2, T, Q> const& center, vec<2, T, Q> const& delta, vec<4, U, Q> const& viewport)
  {
    assert(delta.x > static_cast<T>(0) && delta.y > static_cast<T>(0));
    mat<4, 4, T, Q> Result(static_cast<T>(1));

    if (!(delta.x > static_cast<T>(0) && delta.y > static_cast<T>(0)))
      return Result; // Error

    vec<3, T, Q> Temp(
      (static_cast<T>(viewport[2]) - static_cast<T>(2) * (center.x - static_cast<T>(viewport[0]))) / delta.x,
      (static_cast<T>(viewport[3]) - static_cast<T>(2) * (center.y - static_cast<T>(viewport[1]))) / delta.y,
      static_cast<T>(0));

    // Translate and scale the picked region to the entire window
    Result = translate(Result, Temp);
    return scale(Result, vec<3, T, Q>(static_cast<T>(viewport[2]) / delta.x, static_cast<T>(viewport[3]) / delta.y, static_cast<T>(1)));
  }
}//namespace glm



/// @ref ext_matrix_clip_space
/// @file glm/ext/matrix_clip_space.hpp
///
/// @defgroup ext_matrix_clip_space GLM_EXT_matrix_clip_space
/// @ingroup ext
///
/// Defines functions that generate clip space transformation matrices.
///
/// The matrices generated by this extension use standard OpenGL fixed-function
/// conventions. For example, the lookAt function generates a transform from world
/// space into the specific eye space that the projective matrix functions
/// (perspective, ortho, etc) are designed to expect. The OpenGL compatibility
/// specifications defines the particular layout of this eye space.
///
/// Include <glm/ext/matrix_clip_space.hpp> to use the features of this extension.
///
/// @see ext_matrix_transform
/// @see ext_matrix_projection



// Dependencies








namespace glm
{
  /// @addtogroup ext_matrix_clip_space
  /// @{

  /// Creates a matrix for projecting two-dimensional coordinates onto the screen.
  ///
  /// @tparam T A floating-point scalar type
  ///
  /// @see - glm::ortho(T const& left, T const& right, T const& bottom, T const& top, T const& zNear, T const& zFar)
  /// @see <a href="https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/gluOrtho2D.xml">gluOrtho2D man page</a>
  template<typename T>
  mat<4, 4, T, defaultp> ortho(
    T left, T right, T bottom, T top);

  /// Creates a matrix for an orthographic parallel viewing volume, using left-handed coordinates.
  /// The near and far clip planes correspond to z normalized device coordinates of 0 and +1 respectively. (Direct3D clip volume definition)
  ///
  /// @tparam T A floating-point scalar type
  ///
  /// @see - glm::ortho(T const& left, T const& right, T const& bottom, T const& top)
  template<typename T>
  mat<4, 4, T, defaultp> orthoLH_ZO(
    T left, T right, T bottom, T top, T zNear, T zFar);

  /// Creates a matrix for an orthographic parallel viewing volume using right-handed coordinates.
  /// The near and far clip planes correspond to z normalized device coordinates of -1 and +1 respectively. (OpenGL clip volume definition)
  ///
  /// @tparam T A floating-point scalar type
  ///
  /// @see - glm::ortho(T const& left, T const& right, T const& bottom, T const& top)
  template<typename T>
  mat<4, 4, T, defaultp> orthoLH_NO(
    T left, T right, T bottom, T top, T zNear, T zFar);

  /// Creates a matrix for an orthographic parallel viewing volume, using left-handed coordinates.
  /// The near and far clip planes correspond to z normalized device coordinates of 0 and +1 respectively. (Direct3D clip volume definition)
  ///
  /// @tparam T A floating-point scalar type
  ///
  /// @see - glm::ortho(T const& left, T const& right, T const& bottom, T const& top)
  template<typename T>
  mat<4, 4, T, defaultp> orthoRH_ZO(
    T left, T right, T bottom, T top, T zNear, T zFar);

  /// Creates a matrix for an orthographic parallel viewing volume, using right-handed coordinates.
  /// The near and far clip planes correspond to z normalized device coordinates of -1 and +1 respectively. (OpenGL clip volume definition)
  ///
  /// @tparam T A floating-point scalar type
  ///
  /// @see - glm::ortho(T const& left, T const& right, T const& bottom, T const& top)
  template<typename T>
  mat<4, 4, T, defaultp> orthoRH_NO(
    T left, T right, T bottom, T top, T zNear, T zFar);

  /// Creates a matrix for an orthographic parallel viewing volume, using left-handed coordinates.
  /// The near and far clip planes correspond to z normalized device coordinates of 0 and +1 respectively. (Direct3D clip volume definition)
  ///
  /// @tparam T A floating-point scalar type
  ///
  /// @see - glm::ortho(T const& left, T const& right, T const& bottom, T const& top)
  template<typename T>
  mat<4, 4, T, defaultp> orthoZO(
    T left, T right, T bottom, T top, T zNear, T zFar);

  /// Creates a matrix for an orthographic parallel viewing volume, using left-handed coordinates if GLM_FORCE_LEFT_HANDED if defined or right-handed coordinates otherwise.
  /// The near and far clip planes correspond to z normalized device coordinates of -1 and +1 respectively. (OpenGL clip volume definition)
  ///
  /// @tparam T A floating-point scalar type
  ///
  /// @see - glm::ortho(T const& left, T const& right, T const& bottom, T const& top)
  template<typename T>
  mat<4, 4, T, defaultp> orthoNO(
    T left, T right, T bottom, T top, T zNear, T zFar);

  /// Creates a matrix for an orthographic parallel viewing volume, using left-handed coordinates.
  /// If GLM_FORCE_DEPTH_ZERO_TO_ONE is defined, the near and far clip planes correspond to z normalized device coordinates of 0 and +1 respectively. (Direct3D clip volume definition)
  /// Otherwise, the near and far clip planes correspond to z normalized device coordinates of -1 and +1 respectively. (OpenGL clip volume definition)
  ///
  /// @tparam T A floating-point scalar type
  ///
  /// @see - glm::ortho(T const& left, T const& right, T const& bottom, T const& top)
  template<typename T>
  mat<4, 4, T, defaultp> orthoLH(
    T left, T right, T bottom, T top, T zNear, T zFar);

  /// Creates a matrix for an orthographic parallel viewing volume, using right-handed coordinates.
  /// If GLM_FORCE_DEPTH_ZERO_TO_ONE is defined, the near and far clip planes correspond to z normalized device coordinates of 0 and +1 respectively. (Direct3D clip volume definition)
  /// Otherwise, the near and far clip planes correspond to z normalized device coordinates of -1 and +1 respectively. (OpenGL clip volume definition)
  ///
  /// @tparam T A floating-point scalar type
  ///
  /// @see - glm::ortho(T const& left, T const& right, T const& bottom, T const& top)
  template<typename T>
  mat<4, 4, T, defaultp> orthoRH(
    T left, T right, T bottom, T top, T zNear, T zFar);

  /// Creates a matrix for an orthographic parallel viewing volume, using the default handedness and default near and far clip planes definition.
  /// To change default handedness use GLM_FORCE_LEFT_HANDED. To change default near and far clip planes definition use GLM_FORCE_DEPTH_ZERO_TO_ONE.
  ///
  /// @tparam T A floating-point scalar type
  ///
  /// @see - glm::ortho(T const& left, T const& right, T const& bottom, T const& top)
  /// @see <a href="https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glOrtho.xml">glOrtho man page</a>
  template<typename T>
  mat<4, 4, T, defaultp> ortho(
    T left, T right, T bottom, T top, T zNear, T zFar);

  /// Creates a left handed frustum matrix.
  /// The near and far clip planes correspond to z normalized device coordinates of 0 and +1 respectively. (Direct3D clip volume definition)
  ///
  /// @tparam T A floating-point scalar type
  template<typename T>
  mat<4, 4, T, defaultp> frustumLH_ZO(
    T left, T right, T bottom, T top, T near, T far);

  /// Creates a left handed frustum matrix.
  /// The near and far clip planes correspond to z normalized device coordinates of -1 and +1 respectively. (OpenGL clip volume definition)
  ///
  /// @tparam T A floating-point scalar type
  template<typename T>
  mat<4, 4, T, defaultp> frustumLH_NO(
    T left, T right, T bottom, T top, T near, T far);

  /// Creates a right handed frustum matrix.
  /// The near and far clip planes correspond to z normalized device coordinates of 0 and +1 respectively. (Direct3D clip volume definition)
  ///
  /// @tparam T A floating-point scalar type
  template<typename T>
  mat<4, 4, T, defaultp> frustumRH_ZO(
    T left, T right, T bottom, T top, T near, T far);

  /// Creates a right handed frustum matrix.
  /// The near and far clip planes correspond to z normalized device coordinates of -1 and +1 respectively. (OpenGL clip volume definition)
  ///
  /// @tparam T A floating-point scalar type
  template<typename T>
  mat<4, 4, T, defaultp> frustumRH_NO(
    T left, T right, T bottom, T top, T near, T far);

  /// Creates a frustum matrix using left-handed coordinates if GLM_FORCE_LEFT_HANDED if defined or right-handed coordinates otherwise.
  /// The near and far clip planes correspond to z normalized device coordinates of 0 and +1 respectively. (Direct3D clip volume definition)
  ///
  /// @tparam T A floating-point scalar type
  template<typename T>
  mat<4, 4, T, defaultp> frustumZO(
    T left, T right, T bottom, T top, T near, T far);

  /// Creates a frustum matrix using left-handed coordinates if GLM_FORCE_LEFT_HANDED if defined or right-handed coordinates otherwise.
  /// The near and far clip planes correspond to z normalized device coordinates of -1 and +1 respectively. (OpenGL clip volume definition)
  ///
  /// @tparam T A floating-point scalar type
  template<typename T>
  mat<4, 4, T, defaultp> frustumNO(
    T left, T right, T bottom, T top, T near, T far);

  /// Creates a left handed frustum matrix.
  /// If GLM_FORCE_DEPTH_ZERO_TO_ONE is defined, the near and far clip planes correspond to z normalized device coordinates of 0 and +1 respectively. (Direct3D clip volume definition)
  /// Otherwise, the near and far clip planes correspond to z normalized device coordinates of -1 and +1 respectively. (OpenGL clip volume definition)
  ///
  /// @tparam T A floating-point scalar type
  template<typename T>
  mat<4, 4, T, defaultp> frustumLH(
    T left, T right, T bottom, T top, T near, T far);

  /// Creates a right handed frustum matrix.
  /// If GLM_FORCE_DEPTH_ZERO_TO_ONE is defined, the near and far clip planes correspond to z normalized device coordinates of 0 and +1 respectively. (Direct3D clip volume definition)
  /// Otherwise, the near and far clip planes correspond to z normalized device coordinates of -1 and +1 respectively. (OpenGL clip volume definition)
  ///
  /// @tparam T A floating-point scalar type
  template<typename T>
  mat<4, 4, T, defaultp> frustumRH(
    T left, T right, T bottom, T top, T near, T far);

  /// Creates a frustum matrix with default handedness, using the default handedness and default near and far clip planes definition.
  /// To change default handedness use GLM_FORCE_LEFT_HANDED. To change default near and far clip planes definition use GLM_FORCE_DEPTH_ZERO_TO_ONE.
  ///
  /// @tparam T A floating-point scalar type
  /// @see <a href="https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glFrustum.xml">glFrustum man page</a>
  template<typename T>
  mat<4, 4, T, defaultp> frustum(
    T left, T right, T bottom, T top, T near, T far);


  /// Creates a matrix for a right handed, symetric perspective-view frustum.
  /// The near and far clip planes correspond to z normalized device coordinates of 0 and +1 respectively. (Direct3D clip volume definition)
  ///
  /// @param fovy Specifies the field of view angle, in degrees, in the y direction. Expressed in radians.
  /// @param aspect Specifies the aspect ratio that determines the field of view in the x direction. The aspect ratio is the ratio of x (width) to y (height).
  /// @param near Specifies the distance from the viewer to the near clipping plane (always positive).
  /// @param far Specifies the distance from the viewer to the far clipping plane (always positive).
  ///
  /// @tparam T A floating-point scalar type
  template<typename T>
  mat<4, 4, T, defaultp> perspectiveRH_ZO(
    T fovy, T aspect, T near, T far);

  /// Creates a matrix for a right handed, symetric perspective-view frustum.
  /// The near and far clip planes correspond to z normalized device coordinates of -1 and +1 respectively. (OpenGL clip volume definition)
  ///
  /// @param fovy Specifies the field of view angle, in degrees, in the y direction. Expressed in radians.
  /// @param aspect Specifies the aspect ratio that determines the field of view in the x direction. The aspect ratio is the ratio of x (width) to y (height).
  /// @param near Specifies the distance from the viewer to the near clipping plane (always positive).
  /// @param far Specifies the distance from the viewer to the far clipping plane (always positive).
  ///
  /// @tparam T A floating-point scalar type
  template<typename T>
  mat<4, 4, T, defaultp> perspectiveRH_NO(
    T fovy, T aspect, T near, T far);

  /// Creates a matrix for a left handed, symetric perspective-view frustum.
  /// The near and far clip planes correspond to z normalized device coordinates of 0 and +1 respectively. (Direct3D clip volume definition)
  ///
  /// @param fovy Specifies the field of view angle, in degrees, in the y direction. Expressed in radians.
  /// @param aspect Specifies the aspect ratio that determines the field of view in the x direction. The aspect ratio is the ratio of x (width) to y (height).
  /// @param near Specifies the distance from the viewer to the near clipping plane (always positive).
  /// @param far Specifies the distance from the viewer to the far clipping plane (always positive).
  ///
  /// @tparam T A floating-point scalar type
  template<typename T>
  mat<4, 4, T, defaultp> perspectiveLH_ZO(
    T fovy, T aspect, T near, T far);

  /// Creates a matrix for a left handed, symetric perspective-view frustum.
  /// The near and far clip planes correspond to z normalized device coordinates of -1 and +1 respectively. (OpenGL clip volume definition)
  ///
  /// @param fovy Specifies the field of view angle, in degrees, in the y direction. Expressed in radians.
  /// @param aspect Specifies the aspect ratio that determines the field of view in the x direction. The aspect ratio is the ratio of x (width) to y (height).
  /// @param near Specifies the distance from the viewer to the near clipping plane (always positive).
  /// @param far Specifies the distance from the viewer to the far clipping plane (always positive).
  ///
  /// @tparam T A floating-point scalar type
  template<typename T>
  mat<4, 4, T, defaultp> perspectiveLH_NO(
    T fovy, T aspect, T near, T far);

  /// Creates a matrix for a symetric perspective-view frustum using left-handed coordinates if GLM_FORCE_LEFT_HANDED if defined or right-handed coordinates otherwise.
  /// The near and far clip planes correspond to z normalized device coordinates of 0 and +1 respectively. (Direct3D clip volume definition)
  ///
  /// @param fovy Specifies the field of view angle, in degrees, in the y direction. Expressed in radians.
  /// @param aspect Specifies the aspect ratio that determines the field of view in the x direction. The aspect ratio is the ratio of x (width) to y (height).
  /// @param near Specifies the distance from the viewer to the near clipping plane (always positive).
  /// @param far Specifies the distance from the viewer to the far clipping plane (always positive).
  ///
  /// @tparam T A floating-point scalar type
  template<typename T>
  mat<4, 4, T, defaultp> perspectiveZO(
    T fovy, T aspect, T near, T far);

  /// Creates a matrix for a symetric perspective-view frustum using left-handed coordinates if GLM_FORCE_LEFT_HANDED if defined or right-handed coordinates otherwise.
  /// The near and far clip planes correspond to z normalized device coordinates of -1 and +1 respectively. (OpenGL clip volume definition)
  ///
  /// @param fovy Specifies the field of view angle, in degrees, in the y direction. Expressed in radians.
  /// @param aspect Specifies the aspect ratio that determines the field of view in the x direction. The aspect ratio is the ratio of x (width) to y (height).
  /// @param near Specifies the distance from the viewer to the near clipping plane (always positive).
  /// @param far Specifies the distance from the viewer to the far clipping plane (always positive).
  ///
  /// @tparam T A floating-point scalar type
  template<typename T>
  mat<4, 4, T, defaultp> perspectiveNO(
    T fovy, T aspect, T near, T far);

  /// Creates a matrix for a right handed, symetric perspective-view frustum.
  /// If GLM_FORCE_DEPTH_ZERO_TO_ONE is defined, the near and far clip planes correspond to z normalized device coordinates of 0 and +1 respectively. (Direct3D clip volume definition)
  /// Otherwise, the near and far clip planes correspond to z normalized device coordinates of -1 and +1 respectively. (OpenGL clip volume definition)
  ///
  /// @param fovy Specifies the field of view angle, in degrees, in the y direction. Expressed in radians.
  /// @param aspect Specifies the aspect ratio that determines the field of view in the x direction. The aspect ratio is the ratio of x (width) to y (height).
  /// @param near Specifies the distance from the viewer to the near clipping plane (always positive).
  /// @param far Specifies the distance from the viewer to the far clipping plane (always positive).
  ///
  /// @tparam T A floating-point scalar type
  template<typename T>
  mat<4, 4, T, defaultp> perspectiveRH(
    T fovy, T aspect, T near, T far);

  /// Creates a matrix for a left handed, symetric perspective-view frustum.
  /// If GLM_FORCE_DEPTH_ZERO_TO_ONE is defined, the near and far clip planes correspond to z normalized device coordinates of 0 and +1 respectively. (Direct3D clip volume definition)
  /// Otherwise, the near and far clip planes correspond to z normalized device coordinates of -1 and +1 respectively. (OpenGL clip volume definition)
  ///
  /// @param fovy Specifies the field of view angle, in degrees, in the y direction. Expressed in radians.
  /// @param aspect Specifies the aspect ratio that determines the field of view in the x direction. The aspect ratio is the ratio of x (width) to y (height).
  /// @param near Specifies the distance from the viewer to the near clipping plane (always positive).
  /// @param far Specifies the distance from the viewer to the far clipping plane (always positive).
  ///
  /// @tparam T A floating-point scalar type
  template<typename T>
  mat<4, 4, T, defaultp> perspectiveLH(
    T fovy, T aspect, T near, T far);

  /// Creates a matrix for a symetric perspective-view frustum based on the default handedness and default near and far clip planes definition.
  /// To change default handedness use GLM_FORCE_LEFT_HANDED. To change default near and far clip planes definition use GLM_FORCE_DEPTH_ZERO_TO_ONE.
  ///
  /// @param fovy Specifies the field of view angle in the y direction. Expressed in radians.
  /// @param aspect Specifies the aspect ratio that determines the field of view in the x direction. The aspect ratio is the ratio of x (width) to y (height).
  /// @param near Specifies the distance from the viewer to the near clipping plane (always positive).
  /// @param far Specifies the distance from the viewer to the far clipping plane (always positive).
  ///
  /// @tparam T A floating-point scalar type
  /// @see <a href="https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/gluPerspective.xml">gluPerspective man page</a>
  template<typename T>
  mat<4, 4, T, defaultp> perspective(
    T fovy, T aspect, T near, T far);

  /// Builds a perspective projection matrix based on a field of view using right-handed coordinates.
  /// The near and far clip planes correspond to z normalized device coordinates of 0 and +1 respectively. (Direct3D clip volume definition)
  ///
  /// @param fov Expressed in radians.
  /// @param width Width of the viewport
  /// @param height Height of the viewport
  /// @param near Specifies the distance from the viewer to the near clipping plane (always positive).
  /// @param far Specifies the distance from the viewer to the far clipping plane (always positive).
  ///
  /// @tparam T A floating-point scalar type
  template<typename T>
  mat<4, 4, T, defaultp> perspectiveFovRH_ZO(
    T fov, T width, T height, T near, T far);

  /// Builds a perspective projection matrix based on a field of view using right-handed coordinates.
  /// The near and far clip planes correspond to z normalized device coordinates of -1 and +1 respectively. (OpenGL clip volume definition)
  ///
  /// @param fov Expressed in radians.
  /// @param width Width of the viewport
  /// @param height Height of the viewport
  /// @param near Specifies the distance from the viewer to the near clipping plane (always positive).
  /// @param far Specifies the distance from the viewer to the far clipping plane (always positive).
  ///
  /// @tparam T A floating-point scalar type
  template<typename T>
  mat<4, 4, T, defaultp> perspectiveFovRH_NO(
    T fov, T width, T height, T near, T far);

  /// Builds a perspective projection matrix based on a field of view using left-handed coordinates.
  /// The near and far clip planes correspond to z normalized device coordinates of 0 and +1 respectively. (Direct3D clip volume definition)
  ///
  /// @param fov Expressed in radians.
  /// @param width Width of the viewport
  /// @param height Height of the viewport
  /// @param near Specifies the distance from the viewer to the near clipping plane (always positive).
  /// @param far Specifies the distance from the viewer to the far clipping plane (always positive).
  ///
  /// @tparam T A floating-point scalar type
  template<typename T>
  mat<4, 4, T, defaultp> perspectiveFovLH_ZO(
    T fov, T width, T height, T near, T far);

  /// Builds a perspective projection matrix based on a field of view using left-handed coordinates.
  /// The near and far clip planes correspond to z normalized device coordinates of -1 and +1 respectively. (OpenGL clip volume definition)
  ///
  /// @param fov Expressed in radians.
  /// @param width Width of the viewport
  /// @param height Height of the viewport
  /// @param near Specifies the distance from the viewer to the near clipping plane (always positive).
  /// @param far Specifies the distance from the viewer to the far clipping plane (always positive).
  ///
  /// @tparam T A floating-point scalar type
  template<typename T>
  mat<4, 4, T, defaultp> perspectiveFovLH_NO(
    T fov, T width, T height, T near, T far);

  /// Builds a perspective projection matrix based on a field of view using left-handed coordinates if GLM_FORCE_LEFT_HANDED if defined or right-handed coordinates otherwise.
  /// The near and far clip planes correspond to z normalized device coordinates of 0 and +1 respectively. (Direct3D clip volume definition)
  ///
  /// @param fov Expressed in radians.
  /// @param width Width of the viewport
  /// @param height Height of the viewport
  /// @param near Specifies the distance from the viewer to the near clipping plane (always positive).
  /// @param far Specifies the distance from the viewer to the far clipping plane (always positive).
  ///
  /// @tparam T A floating-point scalar type
  template<typename T>
  mat<4, 4, T, defaultp> perspectiveFovZO(
    T fov, T width, T height, T near, T far);

  /// Builds a perspective projection matrix based on a field of view using left-handed coordinates if GLM_FORCE_LEFT_HANDED if defined or right-handed coordinates otherwise.
  /// The near and far clip planes correspond to z normalized device coordinates of -1 and +1 respectively. (OpenGL clip volume definition)
  ///
  /// @param fov Expressed in radians.
  /// @param width Width of the viewport
  /// @param height Height of the viewport
  /// @param near Specifies the distance from the viewer to the near clipping plane (always positive).
  /// @param far Specifies the distance from the viewer to the far clipping plane (always positive).
  ///
  /// @tparam T A floating-point scalar type
  template<typename T>
  mat<4, 4, T, defaultp> perspectiveFovNO(
    T fov, T width, T height, T near, T far);

  /// Builds a right handed perspective projection matrix based on a field of view.
  /// If GLM_FORCE_DEPTH_ZERO_TO_ONE is defined, the near and far clip planes correspond to z normalized device coordinates of 0 and +1 respectively. (Direct3D clip volume definition)
  /// Otherwise, the near and far clip planes correspond to z normalized device coordinates of -1 and +1 respectively. (OpenGL clip volume definition)
  ///
  /// @param fov Expressed in radians.
  /// @param width Width of the viewport
  /// @param height Height of the viewport
  /// @param near Specifies the distance from the viewer to the near clipping plane (always positive).
  /// @param far Specifies the distance from the viewer to the far clipping plane (always positive).
  ///
  /// @tparam T A floating-point scalar type
  template<typename T>
  mat<4, 4, T, defaultp> perspectiveFovRH(
    T fov, T width, T height, T near, T far);

  /// Builds a left handed perspective projection matrix based on a field of view.
  /// If GLM_FORCE_DEPTH_ZERO_TO_ONE is defined, the near and far clip planes correspond to z normalized device coordinates of 0 and +1 respectively. (Direct3D clip volume definition)
  /// Otherwise, the near and far clip planes correspond to z normalized device coordinates of -1 and +1 respectively. (OpenGL clip volume definition)
  ///
  /// @param fov Expressed in radians.
  /// @param width Width of the viewport
  /// @param height Height of the viewport
  /// @param near Specifies the distance from the viewer to the near clipping plane (always positive).
  /// @param far Specifies the distance from the viewer to the far clipping plane (always positive).
  ///
  /// @tparam T A floating-point scalar type
  template<typename T>
  mat<4, 4, T, defaultp> perspectiveFovLH(
    T fov, T width, T height, T near, T far);

  /// Builds a perspective projection matrix based on a field of view and the default handedness and default near and far clip planes definition.
  /// To change default handedness use GLM_FORCE_LEFT_HANDED. To change default near and far clip planes definition use GLM_FORCE_DEPTH_ZERO_TO_ONE.
  ///
  /// @param fov Expressed in radians.
  /// @param width Width of the viewport
  /// @param height Height of the viewport
  /// @param near Specifies the distance from the viewer to the near clipping plane (always positive).
  /// @param far Specifies the distance from the viewer to the far clipping plane (always positive).
  ///
  /// @tparam T A floating-point scalar type
  template<typename T>
  mat<4, 4, T, defaultp> perspectiveFov(
    T fov, T width, T height, T near, T far);

  /// Creates a matrix for a left handed, symmetric perspective-view frustum with far plane at infinite.
  ///
  /// @param fovy Specifies the field of view angle, in degrees, in the y direction. Expressed in radians.
  /// @param aspect Specifies the aspect ratio that determines the field of view in the x direction. The aspect ratio is the ratio of x (width) to y (height).
  /// @param near Specifies the distance from the viewer to the near clipping plane (always positive).
  ///
  /// @tparam T A floating-point scalar type
  template<typename T>
  mat<4, 4, T, defaultp> infinitePerspectiveLH(
    T fovy, T aspect, T near);

  /// Creates a matrix for a right handed, symmetric perspective-view frustum with far plane at infinite.
  ///
  /// @param fovy Specifies the field of view angle, in degrees, in the y direction. Expressed in radians.
  /// @param aspect Specifies the aspect ratio that determines the field of view in the x direction. The aspect ratio is the ratio of x (width) to y (height).
  /// @param near Specifies the distance from the viewer to the near clipping plane (always positive).
  ///
  /// @tparam T A floating-point scalar type
  template<typename T>
  mat<4, 4, T, defaultp> infinitePerspectiveRH(
    T fovy, T aspect, T near);

  /// Creates a matrix for a symmetric perspective-view frustum with far plane at infinite with default handedness.
  ///
  /// @param fovy Specifies the field of view angle, in degrees, in the y direction. Expressed in radians.
  /// @param aspect Specifies the aspect ratio that determines the field of view in the x direction. The aspect ratio is the ratio of x (width) to y (height).
  /// @param near Specifies the distance from the viewer to the near clipping plane (always positive).
  ///
  /// @tparam T A floating-point scalar type
  template<typename T>
  mat<4, 4, T, defaultp> infinitePerspective(
    T fovy, T aspect, T near);

  /// Creates a matrix for a symmetric perspective-view frustum with far plane at infinite for graphics hardware that doesn't support depth clamping.
  ///
  /// @param fovy Specifies the field of view angle, in degrees, in the y direction. Expressed in radians.
  /// @param aspect Specifies the aspect ratio that determines the field of view in the x direction. The aspect ratio is the ratio of x (width) to y (height).
  /// @param near Specifies the distance from the viewer to the near clipping plane (always positive).
  ///
  /// @tparam T A floating-point scalar type
  template<typename T>
  mat<4, 4, T, defaultp> tweakedInfinitePerspective(
    T fovy, T aspect, T near);

  /// Creates a matrix for a symmetric perspective-view frustum with far plane at infinite for graphics hardware that doesn't support depth clamping.
  ///
  /// @param fovy Specifies the field of view angle, in degrees, in the y direction. Expressed in radians.
  /// @param aspect Specifies the aspect ratio that determines the field of view in the x direction. The aspect ratio is the ratio of x (width) to y (height).
  /// @param near Specifies the distance from the viewer to the near clipping plane (always positive).
  /// @param ep Epsilon
  ///
  /// @tparam T A floating-point scalar type
  template<typename T>
  mat<4, 4, T, defaultp> tweakedInfinitePerspective(
    T fovy, T aspect, T near, T ep);

  /// @}
}//namespace glm


namespace glm
{
  template<typename T>
  inline mat<4, 4, T, defaultp> ortho(T left, T right, T bottom, T top)
  {
    mat<4, 4, T, defaultp> Result(static_cast<T>(1));
    Result[0][0] = static_cast<T>(2) / (right - left);
    Result[1][1] = static_cast<T>(2) / (top - bottom);
    Result[2][2] = -static_cast<T>(1);
    Result[3][0] = -(right + left) / (right - left);
    Result[3][1] = -(top + bottom) / (top - bottom);
    return Result;
  }

  template<typename T>
  inline mat<4, 4, T, defaultp> orthoLH_ZO(T left, T right, T bottom, T top, T zNear, T zFar)
  {
    mat<4, 4, T, defaultp> Result(1);
    Result[0][0] = static_cast<T>(2) / (right - left);
    Result[1][1] = static_cast<T>(2) / (top - bottom);
    Result[2][2] = static_cast<T>(1) / (zFar - zNear);
    Result[3][0] = -(right + left) / (right - left);
    Result[3][1] = -(top + bottom) / (top - bottom);
    Result[3][2] = -zNear / (zFar - zNear);
    return Result;
  }

  template<typename T>
  inline mat<4, 4, T, defaultp> orthoLH_NO(T left, T right, T bottom, T top, T zNear, T zFar)
  {
    mat<4, 4, T, defaultp> Result(1);
    Result[0][0] = static_cast<T>(2) / (right - left);
    Result[1][1] = static_cast<T>(2) / (top - bottom);
    Result[2][2] = static_cast<T>(2) / (zFar - zNear);
    Result[3][0] = -(right + left) / (right - left);
    Result[3][1] = -(top + bottom) / (top - bottom);
    Result[3][2] = -(zFar + zNear) / (zFar - zNear);
    return Result;
  }

  template<typename T>
  inline mat<4, 4, T, defaultp> orthoRH_ZO(T left, T right, T bottom, T top, T zNear, T zFar)
  {
    mat<4, 4, T, defaultp> Result(1);
    Result[0][0] = static_cast<T>(2) / (right - left);
    Result[1][1] = static_cast<T>(2) / (top - bottom);
    Result[2][2] = -static_cast<T>(1) / (zFar - zNear);
    Result[3][0] = -(right + left) / (right - left);
    Result[3][1] = -(top + bottom) / (top - bottom);
    Result[3][2] = -zNear / (zFar - zNear);
    return Result;
  }

  template<typename T>
  inline mat<4, 4, T, defaultp> orthoRH_NO(T left, T right, T bottom, T top, T zNear, T zFar)
  {
    mat<4, 4, T, defaultp> Result(1);
    Result[0][0] = static_cast<T>(2) / (right - left);
    Result[1][1] = static_cast<T>(2) / (top - bottom);
    Result[2][2] = -static_cast<T>(2) / (zFar - zNear);
    Result[3][0] = -(right + left) / (right - left);
    Result[3][1] = -(top + bottom) / (top - bottom);
    Result[3][2] = -(zFar + zNear) / (zFar - zNear);
    return Result;
  }

  template<typename T>
  inline mat<4, 4, T, defaultp> orthoZO(T left, T right, T bottom, T top, T zNear, T zFar)
  {



    return orthoRH_ZO(left, right, bottom, top, zNear, zFar);

  }

  template<typename T>
  inline mat<4, 4, T, defaultp> orthoNO(T left, T right, T bottom, T top, T zNear, T zFar)
  {
    return orthoRH_NO(left, right, bottom, top, zNear, zFar);
  }

  template<typename T>
  inline mat<4, 4, T, defaultp> orthoLH(T left, T right, T bottom, T top, T zNear, T zFar)
  {
    return orthoLH_NO(left, right, bottom, top, zNear, zFar);
  }

  template<typename T>
  inline mat<4, 4, T, defaultp> orthoRH(T left, T right, T bottom, T top, T zNear, T zFar)
  {
    return orthoRH_NO(left, right, bottom, top, zNear, zFar);
  }

  template<typename T>
  inline mat<4, 4, T, defaultp> ortho(T left, T right, T bottom, T top, T zNear, T zFar)
  {
    return orthoRH_NO(left, right, bottom, top, zNear, zFar);
  }

  template<typename T>
  inline mat<4, 4, T, defaultp> frustumLH_ZO(T left, T right, T bottom, T top, T nearVal, T farVal)
  {
    mat<4, 4, T, defaultp> Result(0);
    Result[0][0] = (static_cast<T>(2) * nearVal) / (right - left);
    Result[1][1] = (static_cast<T>(2) * nearVal) / (top - bottom);
    Result[2][0] = (right + left) / (right - left);
    Result[2][1] = (top + bottom) / (top - bottom);
    Result[2][2] = farVal / (farVal - nearVal);
    Result[2][3] = static_cast<T>(1);
    Result[3][2] = -(farVal * nearVal) / (farVal - nearVal);
    return Result;
  }

  template<typename T>
  inline mat<4, 4, T, defaultp> frustumLH_NO(T left, T right, T bottom, T top, T nearVal, T farVal)
  {
    mat<4, 4, T, defaultp> Result(0);
    Result[0][0] = (static_cast<T>(2) * nearVal) / (right - left);
    Result[1][1] = (static_cast<T>(2) * nearVal) / (top - bottom);
    Result[2][0] = (right + left) / (right - left);
    Result[2][1] = (top + bottom) / (top - bottom);
    Result[2][2] = (farVal + nearVal) / (farVal - nearVal);
    Result[2][3] = static_cast<T>(1);
    Result[3][2] = -(static_cast<T>(2) * farVal * nearVal) / (farVal - nearVal);
    return Result;
  }

  template<typename T>
  inline mat<4, 4, T, defaultp> frustumRH_ZO(T left, T right, T bottom, T top, T nearVal, T farVal)
  {
    mat<4, 4, T, defaultp> Result(0);
    Result[0][0] = (static_cast<T>(2) * nearVal) / (right - left);
    Result[1][1] = (static_cast<T>(2) * nearVal) / (top - bottom);
    Result[2][0] = (right + left) / (right - left);
    Result[2][1] = (top + bottom) / (top - bottom);
    Result[2][2] = farVal / (nearVal - farVal);
    Result[2][3] = static_cast<T>(-1);
    Result[3][2] = -(farVal * nearVal) / (farVal - nearVal);
    return Result;
  }

  template<typename T>
  inline mat<4, 4, T, defaultp> frustumRH_NO(T left, T right, T bottom, T top, T nearVal, T farVal)
  {
    mat<4, 4, T, defaultp> Result(0);
    Result[0][0] = (static_cast<T>(2) * nearVal) / (right - left);
    Result[1][1] = (static_cast<T>(2) * nearVal) / (top - bottom);
    Result[2][0] = (right + left) / (right - left);
    Result[2][1] = (top + bottom) / (top - bottom);
    Result[2][2] = -(farVal + nearVal) / (farVal - nearVal);
    Result[2][3] = static_cast<T>(-1);
    Result[3][2] = -(static_cast<T>(2) * farVal * nearVal) / (farVal - nearVal);
    return Result;
  }

  template<typename T>
  inline mat<4, 4, T, defaultp> frustumZO(T left, T right, T bottom, T top, T nearVal, T farVal)
  {
    return frustumRH_ZO(left, right, bottom, top, nearVal, farVal);
  }

  template<typename T>
  inline mat<4, 4, T, defaultp> frustumNO(T left, T right, T bottom, T top, T nearVal, T farVal)
  {
    return frustumRH_NO(left, right, bottom, top, nearVal, farVal);
  }

  template<typename T>
  inline mat<4, 4, T, defaultp> frustumLH(T left, T right, T bottom, T top, T nearVal, T farVal)
  {
    return frustumLH_NO(left, right, bottom, top, nearVal, farVal);
  }

  template<typename T>
  inline mat<4, 4, T, defaultp> frustumRH(T left, T right, T bottom, T top, T nearVal, T farVal)
  {
    return frustumRH_NO(left, right, bottom, top, nearVal, farVal);
  }

  template<typename T>
  inline mat<4, 4, T, defaultp> frustum(T left, T right, T bottom, T top, T nearVal, T farVal)
  {
    return frustumRH_NO(left, right, bottom, top, nearVal, farVal);
  }

  template<typename T>
  inline mat<4, 4, T, defaultp> perspectiveRH_ZO(T fovy, T aspect, T zNear, T zFar)
  {
    assert(abs(aspect - std::numeric_limits<T>::epsilon()) > static_cast<T>(0));

    T const tanHalfFovy = tan(fovy / static_cast<T>(2));

    mat<4, 4, T, defaultp> Result(static_cast<T>(0));
    Result[0][0] = static_cast<T>(1) / (aspect * tanHalfFovy);
    Result[1][1] = static_cast<T>(1) / (tanHalfFovy);
    Result[2][2] = zFar / (zNear - zFar);
    Result[2][3] = -static_cast<T>(1);
    Result[3][2] = -(zFar * zNear) / (zFar - zNear);
    return Result;
  }

  template<typename T>
  inline mat<4, 4, T, defaultp> perspectiveRH_NO(T fovy, T aspect, T zNear, T zFar)
  {
    assert(abs(aspect - std::numeric_limits<T>::epsilon()) > static_cast<T>(0));

    T const tanHalfFovy = tan(fovy / static_cast<T>(2));

    mat<4, 4, T, defaultp> Result(static_cast<T>(0));
    Result[0][0] = static_cast<T>(1) / (aspect * tanHalfFovy);
    Result[1][1] = static_cast<T>(1) / (tanHalfFovy);
    Result[2][2] = -(zFar + zNear) / (zFar - zNear);
    Result[2][3] = -static_cast<T>(1);
    Result[3][2] = -(static_cast<T>(2) * zFar * zNear) / (zFar - zNear);
    return Result;
  }

  template<typename T>
  inline mat<4, 4, T, defaultp> perspectiveLH_ZO(T fovy, T aspect, T zNear, T zFar)
  {
    assert(abs(aspect - std::numeric_limits<T>::epsilon()) > static_cast<T>(0));

    T const tanHalfFovy = tan(fovy / static_cast<T>(2));

    mat<4, 4, T, defaultp> Result(static_cast<T>(0));
    Result[0][0] = static_cast<T>(1) / (aspect * tanHalfFovy);
    Result[1][1] = static_cast<T>(1) / (tanHalfFovy);
    Result[2][2] = zFar / (zFar - zNear);
    Result[2][3] = static_cast<T>(1);
    Result[3][2] = -(zFar * zNear) / (zFar - zNear);
    return Result;
  }

  template<typename T>
  inline mat<4, 4, T, defaultp> perspectiveLH_NO(T fovy, T aspect, T zNear, T zFar)
  {
    assert(abs(aspect - std::numeric_limits<T>::epsilon()) > static_cast<T>(0));

    T const tanHalfFovy = tan(fovy / static_cast<T>(2));

    mat<4, 4, T, defaultp> Result(static_cast<T>(0));
    Result[0][0] = static_cast<T>(1) / (aspect * tanHalfFovy);
    Result[1][1] = static_cast<T>(1) / (tanHalfFovy);
    Result[2][2] = (zFar + zNear) / (zFar - zNear);
    Result[2][3] = static_cast<T>(1);
    Result[3][2] = -(static_cast<T>(2) * zFar * zNear) / (zFar - zNear);
    return Result;
  }

  template<typename T>
  inline mat<4, 4, T, defaultp> perspectiveZO(T fovy, T aspect, T zNear, T zFar)
  {



    return perspectiveRH_ZO(fovy, aspect, zNear, zFar);

  }

  template<typename T>
  inline mat<4, 4, T, defaultp> perspectiveNO(T fovy, T aspect, T zNear, T zFar)
  {



    return perspectiveRH_NO(fovy, aspect, zNear, zFar);

  }

  template<typename T>
  inline mat<4, 4, T, defaultp> perspectiveLH(T fovy, T aspect, T zNear, T zFar)
  {



    return perspectiveLH_NO(fovy, aspect, zNear, zFar);


  }

  template<typename T>
  inline mat<4, 4, T, defaultp> perspectiveRH(T fovy, T aspect, T zNear, T zFar)
  {



    return perspectiveRH_NO(fovy, aspect, zNear, zFar);

  }

  template<typename T>
  inline mat<4, 4, T, defaultp> perspective(T fovy, T aspect, T zNear, T zFar)
  {







    return perspectiveRH_NO(fovy, aspect, zNear, zFar);

  }

  template<typename T>
  inline mat<4, 4, T, defaultp> perspectiveFovRH_ZO(T fov, T width, T height, T zNear, T zFar)
  {
    assert(width > static_cast<T>(0));
    assert(height > static_cast<T>(0));
    assert(fov > static_cast<T>(0));

    T const rad = fov;
    T const h = glm::cos(static_cast<T>(0.5) * rad) / glm::sin(static_cast<T>(0.5) * rad);
    T const w = h * height / width; ///todo max(width , Height) / min(width , Height)?

    mat<4, 4, T, defaultp> Result(static_cast<T>(0));
    Result[0][0] = w;
    Result[1][1] = h;
    Result[2][2] = zFar / (zNear - zFar);
    Result[2][3] = -static_cast<T>(1);
    Result[3][2] = -(zFar * zNear) / (zFar - zNear);
    return Result;
  }

  template<typename T>
  inline mat<4, 4, T, defaultp> perspectiveFovRH_NO(T fov, T width, T height, T zNear, T zFar)
  {
    assert(width > static_cast<T>(0));
    assert(height > static_cast<T>(0));
    assert(fov > static_cast<T>(0));

    T const rad = fov;
    T const h = glm::cos(static_cast<T>(0.5) * rad) / glm::sin(static_cast<T>(0.5) * rad);
    T const w = h * height / width; ///todo max(width , Height) / min(width , Height)?

    mat<4, 4, T, defaultp> Result(static_cast<T>(0));
    Result[0][0] = w;
    Result[1][1] = h;
    Result[2][2] = -(zFar + zNear) / (zFar - zNear);
    Result[2][3] = -static_cast<T>(1);
    Result[3][2] = -(static_cast<T>(2) * zFar * zNear) / (zFar - zNear);
    return Result;
  }

  template<typename T>
  inline mat<4, 4, T, defaultp> perspectiveFovLH_ZO(T fov, T width, T height, T zNear, T zFar)
  {
    assert(width > static_cast<T>(0));
    assert(height > static_cast<T>(0));
    assert(fov > static_cast<T>(0));

    T const rad = fov;
    T const h = glm::cos(static_cast<T>(0.5) * rad) / glm::sin(static_cast<T>(0.5) * rad);
    T const w = h * height / width; ///todo max(width , Height) / min(width , Height)?

    mat<4, 4, T, defaultp> Result(static_cast<T>(0));
    Result[0][0] = w;
    Result[1][1] = h;
    Result[2][2] = zFar / (zFar - zNear);
    Result[2][3] = static_cast<T>(1);
    Result[3][2] = -(zFar * zNear) / (zFar - zNear);
    return Result;
  }

  template<typename T>
  inline mat<4, 4, T, defaultp> perspectiveFovLH_NO(T fov, T width, T height, T zNear, T zFar)
  {
    assert(width > static_cast<T>(0));
    assert(height > static_cast<T>(0));
    assert(fov > static_cast<T>(0));

    T const rad = fov;
    T const h = glm::cos(static_cast<T>(0.5) * rad) / glm::sin(static_cast<T>(0.5) * rad);
    T const w = h * height / width; ///todo max(width , Height) / min(width , Height)?

    mat<4, 4, T, defaultp> Result(static_cast<T>(0));
    Result[0][0] = w;
    Result[1][1] = h;
    Result[2][2] = (zFar + zNear) / (zFar - zNear);
    Result[2][3] = static_cast<T>(1);
    Result[3][2] = -(static_cast<T>(2) * zFar * zNear) / (zFar - zNear);
    return Result;
  }

  template<typename T>
  inline mat<4, 4, T, defaultp> perspectiveFovZO(T fov, T width, T height, T zNear, T zFar)
  {



    return perspectiveFovRH_ZO(fov, width, height, zNear, zFar);

  }

  template<typename T>
  inline mat<4, 4, T, defaultp> perspectiveFovNO(T fov, T width, T height, T zNear, T zFar)
  {



    return perspectiveFovRH_NO(fov, width, height, zNear, zFar);

  }

  template<typename T>
  inline mat<4, 4, T, defaultp> perspectiveFovLH(T fov, T width, T height, T zNear, T zFar)
  {



    return perspectiveFovLH_NO(fov, width, height, zNear, zFar);

  }

  template<typename T>
  inline mat<4, 4, T, defaultp> perspectiveFovRH(T fov, T width, T height, T zNear, T zFar)
  {



    return perspectiveFovRH_NO(fov, width, height, zNear, zFar);

  }

  template<typename T>
  inline mat<4, 4, T, defaultp> perspectiveFov(T fov, T width, T height, T zNear, T zFar)
  {







    return perspectiveFovRH_NO(fov, width, height, zNear, zFar);

  }

  template<typename T>
  inline mat<4, 4, T, defaultp> infinitePerspectiveRH(T fovy, T aspect, T zNear)
  {
    T const range = tan(fovy / static_cast<T>(2)) * zNear;
    T const left = -range * aspect;
    T const right = range * aspect;
    T const bottom = -range;
    T const top = range;

    mat<4, 4, T, defaultp> Result(static_cast<T>(0));
    Result[0][0] = (static_cast<T>(2) * zNear) / (right - left);
    Result[1][1] = (static_cast<T>(2) * zNear) / (top - bottom);
    Result[2][2] = -static_cast<T>(1);
    Result[2][3] = -static_cast<T>(1);
    Result[3][2] = -static_cast<T>(2) * zNear;
    return Result;
  }

  template<typename T>
  inline mat<4, 4, T, defaultp> infinitePerspectiveLH(T fovy, T aspect, T zNear)
  {
    T const range = tan(fovy / static_cast<T>(2)) * zNear;
    T const left = -range * aspect;
    T const right = range * aspect;
    T const bottom = -range;
    T const top = range;

    mat<4, 4, T, defaultp> Result(T(0));
    Result[0][0] = (static_cast<T>(2) * zNear) / (right - left);
    Result[1][1] = (static_cast<T>(2) * zNear) / (top - bottom);
    Result[2][2] = static_cast<T>(1);
    Result[2][3] = static_cast<T>(1);
    Result[3][2] = -static_cast<T>(2) * zNear;
    return Result;
  }

  template<typename T>
  inline mat<4, 4, T, defaultp> infinitePerspective(T fovy, T aspect, T zNear)
  {



    return infinitePerspectiveRH(fovy, aspect, zNear);

  }

  // Infinite projection matrix: http://www.terathon.com/gdc07_lengyel.pdf
  template<typename T>
  inline mat<4, 4, T, defaultp> tweakedInfinitePerspective(T fovy, T aspect, T zNear, T ep)
  {
    T const range = tan(fovy / static_cast<T>(2)) * zNear;
    T const left = -range * aspect;
    T const right = range * aspect;
    T const bottom = -range;
    T const top = range;

    mat<4, 4, T, defaultp> Result(static_cast<T>(0));
    Result[0][0] = (static_cast<T>(2) * zNear) / (right - left);
    Result[1][1] = (static_cast<T>(2) * zNear) / (top - bottom);
    Result[2][2] = ep - static_cast<T>(1);
    Result[2][3] = static_cast<T>(-1);
    Result[3][2] = (ep - static_cast<T>(2)) * zNear;
    return Result;
  }

  template<typename T>
  inline mat<4, 4, T, defaultp> tweakedInfinitePerspective(T fovy, T aspect, T zNear)
  {
    return tweakedInfinitePerspective(fovy, aspect, zNear, epsilon<T>());
  }
}//namespace glm



/// @ref ext_matrix_transform
/// @file glm/ext/matrix_transform.hpp
///
/// @defgroup ext_matrix_transform GLM_EXT_matrix_transform
/// @ingroup ext
///
/// Defines functions that generate common transformation matrices.
///
/// The matrices generated by this extension use standard OpenGL fixed-function
/// conventions. For example, the lookAt function generates a transform from world
/// space into the specific eye space that the projective matrix functions
/// (perspective, ortho, etc) are designed to expect. The OpenGL compatibility
/// specifications defines the particular layout of this eye space.
///
/// Include <glm/ext/matrix_transform.hpp> to use the features of this extension.
///
/// @see ext_matrix_projection
/// @see ext_matrix_clip_space



// Dependencies









namespace glm
{
  /// @addtogroup ext_matrix_transform
  /// @{

  /// Builds an identity matrix.
  template<typename genType>
  constexpr genType identity();

  /// Builds a translation 4 * 4 matrix created from a vector of 3 components.
  ///
  /// @param m Input matrix multiplied by this translation matrix.
  /// @param v Coordinates of a translation vector.
  ///
  /// @tparam T A floating-point scalar type
  /// @tparam Q A value from qualifier enum
  ///
  /// @code
  /// #include <glm/glm.hpp>
  /// #include <glm/gtc/matrix_transform.hpp>
  /// ...
  /// glm::mat4 m = glm::translate(glm::mat4(1.0f), glm::vec3(1.0f));
  /// // m[0][0] == 1.0f, m[0][1] == 0.0f, m[0][2] == 0.0f, m[0][3] == 0.0f
  /// // m[1][0] == 0.0f, m[1][1] == 1.0f, m[1][2] == 0.0f, m[1][3] == 0.0f
  /// // m[2][0] == 0.0f, m[2][1] == 0.0f, m[2][2] == 1.0f, m[2][3] == 0.0f
  /// // m[3][0] == 1.0f, m[3][1] == 1.0f, m[3][2] == 1.0f, m[3][3] == 1.0f
  /// @endcode
  ///
  /// @see - translate(mat<4, 4, T, Q> const& m, T x, T y, T z)
  /// @see - translate(vec<3, T, Q> const& v)
  /// @see <a href="https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glTranslate.xml">glTranslate man page</a>
  template<typename T, qualifier Q>
  mat<4, 4, T, Q> translate(
    mat<4, 4, T, Q> const& m, vec<3, T, Q> const& v);

  /// Builds a rotation 4 * 4 matrix created from an axis vector and an angle.
  ///
  /// @param m Input matrix multiplied by this rotation matrix.
  /// @param angle Rotation angle expressed in radians.
  /// @param axis Rotation axis, recommended to be normalized.
  ///
  /// @tparam T A floating-point scalar type
  /// @tparam Q A value from qualifier enum
  ///
  /// @see - rotate(mat<4, 4, T, Q> const& m, T angle, T x, T y, T z)
  /// @see - rotate(T angle, vec<3, T, Q> const& v)
  /// @see <a href="https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glRotate.xml">glRotate man page</a>
  template<typename T, qualifier Q>
  mat<4, 4, T, Q> rotate(
    mat<4, 4, T, Q> const& m, T angle, vec<3, T, Q> const& axis);

  /// Builds a scale 4 * 4 matrix created from 3 scalars.
  ///
  /// @param m Input matrix multiplied by this scale matrix.
  /// @param v Ratio of scaling for each axis.
  ///
  /// @tparam T A floating-point scalar type
  /// @tparam Q A value from qualifier enum
  ///
  /// @see - scale(mat<4, 4, T, Q> const& m, T x, T y, T z)
  /// @see - scale(vec<3, T, Q> const& v)
  /// @see <a href="https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glScale.xml">glScale man page</a>
  template<typename T, qualifier Q>
  mat<4, 4, T, Q> scale(
    mat<4, 4, T, Q> const& m, vec<3, T, Q> const& v);

  /// Build a right handed look at view matrix.
  ///
  /// @param eye Position of the camera
  /// @param center Position where the camera is looking at
  /// @param up Normalized up vector, how the camera is oriented. Typically (0, 0, 1)
  ///
  /// @tparam T A floating-point scalar type
  /// @tparam Q A value from qualifier enum
  ///
  /// @see - frustum(T const& left, T const& right, T const& bottom, T const& top, T const& nearVal, T const& farVal) frustum(T const& left, T const& right, T const& bottom, T const& top, T const& nearVal, T const& farVal)
  template<typename T, qualifier Q>
  mat<4, 4, T, Q> lookAtRH(
    vec<3, T, Q> const& eye, vec<3, T, Q> const& center, vec<3, T, Q> const& up);

  /// Build a left handed look at view matrix.
  ///
  /// @param eye Position of the camera
  /// @param center Position where the camera is looking at
  /// @param up Normalized up vector, how the camera is oriented. Typically (0, 0, 1)
  ///
  /// @tparam T A floating-point scalar type
  /// @tparam Q A value from qualifier enum
  ///
  /// @see - frustum(T const& left, T const& right, T const& bottom, T const& top, T const& nearVal, T const& farVal) frustum(T const& left, T const& right, T const& bottom, T const& top, T const& nearVal, T const& farVal)
  template<typename T, qualifier Q>
  mat<4, 4, T, Q> lookAtLH(
    vec<3, T, Q> const& eye, vec<3, T, Q> const& center, vec<3, T, Q> const& up);

  /// Build a look at view matrix based on the default handedness.
  ///
  /// @param eye Position of the camera
  /// @param center Position where the camera is looking at
  /// @param up Normalized up vector, how the camera is oriented. Typically (0, 0, 1)
  ///
  /// @tparam T A floating-point scalar type
  /// @tparam Q A value from qualifier enum
  ///
  /// @see - frustum(T const& left, T const& right, T const& bottom, T const& top, T const& nearVal, T const& farVal) frustum(T const& left, T const& right, T const& bottom, T const& top, T const& nearVal, T const& farVal)
  /// @see <a href="https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/gluLookAt.xml">gluLookAt man page</a>
  template<typename T, qualifier Q>
  mat<4, 4, T, Q> lookAt(
    vec<3, T, Q> const& eye, vec<3, T, Q> const& center, vec<3, T, Q> const& up);

  /// @}
}//namespace glm


namespace glm
{
  template<typename genType>
  inline constexpr genType identity()
  {
    return detail::init_gentype<genType, detail::genTypeTrait<genType>::GENTYPE>::identity();
  }

  template<typename T, qualifier Q>
  inline mat<4, 4, T, Q> translate(mat<4, 4, T, Q> const& m, vec<3, T, Q> const& v)
  {
    mat<4, 4, T, Q> Result(m);
    Result[3] = m[0] * v[0] + m[1] * v[1] + m[2] * v[2] + m[3];
    return Result;
  }

  template<typename T, qualifier Q>
  inline mat<4, 4, T, Q> rotate(mat<4, 4, T, Q> const& m, T angle, vec<3, T, Q> const& v)
  {
    T const a = angle;
    T const c = cos(a);
    T const s = sin(a);

    vec<3, T, Q> axis(normalize(v));
    vec<3, T, Q> temp((T(1) - c) * axis);

    mat<4, 4, T, Q> Rotate;
    Rotate[0][0] = c + temp[0] * axis[0];
    Rotate[0][1] = temp[0] * axis[1] + s * axis[2];
    Rotate[0][2] = temp[0] * axis[2] - s * axis[1];

    Rotate[1][0] = temp[1] * axis[0] - s * axis[2];
    Rotate[1][1] = c + temp[1] * axis[1];
    Rotate[1][2] = temp[1] * axis[2] + s * axis[0];

    Rotate[2][0] = temp[2] * axis[0] + s * axis[1];
    Rotate[2][1] = temp[2] * axis[1] - s * axis[0];
    Rotate[2][2] = c + temp[2] * axis[2];

    mat<4, 4, T, Q> Result;
    Result[0] = m[0] * Rotate[0][0] + m[1] * Rotate[0][1] + m[2] * Rotate[0][2];
    Result[1] = m[0] * Rotate[1][0] + m[1] * Rotate[1][1] + m[2] * Rotate[1][2];
    Result[2] = m[0] * Rotate[2][0] + m[1] * Rotate[2][1] + m[2] * Rotate[2][2];
    Result[3] = m[3];
    return Result;
  }

  template<typename T, qualifier Q>
  inline mat<4, 4, T, Q> rotate_slow(mat<4, 4, T, Q> const& m, T angle, vec<3, T, Q> const& v)
  {
    T const a = angle;
    T const c = cos(a);
    T const s = sin(a);
    mat<4, 4, T, Q> Result;

    vec<3, T, Q> axis = normalize(v);

    Result[0][0] = c + (static_cast<T>(1) - c) * axis.x * axis.x;
    Result[0][1] = (static_cast<T>(1) - c) * axis.x * axis.y + s * axis.z;
    Result[0][2] = (static_cast<T>(1) - c) * axis.x * axis.z - s * axis.y;
    Result[0][3] = static_cast<T>(0);

    Result[1][0] = (static_cast<T>(1) - c) * axis.y * axis.x - s * axis.z;
    Result[1][1] = c + (static_cast<T>(1) - c) * axis.y * axis.y;
    Result[1][2] = (static_cast<T>(1) - c) * axis.y * axis.z + s * axis.x;
    Result[1][3] = static_cast<T>(0);

    Result[2][0] = (static_cast<T>(1) - c) * axis.z * axis.x + s * axis.y;
    Result[2][1] = (static_cast<T>(1) - c) * axis.z * axis.y - s * axis.x;
    Result[2][2] = c + (static_cast<T>(1) - c) * axis.z * axis.z;
    Result[2][3] = static_cast<T>(0);

    Result[3] = vec<4, T, Q>(0, 0, 0, 1);
    return m * Result;
  }

  template<typename T, qualifier Q>
  inline mat<4, 4, T, Q> scale(mat<4, 4, T, Q> const& m, vec<3, T, Q> const& v)
  {
    mat<4, 4, T, Q> Result;
    Result[0] = m[0] * v[0];
    Result[1] = m[1] * v[1];
    Result[2] = m[2] * v[2];
    Result[3] = m[3];
    return Result;
  }

  template<typename T, qualifier Q>
  inline mat<4, 4, T, Q> scale_slow(mat<4, 4, T, Q> const& m, vec<3, T, Q> const& v)
  {
    mat<4, 4, T, Q> Result(T(1));
    Result[0][0] = v.x;
    Result[1][1] = v.y;
    Result[2][2] = v.z;
    return m * Result;
  }

  template<typename T, qualifier Q>
  inline mat<4, 4, T, Q> lookAtRH(vec<3, T, Q> const& eye, vec<3, T, Q> const& center, vec<3, T, Q> const& up)
  {
    vec<3, T, Q> const f(normalize(center - eye));
    vec<3, T, Q> const s(normalize(cross(f, up)));
    vec<3, T, Q> const u(cross(s, f));

    mat<4, 4, T, Q> Result(1);
    Result[0][0] = s.x;
    Result[1][0] = s.y;
    Result[2][0] = s.z;
    Result[0][1] = u.x;
    Result[1][1] = u.y;
    Result[2][1] = u.z;
    Result[0][2] = -f.x;
    Result[1][2] = -f.y;
    Result[2][2] = -f.z;
    Result[3][0] = -dot(s, eye);
    Result[3][1] = -dot(u, eye);
    Result[3][2] = dot(f, eye);
    return Result;
  }

  template<typename T, qualifier Q>
  inline mat<4, 4, T, Q> lookAtLH(vec<3, T, Q> const& eye, vec<3, T, Q> const& center, vec<3, T, Q> const& up)
  {
    vec<3, T, Q> const f(normalize(center - eye));
    vec<3, T, Q> const s(normalize(cross(up, f)));
    vec<3, T, Q> const u(cross(f, s));

    mat<4, 4, T, Q> Result(1);
    Result[0][0] = s.x;
    Result[1][0] = s.y;
    Result[2][0] = s.z;
    Result[0][1] = u.x;
    Result[1][1] = u.y;
    Result[2][1] = u.z;
    Result[0][2] = f.x;
    Result[1][2] = f.y;
    Result[2][2] = f.z;
    Result[3][0] = -dot(s, eye);
    Result[3][1] = -dot(u, eye);
    Result[3][2] = -dot(f, eye);
    return Result;
  }

  template<typename T, qualifier Q>
  inline mat<4, 4, T, Q> lookAt(vec<3, T, Q> const& eye, vec<3, T, Q> const& center, vec<3, T, Q> const& up)
  {
    if constexpr (((1 << 3) | (1 << 1)) & (1 << 2))
      return lookAtLH(eye, center, up);
    else
      return lookAtRH(eye, center, up);
  }
}//namespace glm














/// @ref gtc_type_ptr
/// @file glm/gtc/type_ptr.hpp
///
/// @see core (dependence)
/// @see gtc_quaternion (dependence)
///
/// @defgroup gtc_type_ptr GLM_GTC_type_ptr
/// @ingroup gtc
///
/// Include <glm/gtc/type_ptr.hpp> to use the features of this extension.
///
/// Handles the interaction between pointers and vector, matrix types.
///
/// This extension defines an overloaded function, glm::value_ptr. It returns
/// a pointer to the memory layout of the object. Matrix types store their values
/// in column-major order.
///
/// This is useful for uploading data to matrices or copying data to buffer objects.
///
/// Example:
/// @code
/// #include <glm/glm.hpp>
/// #include <glm/gtc/type_ptr.hpp>
///
/// glm::vec3 aVector(3);
/// glm::mat4 someMatrix(1.0);
///
/// glUniform3fv(uniformLoc, 1, glm::value_ptr(aVector));
/// glUniformMatrix4fv(uniformMatrixLoc, 1, GL_FALSE, glm::value_ptr(someMatrix));
/// @endcode
///
/// <glm/gtc/type_ptr.hpp> need to be included to use the features of this extension.



// Dependency:

/// @ref gtc_quaternion
/// @file glm/gtc/quaternion.hpp
///
/// @see core (dependence)
/// @see gtc_constants (dependence)
///
/// @defgroup gtc_quaternion GLM_GTC_quaternion
/// @ingroup gtc
///
/// Include <glm/gtc/quaternion.hpp> to use the features of this extension.
///
/// Defines a templated quaternion type and several quaternion operations.



// Dependency:



/// @ref ext_vector_relational
/// @file glm/ext/vector_relational.hpp
///
/// @see core (dependence)
/// @see ext_scalar_integer (dependence)
///
/// @defgroup ext_vector_relational GLM_EXT_vector_relational
/// @ingroup ext
///
/// Exposes comparison functions for vector types that take a user defined epsilon values.
///
/// Include <glm/ext/vector_relational.hpp> to use the features of this extension.
///
/// @see core_vector_relational
/// @see ext_scalar_relational
/// @see ext_matrix_relational



// Dependencies






namespace glm
{
  /// @addtogroup ext_vector_relational
  /// @{

  /// Returns the component-wise comparison of |x - y| < epsilon.
  /// True if this expression is satisfied.
  ///
  /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector
  /// @tparam T Floating-point or integer scalar types
  /// @tparam Q Value from qualifier enum
  template<length_t L, typename T, qualifier Q>
  constexpr vec<L, bool, Q> equal(vec<L, T, Q> const& x, vec<L, T, Q> const& y, T epsilon);

  /// Returns the component-wise comparison of |x - y| < epsilon.
  /// True if this expression is satisfied.
  ///
  /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector
  /// @tparam T Floating-point or integer scalar types
  /// @tparam Q Value from qualifier enum
  template<length_t L, typename T, qualifier Q>
  constexpr vec<L, bool, Q> equal(vec<L, T, Q> const& x, vec<L, T, Q> const& y, vec<L, T, Q> const& epsilon);

  /// Returns the component-wise comparison of |x - y| >= epsilon.
  /// True if this expression is not satisfied.
  ///
  /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector
  /// @tparam T Floating-point or integer scalar types
  /// @tparam Q Value from qualifier enum
  template<length_t L, typename T, qualifier Q>
  constexpr vec<L, bool, Q> notEqual(vec<L, T, Q> const& x, vec<L, T, Q> const& y, T epsilon);

  /// Returns the component-wise comparison of |x - y| >= epsilon.
  /// True if this expression is not satisfied.
  ///
  /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector
  /// @tparam T Floating-point or integer scalar types
  /// @tparam Q Value from qualifier enum
  template<length_t L, typename T, qualifier Q>
  constexpr vec<L, bool, Q> notEqual(vec<L, T, Q> const& x, vec<L, T, Q> const& y, vec<L, T, Q> const& epsilon);

  /// Returns the component-wise comparison between two vectors in term of ULPs.
  /// True if this expression is satisfied.
  ///
  /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector
  /// @tparam T Floating-point
  /// @tparam Q Value from qualifier enum
  template<length_t L, typename T, qualifier Q>
  constexpr vec<L, bool, Q> equal(vec<L, T, Q> const& x, vec<L, T, Q> const& y, int ULPs);

  /// Returns the component-wise comparison between two vectors in term of ULPs.
  /// True if this expression is satisfied.
  ///
  /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector
  /// @tparam T Floating-point
  /// @tparam Q Value from qualifier enum
  template<length_t L, typename T, qualifier Q>
  constexpr vec<L, bool, Q> equal(vec<L, T, Q> const& x, vec<L, T, Q> const& y, vec<L, int, Q> const& ULPs);

  /// Returns the component-wise comparison between two vectors in term of ULPs.
  /// True if this expression is not satisfied.
  ///
  /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector
  /// @tparam T Floating-point
  /// @tparam Q Value from qualifier enum
  template<length_t L, typename T, qualifier Q>
  constexpr vec<L, bool, Q> notEqual(vec<L, T, Q> const& x, vec<L, T, Q> const& y, int ULPs);

  /// Returns the component-wise comparison between two vectors in term of ULPs.
  /// True if this expression is not satisfied.
  ///
  /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector
  /// @tparam T Floating-point
  /// @tparam Q Value from qualifier enum
  template<length_t L, typename T, qualifier Q>
  constexpr vec<L, bool, Q> notEqual(vec<L, T, Q> const& x, vec<L, T, Q> const& y, vec<L, int, Q> const& ULPs);

  /// @}
}//namespace glm

namespace glm {
  namespace detail
  {
    template <typename T>
    union float_t
    {};

    // https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/
    template <>
    union float_t<float>
    {
      typedef int int_type;
      typedef float float_type;

      constexpr float_t(float_type Num = 0.0f) : f(Num) {}

      constexpr float_t& operator=(float_t const& x)
      {
        f = x.f;
        return *this;
      }

      // Portable extraction of components.
      constexpr bool negative() const { return i < 0; }
      constexpr int_type mantissa() const { return i & ((1 << 23) - 1); }
      constexpr int_type exponent() const { return (i >> 23) & ((1 << 8) - 1); }

      int_type i;
      float_type f;
    };

    template <>
    union float_t<double>
    {
      typedef detail::int64 int_type;
      typedef double float_type;

      constexpr float_t(float_type Num = static_cast<float_type>(0)) : f(Num) {}

      constexpr float_t& operator=(float_t const& x)
      {
        f = x.f;
        return *this;
      }

      // Portable extraction of components.
      constexpr bool negative() const { return i < 0; }
      constexpr int_type mantissa() const { return i & ((int_type(1) << 52) - 1); }
      constexpr int_type exponent() const { return (i >> 52) & ((int_type(1) << 11) - 1); }

      int_type i;
      float_type f;
    };
  }//namespace detail
}//namespace glm






namespace glm
{
  template<length_t L, typename T, qualifier Q>
  inline constexpr vec<L, bool, Q> equal(vec<L, T, Q> const& x, vec<L, T, Q> const& y, T Epsilon)
  {
    return equal(x, y, vec<L, T, Q>(Epsilon));
  }

  template<length_t L, typename T, qualifier Q>
  inline constexpr vec<L, bool, Q> equal(vec<L, T, Q> const& x, vec<L, T, Q> const& y, vec<L, T, Q> const& Epsilon)
  {
    return lessThanEqual(abs(x - y), Epsilon);
  }

  template<length_t L, typename T, qualifier Q>
  inline constexpr vec<L, bool, Q> notEqual(vec<L, T, Q> const& x, vec<L, T, Q> const& y, T Epsilon)
  {
    return notEqual(x, y, vec<L, T, Q>(Epsilon));
  }

  template<length_t L, typename T, qualifier Q>
  inline constexpr vec<L, bool, Q> notEqual(vec<L, T, Q> const& x, vec<L, T, Q> const& y, vec<L, T, Q> const& Epsilon)
  {
    return greaterThan(abs(x - y), Epsilon);
  }


  template<length_t L, typename T, qualifier Q>
  inline constexpr vec<L, bool, Q> equal(vec<L, T, Q> const& x, vec<L, T, Q> const& y, int MaxULPs)
  {
    return equal(x, y, vec<L, int, Q>(MaxULPs));
  }

  template<length_t L, typename T, qualifier Q>
  inline constexpr vec<L, bool, Q> equal(vec<L, T, Q> const& x, vec<L, T, Q> const& y, vec<L, int, Q> const& MaxULPs)
  {
    vec<L, bool, Q> Result(false);
    for (length_t i = 0; i < L; ++i)
    {
      detail::float_t<T> const a(x[i]);
      detail::float_t<T> const b(y[i]);

      // Different signs means they do not match.
      if (a.negative() != b.negative())
      {
        // Check for equality to make sure +0==-0
        Result[i] = a.mantissa() == b.mantissa() && a.exponent() == b.exponent();
      }
      else
      {
        // Find the difference in ULPs.
        typename detail::float_t<T>::int_type const DiffULPs = abs(a.i - b.i);
        Result[i] = DiffULPs <= MaxULPs[i];
      }
    }
    return Result;
  }

  template<length_t L, typename T, qualifier Q>
  inline constexpr vec<L, bool, Q> notEqual(vec<L, T, Q> const& x, vec<L, T, Q> const& y, int MaxULPs)
  {
    return notEqual(x, y, vec<L, int, Q>(MaxULPs));
  }

  template<length_t L, typename T, qualifier Q>
  inline constexpr vec<L, bool, Q> notEqual(vec<L, T, Q> const& x, vec<L, T, Q> const& y, vec<L, int, Q> const& MaxULPs)
  {
    return not_(equal(x, y, MaxULPs));
  }
}//namespace glm

namespace glm
{
  /// @addtogroup ext_quaternion_geometric
  /// @{

  /// Returns the norm of a quaternions
  ///
  /// @tparam T Floating-point scalar types
  /// @tparam Q Value from qualifier enum
  ///
  /// @see ext_quaternion_geometric
  template<typename T, qualifier Q>
  T length(qua<T, Q> const& q);

  /// Returns the normalized quaternion.
  ///
  /// @tparam T Floating-point scalar types
  /// @tparam Q Value from qualifier enum
  ///
  /// @see ext_quaternion_geometric
  template<typename T, qualifier Q>
  qua<T, Q> normalize(qua<T, Q> const& q);

  /// Returns dot product of q1 and q2, i.e., q1[0] * q2[0] + q1[1] * q2[1] + ...
  ///
  /// @tparam T Floating-point scalar types.
  /// @tparam Q Value from qualifier enum
  ///
  /// @see ext_quaternion_geometric
  template<typename T, qualifier Q>
  T dot(qua<T, Q> const& x, qua<T, Q> const& y);

  /// Compute a cross product.
  ///
  /// @tparam T Floating-point scalar types
  /// @tparam Q Value from qualifier enum
  ///
  /// @see ext_quaternion_geometric
  template<typename T, qualifier Q>
  inline qua<T, Q> cross(qua<T, Q> const& q1, qua<T, Q> const& q2);

  /// @}
} //namespace glm


namespace glm
{
  template<typename T, qualifier Q>
  inline T dot(qua<T, Q> const& x, qua<T, Q> const& y)
  {
    static_assert(std::numeric_limits<T>::is_iec559, "'dot' accepts only floating-point inputs");
    return detail::compute_dot<qua<T, Q>, T, detail::is_aligned<Q>::value>::call(x, y);
  }

  template<typename T, qualifier Q>
  inline T length(qua<T, Q> const& q)
  {
    return glm::sqrt(dot(q, q));
  }

  template<typename T, qualifier Q>
  inline qua<T, Q> normalize(qua<T, Q> const& q)
  {
    T len = length(q);
    if (len <= static_cast<T>(0)) // Problem
      return qua<T, Q>(static_cast<T>(1), static_cast<T>(0), static_cast<T>(0), static_cast<T>(0));
    T oneOverLen = static_cast<T>(1) / len;
    return qua<T, Q>(q.w * oneOverLen, q.x * oneOverLen, q.y * oneOverLen, q.z * oneOverLen);
  }

  template<typename T, qualifier Q>
  inline qua<T, Q> cross(qua<T, Q> const& q1, qua<T, Q> const& q2)
  {
    return qua<T, Q>(
      q1.w * q2.w - q1.x * q2.x - q1.y * q2.y - q1.z * q2.z,
      q1.w * q2.x + q1.x * q2.w + q1.y * q2.z - q1.z * q2.y,
      q1.w * q2.y + q1.y * q2.w + q1.z * q2.x - q1.x * q2.z,
      q1.w * q2.z + q1.z * q2.w + q1.x * q2.y - q1.y * q2.x);
  }
}//namespace glm

namespace glm
{
  /// @addtogroup ext_quaternion_common
  /// @{

  /// Spherical linear interpolation of two quaternions.
  /// The interpolation is oriented and the rotation is performed at constant speed.
  /// For short path spherical linear interpolation, use the slerp function.
  ///
  /// @param x A quaternion
  /// @param y A quaternion
  /// @param a Interpolation factor. The interpolation is defined beyond the range [0, 1].
  ///
  /// @tparam T A floating-point scalar type
  /// @tparam Q A value from qualifier enum
  ///
  /// @see - slerp(qua<T, Q> const& x, qua<T, Q> const& y, T const& a)
  template<typename T, qualifier Q>
  qua<T, Q> mix(qua<T, Q> const& x, qua<T, Q> const& y, T a);

  /// Linear interpolation of two quaternions.
  /// The interpolation is oriented.
  ///
  /// @param x A quaternion
  /// @param y A quaternion
  /// @param a Interpolation factor. The interpolation is defined in the range [0, 1].
  ///
  /// @tparam T A floating-point scalar type
  /// @tparam Q A value from qualifier enum
  template<typename T, qualifier Q>
  qua<T, Q> lerp(qua<T, Q> const& x, qua<T, Q> const& y, T a);

  /// Spherical linear interpolation of two quaternions.
  /// The interpolation always take the short path and the rotation is performed at constant speed.
  ///
  /// @param x A quaternion
  /// @param y A quaternion
  /// @param a Interpolation factor. The interpolation is defined beyond the range [0, 1].
  ///
  /// @tparam T A floating-point scalar type
  /// @tparam Q A value from qualifier enum
  template<typename T, qualifier Q>
  qua<T, Q> slerp(qua<T, Q> const& x, qua<T, Q> const& y, T a);

  /// Spherical linear interpolation of two quaternions with multiple spins over rotation axis.
  /// The interpolation always take the short path when the spin count is positive and long path
  /// when count is negative. Rotation is performed at constant speed.
  ///
  /// @param x A quaternion
  /// @param y A quaternion
  /// @param a Interpolation factor. The interpolation is defined beyond the range [0, 1].
  /// @param k Additional spin count. If Value is negative interpolation will be on "long" path.
  ///
  /// @tparam T A floating-point scalar type
  /// @tparam S An integer scalar type
  /// @tparam Q A value from qualifier enum
  template<typename T, typename S, qualifier Q>
  qua<T, Q> slerp(qua<T, Q> const& x, qua<T, Q> const& y, T a, S k);

  /// Returns the q conjugate.
  ///
  /// @tparam T A floating-point scalar type
  /// @tparam Q A value from qualifier enum
  template<typename T, qualifier Q>
  qua<T, Q> conjugate(qua<T, Q> const& q);

  /// Returns the q inverse.
  ///
  /// @tparam T A floating-point scalar type
  /// @tparam Q A value from qualifier enum
  template<typename T, qualifier Q>
  qua<T, Q> inverse(qua<T, Q> const& q);

  /// Returns true if x holds a NaN (not a number)
  /// representation in the underlying implementation's set of
  /// floating point representations. Returns false otherwise,
  /// including for implementations with no NaN
  /// representations.
  ///
  /// /!\ When using compiler fast math, this function may fail.
  ///
  /// @tparam T A floating-point scalar type
  /// @tparam Q A value from qualifier enum
  template<typename T, qualifier Q>
  vec<4, bool, Q> isnan(qua<T, Q> const& x);

  /// Returns true if x holds a positive infinity or negative
  /// infinity representation in the underlying implementation's
  /// set of floating point representations. Returns false
  /// otherwise, including for implementations with no infinity
  /// representations.
  ///
  /// @tparam T A floating-point scalar type
  /// @tparam Q A value from qualifier enum
  template<typename T, qualifier Q>
  vec<4, bool, Q> isinf(qua<T, Q> const& x);

  /// @}
} //namespace glm


namespace glm
{
  template<typename T, qualifier Q>
  inline qua<T, Q> mix(qua<T, Q> const& x, qua<T, Q> const& y, T a)
  {
    static_assert(std::numeric_limits<T>::is_iec559, "'mix' only accept floating-point inputs");

    T const cosTheta = dot(x, y);

    // Perform a linear interpolation when cosTheta is close to 1 to avoid side effect of sin(angle) becoming a zero denominator
    if (cosTheta > static_cast<T>(1) - epsilon<T>())
    {
      // Linear interpolation
      return qua<T, Q>(
        mix(x.w, y.w, a),
        mix(x.x, y.x, a),
        mix(x.y, y.y, a),
        mix(x.z, y.z, a));
    }
    else
    {
      // Essential Mathematics, page 467
      T angle = acos(cosTheta);
      return (sin((static_cast<T>(1) - a) * angle) * x + sin(a * angle) * y) / sin(angle);
    }
  }

  template<typename T, qualifier Q>
  inline qua<T, Q> lerp(qua<T, Q> const& x, qua<T, Q> const& y, T a)
  {
    static_assert(std::numeric_limits<T>::is_iec559, "'lerp' only accept floating-point inputs");

    // Lerp is only defined in [0, 1]
    assert(a >= static_cast<T>(0));
    assert(a <= static_cast<T>(1));

    return x * (static_cast<T>(1) - a) + (y * a);
  }

  template<typename T, qualifier Q>
  inline qua<T, Q> slerp(qua<T, Q> const& x, qua<T, Q> const& y, T a)
  {
    static_assert(std::numeric_limits<T>::is_iec559, "'slerp' only accept floating-point inputs");

    qua<T, Q> z = y;

    T cosTheta = dot(x, y);

    // If cosTheta < 0, the interpolation will take the long way around the sphere.
    // To fix this, one quat must be negated.
    if (cosTheta < static_cast<T>(0))
    {
      z = -y;
      cosTheta = -cosTheta;
    }

    // Perform a linear interpolation when cosTheta is close to 1 to avoid side effect of sin(angle) becoming a zero denominator
    if (cosTheta > static_cast<T>(1) - epsilon<T>())
    {
      // Linear interpolation
      return qua<T, Q>(
        mix(x.w, z.w, a),
        mix(x.x, z.x, a),
        mix(x.y, z.y, a),
        mix(x.z, z.z, a));
    }
    else
    {
      // Essential Mathematics, page 467
      T angle = acos(cosTheta);
      return (sin((static_cast<T>(1) - a) * angle) * x + sin(a * angle) * z) / sin(angle);
    }
  }

  template<typename T, typename S, qualifier Q>
  inline qua<T, Q> slerp(qua<T, Q> const& x, qua<T, Q> const& y, T a, S k)
  {
    static_assert(std::numeric_limits<T>::is_iec559, "'slerp' only accept floating-point inputs");
    static_assert(std::numeric_limits<S>::is_integer, "'slerp' only accept integer for spin count");

    qua<T, Q> z = y;

    T cosTheta = dot(x, y);

    // If cosTheta < 0, the interpolation will take the long way around the sphere.
    // To fix this, one quat must be negated.
    if (cosTheta < static_cast<T>(0))
    {
      z = -y;
      cosTheta = -cosTheta;
    }

    // Perform a linear interpolation when cosTheta is close to 1 to avoid side effect of sin(angle) becoming a zero denominator
    if (cosTheta > static_cast<T>(1) - epsilon<T>())
    {
      // Linear interpolation
      return qua<T, Q>(
        mix(x.w, z.w, a),
        mix(x.x, z.x, a),
        mix(x.y, z.y, a),
        mix(x.z, z.z, a));
    }
    else
    {
      // Graphics Gems III, page 96
      T angle = acos(cosTheta);
      T phi = angle + k * glm::pi<T>();
      return (sin(angle - a * phi) * x + sin(a * phi) * z) / sin(angle);
    }
  }

  template<typename T, qualifier Q>
  inline qua<T, Q> conjugate(qua<T, Q> const& q)
  {
    return qua<T, Q>(q.w, -q.x, -q.y, -q.z);
  }

  template<typename T, qualifier Q>
  inline qua<T, Q> inverse(qua<T, Q> const& q)
  {
    return conjugate(q) / dot(q, q);
  }

  template<typename T, qualifier Q>
  inline vec<4, bool, Q> isnan(qua<T, Q> const& q)
  {
    static_assert(std::numeric_limits<T>::is_iec559, "'isnan' only accept floating-point inputs");

    return vec<4, bool, Q>(isnan(q.x), isnan(q.y), isnan(q.z), isnan(q.w));
  }

  template<typename T, qualifier Q>
  inline vec<4, bool, Q> isinf(qua<T, Q> const& q)
  {
    static_assert(std::numeric_limits<T>::is_iec559, "'isinf' only accept floating-point inputs");

    return vec<4, bool, Q>(isinf(q.x), isinf(q.y), isinf(q.z), isinf(q.w));
  }
}//namespace glm


namespace glm
{
  /// @addtogroup ext_quaternion_relational
  /// @{

  /// Returns the component-wise comparison of result x == y.
  ///
  /// @tparam T Floating-point scalar types
  /// @tparam Q Value from qualifier enum
  template<typename T, qualifier Q>
  vec<4, bool, Q> equal(qua<T, Q> const& x, qua<T, Q> const& y);

  /// Returns the component-wise comparison of |x - y| < epsilon.
  ///
  /// @tparam T Floating-point scalar types
  /// @tparam Q Value from qualifier enum
  template<typename T, qualifier Q>
  vec<4, bool, Q> equal(qua<T, Q> const& x, qua<T, Q> const& y, T epsilon);

  /// Returns the component-wise comparison of result x != y.
  ///
  /// @tparam T Floating-point scalar types
  /// @tparam Q Value from qualifier enum
  template<typename T, qualifier Q>
  vec<4, bool, Q> notEqual(qua<T, Q> const& x, qua<T, Q> const& y);

  /// Returns the component-wise comparison of |x - y| >= epsilon.
  ///
  /// @tparam T Floating-point scalar types
  /// @tparam Q Value from qualifier enum
  template<typename T, qualifier Q>
  vec<4, bool, Q> notEqual(qua<T, Q> const& x, qua<T, Q> const& y, T epsilon);

  /// @}
} //namespace glm


namespace glm
{
  template<typename T, qualifier Q>
  inline vec<4, bool, Q> equal(qua<T, Q> const& x, qua<T, Q> const& y)
  {
    vec<4, bool, Q> Result;
    for (length_t i = 0; i < x.length(); ++i)
      Result[i] = x[i] == y[i];
    return Result;
  }

  template<typename T, qualifier Q>
  inline vec<4, bool, Q> equal(qua<T, Q> const& x, qua<T, Q> const& y, T epsilon)
  {
    vec<4, T, Q> v(x.x - y.x, x.y - y.y, x.z - y.z, x.w - y.w);
    return lessThan(abs(v), vec<4, T, Q>(epsilon));
  }

  template<typename T, qualifier Q>
  inline vec<4, bool, Q> notEqual(qua<T, Q> const& x, qua<T, Q> const& y)
  {
    vec<4, bool, Q> Result;
    for (length_t i = 0; i < x.length(); ++i)
      Result[i] = x[i] != y[i];
    return Result;
  }

  template<typename T, qualifier Q>
  inline vec<4, bool, Q> notEqual(qua<T, Q> const& x, qua<T, Q> const& y, T epsilon)
  {
    vec<4, T, Q> v(x.x - y.x, x.y - y.y, x.z - y.z, x.w - y.w);
    return greaterThanEqual(abs(v), vec<4, T, Q>(epsilon));
  }
}//namespace glm

namespace glm
{
  template<typename T, qualifier Q>
  struct qua
  {
    // -- Implementation detail --

    typedef qua<T, Q> type;
    typedef T value_type;

    union
    {
      struct { T x, y, z, w; };

      typename detail::storage<4, T, detail::is_aligned<Q>::value>::type data;
    };

    // -- Component accesses --

    typedef length_t length_type;

    /// Return the count of components of a quaternion
    static constexpr length_type length() { return 4; }

    constexpr T& operator[](length_type i);
    constexpr T const& operator[](length_type i) const;

    // -- Implicit basic constructors --

    constexpr qua() = default;
    constexpr qua(qua<T, Q> const& q) = default;
    template<qualifier P>
    constexpr qua(qua<T, P> const& q);

    // -- Explicit basic constructors --

    constexpr qua(T s, vec<3, T, Q> const& v);
    constexpr qua(T w, T x, T y, T z);

    // -- Conversion constructors --

    template<typename U, qualifier P>
    constexpr  qua(qua<U, P> const& q);

    /// Explicit conversion operators

    explicit operator mat<3, 3, T, Q>() const;
    explicit operator mat<4, 4, T, Q>() const;


    /// Create a quaternion from two normalized axis
    ///
    /// @param u A first normalized axis
    /// @param v A second normalized axis
    /// @see gtc_quaternion
    /// @see http://lolengine.net/blog/2013/09/18/beautiful-maths-quaternion-from-vectors
    qua(vec<3, T, Q> const& u, vec<3, T, Q> const& v);

    /// Build a quaternion from euler angles (pitch, yaw, roll), in radians.
    constexpr  qua(vec<3, T, Q> const& eulerAngles);
    qua(mat<3, 3, T, Q> const& q);
    qua(mat<4, 4, T, Q> const& q);

    // -- Unary arithmetic operators --

    constexpr qua<T, Q>& operator=(qua<T, Q> const& q) = default;

    template<typename U>
    constexpr qua<T, Q>& operator=(qua<U, Q> const& q);
    template<typename U>
    constexpr qua<T, Q>& operator+=(qua<U, Q> const& q);
    template<typename U>
    constexpr qua<T, Q>& operator-=(qua<U, Q> const& q);
    template<typename U>
    constexpr qua<T, Q>& operator*=(qua<U, Q> const& q);
    template<typename U>
    constexpr qua<T, Q>& operator*=(U s);
    template<typename U>
    constexpr qua<T, Q>& operator/=(U s);
  };

  // -- Unary bit operators --

  template<typename T, qualifier Q>
  constexpr qua<T, Q> operator+(qua<T, Q> const& q);

  template<typename T, qualifier Q>
  constexpr qua<T, Q> operator-(qua<T, Q> const& q);

  // -- Binary operators --

  template<typename T, qualifier Q>
  constexpr qua<T, Q> operator+(qua<T, Q> const& q, qua<T, Q> const& p);

  template<typename T, qualifier Q>
  constexpr qua<T, Q> operator-(qua<T, Q> const& q, qua<T, Q> const& p);

  template<typename T, qualifier Q>
  constexpr qua<T, Q> operator*(qua<T, Q> const& q, qua<T, Q> const& p);

  template<typename T, qualifier Q>
  constexpr vec<3, T, Q> operator*(qua<T, Q> const& q, vec<3, T, Q> const& v);

  template<typename T, qualifier Q>
  constexpr vec<3, T, Q> operator*(vec<3, T, Q> const& v, qua<T, Q> const& q);

  template<typename T, qualifier Q>
  constexpr vec<4, T, Q> operator*(qua<T, Q> const& q, vec<4, T, Q> const& v);

  template<typename T, qualifier Q>
  constexpr vec<4, T, Q> operator*(vec<4, T, Q> const& v, qua<T, Q> const& q);

  template<typename T, qualifier Q>
  constexpr qua<T, Q> operator*(qua<T, Q> const& q, T const& s);

  template<typename T, qualifier Q>
  constexpr qua<T, Q> operator*(T const& s, qua<T, Q> const& q);

  template<typename T, qualifier Q>
  constexpr qua<T, Q> operator/(qua<T, Q> const& q, T const& s);

  // -- Boolean operators --

  template<typename T, qualifier Q>
  constexpr bool operator==(qua<T, Q> const& q1, qua<T, Q> const& q2);

  template<typename T, qualifier Q>
  constexpr bool operator!=(qua<T, Q> const& q1, qua<T, Q> const& q2);
} //namespace glm

namespace glm {
  namespace detail
  {
    template <typename T>
    struct genTypeTrait<qua<T> >
    {
      static const genTypeEnum GENTYPE = GENTYPE_QUAT;
    };

    template<typename T, qualifier Q, bool Aligned>
    struct compute_dot<qua<T, Q>, T, Aligned>
    {
      inline constexpr static T call(qua<T, Q> const& a, qua<T, Q> const& b)
      {
        vec<4, T, Q> tmp(a.w * b.w, a.x * b.x, a.y * b.y, a.z * b.z);
        return (tmp.x + tmp.y) + (tmp.z + tmp.w);
      }
    };

    template<typename T, qualifier Q, bool Aligned>
    struct compute_quat_add
    {
      inline constexpr static qua<T, Q> call(qua<T, Q> const& q, qua<T, Q> const& p)
      {
        return qua<T, Q>(q.w + p.w, q.x + p.x, q.y + p.y, q.z + p.z);
      }
    };

    template<typename T, qualifier Q, bool Aligned>
    struct compute_quat_sub
    {
      inline constexpr static qua<T, Q> call(qua<T, Q> const& q, qua<T, Q> const& p)
      {
        return qua<T, Q>(q.w - p.w, q.x - p.x, q.y - p.y, q.z - p.z);
      }
    };

    template<typename T, qualifier Q, bool Aligned>
    struct compute_quat_mul_scalar
    {
      inline constexpr static qua<T, Q> call(qua<T, Q> const& q, T s)
      {
        return qua<T, Q>(q.w * s, q.x * s, q.y * s, q.z * s);
      }
    };

    template<typename T, qualifier Q, bool Aligned>
    struct compute_quat_div_scalar
    {
      inline constexpr static qua<T, Q> call(qua<T, Q> const& q, T s)
      {
        return qua<T, Q>(q.w / s, q.x / s, q.y / s, q.z / s);
      }
    };

    template<typename T, qualifier Q, bool Aligned>
    struct compute_quat_mul_vec4
    {
      inline constexpr static vec<4, T, Q> call(qua<T, Q> const& q, vec<4, T, Q> const& v)
      {
        return vec<4, T, Q>(q * vec<3, T, Q>(v), v.w);
      }
    };
  }//namespace detail

    // -- Component accesses --

  template<typename T, qualifier Q>
  inline constexpr T& qua<T, Q>::operator[](typename qua<T, Q>::length_type i)
  {
    assert(i >= 0 && i < this->length());



    return (&x)[i];

  }

  template<typename T, qualifier Q>
  inline constexpr T const& qua<T, Q>::operator[](typename qua<T, Q>::length_type i) const
  {
    assert(i >= 0 && i < this->length());



    return (&x)[i];

  }

  template<typename T, qualifier Q>
  template<qualifier P>
  inline constexpr qua<T, Q>::qua(qua<T, P> const& q)



    : x(q.x), y(q.y), z(q.z), w(q.w)

  {}

  // -- Explicit basic constructors --

  template<typename T, qualifier Q>
  inline constexpr qua<T, Q>::qua(T s, vec<3, T, Q> const& v)



    : x(v.x), y(v.y), z(v.z), w(s)

  {}

  template <typename T, qualifier Q>
  inline constexpr qua<T, Q>::qua(T _w, T _x, T _y, T _z)



    : x(_x), y(_y), z(_z), w(_w)

  {}

  // -- Conversion constructors --

  template<typename T, qualifier Q>
  template<typename U, qualifier P>
  inline constexpr qua<T, Q>::qua(qua<U, P> const& q)



    : x(static_cast<T>(q.x)), y(static_cast<T>(q.y)), z(static_cast<T>(q.z)), w(static_cast<T>(q.w))

  {}

  //template<typename valType>
  //GLM_FUNC_QUALIFIER qua<valType>::qua
  //(
  //	valType const& pitch,
  //	valType const& yaw,
  //	valType const& roll
  //)
  //{
  //	vec<3, valType> eulerAngle(pitch * valType(0.5), yaw * valType(0.5), roll * valType(0.5));
  //	vec<3, valType> c = glm::cos(eulerAngle * valType(0.5));
  //	vec<3, valType> s = glm::sin(eulerAngle * valType(0.5));
  //
  //	this->w = c.x * c.y * c.z + s.x * s.y * s.z;
  //	this->x = s.x * c.y * c.z - c.x * s.y * s.z;
  //	this->y = c.x * s.y * c.z + s.x * c.y * s.z;
  //	this->z = c.x * c.y * s.z - s.x * s.y * c.z;
  //}

  template<typename T, qualifier Q>
  inline qua<T, Q>::qua(vec<3, T, Q> const& u, vec<3, T, Q> const& v)
  {
    T norm_u_norm_v = sqrt(dot(u, u) * dot(v, v));
    T real_part = norm_u_norm_v + dot(u, v);
    vec<3, T, Q> t;

    if (real_part < static_cast<T>(1.e-6f) * norm_u_norm_v)
    {
      // If u and v are exactly opposite, rotate 180 degrees
      // around an arbitrary orthogonal axis. Axis normalisation
      // can happen later, when we normalise the quaternion.
      real_part = static_cast<T>(0);
      t = abs(u.x) > abs(u.z) ? vec<3, T, Q>(-u.y, u.x, static_cast<T>(0)) : vec<3, T, Q>(static_cast<T>(0), -u.z, u.y);
    }
    else
    {
      // Otherwise, build quaternion the standard way.
      t = cross(u, v);
    }

    *this = normalize(qua<T, Q>(real_part, t.x, t.y, t.z));
  }

  template<typename T, qualifier Q>
  inline constexpr qua<T, Q>::qua(vec<3, T, Q> const& eulerAngle)
  {
    vec<3, T, Q> c = glm::cos(eulerAngle * T(0.5));
    vec<3, T, Q> s = glm::sin(eulerAngle * T(0.5));

    this->w = c.x * c.y * c.z + s.x * s.y * s.z;
    this->x = s.x * c.y * c.z - c.x * s.y * s.z;
    this->y = c.x * s.y * c.z + s.x * c.y * s.z;
    this->z = c.x * c.y * s.z - s.x * s.y * c.z;
  }

  template<typename T, qualifier Q>
  inline qua<T, Q>::qua(mat<3, 3, T, Q> const& m)
  {
    *this = quat_cast(m);
  }

  template<typename T, qualifier Q>
  inline qua<T, Q>::qua(mat<4, 4, T, Q> const& m)
  {
    *this = quat_cast(m);
  }


  template<typename T, qualifier Q>
  inline qua<T, Q>::operator mat<3, 3, T, Q>() const
  {
    return mat3_cast(*this);
  }

  template<typename T, qualifier Q>
  inline qua<T, Q>::operator mat<4, 4, T, Q>() const
  {
    return mat4_cast(*this);
  }


  // -- Unary arithmetic operators --













  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr qua<T, Q>& qua<T, Q>::operator=(qua<U, Q> const& q)
  {
    this->w = static_cast<T>(q.w);
    this->x = static_cast<T>(q.x);
    this->y = static_cast<T>(q.y);
    this->z = static_cast<T>(q.z);
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr qua<T, Q>& qua<T, Q>::operator+=(qua<U, Q> const& q)
  {
    return (*this = detail::compute_quat_add<T, Q, detail::is_aligned<Q>::value>::call(*this, qua<T, Q>(q)));
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr qua<T, Q>& qua<T, Q>::operator-=(qua<U, Q> const& q)
  {
    return (*this = detail::compute_quat_sub<T, Q, detail::is_aligned<Q>::value>::call(*this, qua<T, Q>(q)));
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr qua<T, Q>& qua<T, Q>::operator*=(qua<U, Q> const& r)
  {
    qua<T, Q> const p(*this);
    qua<T, Q> const q(r);

    this->w = p.w * q.w - p.x * q.x - p.y * q.y - p.z * q.z;
    this->x = p.w * q.x + p.x * q.w + p.y * q.z - p.z * q.y;
    this->y = p.w * q.y + p.y * q.w + p.z * q.x - p.x * q.z;
    this->z = p.w * q.z + p.z * q.w + p.x * q.y - p.y * q.x;
    return *this;
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr qua<T, Q>& qua<T, Q>::operator*=(U s)
  {
    return (*this = detail::compute_quat_mul_scalar<T, Q, detail::is_aligned<Q>::value>::call(*this, static_cast<U>(s)));
  }

  template<typename T, qualifier Q>
  template<typename U>
  inline constexpr qua<T, Q>& qua<T, Q>::operator/=(U s)
  {
    return (*this = detail::compute_quat_div_scalar<T, Q, detail::is_aligned<Q>::value>::call(*this, static_cast<U>(s)));
  }

  // -- Unary bit operators --

  template<typename T, qualifier Q>
  inline constexpr qua<T, Q> operator+(qua<T, Q> const& q)
  {
    return q;
  }

  template<typename T, qualifier Q>
  inline constexpr qua<T, Q> operator-(qua<T, Q> const& q)
  {
    return qua<T, Q>(-q.w, -q.x, -q.y, -q.z);
  }

  // -- Binary operators --

  template<typename T, qualifier Q>
  inline constexpr qua<T, Q> operator+(qua<T, Q> const& q, qua<T, Q> const& p)
  {
    return qua<T, Q>(q) += p;
  }

  template<typename T, qualifier Q>
  inline constexpr qua<T, Q> operator-(qua<T, Q> const& q, qua<T, Q> const& p)
  {
    return qua<T, Q>(q) -= p;
  }

  template<typename T, qualifier Q>
  inline constexpr qua<T, Q> operator*(qua<T, Q> const& q, qua<T, Q> const& p)
  {
    return qua<T, Q>(q) *= p;
  }

  template<typename T, qualifier Q>
  inline constexpr vec<3, T, Q> operator*(qua<T, Q> const& q, vec<3, T, Q> const& v)
  {
    vec<3, T, Q> const QuatVector(q.x, q.y, q.z);
    vec<3, T, Q> const uv(glm::cross(QuatVector, v));
    vec<3, T, Q> const uuv(glm::cross(QuatVector, uv));

    return v + ((uv * q.w) + uuv) * static_cast<T>(2);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<3, T, Q> operator*(vec<3, T, Q> const& v, qua<T, Q> const& q)
  {
    return glm::inverse(q) * v;
  }

  template<typename T, qualifier Q>
  inline constexpr vec<4, T, Q> operator*(qua<T, Q> const& q, vec<4, T, Q> const& v)
  {
    return detail::compute_quat_mul_vec4<T, Q, detail::is_aligned<Q>::value>::call(q, v);
  }

  template<typename T, qualifier Q>
  inline constexpr vec<4, T, Q> operator*(vec<4, T, Q> const& v, qua<T, Q> const& q)
  {
    return glm::inverse(q) * v;
  }

  template<typename T, qualifier Q>
  inline constexpr qua<T, Q> operator*(qua<T, Q> const& q, T const& s)
  {
    return qua<T, Q>(
      q.w * s, q.x * s, q.y * s, q.z * s);
  }

  template<typename T, qualifier Q>
  inline constexpr qua<T, Q> operator*(T const& s, qua<T, Q> const& q)
  {
    return q * s;
  }

  template<typename T, qualifier Q>
  inline constexpr qua<T, Q> operator/(qua<T, Q> const& q, T const& s)
  {
    return qua<T, Q>(
      q.w / s, q.x / s, q.y / s, q.z / s);
  }

  // -- Boolean operators --

  template<typename T, qualifier Q>
  inline constexpr bool operator==(qua<T, Q> const& q1, qua<T, Q> const& q2)
  {
    return q1.x == q2.x && q1.y == q2.y && q1.z == q2.z && q1.w == q2.w;
  }

  template<typename T, qualifier Q>
  inline constexpr bool operator!=(qua<T, Q> const& q1, qua<T, Q> const& q2)
  {
    return q1.x != q2.x || q1.y != q2.y || q1.z != q2.z || q1.w != q2.w;
  }
}//namespace glm













namespace glm
{
  /// @addtogroup ext_quaternion_float
  /// @{

  /// Quaternion of single-precision floating-point numbers.
  typedef qua<float, defaultp>		quat;

  /// @}
} //namespace glm



/// @ref ext_quaternion_float_precision
/// @file glm/ext/quaternion_float_precision.hpp
///
/// @defgroup ext_quaternion_float_precision GLM_EXT_quaternion_float_precision
/// @ingroup ext
///
/// Exposes single-precision floating point quaternion type with various precision in term of ULPs.
///
/// Include <glm/ext/quaternion_float_precision.hpp> to use the features of this extension.



// Dependency:






namespace glm
{
  /// @addtogroup ext_quaternion_float_precision
  /// @{

  /// Quaternion of single-precision floating-point numbers using high precision arithmetic in term of ULPs.
  typedef qua<float, lowp>		lowp_quat;

  /// Quaternion of single-precision floating-point numbers using high precision arithmetic in term of ULPs.
  typedef qua<float, mediump>		mediump_quat;

  /// Quaternion of single-precision floating-point numbers using high precision arithmetic in term of ULPs.
  typedef qua<float, highp>		highp_quat;

  /// @}
} //namespace glm



/// @ref ext_quaternion_double
/// @file glm/ext/quaternion_double.hpp
///
/// @defgroup ext_quaternion_double GLM_EXT_quaternion_double
/// @ingroup ext
///
/// Exposes double-precision floating point quaternion type.
///
/// Include <glm/ext/quaternion_double.hpp> to use the features of this extension.
///
/// @see ext_quaternion_float
/// @see ext_quaternion_double_precision
/// @see ext_quaternion_common
/// @see ext_quaternion_exponential
/// @see ext_quaternion_geometric
/// @see ext_quaternion_relational
/// @see ext_quaternion_transform
/// @see ext_quaternion_trigonometric



// Dependency:






namespace glm
{
  /// @addtogroup ext_quaternion_double
  /// @{

  /// Quaternion of double-precision floating-point numbers.
  typedef qua<double, defaultp>		dquat;

  /// @}
} //namespace glm



/// @ref ext_quaternion_double_precision
/// @file glm/ext/quaternion_double_precision.hpp
///
/// @defgroup ext_quaternion_double_precision GLM_EXT_quaternion_double_precision
/// @ingroup ext
///
/// Exposes double-precision floating point quaternion type with various precision in term of ULPs.
///
/// Include <glm/ext/quaternion_double_precision.hpp> to use the features of this extension.



// Dependency:






namespace glm
{
  /// @addtogroup ext_quaternion_double_precision
  /// @{

  /// Quaternion of double-precision floating-point numbers using high precision arithmetic in term of ULPs.
  ///
  /// @see ext_quaternion_double_precision
  typedef qua<double, lowp>		lowp_dquat;

  /// Quaternion of medium double-qualifier floating-point numbers using high precision arithmetic in term of ULPs.
  ///
  /// @see ext_quaternion_double_precision
  typedef qua<double, mediump>	mediump_dquat;

  /// Quaternion of high double-qualifier floating-point numbers using high precision arithmetic in term of ULPs.
  ///
  /// @see ext_quaternion_double_precision
  typedef qua<double, highp>		highp_dquat;

  /// @}
} //namespace glm

namespace glm
{
  /// @addtogroup ext_quaternion_trigonometric
  /// @{

  /// Returns the quaternion rotation angle.
  ///
  /// @tparam T A floating-point scalar type
  /// @tparam Q A value from qualifier enum
  template<typename T, qualifier Q>
  T angle(qua<T, Q> const& x);

  /// Returns the q rotation axis.
  ///
  /// @tparam T A floating-point scalar type
  /// @tparam Q A value from qualifier enum
  template<typename T, qualifier Q>
  vec<3, T, Q> axis(qua<T, Q> const& x);

  /// Build a quaternion from an angle and a normalized axis.
  ///
  /// @param angle Angle expressed in radians.
  /// @param axis Axis of the quaternion, must be normalized.
  ///
  /// @tparam T A floating-point scalar type
  /// @tparam Q A value from qualifier enum
  template<typename T, qualifier Q>
  qua<T, Q> angleAxis(T const& angle, vec<3, T, Q> const& axis);

  /// @}
} //namespace glm




namespace glm
{
  template<typename T, qualifier Q>
  inline T angle(qua<T, Q> const& x)
  {
    if (abs(x.w) > cos_one_over_two<T>())
    {
      return asin(sqrt(x.x * x.x + x.y * x.y + x.z * x.z)) * static_cast<T>(2);
    }

    return acos(x.w) * static_cast<T>(2);
  }

  template<typename T, qualifier Q>
  inline vec<3, T, Q> axis(qua<T, Q> const& x)
  {
    T const tmp1 = static_cast<T>(1) - x.w * x.w;
    if (tmp1 <= static_cast<T>(0))
      return vec<3, T, Q>(0, 0, 1);
    T const tmp2 = static_cast<T>(1) / sqrt(tmp1);
    return vec<3, T, Q>(x.x * tmp2, x.y * tmp2, x.z * tmp2);
  }

  template<typename T, qualifier Q>
  inline qua<T, Q> angleAxis(T const& angle, vec<3, T, Q> const& v)
  {
    T const a(angle);
    T const s = glm::sin(a * static_cast<T>(0.5));

    return qua<T, Q>(glm::cos(a * static_cast<T>(0.5)), v * s);
  }
}//namespace glm



/// @ref ext_quaternion_transform
/// @file glm/ext/quaternion_transform.hpp
///
/// @defgroup ext_quaternion_transform GLM_EXT_quaternion_transform
/// @ingroup ext
///
/// Provides transformation functions for quaternion types
///
/// Include <glm/ext/quaternion_transform.hpp> to use the features of this extension.
///
/// @see ext_quaternion_float
/// @see ext_quaternion_double
/// @see ext_quaternion_exponential
/// @see ext_quaternion_geometric
/// @see ext_quaternion_relational
/// @see ext_quaternion_trigonometric



// Dependency:








namespace glm
{
  /// @addtogroup ext_quaternion_transform
  /// @{

  /// Rotates a quaternion from a vector of 3 components axis and an angle.
  ///
  /// @param q Source orientation
  /// @param angle Angle expressed in radians.
  /// @param axis Axis of the rotation
  ///
  /// @tparam T Floating-point scalar types
  /// @tparam Q Value from qualifier enum
  template<typename T, qualifier Q>
  qua<T, Q> rotate(qua<T, Q> const& q, T const& angle, vec<3, T, Q> const& axis);
  /// @}
} //namespace glm


namespace glm
{
  template<typename T, qualifier Q>
  inline qua<T, Q> rotate(qua<T, Q> const& q, T const& angle, vec<3, T, Q> const& v)
  {
    vec<3, T, Q> Tmp = v;

    // Axis of rotation must be normalised
    T len = glm::length(Tmp);
    if (abs(len - static_cast<T>(1)) > static_cast<T>(0.001))
    {
      T oneOverLen = static_cast<T>(1) / len;
      Tmp.x *= oneOverLen;
      Tmp.y *= oneOverLen;
      Tmp.z *= oneOverLen;
    }

    T const AngleRad(angle);
    T const Sin = sin(AngleRad * static_cast<T>(0.5));

    return q * qua<T, Q>(cos(AngleRad * static_cast<T>(0.5)), Tmp.x * Sin, Tmp.y * Sin, Tmp.z * Sin);
  }
}//namespace glm

namespace glm
{
  /// @addtogroup gtc_quaternion
  /// @{

  /// Returns euler angles, pitch as x, yaw as y, roll as z.
  /// The result is expressed in radians.
  ///
  /// @tparam T Floating-point scalar types.
  ///
  /// @see gtc_quaternion
  template<typename T, qualifier Q>
  vec<3, T, Q> eulerAngles(qua<T, Q> const& x);

  /// Returns roll value of euler angles expressed in radians.
  ///
  /// @tparam T Floating-point scalar types.
  ///
  /// @see gtc_quaternion
  template<typename T, qualifier Q>
  T roll(qua<T, Q> const& x);

  /// Returns pitch value of euler angles expressed in radians.
  ///
  /// @tparam T Floating-point scalar types.
  ///
  /// @see gtc_quaternion
  template<typename T, qualifier Q>
  T pitch(qua<T, Q> const& x);

  /// Returns yaw value of euler angles expressed in radians.
  ///
  /// @tparam T Floating-point scalar types.
  ///
  /// @see gtc_quaternion
  template<typename T, qualifier Q>
  T yaw(qua<T, Q> const& x);

  /// Converts a quaternion to a 3 * 3 matrix.
  ///
  /// @tparam T Floating-point scalar types.
  ///
  /// @see gtc_quaternion
  template<typename T, qualifier Q>
  mat<3, 3, T, Q> mat3_cast(qua<T, Q> const& x);

  /// Converts a quaternion to a 4 * 4 matrix.
  ///
  /// @tparam T Floating-point scalar types.
  ///
  /// @see gtc_quaternion
  template<typename T, qualifier Q>
  mat<4, 4, T, Q> mat4_cast(qua<T, Q> const& x);

  /// Converts a pure rotation 3 * 3 matrix to a quaternion.
  ///
  /// @tparam T Floating-point scalar types.
  ///
  /// @see gtc_quaternion
  template<typename T, qualifier Q>
  qua<T, Q> quat_cast(mat<3, 3, T, Q> const& x);

  /// Converts a pure rotation 4 * 4 matrix to a quaternion.
  ///
  /// @tparam T Floating-point scalar types.
  ///
  /// @see gtc_quaternion
  template<typename T, qualifier Q>
  qua<T, Q> quat_cast(mat<4, 4, T, Q> const& x);

  /// Returns the component-wise comparison result of x < y.
  ///
  /// @tparam T Floating-point scalar types
  /// @tparam Q Value from qualifier enum
  ///
  /// @see ext_quaternion_relational
  template<typename T, qualifier Q>
  vec<4, bool, Q> lessThan(qua<T, Q> const& x, qua<T, Q> const& y);

  /// Returns the component-wise comparison of result x <= y.
  ///
  /// @tparam T Floating-point scalar types
  /// @tparam Q Value from qualifier enum
  ///
  /// @see ext_quaternion_relational
  template<typename T, qualifier Q>
  vec<4, bool, Q> lessThanEqual(qua<T, Q> const& x, qua<T, Q> const& y);

  /// Returns the component-wise comparison of result x > y.
  ///
  /// @tparam T Floating-point scalar types
  /// @tparam Q Value from qualifier enum
  ///
  /// @see ext_quaternion_relational
  template<typename T, qualifier Q>
  vec<4, bool, Q> greaterThan(qua<T, Q> const& x, qua<T, Q> const& y);

  /// Returns the component-wise comparison of result x >= y.
  ///
  /// @tparam T Floating-point scalar types
  /// @tparam Q Value from qualifier enum
  ///
  /// @see ext_quaternion_relational
  template<typename T, qualifier Q>
  vec<4, bool, Q> greaterThanEqual(qua<T, Q> const& x, qua<T, Q> const& y);

  /// Build a look at quaternion based on the default handedness.
  ///
  /// @param direction Desired forward direction. Needs to be normalized.
  /// @param up Up vector, how the camera is oriented. Typically (0, 1, 0).
  template<typename T, qualifier Q>
  qua<T, Q> quatLookAt(
    vec<3, T, Q> const& direction,
    vec<3, T, Q> const& up);

  /// Build a right-handed look at quaternion.
  ///
  /// @param direction Desired forward direction onto which the -z-axis gets mapped. Needs to be normalized.
  /// @param up Up vector, how the camera is oriented. Typically (0, 1, 0).
  template<typename T, qualifier Q>
  qua<T, Q> quatLookAtRH(
    vec<3, T, Q> const& direction,
    vec<3, T, Q> const& up);

  /// Build a left-handed look at quaternion.
  ///
  /// @param direction Desired forward direction onto which the +z-axis gets mapped. Needs to be normalized.
  /// @param up Up vector, how the camera is oriented. Typically (0, 1, 0).
  template<typename T, qualifier Q>
  qua<T, Q> quatLookAtLH(
    vec<3, T, Q> const& direction,
    vec<3, T, Q> const& up);
  /// @}
} //namespace glm

namespace glm
{
  /// @addtogroup gtc_epsilon
  /// @{

  /// Returns the component-wise comparison of |x - y| < epsilon.
  /// True if this expression is satisfied.
  ///
  /// @see gtc_epsilon
  template<length_t L, typename T, qualifier Q>
  vec<L, bool, Q> epsilonEqual(vec<L, T, Q> const& x, vec<L, T, Q> const& y, T const& epsilon);

  /// Returns the component-wise comparison of |x - y| < epsilon.
  /// True if this expression is satisfied.
  ///
  /// @see gtc_epsilon
  template<typename genType>
  bool epsilonEqual(genType const& x, genType const& y, genType const& epsilon);

  /// Returns the component-wise comparison of |x - y| < epsilon.
  /// True if this expression is not satisfied.
  ///
  /// @see gtc_epsilon
  template<length_t L, typename T, qualifier Q>
  vec<L, bool, Q> epsilonNotEqual(vec<L, T, Q> const& x, vec<L, T, Q> const& y, T const& epsilon);

  /// Returns the component-wise comparison of |x - y| >= epsilon.
  /// True if this expression is not satisfied.
  ///
  /// @see gtc_epsilon
  template<typename genType>
  bool epsilonNotEqual(genType const& x, genType const& y, genType const& epsilon);

  /// @}
}//namespace glm

namespace glm
{
  template<>
  inline bool epsilonEqual
  (
    float const& x,
    float const& y,
    float const& epsilon
  )
  {
    return abs(x - y) < epsilon;
  }

  template<>
  inline bool epsilonEqual
  (
    double const& x,
    double const& y,
    double const& epsilon
  )
  {
    return abs(x - y) < epsilon;
  }

  template<length_t L, typename T, qualifier Q>
  inline vec<L, bool, Q> epsilonEqual(vec<L, T, Q> const& x, vec<L, T, Q> const& y, T const& epsilon)
  {
    return lessThan(abs(x - y), vec<L, T, Q>(epsilon));
  }

  template<length_t L, typename T, qualifier Q>
  inline vec<L, bool, Q> epsilonEqual(vec<L, T, Q> const& x, vec<L, T, Q> const& y, vec<L, T, Q> const& epsilon)
  {
    return lessThan(abs(x - y), vec<L, T, Q>(epsilon));
  }

  template<>
  inline bool epsilonNotEqual(float const& x, float const& y, float const& epsilon)
  {
    return abs(x - y) >= epsilon;
  }

  template<>
  inline bool epsilonNotEqual(double const& x, double const& y, double const& epsilon)
  {
    return abs(x - y) >= epsilon;
  }

  template<length_t L, typename T, qualifier Q>
  inline vec<L, bool, Q> epsilonNotEqual(vec<L, T, Q> const& x, vec<L, T, Q> const& y, T const& epsilon)
  {
    return greaterThanEqual(abs(x - y), vec<L, T, Q>(epsilon));
  }

  template<length_t L, typename T, qualifier Q>
  inline vec<L, bool, Q> epsilonNotEqual(vec<L, T, Q> const& x, vec<L, T, Q> const& y, vec<L, T, Q> const& epsilon)
  {
    return greaterThanEqual(abs(x - y), vec<L, T, Q>(epsilon));
  }

  template<typename T, qualifier Q>
  inline vec<4, bool, Q> epsilonEqual(qua<T, Q> const& x, qua<T, Q> const& y, T const& epsilon)
  {
    vec<4, T, Q> v(x.x - y.x, x.y - y.y, x.z - y.z, x.w - y.w);
    return lessThan(abs(v), vec<4, T, Q>(epsilon));
  }

  template<typename T, qualifier Q>
  inline vec<4, bool, Q> epsilonNotEqual(qua<T, Q> const& x, qua<T, Q> const& y, T const& epsilon)
  {
    vec<4, T, Q> v(x.x - y.x, x.y - y.y, x.z - y.z, x.w - y.w);
    return greaterThanEqual(abs(v), vec<4, T, Q>(epsilon));
  }
}//namespace glm

namespace glm
{
  template<typename T, qualifier Q>
  inline vec<3, T, Q> eulerAngles(qua<T, Q> const& x)
  {
    return vec<3, T, Q>(pitch(x), yaw(x), roll(x));
  }

  template<typename T, qualifier Q>
  inline T roll(qua<T, Q> const& q)
  {
    return static_cast<T>(atan(static_cast<T>(2) * (q.x * q.y + q.w * q.z), q.w * q.w + q.x * q.x - q.y * q.y - q.z * q.z));
  }

  template<typename T, qualifier Q>
  inline T pitch(qua<T, Q> const& q)
  {
    //return T(atan(T(2) * (q.y * q.z + q.w * q.x), q.w * q.w - q.x * q.x - q.y * q.y + q.z * q.z));
    T const y = static_cast<T>(2) * (q.y * q.z + q.w * q.x);
    T const x = q.w * q.w - q.x * q.x - q.y * q.y + q.z * q.z;

    if (all(equal(vec<2, T, Q>(x, y), vec<2, T, Q>(0), epsilon<T>()))) //avoid atan2(0,0) - handle singularity - Matiis
      return static_cast<T>(static_cast<T>(2) * atan(q.x, q.w));

    return static_cast<T>(atan(y, x));
  }

  template<typename T, qualifier Q>
  inline T yaw(qua<T, Q> const& q)
  {
    return asin(clamp(static_cast<T>(-2) * (q.x * q.z - q.w * q.y), static_cast<T>(-1), static_cast<T>(1)));
  }

  template<typename T, qualifier Q>
  inline mat<3, 3, T, Q> mat3_cast(qua<T, Q> const& q)
  {
    mat<3, 3, T, Q> Result(T(1));
    T qxx(q.x * q.x);
    T qyy(q.y * q.y);
    T qzz(q.z * q.z);
    T qxz(q.x * q.z);
    T qxy(q.x * q.y);
    T qyz(q.y * q.z);
    T qwx(q.w * q.x);
    T qwy(q.w * q.y);
    T qwz(q.w * q.z);

    Result[0][0] = T(1) - T(2) * (qyy + qzz);
    Result[0][1] = T(2) * (qxy + qwz);
    Result[0][2] = T(2) * (qxz - qwy);

    Result[1][0] = T(2) * (qxy - qwz);
    Result[1][1] = T(1) - T(2) * (qxx + qzz);
    Result[1][2] = T(2) * (qyz + qwx);

    Result[2][0] = T(2) * (qxz + qwy);
    Result[2][1] = T(2) * (qyz - qwx);
    Result[2][2] = T(1) - T(2) * (qxx + qyy);
    return Result;
  }

  template<typename T, qualifier Q>
  inline mat<4, 4, T, Q> mat4_cast(qua<T, Q> const& q)
  {
    return mat<4, 4, T, Q>(mat3_cast(q));
  }

  template<typename T, qualifier Q>
  inline qua<T, Q> quat_cast(mat<3, 3, T, Q> const& m)
  {
    T fourXSquaredMinus1 = m[0][0] - m[1][1] - m[2][2];
    T fourYSquaredMinus1 = m[1][1] - m[0][0] - m[2][2];
    T fourZSquaredMinus1 = m[2][2] - m[0][0] - m[1][1];
    T fourWSquaredMinus1 = m[0][0] + m[1][1] + m[2][2];

    int biggestIndex = 0;
    T fourBiggestSquaredMinus1 = fourWSquaredMinus1;
    if (fourXSquaredMinus1 > fourBiggestSquaredMinus1)
    {
      fourBiggestSquaredMinus1 = fourXSquaredMinus1;
      biggestIndex = 1;
    }
    if (fourYSquaredMinus1 > fourBiggestSquaredMinus1)
    {
      fourBiggestSquaredMinus1 = fourYSquaredMinus1;
      biggestIndex = 2;
    }
    if (fourZSquaredMinus1 > fourBiggestSquaredMinus1)
    {
      fourBiggestSquaredMinus1 = fourZSquaredMinus1;
      biggestIndex = 3;
    }

    T biggestVal = sqrt(fourBiggestSquaredMinus1 + static_cast<T>(1)) * static_cast<T>(0.5);
    T mult = static_cast<T>(0.25) / biggestVal;

    switch (biggestIndex)
    {
    case 0:
      return qua<T, Q>(biggestVal, (m[1][2] - m[2][1]) * mult, (m[2][0] - m[0][2]) * mult, (m[0][1] - m[1][0]) * mult);
    case 1:
      return qua<T, Q>((m[1][2] - m[2][1]) * mult, biggestVal, (m[0][1] + m[1][0]) * mult, (m[2][0] + m[0][2]) * mult);
    case 2:
      return qua<T, Q>((m[2][0] - m[0][2]) * mult, (m[0][1] + m[1][0]) * mult, biggestVal, (m[1][2] + m[2][1]) * mult);
    case 3:
      return qua<T, Q>((m[0][1] - m[1][0]) * mult, (m[2][0] + m[0][2]) * mult, (m[1][2] + m[2][1]) * mult, biggestVal);
    default: // Silence a -Wswitch-default warning in GCC. Should never actually get here. Assert is just for sanity.
      assert(false);
      return qua<T, Q>(1, 0, 0, 0);
    }
  }

  template<typename T, qualifier Q>
  inline qua<T, Q> quat_cast(mat<4, 4, T, Q> const& m4)
  {
    return quat_cast(mat<3, 3, T, Q>(m4));
  }

  template<typename T, qualifier Q>
  inline vec<4, bool, Q> lessThan(qua<T, Q> const& x, qua<T, Q> const& y)
  {
    vec<4, bool, Q> Result;
    for (length_t i = 0; i < x.length(); ++i)
      Result[i] = x[i] < y[i];
    return Result;
  }

  template<typename T, qualifier Q>
  inline vec<4, bool, Q> lessThanEqual(qua<T, Q> const& x, qua<T, Q> const& y)
  {
    vec<4, bool, Q> Result;
    for (length_t i = 0; i < x.length(); ++i)
      Result[i] = x[i] <= y[i];
    return Result;
  }

  template<typename T, qualifier Q>
  inline vec<4, bool, Q> greaterThan(qua<T, Q> const& x, qua<T, Q> const& y)
  {
    vec<4, bool, Q> Result;
    for (length_t i = 0; i < x.length(); ++i)
      Result[i] = x[i] > y[i];
    return Result;
  }

  template<typename T, qualifier Q>
  inline vec<4, bool, Q> greaterThanEqual(qua<T, Q> const& x, qua<T, Q> const& y)
  {
    vec<4, bool, Q> Result;
    for (length_t i = 0; i < x.length(); ++i)
      Result[i] = x[i] >= y[i];
    return Result;
  }


  template<typename T, qualifier Q>
  inline qua<T, Q> quatLookAt(vec<3, T, Q> const& direction, vec<3, T, Q> const& up)
  {



    return quatLookAtRH(direction, up);

  }

  template<typename T, qualifier Q>
  inline qua<T, Q> quatLookAtRH(vec<3, T, Q> const& direction, vec<3, T, Q> const& up)
  {
    mat<3, 3, T, Q> Result;

    Result[2] = -direction;
    vec<3, T, Q> const& Right = cross(up, Result[2]);
    Result[0] = Right * inversesqrt(max(static_cast<T>(0.00001), dot(Right, Right)));
    Result[1] = cross(Result[2], Result[0]);

    return quat_cast(Result);
  }

  template<typename T, qualifier Q>
  inline qua<T, Q> quatLookAtLH(vec<3, T, Q> const& direction, vec<3, T, Q> const& up)
  {
    mat<3, 3, T, Q> Result;

    Result[2] = direction;
    vec<3, T, Q> const& Right = cross(up, Result[2]);
    Result[0] = Right * inversesqrt(max(static_cast<T>(0.00001), dot(Right, Right)));
    Result[1] = cross(Result[2], Result[0]);

    return quat_cast(Result);
  }
}//namespace glm








/// @ref gtc_vec1
/// @file glm/gtc/vec1.hpp
///
/// @see core (dependence)
///
/// @defgroup gtc_vec1 GLM_GTC_vec1
/// @ingroup gtc
///
/// Include <glm/gtc/vec1.hpp> to use the features of this extension.
///
/// Add vec1, ivec1, uvec1 and bvec1 types.



// Dependency:

/// @ref ext_vector_bool1
/// @file glm/ext/vector_bool1.hpp
///
/// @defgroup ext_vector_bool1 GLM_EXT_vector_bool1
/// @ingroup ext
///
/// Exposes bvec1 vector type.
///
/// Include <glm/ext/vector_bool1.hpp> to use the features of this extension.
///
/// @see ext_vector_bool1_precision extension.









namespace glm
{
  /// @addtogroup ext_vector_bool1
  /// @{

  /// 1 components vector of boolean.
  typedef vec<1, bool, defaultp>		bvec1;

  /// @}
}//namespace glm


/// @ref ext_vector_bool1_precision
/// @file glm/ext/vector_bool1_precision.hpp
///
/// @defgroup ext_vector_bool1_precision GLM_EXT_vector_bool1_precision
/// @ingroup ext
///
/// Exposes highp_bvec1, mediump_bvec1 and lowp_bvec1 types.
///
/// Include <glm/ext/vector_bool1_precision.hpp> to use the features of this extension.









namespace glm
{
  /// @addtogroup ext_vector_bool1_precision
  /// @{

  /// 1 component vector of bool values.
  typedef vec<1, bool, highp>			highp_bvec1;

  /// 1 component vector of bool values.
  typedef vec<1, bool, mediump>		mediump_bvec1;

  /// 1 component vector of bool values.
  typedef vec<1, bool, lowp>			lowp_bvec1;

  /// @}
}//namespace glm


/// @ref ext_vector_float1
/// @file glm/ext/vector_float1.hpp
///
/// @defgroup ext_vector_float1 GLM_EXT_vector_float1
/// @ingroup ext
///
/// Exposes single-precision floating point vector type with one component.
///
/// Include <glm/ext/vector_float1.hpp> to use the features of this extension.
///
/// @see ext_vector_float1_precision extension.
/// @see ext_vector_double1 extension.









namespace glm
{
  /// @addtogroup ext_vector_float1
  /// @{

  /// 1 components vector of single-precision floating-point numbers.
  typedef vec<1, float, defaultp>		vec1;

  /// @}
}//namespace glm

namespace glm
{
  /// @addtogroup ext_vector_float1_precision
  /// @{

  /// 1 component vector of single-precision floating-point numbers using high precision arithmetic in term of ULPs.
  typedef vec<1, float, highp>		highp_vec1;

  /// 1 component vector of single-precision floating-point numbers using medium precision arithmetic in term of ULPs.
  typedef vec<1, float, mediump>		mediump_vec1;

  /// 1 component vector of single-precision floating-point numbers using low precision arithmetic in term of ULPs.
  typedef vec<1, float, lowp>			lowp_vec1;

  /// @}
}//namespace glm

namespace glm
{
  /// @addtogroup ext_vector_double1
  /// @{

  /// 1 components vector of double-precision floating-point numbers.
  typedef vec<1, double, defaultp>		dvec1;

  /// @}
}//namespace glm

namespace glm
{
  /// @addtogroup ext_vector_double1_precision
  /// @{

  /// 1 component vector of double-precision floating-point numbers using high precision arithmetic in term of ULPs.
  typedef vec<1, double, highp>		highp_dvec1;

  /// 1 component vector of double-precision floating-point numbers using medium precision arithmetic in term of ULPs.
  typedef vec<1, double, mediump>		mediump_dvec1;

  /// 1 component vector of double-precision floating-point numbers using low precision arithmetic in term of ULPs.
  typedef vec<1, double, lowp>		lowp_dvec1;

  /// @}
}//namespace glm

namespace glm
{
  /// @addtogroup ext_vector_int1
  /// @{

  /// 1 component vector of signed integer numbers.
  typedef vec<1, int, defaultp>			ivec1;

  /// @}
}//namespace glm


namespace glm
{
  /// @addtogroup ext_vector_int1_sized
  /// @{

  /// 8 bit signed integer vector of 1 component type.
  ///
  /// @see ext_vector_int1_sized
  typedef vec<1, int8, defaultp>	i8vec1;

  /// 16 bit signed integer vector of 1 component type.
  ///
  /// @see ext_vector_int1_sized
  typedef vec<1, int16, defaultp>	i16vec1;

  /// 32 bit signed integer vector of 1 component type.
  ///
  /// @see ext_vector_int1_sized
  typedef vec<1, int32, defaultp>	i32vec1;

  /// 64 bit signed integer vector of 1 component type.
  ///
  /// @see ext_vector_int1_sized
  typedef vec<1, int64, defaultp>	i64vec1;

  /// @}
}//namespace glm

namespace glm
{
  /// @addtogroup ext_vector_uint1
  /// @{

  /// 1 component vector of unsigned integer numbers.
  typedef vec<1, unsigned int, defaultp>			uvec1;

  /// @}
}//namespace glm


namespace glm
{
  /// @addtogroup ext_vector_uint1_sized
  /// @{

  /// 8 bit unsigned integer vector of 1 component type.
  ///
  /// @see ext_vector_uint1_sized
  typedef vec<1, uint8, defaultp>		u8vec1;

  /// 16 bit unsigned integer vector of 1 component type.
  ///
  /// @see ext_vector_uint1_sized
  typedef vec<1, uint16, defaultp>	u16vec1;

  /// 32 bit unsigned integer vector of 1 component type.
  ///
  /// @see ext_vector_uint1_sized
  typedef vec<1, uint32, defaultp>	u32vec1;

  /// 64 bit unsigned integer vector of 1 component type.
  ///
  /// @see ext_vector_uint1_sized
  typedef vec<1, uint64, defaultp>	u64vec1;

  /// @}
}//namespace glm

namespace glm
{
  /// @addtogroup gtc_type_ptr
  /// @{

  /// Return the constant address to the data of the input parameter.
  /// @see gtc_type_ptr
  template<typename genType>
  typename genType::value_type const* value_ptr(genType const& v);

  /// Build a vector from a pointer.
  /// @see gtc_type_ptr
  template <typename T, qualifier Q>
  vec<1, T, Q> make_vec1(vec<1, T, Q> const& v);

  /// Build a vector from a pointer.
  /// @see gtc_type_ptr
  template <typename T, qualifier Q>
  vec<1, T, Q> make_vec1(vec<2, T, Q> const& v);

  /// Build a vector from a pointer.
  /// @see gtc_type_ptr
  template <typename T, qualifier Q>
  vec<1, T, Q> make_vec1(vec<3, T, Q> const& v);

  /// Build a vector from a pointer.
  /// @see gtc_type_ptr
  template <typename T, qualifier Q>
  vec<1, T, Q> make_vec1(vec<4, T, Q> const& v);

  /// Build a vector from a pointer.
  /// @see gtc_type_ptr
  template <typename T, qualifier Q>
  vec<2, T, Q> make_vec2(vec<1, T, Q> const& v);

  /// Build a vector from a pointer.
  /// @see gtc_type_ptr
  template <typename T, qualifier Q>
  vec<2, T, Q> make_vec2(vec<2, T, Q> const& v);

  /// Build a vector from a pointer.
  /// @see gtc_type_ptr
  template <typename T, qualifier Q>
  vec<2, T, Q> make_vec2(vec<3, T, Q> const& v);

  /// Build a vector from a pointer.
  /// @see gtc_type_ptr
  template <typename T, qualifier Q>
  vec<2, T, Q> make_vec2(vec<4, T, Q> const& v);

  /// Build a vector from a pointer.
  /// @see gtc_type_ptr
  template <typename T, qualifier Q>
  vec<3, T, Q> make_vec3(vec<1, T, Q> const& v);

  /// Build a vector from a pointer.
  /// @see gtc_type_ptr
  template <typename T, qualifier Q>
  vec<3, T, Q> make_vec3(vec<2, T, Q> const& v);

  /// Build a vector from a pointer.
  /// @see gtc_type_ptr
  template <typename T, qualifier Q>
  vec<3, T, Q> make_vec3(vec<3, T, Q> const& v);

  /// Build a vector from a pointer.
  /// @see gtc_type_ptr
  template <typename T, qualifier Q>
  vec<3, T, Q> make_vec3(vec<4, T, Q> const& v);

  /// Build a vector from a pointer.
  /// @see gtc_type_ptr
  template <typename T, qualifier Q>
  vec<4, T, Q> make_vec4(vec<1, T, Q> const& v);

  /// Build a vector from a pointer.
  /// @see gtc_type_ptr
  template <typename T, qualifier Q>
  vec<4, T, Q> make_vec4(vec<2, T, Q> const& v);

  /// Build a vector from a pointer.
  /// @see gtc_type_ptr
  template <typename T, qualifier Q>
  vec<4, T, Q> make_vec4(vec<3, T, Q> const& v);

  /// Build a vector from a pointer.
  /// @see gtc_type_ptr
  template <typename T, qualifier Q>
  vec<4, T, Q> make_vec4(vec<4, T, Q> const& v);

  /// Build a vector from a pointer.
  /// @see gtc_type_ptr
  template<typename T>
  vec<2, T, defaultp> make_vec2(T const* const ptr);

  /// Build a vector from a pointer.
  /// @see gtc_type_ptr
  template<typename T>
  vec<3, T, defaultp> make_vec3(T const* const ptr);

  /// Build a vector from a pointer.
  /// @see gtc_type_ptr
  template<typename T>
  vec<4, T, defaultp> make_vec4(T const* const ptr);

  /// Build a matrix from a pointer.
  /// @see gtc_type_ptr
  template<typename T>
  mat<2, 2, T, defaultp> make_mat2x2(T const* const ptr);

  /// Build a matrix from a pointer.
  /// @see gtc_type_ptr
  template<typename T>
  mat<2, 3, T, defaultp> make_mat2x3(T const* const ptr);

  /// Build a matrix from a pointer.
  /// @see gtc_type_ptr
  template<typename T>
  mat<2, 4, T, defaultp> make_mat2x4(T const* const ptr);

  /// Build a matrix from a pointer.
  /// @see gtc_type_ptr
  template<typename T>
  mat<3, 2, T, defaultp> make_mat3x2(T const* const ptr);

  /// Build a matrix from a pointer.
  /// @see gtc_type_ptr
  template<typename T>
  mat<3, 3, T, defaultp> make_mat3x3(T const* const ptr);

  /// Build a matrix from a pointer.
  /// @see gtc_type_ptr
  template<typename T>
  mat<3, 4, T, defaultp> make_mat3x4(T const* const ptr);

  /// Build a matrix from a pointer.
  /// @see gtc_type_ptr
  template<typename T>
  mat<4, 2, T, defaultp> make_mat4x2(T const* const ptr);

  /// Build a matrix from a pointer.
  /// @see gtc_type_ptr
  template<typename T>
  mat<4, 3, T, defaultp> make_mat4x3(T const* const ptr);

  /// Build a matrix from a pointer.
  /// @see gtc_type_ptr
  template<typename T>
  mat<4, 4, T, defaultp> make_mat4x4(T const* const ptr);

  /// Build a matrix from a pointer.
  /// @see gtc_type_ptr
  template<typename T>
  mat<2, 2, T, defaultp> make_mat2(T const* const ptr);

  /// Build a matrix from a pointer.
  /// @see gtc_type_ptr
  template<typename T>
  mat<3, 3, T, defaultp> make_mat3(T const* const ptr);

  /// Build a matrix from a pointer.
  /// @see gtc_type_ptr
  template<typename T>
  mat<4, 4, T, defaultp> make_mat4(T const* const ptr);

  /// Build a quaternion from a pointer.
  /// @see gtc_type_ptr
  template<typename T>
  qua<T, defaultp> make_quat(T const* const ptr);

  /// @}
}//namespace glm

namespace glm
{
  /// @addtogroup gtc_type_ptr
  /// @{

  template<typename T, qualifier Q>
  inline T const* value_ptr(vec<2, T, Q> const& v)
  {
    return &(v.x);
  }

  template<typename T, qualifier Q>
  inline T* value_ptr(vec<2, T, Q>& v)
  {
    return &(v.x);
  }

  template<typename T, qualifier Q>
  inline T const* value_ptr(vec<3, T, Q> const& v)
  {
    return &(v.x);
  }

  template<typename T, qualifier Q>
  inline T* value_ptr(vec<3, T, Q>& v)
  {
    return &(v.x);
  }

  template<typename T, qualifier Q>
  inline T const* value_ptr(vec<4, T, Q> const& v)
  {
    return &(v.x);
  }

  template<typename T, qualifier Q>
  inline T* value_ptr(vec<4, T, Q>& v)
  {
    return &(v.x);
  }

  template<typename T, qualifier Q>
  inline T const* value_ptr(mat<2, 2, T, Q> const& m)
  {
    return &(m[0].x);
  }

  template<typename T, qualifier Q>
  inline T* value_ptr(mat<2, 2, T, Q>& m)
  {
    return &(m[0].x);
  }

  template<typename T, qualifier Q>
  inline T const* value_ptr(mat<3, 3, T, Q> const& m)
  {
    return &(m[0].x);
  }

  template<typename T, qualifier Q>
  inline T* value_ptr(mat<3, 3, T, Q>& m)
  {
    return &(m[0].x);
  }

  template<typename T, qualifier Q>
  inline T const* value_ptr(mat<4, 4, T, Q> const& m)
  {
    return &(m[0].x);
  }

  template<typename T, qualifier Q>
  inline T* value_ptr(mat<4, 4, T, Q>& m)
  {
    return &(m[0].x);
  }

  template<typename T, qualifier Q>
  inline T const* value_ptr(mat<2, 3, T, Q> const& m)
  {
    return &(m[0].x);
  }

  template<typename T, qualifier Q>
  inline T* value_ptr(mat<2, 3, T, Q>& m)
  {
    return &(m[0].x);
  }

  template<typename T, qualifier Q>
  inline T const* value_ptr(mat<3, 2, T, Q> const& m)
  {
    return &(m[0].x);
  }

  template<typename T, qualifier Q>
  inline T* value_ptr(mat<3, 2, T, Q>& m)
  {
    return &(m[0].x);
  }

  template<typename T, qualifier Q>
  inline T const* value_ptr(mat<2, 4, T, Q> const& m)
  {
    return &(m[0].x);
  }

  template<typename T, qualifier Q>
  inline T* value_ptr(mat<2, 4, T, Q>& m)
  {
    return &(m[0].x);
  }

  template<typename T, qualifier Q>
  inline T const* value_ptr(mat<4, 2, T, Q> const& m)
  {
    return &(m[0].x);
  }

  template<typename T, qualifier Q>
  inline T* value_ptr(mat<4, 2, T, Q>& m)
  {
    return &(m[0].x);
  }

  template<typename T, qualifier Q>
  inline T const* value_ptr(mat<3, 4, T, Q> const& m)
  {
    return &(m[0].x);
  }

  template<typename T, qualifier Q>
  inline T* value_ptr(mat<3, 4, T, Q>& m)
  {
    return &(m[0].x);
  }

  template<typename T, qualifier Q>
  inline T const* value_ptr(mat<4, 3, T, Q> const& m)
  {
    return &(m[0].x);
  }

  template<typename T, qualifier Q>
  inline T* value_ptr(mat<4, 3, T, Q>& m)
  {
    return &(m[0].x);
  }

  template<typename T, qualifier Q>
  inline T const* value_ptr(qua<T, Q> const& q)
  {
    return &(q[0]);
  }

  template<typename T, qualifier Q>
  inline T* value_ptr(qua<T, Q>& q)
  {
    return &(q[0]);
  }

  template <typename T, qualifier Q>
  inline vec<1, T, Q> make_vec1(vec<1, T, Q> const& v)
  {
    return v;
  }

  template <typename T, qualifier Q>
  inline vec<1, T, Q> make_vec1(vec<2, T, Q> const& v)
  {
    return vec<1, T, Q>(v);
  }

  template <typename T, qualifier Q>
  inline vec<1, T, Q> make_vec1(vec<3, T, Q> const& v)
  {
    return vec<1, T, Q>(v);
  }

  template <typename T, qualifier Q>
  inline vec<1, T, Q> make_vec1(vec<4, T, Q> const& v)
  {
    return vec<1, T, Q>(v);
  }

  template <typename T, qualifier Q>
  inline vec<2, T, Q> make_vec2(vec<1, T, Q> const& v)
  {
    return vec<2, T, Q>(v.x, static_cast<T>(0));
  }

  template <typename T, qualifier Q>
  inline vec<2, T, Q> make_vec2(vec<2, T, Q> const& v)
  {
    return v;
  }

  template <typename T, qualifier Q>
  inline vec<2, T, Q> make_vec2(vec<3, T, Q> const& v)
  {
    return vec<2, T, Q>(v);
  }

  template <typename T, qualifier Q>
  inline vec<2, T, Q> make_vec2(vec<4, T, Q> const& v)
  {
    return vec<2, T, Q>(v);
  }

  template <typename T, qualifier Q>
  inline vec<3, T, Q> make_vec3(vec<1, T, Q> const& v)
  {
    return vec<3, T, Q>(v.x, static_cast<T>(0), static_cast<T>(0));
  }

  template <typename T, qualifier Q>
  inline vec<3, T, Q> make_vec3(vec<2, T, Q> const& v)
  {
    return vec<3, T, Q>(v.x, v.y, static_cast<T>(0));
  }

  template <typename T, qualifier Q>
  inline vec<3, T, Q> make_vec3(vec<3, T, Q> const& v)
  {
    return v;
  }

  template <typename T, qualifier Q>
  inline vec<3, T, Q> make_vec3(vec<4, T, Q> const& v)
  {
    return vec<3, T, Q>(v);
  }

  template <typename T, qualifier Q>
  inline vec<4, T, Q> make_vec4(vec<1, T, Q> const& v)
  {
    return vec<4, T, Q>(v.x, static_cast<T>(0), static_cast<T>(0), static_cast<T>(1));
  }

  template <typename T, qualifier Q>
  inline vec<4, T, Q> make_vec4(vec<2, T, Q> const& v)
  {
    return vec<4, T, Q>(v.x, v.y, static_cast<T>(0), static_cast<T>(1));
  }

  template <typename T, qualifier Q>
  inline vec<4, T, Q> make_vec4(vec<3, T, Q> const& v)
  {
    return vec<4, T, Q>(v.x, v.y, v.z, static_cast<T>(1));
  }

  template <typename T, qualifier Q>
  inline vec<4, T, Q> make_vec4(vec<4, T, Q> const& v)
  {
    return v;
  }

  template<typename T>
  inline vec<2, T, defaultp> make_vec2(T const* const ptr)
  {
    vec<2, T, defaultp> Result;
    memcpy(value_ptr(Result), ptr, sizeof(vec<2, T, defaultp>));
    return Result;
  }

  template<typename T>
  inline vec<3, T, defaultp> make_vec3(T const* const ptr)
  {
    vec<3, T, defaultp> Result;
    memcpy(value_ptr(Result), ptr, sizeof(vec<3, T, defaultp>));
    return Result;
  }

  template<typename T>
  inline vec<4, T, defaultp> make_vec4(T const* const ptr)
  {
    vec<4, T, defaultp> Result;
    memcpy(value_ptr(Result), ptr, sizeof(vec<4, T, defaultp>));
    return Result;
  }

  template<typename T>
  inline mat<2, 2, T, defaultp> make_mat2x2(T const* const ptr)
  {
    mat<2, 2, T, defaultp> Result;
    memcpy(value_ptr(Result), ptr, sizeof(mat<2, 2, T, defaultp>));
    return Result;
  }

  template<typename T>
  inline mat<2, 3, T, defaultp> make_mat2x3(T const* const ptr)
  {
    mat<2, 3, T, defaultp> Result;
    memcpy(value_ptr(Result), ptr, sizeof(mat<2, 3, T, defaultp>));
    return Result;
  }

  template<typename T>
  inline mat<2, 4, T, defaultp> make_mat2x4(T const* const ptr)
  {
    mat<2, 4, T, defaultp> Result;
    memcpy(value_ptr(Result), ptr, sizeof(mat<2, 4, T, defaultp>));
    return Result;
  }

  template<typename T>
  inline mat<3, 2, T, defaultp> make_mat3x2(T const* const ptr)
  {
    mat<3, 2, T, defaultp> Result;
    memcpy(value_ptr(Result), ptr, sizeof(mat<3, 2, T, defaultp>));
    return Result;
  }

  template<typename T>
  inline mat<3, 3, T, defaultp> make_mat3x3(T const* const ptr)
  {
    mat<3, 3, T, defaultp> Result;
    memcpy(value_ptr(Result), ptr, sizeof(mat<3, 3, T, defaultp>));
    return Result;
  }

  template<typename T>
  inline mat<3, 4, T, defaultp> make_mat3x4(T const* const ptr)
  {
    mat<3, 4, T, defaultp> Result;
    memcpy(value_ptr(Result), ptr, sizeof(mat<3, 4, T, defaultp>));
    return Result;
  }

  template<typename T>
  inline mat<4, 2, T, defaultp> make_mat4x2(T const* const ptr)
  {
    mat<4, 2, T, defaultp> Result;
    memcpy(value_ptr(Result), ptr, sizeof(mat<4, 2, T, defaultp>));
    return Result;
  }

  template<typename T>
  inline mat<4, 3, T, defaultp> make_mat4x3(T const* const ptr)
  {
    mat<4, 3, T, defaultp> Result;
    memcpy(value_ptr(Result), ptr, sizeof(mat<4, 3, T, defaultp>));
    return Result;
  }

  template<typename T>
  inline mat<4, 4, T, defaultp> make_mat4x4(T const* const ptr)
  {
    mat<4, 4, T, defaultp> Result;
    memcpy(value_ptr(Result), ptr, sizeof(mat<4, 4, T, defaultp>));
    return Result;
  }

  template<typename T>
  inline mat<2, 2, T, defaultp> make_mat2(T const* const ptr)
  {
    return make_mat2x2(ptr);
  }

  template<typename T>
  inline mat<3, 3, T, defaultp> make_mat3(T const* const ptr)
  {
    return make_mat3x3(ptr);
  }

  template<typename T>
  inline mat<4, 4, T, defaultp> make_mat4(T const* const ptr)
  {
    return make_mat4x4(ptr);
  }

  template<typename T>
  inline qua<T, defaultp> make_quat(T const* const ptr)
  {
    qua<T, defaultp> Result;
    memcpy(value_ptr(Result), ptr, sizeof(qua<T, defaultp>));
    return Result;
  }

  /// @}
}//namespace glm





namespace FFTSG
{
  static void cftf161(double* a, double* w)
  {
    double wn4r, wk1r, wk1i,
      x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i,
      y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i,
      y4r, y4i, y5r, y5i, y6r, y6i, y7r, y7i,
      y8r, y8i, y9r, y9i, y10r, y10i, y11r, y11i,
      y12r, y12i, y13r, y13i, y14r, y14i, y15r, y15i;

    wn4r = w[1];
    wk1r = w[2];
    wk1i = w[3];
    x0r = a[0] + a[16];
    x0i = a[1] + a[17];
    x1r = a[0] - a[16];
    x1i = a[1] - a[17];
    x2r = a[8] + a[24];
    x2i = a[9] + a[25];
    x3r = a[8] - a[24];
    x3i = a[9] - a[25];
    y0r = x0r + x2r;
    y0i = x0i + x2i;
    y4r = x0r - x2r;
    y4i = x0i - x2i;
    y8r = x1r - x3i;
    y8i = x1i + x3r;
    y12r = x1r + x3i;
    y12i = x1i - x3r;
    x0r = a[2] + a[18];
    x0i = a[3] + a[19];
    x1r = a[2] - a[18];
    x1i = a[3] - a[19];
    x2r = a[10] + a[26];
    x2i = a[11] + a[27];
    x3r = a[10] - a[26];
    x3i = a[11] - a[27];
    y1r = x0r + x2r;
    y1i = x0i + x2i;
    y5r = x0r - x2r;
    y5i = x0i - x2i;
    x0r = x1r - x3i;
    x0i = x1i + x3r;
    y9r = wk1r * x0r - wk1i * x0i;
    y9i = wk1r * x0i + wk1i * x0r;
    x0r = x1r + x3i;
    x0i = x1i - x3r;
    y13r = wk1i * x0r - wk1r * x0i;
    y13i = wk1i * x0i + wk1r * x0r;
    x0r = a[4] + a[20];
    x0i = a[5] + a[21];
    x1r = a[4] - a[20];
    x1i = a[5] - a[21];
    x2r = a[12] + a[28];
    x2i = a[13] + a[29];
    x3r = a[12] - a[28];
    x3i = a[13] - a[29];
    y2r = x0r + x2r;
    y2i = x0i + x2i;
    y6r = x0r - x2r;
    y6i = x0i - x2i;
    x0r = x1r - x3i;
    x0i = x1i + x3r;
    y10r = wn4r * (x0r - x0i);
    y10i = wn4r * (x0i + x0r);
    x0r = x1r + x3i;
    x0i = x1i - x3r;
    y14r = wn4r * (x0r + x0i);
    y14i = wn4r * (x0i - x0r);
    x0r = a[6] + a[22];
    x0i = a[7] + a[23];
    x1r = a[6] - a[22];
    x1i = a[7] - a[23];
    x2r = a[14] + a[30];
    x2i = a[15] + a[31];
    x3r = a[14] - a[30];
    x3i = a[15] - a[31];
    y3r = x0r + x2r;
    y3i = x0i + x2i;
    y7r = x0r - x2r;
    y7i = x0i - x2i;
    x0r = x1r - x3i;
    x0i = x1i + x3r;
    y11r = wk1i * x0r - wk1r * x0i;
    y11i = wk1i * x0i + wk1r * x0r;
    x0r = x1r + x3i;
    x0i = x1i - x3r;
    y15r = wk1r * x0r - wk1i * x0i;
    y15i = wk1r * x0i + wk1i * x0r;
    x0r = y12r - y14r;
    x0i = y12i - y14i;
    x1r = y12r + y14r;
    x1i = y12i + y14i;
    x2r = y13r - y15r;
    x2i = y13i - y15i;
    x3r = y13r + y15r;
    x3i = y13i + y15i;
    a[24] = x0r + x2r;
    a[25] = x0i + x2i;
    a[26] = x0r - x2r;
    a[27] = x0i - x2i;
    a[28] = x1r - x3i;
    a[29] = x1i + x3r;
    a[30] = x1r + x3i;
    a[31] = x1i - x3r;
    x0r = y8r + y10r;
    x0i = y8i + y10i;
    x1r = y8r - y10r;
    x1i = y8i - y10i;
    x2r = y9r + y11r;
    x2i = y9i + y11i;
    x3r = y9r - y11r;
    x3i = y9i - y11i;
    a[16] = x0r + x2r;
    a[17] = x0i + x2i;
    a[18] = x0r - x2r;
    a[19] = x0i - x2i;
    a[20] = x1r - x3i;
    a[21] = x1i + x3r;
    a[22] = x1r + x3i;
    a[23] = x1i - x3r;
    x0r = y5r - y7i;
    x0i = y5i + y7r;
    x2r = wn4r * (x0r - x0i);
    x2i = wn4r * (x0i + x0r);
    x0r = y5r + y7i;
    x0i = y5i - y7r;
    x3r = wn4r * (x0r - x0i);
    x3i = wn4r * (x0i + x0r);
    x0r = y4r - y6i;
    x0i = y4i + y6r;
    x1r = y4r + y6i;
    x1i = y4i - y6r;
    a[8] = x0r + x2r;
    a[9] = x0i + x2i;
    a[10] = x0r - x2r;
    a[11] = x0i - x2i;
    a[12] = x1r - x3i;
    a[13] = x1i + x3r;
    a[14] = x1r + x3i;
    a[15] = x1i - x3r;
    x0r = y0r + y2r;
    x0i = y0i + y2i;
    x1r = y0r - y2r;
    x1i = y0i - y2i;
    x2r = y1r + y3r;
    x2i = y1i + y3i;
    x3r = y1r - y3r;
    x3i = y1i - y3i;
    a[0] = x0r + x2r;
    a[1] = x0i + x2i;
    a[2] = x0r - x2r;
    a[3] = x0i - x2i;
    a[4] = x1r - x3i;
    a[5] = x1i + x3r;
    a[6] = x1r + x3i;
    a[7] = x1i - x3r;
  }

  static void cftf162(double* a, double* w)
  {
    double wn4r, wk1r, wk1i, wk2r, wk2i, wk3r, wk3i,
      x0r, x0i, x1r, x1i, x2r, x2i,
      y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i,
      y4r, y4i, y5r, y5i, y6r, y6i, y7r, y7i,
      y8r, y8i, y9r, y9i, y10r, y10i, y11r, y11i,
      y12r, y12i, y13r, y13i, y14r, y14i, y15r, y15i;

    wn4r = w[1];
    wk1r = w[4];
    wk1i = w[5];
    wk3r = w[6];
    wk3i = -w[7];
    wk2r = w[8];
    wk2i = w[9];
    x1r = a[0] - a[17];
    x1i = a[1] + a[16];
    x0r = a[8] - a[25];
    x0i = a[9] + a[24];
    x2r = wn4r * (x0r - x0i);
    x2i = wn4r * (x0i + x0r);
    y0r = x1r + x2r;
    y0i = x1i + x2i;
    y4r = x1r - x2r;
    y4i = x1i - x2i;
    x1r = a[0] + a[17];
    x1i = a[1] - a[16];
    x0r = a[8] + a[25];
    x0i = a[9] - a[24];
    x2r = wn4r * (x0r - x0i);
    x2i = wn4r * (x0i + x0r);
    y8r = x1r - x2i;
    y8i = x1i + x2r;
    y12r = x1r + x2i;
    y12i = x1i - x2r;
    x0r = a[2] - a[19];
    x0i = a[3] + a[18];
    x1r = wk1r * x0r - wk1i * x0i;
    x1i = wk1r * x0i + wk1i * x0r;
    x0r = a[10] - a[27];
    x0i = a[11] + a[26];
    x2r = wk3i * x0r - wk3r * x0i;
    x2i = wk3i * x0i + wk3r * x0r;
    y1r = x1r + x2r;
    y1i = x1i + x2i;
    y5r = x1r - x2r;
    y5i = x1i - x2i;
    x0r = a[2] + a[19];
    x0i = a[3] - a[18];
    x1r = wk3r * x0r - wk3i * x0i;
    x1i = wk3r * x0i + wk3i * x0r;
    x0r = a[10] + a[27];
    x0i = a[11] - a[26];
    x2r = wk1r * x0r + wk1i * x0i;
    x2i = wk1r * x0i - wk1i * x0r;
    y9r = x1r - x2r;
    y9i = x1i - x2i;
    y13r = x1r + x2r;
    y13i = x1i + x2i;
    x0r = a[4] - a[21];
    x0i = a[5] + a[20];
    x1r = wk2r * x0r - wk2i * x0i;
    x1i = wk2r * x0i + wk2i * x0r;
    x0r = a[12] - a[29];
    x0i = a[13] + a[28];
    x2r = wk2i * x0r - wk2r * x0i;
    x2i = wk2i * x0i + wk2r * x0r;
    y2r = x1r + x2r;
    y2i = x1i + x2i;
    y6r = x1r - x2r;
    y6i = x1i - x2i;
    x0r = a[4] + a[21];
    x0i = a[5] - a[20];
    x1r = wk2i * x0r - wk2r * x0i;
    x1i = wk2i * x0i + wk2r * x0r;
    x0r = a[12] + a[29];
    x0i = a[13] - a[28];
    x2r = wk2r * x0r - wk2i * x0i;
    x2i = wk2r * x0i + wk2i * x0r;
    y10r = x1r - x2r;
    y10i = x1i - x2i;
    y14r = x1r + x2r;
    y14i = x1i + x2i;
    x0r = a[6] - a[23];
    x0i = a[7] + a[22];
    x1r = wk3r * x0r - wk3i * x0i;
    x1i = wk3r * x0i + wk3i * x0r;
    x0r = a[14] - a[31];
    x0i = a[15] + a[30];
    x2r = wk1i * x0r - wk1r * x0i;
    x2i = wk1i * x0i + wk1r * x0r;
    y3r = x1r + x2r;
    y3i = x1i + x2i;
    y7r = x1r - x2r;
    y7i = x1i - x2i;
    x0r = a[6] + a[23];
    x0i = a[7] - a[22];
    x1r = wk1i * x0r + wk1r * x0i;
    x1i = wk1i * x0i - wk1r * x0r;
    x0r = a[14] + a[31];
    x0i = a[15] - a[30];
    x2r = wk3i * x0r - wk3r * x0i;
    x2i = wk3i * x0i + wk3r * x0r;
    y11r = x1r + x2r;
    y11i = x1i + x2i;
    y15r = x1r - x2r;
    y15i = x1i - x2i;
    x1r = y0r + y2r;
    x1i = y0i + y2i;
    x2r = y1r + y3r;
    x2i = y1i + y3i;
    a[0] = x1r + x2r;
    a[1] = x1i + x2i;
    a[2] = x1r - x2r;
    a[3] = x1i - x2i;
    x1r = y0r - y2r;
    x1i = y0i - y2i;
    x2r = y1r - y3r;
    x2i = y1i - y3i;
    a[4] = x1r - x2i;
    a[5] = x1i + x2r;
    a[6] = x1r + x2i;
    a[7] = x1i - x2r;
    x1r = y4r - y6i;
    x1i = y4i + y6r;
    x0r = y5r - y7i;
    x0i = y5i + y7r;
    x2r = wn4r * (x0r - x0i);
    x2i = wn4r * (x0i + x0r);
    a[8] = x1r + x2r;
    a[9] = x1i + x2i;
    a[10] = x1r - x2r;
    a[11] = x1i - x2i;
    x1r = y4r + y6i;
    x1i = y4i - y6r;
    x0r = y5r + y7i;
    x0i = y5i - y7r;
    x2r = wn4r * (x0r - x0i);
    x2i = wn4r * (x0i + x0r);
    a[12] = x1r - x2i;
    a[13] = x1i + x2r;
    a[14] = x1r + x2i;
    a[15] = x1i - x2r;
    x1r = y8r + y10r;
    x1i = y8i + y10i;
    x2r = y9r - y11r;
    x2i = y9i - y11i;
    a[16] = x1r + x2r;
    a[17] = x1i + x2i;
    a[18] = x1r - x2r;
    a[19] = x1i - x2i;
    x1r = y8r - y10r;
    x1i = y8i - y10i;
    x2r = y9r + y11r;
    x2i = y9i + y11i;
    a[20] = x1r - x2i;
    a[21] = x1i + x2r;
    a[22] = x1r + x2i;
    a[23] = x1i - x2r;
    x1r = y12r - y14i;
    x1i = y12i + y14r;
    x0r = y13r + y15i;
    x0i = y13i - y15r;
    x2r = wn4r * (x0r - x0i);
    x2i = wn4r * (x0i + x0r);
    a[24] = x1r + x2r;
    a[25] = x1i + x2i;
    a[26] = x1r - x2r;
    a[27] = x1i - x2i;
    x1r = y12r + y14i;
    x1i = y12i - y14r;
    x0r = y13r - y15i;
    x0i = y13i + y15r;
    x2r = wn4r * (x0r - x0i);
    x2i = wn4r * (x0i + x0r);
    a[28] = x1r - x2i;
    a[29] = x1i + x2r;
    a[30] = x1r + x2i;
    a[31] = x1i - x2r;
  }

  static void cftf081(double* a, double* w)
  {
    double wn4r, x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i,
      y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i,
      y4r, y4i, y5r, y5i, y6r, y6i, y7r, y7i;

    wn4r = w[1];
    x0r = a[0] + a[8];
    x0i = a[1] + a[9];
    x1r = a[0] - a[8];
    x1i = a[1] - a[9];
    x2r = a[4] + a[12];
    x2i = a[5] + a[13];
    x3r = a[4] - a[12];
    x3i = a[5] - a[13];
    y0r = x0r + x2r;
    y0i = x0i + x2i;
    y2r = x0r - x2r;
    y2i = x0i - x2i;
    y1r = x1r - x3i;
    y1i = x1i + x3r;
    y3r = x1r + x3i;
    y3i = x1i - x3r;
    x0r = a[2] + a[10];
    x0i = a[3] + a[11];
    x1r = a[2] - a[10];
    x1i = a[3] - a[11];
    x2r = a[6] + a[14];
    x2i = a[7] + a[15];
    x3r = a[6] - a[14];
    x3i = a[7] - a[15];
    y4r = x0r + x2r;
    y4i = x0i + x2i;
    y6r = x0r - x2r;
    y6i = x0i - x2i;
    x0r = x1r - x3i;
    x0i = x1i + x3r;
    x2r = x1r + x3i;
    x2i = x1i - x3r;
    y5r = wn4r * (x0r - x0i);
    y5i = wn4r * (x0r + x0i);
    y7r = wn4r * (x2r - x2i);
    y7i = wn4r * (x2r + x2i);
    a[8] = y1r + y5r;
    a[9] = y1i + y5i;
    a[10] = y1r - y5r;
    a[11] = y1i - y5i;
    a[12] = y3r - y7i;
    a[13] = y3i + y7r;
    a[14] = y3r + y7i;
    a[15] = y3i - y7r;
    a[0] = y0r + y4r;
    a[1] = y0i + y4i;
    a[2] = y0r - y4r;
    a[3] = y0i - y4i;
    a[4] = y2r - y6i;
    a[5] = y2i + y6r;
    a[6] = y2r + y6i;
    a[7] = y2i - y6r;
  }

  static void cftf082(double* a, double* w)
  {
    double wn4r, wk1r, wk1i, x0r, x0i, x1r, x1i,
      y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i,
      y4r, y4i, y5r, y5i, y6r, y6i, y7r, y7i;

    wn4r = w[1];
    wk1r = w[2];
    wk1i = w[3];
    y0r = a[0] - a[9];
    y0i = a[1] + a[8];
    y1r = a[0] + a[9];
    y1i = a[1] - a[8];
    x0r = a[4] - a[13];
    x0i = a[5] + a[12];
    y2r = wn4r * (x0r - x0i);
    y2i = wn4r * (x0i + x0r);
    x0r = a[4] + a[13];
    x0i = a[5] - a[12];
    y3r = wn4r * (x0r - x0i);
    y3i = wn4r * (x0i + x0r);
    x0r = a[2] - a[11];
    x0i = a[3] + a[10];
    y4r = wk1r * x0r - wk1i * x0i;
    y4i = wk1r * x0i + wk1i * x0r;
    x0r = a[2] + a[11];
    x0i = a[3] - a[10];
    y5r = wk1i * x0r - wk1r * x0i;
    y5i = wk1i * x0i + wk1r * x0r;
    x0r = a[6] - a[15];
    x0i = a[7] + a[14];
    y6r = wk1i * x0r - wk1r * x0i;
    y6i = wk1i * x0i + wk1r * x0r;
    x0r = a[6] + a[15];
    x0i = a[7] - a[14];
    y7r = wk1r * x0r - wk1i * x0i;
    y7i = wk1r * x0i + wk1i * x0r;
    x0r = y0r + y2r;
    x0i = y0i + y2i;
    x1r = y4r + y6r;
    x1i = y4i + y6i;
    a[0] = x0r + x1r;
    a[1] = x0i + x1i;
    a[2] = x0r - x1r;
    a[3] = x0i - x1i;
    x0r = y0r - y2r;
    x0i = y0i - y2i;
    x1r = y4r - y6r;
    x1i = y4i - y6i;
    a[4] = x0r - x1i;
    a[5] = x0i + x1r;
    a[6] = x0r + x1i;
    a[7] = x0i - x1r;
    x0r = y1r - y3i;
    x0i = y1i + y3r;
    x1r = y5r - y7r;
    x1i = y5i - y7i;
    a[8] = x0r + x1r;
    a[9] = x0i + x1i;
    a[10] = x0r - x1r;
    a[11] = x0i - x1i;
    x0r = y1r + y3i;
    x0i = y1i - y3r;
    x1r = y5r + y7r;
    x1i = y5i + y7i;
    a[12] = x0r - x1i;
    a[13] = x0i + x1r;
    a[14] = x0r + x1i;
    a[15] = x0i - x1r;
  }

  static void cftf040(double* a)
  {
    double x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i;

    x0r = a[0] + a[4];
    x0i = a[1] + a[5];
    x1r = a[0] - a[4];
    x1i = a[1] - a[5];
    x2r = a[2] + a[6];
    x2i = a[3] + a[7];
    x3r = a[2] - a[6];
    x3i = a[3] - a[7];
    a[0] = x0r + x2r;
    a[1] = x0i + x2i;
    a[2] = x1r - x3i;
    a[3] = x1i + x3r;
    a[4] = x0r - x2r;
    a[5] = x0i - x2i;
    a[6] = x1r + x3i;
    a[7] = x1i - x3r;
  }

  static void cftb040(double* a)
  {
    double x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i;

    x0r = a[0] + a[4];
    x0i = a[1] + a[5];
    x1r = a[0] - a[4];
    x1i = a[1] - a[5];
    x2r = a[2] + a[6];
    x2i = a[3] + a[7];
    x3r = a[2] - a[6];
    x3i = a[3] - a[7];
    a[0] = x0r + x2r;
    a[1] = x0i + x2i;
    a[2] = x1r + x3i;
    a[3] = x1i - x3r;
    a[4] = x0r - x2r;
    a[5] = x0i - x2i;
    a[6] = x1r - x3i;
    a[7] = x1i + x3r;
  }

  static void cftx020(double* a)
  {
    double x0r, x0i;

    x0r = a[0] - a[2];
    x0i = a[1] - a[3];
    a[0] += a[2];
    a[1] += a[3];
    a[2] = x0r;
    a[3] = x0i;
  }

  static void cftmdl1(int n, double* a, double* w)
  {
    int j, j0, j1, j2, j3, k, m, mh;
    double wn4r, wk1r, wk1i, wk3r, wk3i;
    double x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i;

    mh = n >> 3;
    m = 2 * mh;
    j1 = m;
    j2 = j1 + m;
    j3 = j2 + m;
    x0r = a[0] + a[j2];
    x0i = a[1] + a[j2 + 1];
    x1r = a[0] - a[j2];
    x1i = a[1] - a[j2 + 1];
    x2r = a[j1] + a[j3];
    x2i = a[j1 + 1] + a[j3 + 1];
    x3r = a[j1] - a[j3];
    x3i = a[j1 + 1] - a[j3 + 1];
    a[0] = x0r + x2r;
    a[1] = x0i + x2i;
    a[j1] = x0r - x2r;
    a[j1 + 1] = x0i - x2i;
    a[j2] = x1r - x3i;
    a[j2 + 1] = x1i + x3r;
    a[j3] = x1r + x3i;
    a[j3 + 1] = x1i - x3r;
    wn4r = w[1];
    k = 0;
    for (j = 2; j < mh; j += 2) {
      k += 4;
      wk1r = w[k];
      wk1i = w[k + 1];
      wk3r = w[k + 2];
      wk3i = w[k + 3];
      j1 = j + m;
      j2 = j1 + m;
      j3 = j2 + m;
      x0r = a[j] + a[j2];
      x0i = a[j + 1] + a[j2 + 1];
      x1r = a[j] - a[j2];
      x1i = a[j + 1] - a[j2 + 1];
      x2r = a[j1] + a[j3];
      x2i = a[j1 + 1] + a[j3 + 1];
      x3r = a[j1] - a[j3];
      x3i = a[j1 + 1] - a[j3 + 1];
      a[j] = x0r + x2r;
      a[j + 1] = x0i + x2i;
      a[j1] = x0r - x2r;
      a[j1 + 1] = x0i - x2i;
      x0r = x1r - x3i;
      x0i = x1i + x3r;
      a[j2] = wk1r * x0r - wk1i * x0i;
      a[j2 + 1] = wk1r * x0i + wk1i * x0r;
      x0r = x1r + x3i;
      x0i = x1i - x3r;
      a[j3] = wk3r * x0r + wk3i * x0i;
      a[j3 + 1] = wk3r * x0i - wk3i * x0r;
      j0 = m - j;
      j1 = j0 + m;
      j2 = j1 + m;
      j3 = j2 + m;
      x0r = a[j0] + a[j2];
      x0i = a[j0 + 1] + a[j2 + 1];
      x1r = a[j0] - a[j2];
      x1i = a[j0 + 1] - a[j2 + 1];
      x2r = a[j1] + a[j3];
      x2i = a[j1 + 1] + a[j3 + 1];
      x3r = a[j1] - a[j3];
      x3i = a[j1 + 1] - a[j3 + 1];
      a[j0] = x0r + x2r;
      a[j0 + 1] = x0i + x2i;
      a[j1] = x0r - x2r;
      a[j1 + 1] = x0i - x2i;
      x0r = x1r - x3i;
      x0i = x1i + x3r;
      a[j2] = wk1i * x0r - wk1r * x0i;
      a[j2 + 1] = wk1i * x0i + wk1r * x0r;
      x0r = x1r + x3i;
      x0i = x1i - x3r;
      a[j3] = wk3i * x0r + wk3r * x0i;
      a[j3 + 1] = wk3i * x0i - wk3r * x0r;
    }
    j0 = mh;
    j1 = j0 + m;
    j2 = j1 + m;
    j3 = j2 + m;
    x0r = a[j0] + a[j2];
    x0i = a[j0 + 1] + a[j2 + 1];
    x1r = a[j0] - a[j2];
    x1i = a[j0 + 1] - a[j2 + 1];
    x2r = a[j1] + a[j3];
    x2i = a[j1 + 1] + a[j3 + 1];
    x3r = a[j1] - a[j3];
    x3i = a[j1 + 1] - a[j3 + 1];
    a[j0] = x0r + x2r;
    a[j0 + 1] = x0i + x2i;
    a[j1] = x0r - x2r;
    a[j1 + 1] = x0i - x2i;
    x0r = x1r - x3i;
    x0i = x1i + x3r;
    a[j2] = wn4r * (x0r - x0i);
    a[j2 + 1] = wn4r * (x0i + x0r);
    x0r = x1r + x3i;
    x0i = x1i - x3r;
    a[j3] = -wn4r * (x0r + x0i);
    a[j3 + 1] = -wn4r * (x0i - x0r);
  }

  static void cftmdl2(int n, double* a, double* w)
  {
    int j, j0, j1, j2, j3, k, kr, m, mh;
    double wn4r, wk1r, wk1i, wk3r, wk3i, wd1r, wd1i, wd3r, wd3i;
    double x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i, y0r, y0i, y2r, y2i;

    mh = n >> 3;
    m = 2 * mh;
    wn4r = w[1];
    j1 = m;
    j2 = j1 + m;
    j3 = j2 + m;
    x0r = a[0] - a[j2 + 1];
    x0i = a[1] + a[j2];
    x1r = a[0] + a[j2 + 1];
    x1i = a[1] - a[j2];
    x2r = a[j1] - a[j3 + 1];
    x2i = a[j1 + 1] + a[j3];
    x3r = a[j1] + a[j3 + 1];
    x3i = a[j1 + 1] - a[j3];
    y0r = wn4r * (x2r - x2i);
    y0i = wn4r * (x2i + x2r);
    a[0] = x0r + y0r;
    a[1] = x0i + y0i;
    a[j1] = x0r - y0r;
    a[j1 + 1] = x0i - y0i;
    y0r = wn4r * (x3r - x3i);
    y0i = wn4r * (x3i + x3r);
    a[j2] = x1r - y0i;
    a[j2 + 1] = x1i + y0r;
    a[j3] = x1r + y0i;
    a[j3 + 1] = x1i - y0r;
    k = 0;
    kr = 2 * m;
    for (j = 2; j < mh; j += 2) {
      k += 4;
      wk1r = w[k];
      wk1i = w[k + 1];
      wk3r = w[k + 2];
      wk3i = w[k + 3];
      kr -= 4;
      wd1i = w[kr];
      wd1r = w[kr + 1];
      wd3i = w[kr + 2];
      wd3r = w[kr + 3];
      j1 = j + m;
      j2 = j1 + m;
      j3 = j2 + m;
      x0r = a[j] - a[j2 + 1];
      x0i = a[j + 1] + a[j2];
      x1r = a[j] + a[j2 + 1];
      x1i = a[j + 1] - a[j2];
      x2r = a[j1] - a[j3 + 1];
      x2i = a[j1 + 1] + a[j3];
      x3r = a[j1] + a[j3 + 1];
      x3i = a[j1 + 1] - a[j3];
      y0r = wk1r * x0r - wk1i * x0i;
      y0i = wk1r * x0i + wk1i * x0r;
      y2r = wd1r * x2r - wd1i * x2i;
      y2i = wd1r * x2i + wd1i * x2r;
      a[j] = y0r + y2r;
      a[j + 1] = y0i + y2i;
      a[j1] = y0r - y2r;
      a[j1 + 1] = y0i - y2i;
      y0r = wk3r * x1r + wk3i * x1i;
      y0i = wk3r * x1i - wk3i * x1r;
      y2r = wd3r * x3r + wd3i * x3i;
      y2i = wd3r * x3i - wd3i * x3r;
      a[j2] = y0r + y2r;
      a[j2 + 1] = y0i + y2i;
      a[j3] = y0r - y2r;
      a[j3 + 1] = y0i - y2i;
      j0 = m - j;
      j1 = j0 + m;
      j2 = j1 + m;
      j3 = j2 + m;
      x0r = a[j0] - a[j2 + 1];
      x0i = a[j0 + 1] + a[j2];
      x1r = a[j0] + a[j2 + 1];
      x1i = a[j0 + 1] - a[j2];
      x2r = a[j1] - a[j3 + 1];
      x2i = a[j1 + 1] + a[j3];
      x3r = a[j1] + a[j3 + 1];
      x3i = a[j1 + 1] - a[j3];
      y0r = wd1i * x0r - wd1r * x0i;
      y0i = wd1i * x0i + wd1r * x0r;
      y2r = wk1i * x2r - wk1r * x2i;
      y2i = wk1i * x2i + wk1r * x2r;
      a[j0] = y0r + y2r;
      a[j0 + 1] = y0i + y2i;
      a[j1] = y0r - y2r;
      a[j1 + 1] = y0i - y2i;
      y0r = wd3i * x1r + wd3r * x1i;
      y0i = wd3i * x1i - wd3r * x1r;
      y2r = wk3i * x3r + wk3r * x3i;
      y2i = wk3i * x3i - wk3r * x3r;
      a[j2] = y0r + y2r;
      a[j2 + 1] = y0i + y2i;
      a[j3] = y0r - y2r;
      a[j3 + 1] = y0i - y2i;
    }
    wk1r = w[m];
    wk1i = w[m + 1];
    j0 = mh;
    j1 = j0 + m;
    j2 = j1 + m;
    j3 = j2 + m;
    x0r = a[j0] - a[j2 + 1];
    x0i = a[j0 + 1] + a[j2];
    x1r = a[j0] + a[j2 + 1];
    x1i = a[j0 + 1] - a[j2];
    x2r = a[j1] - a[j3 + 1];
    x2i = a[j1 + 1] + a[j3];
    x3r = a[j1] + a[j3 + 1];
    x3i = a[j1 + 1] - a[j3];
    y0r = wk1r * x0r - wk1i * x0i;
    y0i = wk1r * x0i + wk1i * x0r;
    y2r = wk1i * x2r - wk1r * x2i;
    y2i = wk1i * x2i + wk1r * x2r;
    a[j0] = y0r + y2r;
    a[j0 + 1] = y0i + y2i;
    a[j1] = y0r - y2r;
    a[j1 + 1] = y0i - y2i;
    y0r = wk1i * x1r - wk1r * x1i;
    y0i = wk1i * x1i + wk1r * x1r;
    y2r = wk1r * x3r - wk1i * x3i;
    y2i = wk1r * x3i + wk1i * x3r;
    a[j2] = y0r - y2r;
    a[j2 + 1] = y0i - y2i;
    a[j3] = y0r + y2r;
    a[j3 + 1] = y0i + y2i;
  }

  static void bitrv2(int n, int* ip, double* a)
  {
    int j, j1, k, k1, l, m, nh, nm;
    double xr, xi, yr, yi;

    m = 1;
    for (l = n >> 2; l > 8; l >>= 2) {
      m <<= 1;
    }
    nh = n >> 1;
    nm = 4 * m;
    if (l == 8) {
      for (k = 0; k < m; k++) {
        for (j = 0; j < k; j++) {
          j1 = 4 * j + 2 * ip[m + k];
          k1 = 4 * k + 2 * ip[m + j];
          xr = a[j1];
          xi = a[j1 + 1];
          yr = a[k1];
          yi = a[k1 + 1];
          a[j1] = yr;
          a[j1 + 1] = yi;
          a[k1] = xr;
          a[k1 + 1] = xi;
          j1 += nm;
          k1 += 2 * nm;
          xr = a[j1];
          xi = a[j1 + 1];
          yr = a[k1];
          yi = a[k1 + 1];
          a[j1] = yr;
          a[j1 + 1] = yi;
          a[k1] = xr;
          a[k1 + 1] = xi;
          j1 += nm;
          k1 -= nm;
          xr = a[j1];
          xi = a[j1 + 1];
          yr = a[k1];
          yi = a[k1 + 1];
          a[j1] = yr;
          a[j1 + 1] = yi;
          a[k1] = xr;
          a[k1 + 1] = xi;
          j1 += nm;
          k1 += 2 * nm;
          xr = a[j1];
          xi = a[j1 + 1];
          yr = a[k1];
          yi = a[k1 + 1];
          a[j1] = yr;
          a[j1 + 1] = yi;
          a[k1] = xr;
          a[k1 + 1] = xi;
          j1 += nh;
          k1 += 2;
          xr = a[j1];
          xi = a[j1 + 1];
          yr = a[k1];
          yi = a[k1 + 1];
          a[j1] = yr;
          a[j1 + 1] = yi;
          a[k1] = xr;
          a[k1 + 1] = xi;
          j1 -= nm;
          k1 -= 2 * nm;
          xr = a[j1];
          xi = a[j1 + 1];
          yr = a[k1];
          yi = a[k1 + 1];
          a[j1] = yr;
          a[j1 + 1] = yi;
          a[k1] = xr;
          a[k1 + 1] = xi;
          j1 -= nm;
          k1 += nm;
          xr = a[j1];
          xi = a[j1 + 1];
          yr = a[k1];
          yi = a[k1 + 1];
          a[j1] = yr;
          a[j1 + 1] = yi;
          a[k1] = xr;
          a[k1 + 1] = xi;
          j1 -= nm;
          k1 -= 2 * nm;
          xr = a[j1];
          xi = a[j1 + 1];
          yr = a[k1];
          yi = a[k1 + 1];
          a[j1] = yr;
          a[j1 + 1] = yi;
          a[k1] = xr;
          a[k1 + 1] = xi;
          j1 += 2;
          k1 += nh;
          xr = a[j1];
          xi = a[j1 + 1];
          yr = a[k1];
          yi = a[k1 + 1];
          a[j1] = yr;
          a[j1 + 1] = yi;
          a[k1] = xr;
          a[k1 + 1] = xi;
          j1 += nm;
          k1 += 2 * nm;
          xr = a[j1];
          xi = a[j1 + 1];
          yr = a[k1];
          yi = a[k1 + 1];
          a[j1] = yr;
          a[j1 + 1] = yi;
          a[k1] = xr;
          a[k1 + 1] = xi;
          j1 += nm;
          k1 -= nm;
          xr = a[j1];
          xi = a[j1 + 1];
          yr = a[k1];
          yi = a[k1 + 1];
          a[j1] = yr;
          a[j1 + 1] = yi;
          a[k1] = xr;
          a[k1 + 1] = xi;
          j1 += nm;
          k1 += 2 * nm;
          xr = a[j1];
          xi = a[j1 + 1];
          yr = a[k1];
          yi = a[k1 + 1];
          a[j1] = yr;
          a[j1 + 1] = yi;
          a[k1] = xr;
          a[k1 + 1] = xi;
          j1 -= nh;
          k1 -= 2;
          xr = a[j1];
          xi = a[j1 + 1];
          yr = a[k1];
          yi = a[k1 + 1];
          a[j1] = yr;
          a[j1 + 1] = yi;
          a[k1] = xr;
          a[k1 + 1] = xi;
          j1 -= nm;
          k1 -= 2 * nm;
          xr = a[j1];
          xi = a[j1 + 1];
          yr = a[k1];
          yi = a[k1 + 1];
          a[j1] = yr;
          a[j1 + 1] = yi;
          a[k1] = xr;
          a[k1 + 1] = xi;
          j1 -= nm;
          k1 += nm;
          xr = a[j1];
          xi = a[j1 + 1];
          yr = a[k1];
          yi = a[k1 + 1];
          a[j1] = yr;
          a[j1 + 1] = yi;
          a[k1] = xr;
          a[k1 + 1] = xi;
          j1 -= nm;
          k1 -= 2 * nm;
          xr = a[j1];
          xi = a[j1 + 1];
          yr = a[k1];
          yi = a[k1 + 1];
          a[j1] = yr;
          a[j1 + 1] = yi;
          a[k1] = xr;
          a[k1 + 1] = xi;
        }
        k1 = 4 * k + 2 * ip[m + k];
        j1 = k1 + 2;
        k1 += nh;
        xr = a[j1];
        xi = a[j1 + 1];
        yr = a[k1];
        yi = a[k1 + 1];
        a[j1] = yr;
        a[j1 + 1] = yi;
        a[k1] = xr;
        a[k1 + 1] = xi;
        j1 += nm;
        k1 += 2 * nm;
        xr = a[j1];
        xi = a[j1 + 1];
        yr = a[k1];
        yi = a[k1 + 1];
        a[j1] = yr;
        a[j1 + 1] = yi;
        a[k1] = xr;
        a[k1 + 1] = xi;
        j1 += nm;
        k1 -= nm;
        xr = a[j1];
        xi = a[j1 + 1];
        yr = a[k1];
        yi = a[k1 + 1];
        a[j1] = yr;
        a[j1 + 1] = yi;
        a[k1] = xr;
        a[k1 + 1] = xi;
        j1 -= 2;
        k1 -= nh;
        xr = a[j1];
        xi = a[j1 + 1];
        yr = a[k1];
        yi = a[k1 + 1];
        a[j1] = yr;
        a[j1 + 1] = yi;
        a[k1] = xr;
        a[k1 + 1] = xi;
        j1 += nh + 2;
        k1 += nh + 2;
        xr = a[j1];
        xi = a[j1 + 1];
        yr = a[k1];
        yi = a[k1 + 1];
        a[j1] = yr;
        a[j1 + 1] = yi;
        a[k1] = xr;
        a[k1 + 1] = xi;
        j1 -= nh - nm;
        k1 += 2 * nm - 2;
        xr = a[j1];
        xi = a[j1 + 1];
        yr = a[k1];
        yi = a[k1 + 1];
        a[j1] = yr;
        a[j1 + 1] = yi;
        a[k1] = xr;
        a[k1 + 1] = xi;
      }
    }
    else {
      for (k = 0; k < m; k++) {
        for (j = 0; j < k; j++) {
          j1 = 4 * j + ip[m + k];
          k1 = 4 * k + ip[m + j];
          xr = a[j1];
          xi = a[j1 + 1];
          yr = a[k1];
          yi = a[k1 + 1];
          a[j1] = yr;
          a[j1 + 1] = yi;
          a[k1] = xr;
          a[k1 + 1] = xi;
          j1 += nm;
          k1 += nm;
          xr = a[j1];
          xi = a[j1 + 1];
          yr = a[k1];
          yi = a[k1 + 1];
          a[j1] = yr;
          a[j1 + 1] = yi;
          a[k1] = xr;
          a[k1 + 1] = xi;
          j1 += nh;
          k1 += 2;
          xr = a[j1];
          xi = a[j1 + 1];
          yr = a[k1];
          yi = a[k1 + 1];
          a[j1] = yr;
          a[j1 + 1] = yi;
          a[k1] = xr;
          a[k1 + 1] = xi;
          j1 -= nm;
          k1 -= nm;
          xr = a[j1];
          xi = a[j1 + 1];
          yr = a[k1];
          yi = a[k1 + 1];
          a[j1] = yr;
          a[j1 + 1] = yi;
          a[k1] = xr;
          a[k1 + 1] = xi;
          j1 += 2;
          k1 += nh;
          xr = a[j1];
          xi = a[j1 + 1];
          yr = a[k1];
          yi = a[k1 + 1];
          a[j1] = yr;
          a[j1 + 1] = yi;
          a[k1] = xr;
          a[k1 + 1] = xi;
          j1 += nm;
          k1 += nm;
          xr = a[j1];
          xi = a[j1 + 1];
          yr = a[k1];
          yi = a[k1 + 1];
          a[j1] = yr;
          a[j1 + 1] = yi;
          a[k1] = xr;
          a[k1 + 1] = xi;
          j1 -= nh;
          k1 -= 2;
          xr = a[j1];
          xi = a[j1 + 1];
          yr = a[k1];
          yi = a[k1 + 1];
          a[j1] = yr;
          a[j1 + 1] = yi;
          a[k1] = xr;
          a[k1 + 1] = xi;
          j1 -= nm;
          k1 -= nm;
          xr = a[j1];
          xi = a[j1 + 1];
          yr = a[k1];
          yi = a[k1 + 1];
          a[j1] = yr;
          a[j1 + 1] = yi;
          a[k1] = xr;
          a[k1 + 1] = xi;
        }
        k1 = 4 * k + ip[m + k];
        j1 = k1 + 2;
        k1 += nh;
        xr = a[j1];
        xi = a[j1 + 1];
        yr = a[k1];
        yi = a[k1 + 1];
        a[j1] = yr;
        a[j1 + 1] = yi;
        a[k1] = xr;
        a[k1 + 1] = xi;
        j1 += nm;
        k1 += nm;
        xr = a[j1];
        xi = a[j1 + 1];
        yr = a[k1];
        yi = a[k1 + 1];
        a[j1] = yr;
        a[j1 + 1] = yi;
        a[k1] = xr;
        a[k1 + 1] = xi;
      }
    }
  }

  static void bitrv2conj(int n, int* ip, double* a)
  {
    int j, j1, k, k1, l, m, nh, nm;
    double xr, xi, yr, yi;

    m = 1;
    for (l = n >> 2; l > 8; l >>= 2) {
      m <<= 1;
    }
    nh = n >> 1;
    nm = 4 * m;
    if (l == 8) {
      for (k = 0; k < m; k++) {
        for (j = 0; j < k; j++) {
          j1 = 4 * j + 2 * ip[m + k];
          k1 = 4 * k + 2 * ip[m + j];
          xr = a[j1];
          xi = -a[j1 + 1];
          yr = a[k1];
          yi = -a[k1 + 1];
          a[j1] = yr;
          a[j1 + 1] = yi;
          a[k1] = xr;
          a[k1 + 1] = xi;
          j1 += nm;
          k1 += 2 * nm;
          xr = a[j1];
          xi = -a[j1 + 1];
          yr = a[k1];
          yi = -a[k1 + 1];
          a[j1] = yr;
          a[j1 + 1] = yi;
          a[k1] = xr;
          a[k1 + 1] = xi;
          j1 += nm;
          k1 -= nm;
          xr = a[j1];
          xi = -a[j1 + 1];
          yr = a[k1];
          yi = -a[k1 + 1];
          a[j1] = yr;
          a[j1 + 1] = yi;
          a[k1] = xr;
          a[k1 + 1] = xi;
          j1 += nm;
          k1 += 2 * nm;
          xr = a[j1];
          xi = -a[j1 + 1];
          yr = a[k1];
          yi = -a[k1 + 1];
          a[j1] = yr;
          a[j1 + 1] = yi;
          a[k1] = xr;
          a[k1 + 1] = xi;
          j1 += nh;
          k1 += 2;
          xr = a[j1];
          xi = -a[j1 + 1];
          yr = a[k1];
          yi = -a[k1 + 1];
          a[j1] = yr;
          a[j1 + 1] = yi;
          a[k1] = xr;
          a[k1 + 1] = xi;
          j1 -= nm;
          k1 -= 2 * nm;
          xr = a[j1];
          xi = -a[j1 + 1];
          yr = a[k1];
          yi = -a[k1 + 1];
          a[j1] = yr;
          a[j1 + 1] = yi;
          a[k1] = xr;
          a[k1 + 1] = xi;
          j1 -= nm;
          k1 += nm;
          xr = a[j1];
          xi = -a[j1 + 1];
          yr = a[k1];
          yi = -a[k1 + 1];
          a[j1] = yr;
          a[j1 + 1] = yi;
          a[k1] = xr;
          a[k1 + 1] = xi;
          j1 -= nm;
          k1 -= 2 * nm;
          xr = a[j1];
          xi = -a[j1 + 1];
          yr = a[k1];
          yi = -a[k1 + 1];
          a[j1] = yr;
          a[j1 + 1] = yi;
          a[k1] = xr;
          a[k1 + 1] = xi;
          j1 += 2;
          k1 += nh;
          xr = a[j1];
          xi = -a[j1 + 1];
          yr = a[k1];
          yi = -a[k1 + 1];
          a[j1] = yr;
          a[j1 + 1] = yi;
          a[k1] = xr;
          a[k1 + 1] = xi;
          j1 += nm;
          k1 += 2 * nm;
          xr = a[j1];
          xi = -a[j1 + 1];
          yr = a[k1];
          yi = -a[k1 + 1];
          a[j1] = yr;
          a[j1 + 1] = yi;
          a[k1] = xr;
          a[k1 + 1] = xi;
          j1 += nm;
          k1 -= nm;
          xr = a[j1];
          xi = -a[j1 + 1];
          yr = a[k1];
          yi = -a[k1 + 1];
          a[j1] = yr;
          a[j1 + 1] = yi;
          a[k1] = xr;
          a[k1 + 1] = xi;
          j1 += nm;
          k1 += 2 * nm;
          xr = a[j1];
          xi = -a[j1 + 1];
          yr = a[k1];
          yi = -a[k1 + 1];
          a[j1] = yr;
          a[j1 + 1] = yi;
          a[k1] = xr;
          a[k1 + 1] = xi;
          j1 -= nh;
          k1 -= 2;
          xr = a[j1];
          xi = -a[j1 + 1];
          yr = a[k1];
          yi = -a[k1 + 1];
          a[j1] = yr;
          a[j1 + 1] = yi;
          a[k1] = xr;
          a[k1 + 1] = xi;
          j1 -= nm;
          k1 -= 2 * nm;
          xr = a[j1];
          xi = -a[j1 + 1];
          yr = a[k1];
          yi = -a[k1 + 1];
          a[j1] = yr;
          a[j1 + 1] = yi;
          a[k1] = xr;
          a[k1 + 1] = xi;
          j1 -= nm;
          k1 += nm;
          xr = a[j1];
          xi = -a[j1 + 1];
          yr = a[k1];
          yi = -a[k1 + 1];
          a[j1] = yr;
          a[j1 + 1] = yi;
          a[k1] = xr;
          a[k1 + 1] = xi;
          j1 -= nm;
          k1 -= 2 * nm;
          xr = a[j1];
          xi = -a[j1 + 1];
          yr = a[k1];
          yi = -a[k1 + 1];
          a[j1] = yr;
          a[j1 + 1] = yi;
          a[k1] = xr;
          a[k1 + 1] = xi;
        }
        k1 = 4 * k + 2 * ip[m + k];
        j1 = k1 + 2;
        k1 += nh;
        a[j1 - 1] = -a[j1 - 1];
        xr = a[j1];
        xi = -a[j1 + 1];
        yr = a[k1];
        yi = -a[k1 + 1];
        a[j1] = yr;
        a[j1 + 1] = yi;
        a[k1] = xr;
        a[k1 + 1] = xi;
        a[k1 + 3] = -a[k1 + 3];
        j1 += nm;
        k1 += 2 * nm;
        xr = a[j1];
        xi = -a[j1 + 1];
        yr = a[k1];
        yi = -a[k1 + 1];
        a[j1] = yr;
        a[j1 + 1] = yi;
        a[k1] = xr;
        a[k1 + 1] = xi;
        j1 += nm;
        k1 -= nm;
        xr = a[j1];
        xi = -a[j1 + 1];
        yr = a[k1];
        yi = -a[k1 + 1];
        a[j1] = yr;
        a[j1 + 1] = yi;
        a[k1] = xr;
        a[k1 + 1] = xi;
        j1 -= 2;
        k1 -= nh;
        xr = a[j1];
        xi = -a[j1 + 1];
        yr = a[k1];
        yi = -a[k1 + 1];
        a[j1] = yr;
        a[j1 + 1] = yi;
        a[k1] = xr;
        a[k1 + 1] = xi;
        j1 += nh + 2;
        k1 += nh + 2;
        xr = a[j1];
        xi = -a[j1 + 1];
        yr = a[k1];
        yi = -a[k1 + 1];
        a[j1] = yr;
        a[j1 + 1] = yi;
        a[k1] = xr;
        a[k1 + 1] = xi;
        j1 -= nh - nm;
        k1 += 2 * nm - 2;
        a[j1 - 1] = -a[j1 - 1];
        xr = a[j1];
        xi = -a[j1 + 1];
        yr = a[k1];
        yi = -a[k1 + 1];
        a[j1] = yr;
        a[j1 + 1] = yi;
        a[k1] = xr;
        a[k1 + 1] = xi;
        a[k1 + 3] = -a[k1 + 3];
      }
    }
    else {
      for (k = 0; k < m; k++) {
        for (j = 0; j < k; j++) {
          j1 = 4 * j + ip[m + k];
          k1 = 4 * k + ip[m + j];
          xr = a[j1];
          xi = -a[j1 + 1];
          yr = a[k1];
          yi = -a[k1 + 1];
          a[j1] = yr;
          a[j1 + 1] = yi;
          a[k1] = xr;
          a[k1 + 1] = xi;
          j1 += nm;
          k1 += nm;
          xr = a[j1];
          xi = -a[j1 + 1];
          yr = a[k1];
          yi = -a[k1 + 1];
          a[j1] = yr;
          a[j1 + 1] = yi;
          a[k1] = xr;
          a[k1 + 1] = xi;
          j1 += nh;
          k1 += 2;
          xr = a[j1];
          xi = -a[j1 + 1];
          yr = a[k1];
          yi = -a[k1 + 1];
          a[j1] = yr;
          a[j1 + 1] = yi;
          a[k1] = xr;
          a[k1 + 1] = xi;
          j1 -= nm;
          k1 -= nm;
          xr = a[j1];
          xi = -a[j1 + 1];
          yr = a[k1];
          yi = -a[k1 + 1];
          a[j1] = yr;
          a[j1 + 1] = yi;
          a[k1] = xr;
          a[k1 + 1] = xi;
          j1 += 2;
          k1 += nh;
          xr = a[j1];
          xi = -a[j1 + 1];
          yr = a[k1];
          yi = -a[k1 + 1];
          a[j1] = yr;
          a[j1 + 1] = yi;
          a[k1] = xr;
          a[k1 + 1] = xi;
          j1 += nm;
          k1 += nm;
          xr = a[j1];
          xi = -a[j1 + 1];
          yr = a[k1];
          yi = -a[k1 + 1];
          a[j1] = yr;
          a[j1 + 1] = yi;
          a[k1] = xr;
          a[k1 + 1] = xi;
          j1 -= nh;
          k1 -= 2;
          xr = a[j1];
          xi = -a[j1 + 1];
          yr = a[k1];
          yi = -a[k1 + 1];
          a[j1] = yr;
          a[j1 + 1] = yi;
          a[k1] = xr;
          a[k1 + 1] = xi;
          j1 -= nm;
          k1 -= nm;
          xr = a[j1];
          xi = -a[j1 + 1];
          yr = a[k1];
          yi = -a[k1 + 1];
          a[j1] = yr;
          a[j1 + 1] = yi;
          a[k1] = xr;
          a[k1 + 1] = xi;
        }
        k1 = 4 * k + ip[m + k];
        j1 = k1 + 2;
        k1 += nh;
        a[j1 - 1] = -a[j1 - 1];
        xr = a[j1];
        xi = -a[j1 + 1];
        yr = a[k1];
        yi = -a[k1 + 1];
        a[j1] = yr;
        a[j1 + 1] = yi;
        a[k1] = xr;
        a[k1 + 1] = xi;
        a[k1 + 3] = -a[k1 + 3];
        j1 += nm;
        k1 += nm;
        a[j1 - 1] = -a[j1 - 1];
        xr = a[j1];
        xi = -a[j1 + 1];
        yr = a[k1];
        yi = -a[k1 + 1];
        a[j1] = yr;
        a[j1 + 1] = yi;
        a[k1] = xr;
        a[k1 + 1] = xi;
        a[k1 + 3] = -a[k1 + 3];
      }
    }
  }

  static void bitrv216(double* a)
  {
    double x1r, x1i, x2r, x2i, x3r, x3i, x4r, x4i,
      x5r, x5i, x7r, x7i, x8r, x8i, x10r, x10i,
      x11r, x11i, x12r, x12i, x13r, x13i, x14r, x14i;

    x1r = a[2];
    x1i = a[3];
    x2r = a[4];
    x2i = a[5];
    x3r = a[6];
    x3i = a[7];
    x4r = a[8];
    x4i = a[9];
    x5r = a[10];
    x5i = a[11];
    x7r = a[14];
    x7i = a[15];
    x8r = a[16];
    x8i = a[17];
    x10r = a[20];
    x10i = a[21];
    x11r = a[22];
    x11i = a[23];
    x12r = a[24];
    x12i = a[25];
    x13r = a[26];
    x13i = a[27];
    x14r = a[28];
    x14i = a[29];
    a[2] = x8r;
    a[3] = x8i;
    a[4] = x4r;
    a[5] = x4i;
    a[6] = x12r;
    a[7] = x12i;
    a[8] = x2r;
    a[9] = x2i;
    a[10] = x10r;
    a[11] = x10i;
    a[14] = x14r;
    a[15] = x14i;
    a[16] = x1r;
    a[17] = x1i;
    a[20] = x5r;
    a[21] = x5i;
    a[22] = x13r;
    a[23] = x13i;
    a[24] = x3r;
    a[25] = x3i;
    a[26] = x11r;
    a[27] = x11i;
    a[28] = x7r;
    a[29] = x7i;
  }

  static void bitrv216neg(double* a)
  {
    double x1r, x1i, x2r, x2i, x3r, x3i, x4r, x4i,
      x5r, x5i, x6r, x6i, x7r, x7i, x8r, x8i,
      x9r, x9i, x10r, x10i, x11r, x11i, x12r, x12i,
      x13r, x13i, x14r, x14i, x15r, x15i;

    x1r = a[2];
    x1i = a[3];
    x2r = a[4];
    x2i = a[5];
    x3r = a[6];
    x3i = a[7];
    x4r = a[8];
    x4i = a[9];
    x5r = a[10];
    x5i = a[11];
    x6r = a[12];
    x6i = a[13];
    x7r = a[14];
    x7i = a[15];
    x8r = a[16];
    x8i = a[17];
    x9r = a[18];
    x9i = a[19];
    x10r = a[20];
    x10i = a[21];
    x11r = a[22];
    x11i = a[23];
    x12r = a[24];
    x12i = a[25];
    x13r = a[26];
    x13i = a[27];
    x14r = a[28];
    x14i = a[29];
    x15r = a[30];
    x15i = a[31];
    a[2] = x15r;
    a[3] = x15i;
    a[4] = x7r;
    a[5] = x7i;
    a[6] = x11r;
    a[7] = x11i;
    a[8] = x3r;
    a[9] = x3i;
    a[10] = x13r;
    a[11] = x13i;
    a[12] = x5r;
    a[13] = x5i;
    a[14] = x9r;
    a[15] = x9i;
    a[16] = x1r;
    a[17] = x1i;
    a[18] = x14r;
    a[19] = x14i;
    a[20] = x6r;
    a[21] = x6i;
    a[22] = x10r;
    a[23] = x10i;
    a[24] = x2r;
    a[25] = x2i;
    a[26] = x12r;
    a[27] = x12i;
    a[28] = x4r;
    a[29] = x4i;
    a[30] = x8r;
    a[31] = x8i;
  }

  static void bitrv208(double* a)
  {
    double x1r, x1i, x3r, x3i, x4r, x4i, x6r, x6i;

    x1r = a[2];
    x1i = a[3];
    x3r = a[6];
    x3i = a[7];
    x4r = a[8];
    x4i = a[9];
    x6r = a[12];
    x6i = a[13];
    a[2] = x4r;
    a[3] = x4i;
    a[6] = x6r;
    a[7] = x6i;
    a[8] = x1r;
    a[9] = x1i;
    a[12] = x3r;
    a[13] = x3i;
  }

  static void bitrv208neg(double* a)
  {
    double x1r, x1i, x2r, x2i, x3r, x3i, x4r, x4i,
      x5r, x5i, x6r, x6i, x7r, x7i;

    x1r = a[2];
    x1i = a[3];
    x2r = a[4];
    x2i = a[5];
    x3r = a[6];
    x3i = a[7];
    x4r = a[8];
    x4i = a[9];
    x5r = a[10];
    x5i = a[11];
    x6r = a[12];
    x6i = a[13];
    x7r = a[14];
    x7i = a[15];
    a[2] = x7r;
    a[3] = x7i;
    a[4] = x3r;
    a[5] = x3i;
    a[6] = x5r;
    a[7] = x5i;
    a[8] = x1r;
    a[9] = x1i;
    a[10] = x6r;
    a[11] = x6i;
    a[12] = x2r;
    a[13] = x2i;
    a[14] = x4r;
    a[15] = x4i;
  }

  static void cftf1st(int n, double* a, double* w)
  {
    int j, j0, j1, j2, j3, k, m, mh;
    double wn4r, csc1, csc3, wk1r, wk1i, wk3r, wk3i,
      wd1r, wd1i, wd3r, wd3i;
    double x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i,
      y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i;

    mh = n >> 3;
    m = 2 * mh;
    j1 = m;
    j2 = j1 + m;
    j3 = j2 + m;
    x0r = a[0] + a[j2];
    x0i = a[1] + a[j2 + 1];
    x1r = a[0] - a[j2];
    x1i = a[1] - a[j2 + 1];
    x2r = a[j1] + a[j3];
    x2i = a[j1 + 1] + a[j3 + 1];
    x3r = a[j1] - a[j3];
    x3i = a[j1 + 1] - a[j3 + 1];
    a[0] = x0r + x2r;
    a[1] = x0i + x2i;
    a[j1] = x0r - x2r;
    a[j1 + 1] = x0i - x2i;
    a[j2] = x1r - x3i;
    a[j2 + 1] = x1i + x3r;
    a[j3] = x1r + x3i;
    a[j3 + 1] = x1i - x3r;
    wn4r = w[1];
    csc1 = w[2];
    csc3 = w[3];
    wd1r = 1;
    wd1i = 0;
    wd3r = 1;
    wd3i = 0;
    k = 0;
    for (j = 2; j < mh - 2; j += 4) {
      k += 4;
      wk1r = csc1 * (wd1r + w[k]);
      wk1i = csc1 * (wd1i + w[k + 1]);
      wk3r = csc3 * (wd3r + w[k + 2]);
      wk3i = csc3 * (wd3i + w[k + 3]);
      wd1r = w[k];
      wd1i = w[k + 1];
      wd3r = w[k + 2];
      wd3i = w[k + 3];
      j1 = j + m;
      j2 = j1 + m;
      j3 = j2 + m;
      x0r = a[j] + a[j2];
      x0i = a[j + 1] + a[j2 + 1];
      x1r = a[j] - a[j2];
      x1i = a[j + 1] - a[j2 + 1];
      y0r = a[j + 2] + a[j2 + 2];
      y0i = a[j + 3] + a[j2 + 3];
      y1r = a[j + 2] - a[j2 + 2];
      y1i = a[j + 3] - a[j2 + 3];
      x2r = a[j1] + a[j3];
      x2i = a[j1 + 1] + a[j3 + 1];
      x3r = a[j1] - a[j3];
      x3i = a[j1 + 1] - a[j3 + 1];
      y2r = a[j1 + 2] + a[j3 + 2];
      y2i = a[j1 + 3] + a[j3 + 3];
      y3r = a[j1 + 2] - a[j3 + 2];
      y3i = a[j1 + 3] - a[j3 + 3];
      a[j] = x0r + x2r;
      a[j + 1] = x0i + x2i;
      a[j + 2] = y0r + y2r;
      a[j + 3] = y0i + y2i;
      a[j1] = x0r - x2r;
      a[j1 + 1] = x0i - x2i;
      a[j1 + 2] = y0r - y2r;
      a[j1 + 3] = y0i - y2i;
      x0r = x1r - x3i;
      x0i = x1i + x3r;
      a[j2] = wk1r * x0r - wk1i * x0i;
      a[j2 + 1] = wk1r * x0i + wk1i * x0r;
      x0r = y1r - y3i;
      x0i = y1i + y3r;
      a[j2 + 2] = wd1r * x0r - wd1i * x0i;
      a[j2 + 3] = wd1r * x0i + wd1i * x0r;
      x0r = x1r + x3i;
      x0i = x1i - x3r;
      a[j3] = wk3r * x0r + wk3i * x0i;
      a[j3 + 1] = wk3r * x0i - wk3i * x0r;
      x0r = y1r + y3i;
      x0i = y1i - y3r;
      a[j3 + 2] = wd3r * x0r + wd3i * x0i;
      a[j3 + 3] = wd3r * x0i - wd3i * x0r;
      j0 = m - j;
      j1 = j0 + m;
      j2 = j1 + m;
      j3 = j2 + m;
      x0r = a[j0] + a[j2];
      x0i = a[j0 + 1] + a[j2 + 1];
      x1r = a[j0] - a[j2];
      x1i = a[j0 + 1] - a[j2 + 1];
      y0r = a[j0 - 2] + a[j2 - 2];
      y0i = a[j0 - 1] + a[j2 - 1];
      y1r = a[j0 - 2] - a[j2 - 2];
      y1i = a[j0 - 1] - a[j2 - 1];
      x2r = a[j1] + a[j3];
      x2i = a[j1 + 1] + a[j3 + 1];
      x3r = a[j1] - a[j3];
      x3i = a[j1 + 1] - a[j3 + 1];
      y2r = a[j1 - 2] + a[j3 - 2];
      y2i = a[j1 - 1] + a[j3 - 1];
      y3r = a[j1 - 2] - a[j3 - 2];
      y3i = a[j1 - 1] - a[j3 - 1];
      a[j0] = x0r + x2r;
      a[j0 + 1] = x0i + x2i;
      a[j0 - 2] = y0r + y2r;
      a[j0 - 1] = y0i + y2i;
      a[j1] = x0r - x2r;
      a[j1 + 1] = x0i - x2i;
      a[j1 - 2] = y0r - y2r;
      a[j1 - 1] = y0i - y2i;
      x0r = x1r - x3i;
      x0i = x1i + x3r;
      a[j2] = wk1i * x0r - wk1r * x0i;
      a[j2 + 1] = wk1i * x0i + wk1r * x0r;
      x0r = y1r - y3i;
      x0i = y1i + y3r;
      a[j2 - 2] = wd1i * x0r - wd1r * x0i;
      a[j2 - 1] = wd1i * x0i + wd1r * x0r;
      x0r = x1r + x3i;
      x0i = x1i - x3r;
      a[j3] = wk3i * x0r + wk3r * x0i;
      a[j3 + 1] = wk3i * x0i - wk3r * x0r;
      x0r = y1r + y3i;
      x0i = y1i - y3r;
      a[j3 - 2] = wd3i * x0r + wd3r * x0i;
      a[j3 - 1] = wd3i * x0i - wd3r * x0r;
    }
    wk1r = csc1 * (wd1r + wn4r);
    wk1i = csc1 * (wd1i + wn4r);
    wk3r = csc3 * (wd3r - wn4r);
    wk3i = csc3 * (wd3i - wn4r);
    j0 = mh;
    j1 = j0 + m;
    j2 = j1 + m;
    j3 = j2 + m;
    x0r = a[j0 - 2] + a[j2 - 2];
    x0i = a[j0 - 1] + a[j2 - 1];
    x1r = a[j0 - 2] - a[j2 - 2];
    x1i = a[j0 - 1] - a[j2 - 1];
    x2r = a[j1 - 2] + a[j3 - 2];
    x2i = a[j1 - 1] + a[j3 - 1];
    x3r = a[j1 - 2] - a[j3 - 2];
    x3i = a[j1 - 1] - a[j3 - 1];
    a[j0 - 2] = x0r + x2r;
    a[j0 - 1] = x0i + x2i;
    a[j1 - 2] = x0r - x2r;
    a[j1 - 1] = x0i - x2i;
    x0r = x1r - x3i;
    x0i = x1i + x3r;
    a[j2 - 2] = wk1r * x0r - wk1i * x0i;
    a[j2 - 1] = wk1r * x0i + wk1i * x0r;
    x0r = x1r + x3i;
    x0i = x1i - x3r;
    a[j3 - 2] = wk3r * x0r + wk3i * x0i;
    a[j3 - 1] = wk3r * x0i - wk3i * x0r;
    x0r = a[j0] + a[j2];
    x0i = a[j0 + 1] + a[j2 + 1];
    x1r = a[j0] - a[j2];
    x1i = a[j0 + 1] - a[j2 + 1];
    x2r = a[j1] + a[j3];
    x2i = a[j1 + 1] + a[j3 + 1];
    x3r = a[j1] - a[j3];
    x3i = a[j1 + 1] - a[j3 + 1];
    a[j0] = x0r + x2r;
    a[j0 + 1] = x0i + x2i;
    a[j1] = x0r - x2r;
    a[j1 + 1] = x0i - x2i;
    x0r = x1r - x3i;
    x0i = x1i + x3r;
    a[j2] = wn4r * (x0r - x0i);
    a[j2 + 1] = wn4r * (x0i + x0r);
    x0r = x1r + x3i;
    x0i = x1i - x3r;
    a[j3] = -wn4r * (x0r + x0i);
    a[j3 + 1] = -wn4r * (x0i - x0r);
    x0r = a[j0 + 2] + a[j2 + 2];
    x0i = a[j0 + 3] + a[j2 + 3];
    x1r = a[j0 + 2] - a[j2 + 2];
    x1i = a[j0 + 3] - a[j2 + 3];
    x2r = a[j1 + 2] + a[j3 + 2];
    x2i = a[j1 + 3] + a[j3 + 3];
    x3r = a[j1 + 2] - a[j3 + 2];
    x3i = a[j1 + 3] - a[j3 + 3];
    a[j0 + 2] = x0r + x2r;
    a[j0 + 3] = x0i + x2i;
    a[j1 + 2] = x0r - x2r;
    a[j1 + 3] = x0i - x2i;
    x0r = x1r - x3i;
    x0i = x1i + x3r;
    a[j2 + 2] = wk1i * x0r - wk1r * x0i;
    a[j2 + 3] = wk1i * x0i + wk1r * x0r;
    x0r = x1r + x3i;
    x0i = x1i - x3r;
    a[j3 + 2] = wk3i * x0r + wk3r * x0i;
    a[j3 + 3] = wk3i * x0i - wk3r * x0r;
  }

  static void cftb1st(int n, double* a, double* w)
  {
    int j, j0, j1, j2, j3, k, m, mh;
    double wn4r, csc1, csc3, wk1r, wk1i, wk3r, wk3i,
      wd1r, wd1i, wd3r, wd3i;
    double x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i,
      y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i;

    mh = n >> 3;
    m = 2 * mh;
    j1 = m;
    j2 = j1 + m;
    j3 = j2 + m;
    x0r = a[0] + a[j2];
    x0i = -a[1] - a[j2 + 1];
    x1r = a[0] - a[j2];
    x1i = -a[1] + a[j2 + 1];
    x2r = a[j1] + a[j3];
    x2i = a[j1 + 1] + a[j3 + 1];
    x3r = a[j1] - a[j3];
    x3i = a[j1 + 1] - a[j3 + 1];
    a[0] = x0r + x2r;
    a[1] = x0i - x2i;
    a[j1] = x0r - x2r;
    a[j1 + 1] = x0i + x2i;
    a[j2] = x1r + x3i;
    a[j2 + 1] = x1i + x3r;
    a[j3] = x1r - x3i;
    a[j3 + 1] = x1i - x3r;
    wn4r = w[1];
    csc1 = w[2];
    csc3 = w[3];
    wd1r = 1;
    wd1i = 0;
    wd3r = 1;
    wd3i = 0;
    k = 0;
    for (j = 2; j < mh - 2; j += 4) {
      k += 4;
      wk1r = csc1 * (wd1r + w[k]);
      wk1i = csc1 * (wd1i + w[k + 1]);
      wk3r = csc3 * (wd3r + w[k + 2]);
      wk3i = csc3 * (wd3i + w[k + 3]);
      wd1r = w[k];
      wd1i = w[k + 1];
      wd3r = w[k + 2];
      wd3i = w[k + 3];
      j1 = j + m;
      j2 = j1 + m;
      j3 = j2 + m;
      x0r = a[j] + a[j2];
      x0i = -a[j + 1] - a[j2 + 1];
      x1r = a[j] - a[j2];
      x1i = -a[j + 1] + a[j2 + 1];
      y0r = a[j + 2] + a[j2 + 2];
      y0i = -a[j + 3] - a[j2 + 3];
      y1r = a[j + 2] - a[j2 + 2];
      y1i = -a[j + 3] + a[j2 + 3];
      x2r = a[j1] + a[j3];
      x2i = a[j1 + 1] + a[j3 + 1];
      x3r = a[j1] - a[j3];
      x3i = a[j1 + 1] - a[j3 + 1];
      y2r = a[j1 + 2] + a[j3 + 2];
      y2i = a[j1 + 3] + a[j3 + 3];
      y3r = a[j1 + 2] - a[j3 + 2];
      y3i = a[j1 + 3] - a[j3 + 3];
      a[j] = x0r + x2r;
      a[j + 1] = x0i - x2i;
      a[j + 2] = y0r + y2r;
      a[j + 3] = y0i - y2i;
      a[j1] = x0r - x2r;
      a[j1 + 1] = x0i + x2i;
      a[j1 + 2] = y0r - y2r;
      a[j1 + 3] = y0i + y2i;
      x0r = x1r + x3i;
      x0i = x1i + x3r;
      a[j2] = wk1r * x0r - wk1i * x0i;
      a[j2 + 1] = wk1r * x0i + wk1i * x0r;
      x0r = y1r + y3i;
      x0i = y1i + y3r;
      a[j2 + 2] = wd1r * x0r - wd1i * x0i;
      a[j2 + 3] = wd1r * x0i + wd1i * x0r;
      x0r = x1r - x3i;
      x0i = x1i - x3r;
      a[j3] = wk3r * x0r + wk3i * x0i;
      a[j3 + 1] = wk3r * x0i - wk3i * x0r;
      x0r = y1r - y3i;
      x0i = y1i - y3r;
      a[j3 + 2] = wd3r * x0r + wd3i * x0i;
      a[j3 + 3] = wd3r * x0i - wd3i * x0r;
      j0 = m - j;
      j1 = j0 + m;
      j2 = j1 + m;
      j3 = j2 + m;
      x0r = a[j0] + a[j2];
      x0i = -a[j0 + 1] - a[j2 + 1];
      x1r = a[j0] - a[j2];
      x1i = -a[j0 + 1] + a[j2 + 1];
      y0r = a[j0 - 2] + a[j2 - 2];
      y0i = -a[j0 - 1] - a[j2 - 1];
      y1r = a[j0 - 2] - a[j2 - 2];
      y1i = -a[j0 - 1] + a[j2 - 1];
      x2r = a[j1] + a[j3];
      x2i = a[j1 + 1] + a[j3 + 1];
      x3r = a[j1] - a[j3];
      x3i = a[j1 + 1] - a[j3 + 1];
      y2r = a[j1 - 2] + a[j3 - 2];
      y2i = a[j1 - 1] + a[j3 - 1];
      y3r = a[j1 - 2] - a[j3 - 2];
      y3i = a[j1 - 1] - a[j3 - 1];
      a[j0] = x0r + x2r;
      a[j0 + 1] = x0i - x2i;
      a[j0 - 2] = y0r + y2r;
      a[j0 - 1] = y0i - y2i;
      a[j1] = x0r - x2r;
      a[j1 + 1] = x0i + x2i;
      a[j1 - 2] = y0r - y2r;
      a[j1 - 1] = y0i + y2i;
      x0r = x1r + x3i;
      x0i = x1i + x3r;
      a[j2] = wk1i * x0r - wk1r * x0i;
      a[j2 + 1] = wk1i * x0i + wk1r * x0r;
      x0r = y1r + y3i;
      x0i = y1i + y3r;
      a[j2 - 2] = wd1i * x0r - wd1r * x0i;
      a[j2 - 1] = wd1i * x0i + wd1r * x0r;
      x0r = x1r - x3i;
      x0i = x1i - x3r;
      a[j3] = wk3i * x0r + wk3r * x0i;
      a[j3 + 1] = wk3i * x0i - wk3r * x0r;
      x0r = y1r - y3i;
      x0i = y1i - y3r;
      a[j3 - 2] = wd3i * x0r + wd3r * x0i;
      a[j3 - 1] = wd3i * x0i - wd3r * x0r;
    }
    wk1r = csc1 * (wd1r + wn4r);
    wk1i = csc1 * (wd1i + wn4r);
    wk3r = csc3 * (wd3r - wn4r);
    wk3i = csc3 * (wd3i - wn4r);
    j0 = mh;
    j1 = j0 + m;
    j2 = j1 + m;
    j3 = j2 + m;
    x0r = a[j0 - 2] + a[j2 - 2];
    x0i = -a[j0 - 1] - a[j2 - 1];
    x1r = a[j0 - 2] - a[j2 - 2];
    x1i = -a[j0 - 1] + a[j2 - 1];
    x2r = a[j1 - 2] + a[j3 - 2];
    x2i = a[j1 - 1] + a[j3 - 1];
    x3r = a[j1 - 2] - a[j3 - 2];
    x3i = a[j1 - 1] - a[j3 - 1];
    a[j0 - 2] = x0r + x2r;
    a[j0 - 1] = x0i - x2i;
    a[j1 - 2] = x0r - x2r;
    a[j1 - 1] = x0i + x2i;
    x0r = x1r + x3i;
    x0i = x1i + x3r;
    a[j2 - 2] = wk1r * x0r - wk1i * x0i;
    a[j2 - 1] = wk1r * x0i + wk1i * x0r;
    x0r = x1r - x3i;
    x0i = x1i - x3r;
    a[j3 - 2] = wk3r * x0r + wk3i * x0i;
    a[j3 - 1] = wk3r * x0i - wk3i * x0r;
    x0r = a[j0] + a[j2];
    x0i = -a[j0 + 1] - a[j2 + 1];
    x1r = a[j0] - a[j2];
    x1i = -a[j0 + 1] + a[j2 + 1];
    x2r = a[j1] + a[j3];
    x2i = a[j1 + 1] + a[j3 + 1];
    x3r = a[j1] - a[j3];
    x3i = a[j1 + 1] - a[j3 + 1];
    a[j0] = x0r + x2r;
    a[j0 + 1] = x0i - x2i;
    a[j1] = x0r - x2r;
    a[j1 + 1] = x0i + x2i;
    x0r = x1r + x3i;
    x0i = x1i + x3r;
    a[j2] = wn4r * (x0r - x0i);
    a[j2 + 1] = wn4r * (x0i + x0r);
    x0r = x1r - x3i;
    x0i = x1i - x3r;
    a[j3] = -wn4r * (x0r + x0i);
    a[j3 + 1] = -wn4r * (x0i - x0r);
    x0r = a[j0 + 2] + a[j2 + 2];
    x0i = -a[j0 + 3] - a[j2 + 3];
    x1r = a[j0 + 2] - a[j2 + 2];
    x1i = -a[j0 + 3] + a[j2 + 3];
    x2r = a[j1 + 2] + a[j3 + 2];
    x2i = a[j1 + 3] + a[j3 + 3];
    x3r = a[j1 + 2] - a[j3 + 2];
    x3i = a[j1 + 3] - a[j3 + 3];
    a[j0 + 2] = x0r + x2r;
    a[j0 + 3] = x0i - x2i;
    a[j1 + 2] = x0r - x2r;
    a[j1 + 3] = x0i + x2i;
    x0r = x1r + x3i;
    x0i = x1i + x3r;
    a[j2 + 2] = wk1i * x0r - wk1r * x0i;
    a[j2 + 3] = wk1i * x0i + wk1r * x0r;
    x0r = x1r - x3i;
    x0i = x1i - x3r;
    a[j3 + 2] = wk3i * x0r + wk3r * x0i;
    a[j3 + 3] = wk3i * x0i - wk3r * x0r;
  }

  static void rftfsub(int n, double* a, int nc, double* c)
  {
    int j, k, kk, ks, m;
    double wkr, wki, xr, xi, yr, yi;

    m = n >> 1;
    ks = 2 * nc / m;
    kk = 0;
    for (j = 2; j < m; j += 2) {
      k = n - j;
      kk += ks;
      wkr = 0.5 - c[nc - kk];
      wki = c[kk];
      xr = a[j] - a[k];
      xi = a[j + 1] + a[k + 1];
      yr = wkr * xr - wki * xi;
      yi = wkr * xi + wki * xr;
      a[j] -= yr;
      a[j + 1] -= yi;
      a[k] += yr;
      a[k + 1] -= yi;
    }
  }

  static void rftbsub(int n, double* a, int nc, double* c)
  {
    int j, k, kk, ks, m;
    double wkr, wki, xr, xi, yr, yi;

    m = n >> 1;
    ks = 2 * nc / m;
    kk = 0;
    for (j = 2; j < m; j += 2) {
      k = n - j;
      kk += ks;
      wkr = 0.5 - c[nc - kk];
      wki = c[kk];
      xr = a[j] - a[k];
      xi = a[j + 1] + a[k + 1];
      yr = wkr * xr + wki * xi;
      yi = wkr * xi - wki * xr;
      a[j] -= yr;
      a[j + 1] -= yi;
      a[k] += yr;
      a[k + 1] -= yi;
    }
  }

  static void dctsub(int n, double* a, int nc, double* c)
  {
    int j, k, kk, ks, m;
    double wkr, wki, xr;

    m = n >> 1;
    ks = nc / n;
    kk = 0;
    for (j = 1; j < m; j++) {
      k = n - j;
      kk += ks;
      wkr = c[kk] - c[nc - kk];
      wki = c[kk] + c[nc - kk];
      xr = wki * a[j] - wkr * a[k];
      a[j] = wkr * a[j] + wki * a[k];
      a[k] = xr;
    }
    a[m] *= c[0];
  }

  static void dstsub(int n, double* a, int nc, double* c)
  {
    int j, k, kk, ks, m;
    double wkr, wki, xr;

    m = n >> 1;
    ks = nc / n;
    kk = 0;
    for (j = 1; j < m; j++) {
      k = n - j;
      kk += ks;
      wkr = c[kk] - c[nc - kk];
      wki = c[kk] + c[nc - kk];
      xr = wki * a[k] - wkr * a[j];
      a[k] = wkr * a[k] + wki * a[j];
      a[j] = xr;
    }
    a[m] *= c[0];
  }

  static void makeipt(int nw, int* ip)
  {
    int j, l, m, m2, p, q;

    ip[2] = 0;
    ip[3] = 16;
    m = 2;
    for (l = nw; l > 32; l >>= 2) {
      m2 = m << 1;
      q = m2 << 3;
      for (j = m; j < m2; j++) {
        p = ip[j] << 2;
        ip[m + j] = p;
        ip[m2 + j] = p + q;
      }
      m = m2;
    }
  }

  static void makect(int nc, int* ip, double* c)
  {
    int j, nch;
    double delta;

    ip[1] = nc;
    if (nc > 1) {
      nch = nc >> 1;
      delta = atan(1.0) / nch;
      c[0] = cos(delta * nch);
      c[nch] = 0.5 * c[0];
      for (j = 1; j < nch; j++) {
        c[j] = 0.5 * cos(delta * j);
        c[nc - j] = 0.5 * sin(delta * j);
      }
    }
  }

  static void makewt(int nw, int* ip, double* w)
  {
    int j, nwh, nw0, nw1;
    double delta, wn4r, wk1r, wk1i, wk3r, wk3i;

    ip[0] = nw;
    ip[1] = 1;
    if (nw > 2) {
      nwh = nw >> 1;
      delta = atan(1.0) / nwh;
      wn4r = cos(delta * nwh);
      w[0] = 1;
      w[1] = wn4r;
      if (nwh == 4) {
        w[2] = cos(delta * 2);
        w[3] = sin(delta * 2);
      }
      else if (nwh > 4) {
        makeipt(nw, ip);
        w[2] = 0.5 / cos(delta * 2);
        w[3] = 0.5 / cos(delta * 6);
        for (j = 4; j < nwh; j += 4) {
          w[j] = cos(delta * j);
          w[j + 1] = sin(delta * j);
          w[j + 2] = cos(3 * delta * j);
          w[j + 3] = -sin(3 * delta * j);
        }
      }
      nw0 = 0;
      while (nwh > 2) {
        nw1 = nw0 + nwh;
        nwh >>= 1;
        w[nw1] = 1;
        w[nw1 + 1] = wn4r;
        if (nwh == 4) {
          wk1r = w[nw0 + 4];
          wk1i = w[nw0 + 5];
          w[nw1 + 2] = wk1r;
          w[nw1 + 3] = wk1i;
        }
        else if (nwh > 4) {
          wk1r = w[nw0 + 4];
          wk3r = w[nw0 + 6];
          w[nw1 + 2] = 0.5 / wk1r;
          w[nw1 + 3] = 0.5 / wk3r;
          for (j = 4; j < nwh; j += 4) {
            wk1r = w[nw0 + 2 * j];
            wk1i = w[nw0 + 2 * j + 1];
            wk3r = w[nw0 + 2 * j + 2];
            wk3i = w[nw0 + 2 * j + 3];
            w[nw1 + j] = wk1r;
            w[nw1 + j + 1] = wk1i;
            w[nw1 + j + 2] = wk3r;
            w[nw1 + j + 3] = wk3i;
          }
        }
        nw0 = nw1;
      }
    }
  }

  static int cfttree(int n, int j, int k, double* a, int nw, double* w)
  {
    int i, isplt, m;

    if ((k & 3) != 0) {
      isplt = k & 1;
      if (isplt != 0) {
        cftmdl1(n, &a[j - n], &w[nw - (n >> 1)]);
      }
      else {
        cftmdl2(n, &a[j - n], &w[nw - n]);
      }
    }
    else {
      m = n;
      for (i = k; (i & 3) == 0; i >>= 2) {
        m <<= 2;
      }
      isplt = i & 1;
      if (isplt != 0) {
        while (m > 128) {
          cftmdl1(m, &a[j - m], &w[nw - (m >> 1)]);
          m >>= 2;
        }
      }
      else {
        while (m > 128) {
          cftmdl2(m, &a[j - m], &w[nw - m]);
          m >>= 2;
        }
      }
    }
    return isplt;
  }

  static void cftleaf(int n, int isplt, double* a, int nw, double* w)
  {
    if (n == 512) {
      cftmdl1(128, a, &w[nw - 64]);
      cftf161(a, &w[nw - 8]);
      cftf162(&a[32], &w[nw - 32]);
      cftf161(&a[64], &w[nw - 8]);
      cftf161(&a[96], &w[nw - 8]);
      cftmdl2(128, &a[128], &w[nw - 128]);
      cftf161(&a[128], &w[nw - 8]);
      cftf162(&a[160], &w[nw - 32]);
      cftf161(&a[192], &w[nw - 8]);
      cftf162(&a[224], &w[nw - 32]);
      cftmdl1(128, &a[256], &w[nw - 64]);
      cftf161(&a[256], &w[nw - 8]);
      cftf162(&a[288], &w[nw - 32]);
      cftf161(&a[320], &w[nw - 8]);
      cftf161(&a[352], &w[nw - 8]);
      if (isplt != 0) {
        cftmdl1(128, &a[384], &w[nw - 64]);
        cftf161(&a[480], &w[nw - 8]);
      }
      else {
        cftmdl2(128, &a[384], &w[nw - 128]);
        cftf162(&a[480], &w[nw - 32]);
      }
      cftf161(&a[384], &w[nw - 8]);
      cftf162(&a[416], &w[nw - 32]);
      cftf161(&a[448], &w[nw - 8]);
    }
    else {
      cftmdl1(64, a, &w[nw - 32]);
      cftf081(a, &w[nw - 8]);
      cftf082(&a[16], &w[nw - 8]);
      cftf081(&a[32], &w[nw - 8]);
      cftf081(&a[48], &w[nw - 8]);
      cftmdl2(64, &a[64], &w[nw - 64]);
      cftf081(&a[64], &w[nw - 8]);
      cftf082(&a[80], &w[nw - 8]);
      cftf081(&a[96], &w[nw - 8]);
      cftf082(&a[112], &w[nw - 8]);
      cftmdl1(64, &a[128], &w[nw - 32]);
      cftf081(&a[128], &w[nw - 8]);
      cftf082(&a[144], &w[nw - 8]);
      cftf081(&a[160], &w[nw - 8]);
      cftf081(&a[176], &w[nw - 8]);
      if (isplt != 0) {
        cftmdl1(64, &a[192], &w[nw - 32]);
        cftf081(&a[240], &w[nw - 8]);
      }
      else {
        cftmdl2(64, &a[192], &w[nw - 64]);
        cftf082(&a[240], &w[nw - 8]);
      }
      cftf081(&a[192], &w[nw - 8]);
      cftf082(&a[208], &w[nw - 8]);
      cftf081(&a[224], &w[nw - 8]);
    }
  }

  static void cftrec4(int n, double* a, int nw, double* w)
  {
    int isplt, j, k, m;

    m = n;
    while (m > 512) {
      m >>= 2;
      cftmdl1(m, &a[n - m], &w[nw - (m >> 1)]);
    }
    cftleaf(m, 1, &a[n - m], nw, w);
    k = 0;
    for (j = n - m; j > 0; j -= m) {
      k++;
      isplt = cfttree(m, j, k, a, nw, w);
      cftleaf(m, isplt, &a[j - m], nw, w);
    }
  }

  static void cftfx41(int n, double* a, int nw, double* w)
  {
    if (n == 128) {
      cftf161(a, &w[nw - 8]);
      cftf162(&a[32], &w[nw - 32]);
      cftf161(&a[64], &w[nw - 8]);
      cftf161(&a[96], &w[nw - 8]);
    }
    else {
      cftf081(a, &w[nw - 8]);
      cftf082(&a[16], &w[nw - 8]);
      cftf081(&a[32], &w[nw - 8]);
      cftf081(&a[48], &w[nw - 8]);
    }
  }

  static void cftbsub(int n, double* a, int* ip, int nw, double* w)
  {
    if (n > 8) {
      if (n > 32) {
        cftb1st(n, a, &w[nw - (n >> 2)]);
        if (n > 512) {
          cftrec4(n, a, nw, w);
        }
        else if (n > 128) {
          cftleaf(n, 1, a, nw, w);
        }
        else {
          cftfx41(n, a, nw, w);
        }
        bitrv2conj(n, ip, a);
      }
      else if (n == 32) {
        cftf161(a, &w[nw - 8]);
        bitrv216neg(a);
      }
      else {
        cftf081(a, w);
        bitrv208neg(a);
      }
    }
    else if (n == 8) {
      cftb040(a);
    }
    else if (n == 4) {
      cftx020(a);
    }
  }

  static void cftfsub(int n, double* a, int* ip, int nw, double* w)
  {
    if (n > 8) {
      if (n > 32) {
        cftf1st(n, a, &w[nw - (n >> 2)]);
        if (n > 512) {
          cftrec4(n, a, nw, w);
        }
        else if (n > 128) {
          cftleaf(n, 1, a, nw, w);
        }
        else {
          cftfx41(n, a, nw, w);
        }
        bitrv2(n, ip, a);
      }
      else if (n == 32) {
        cftf161(a, &w[nw - 8]);
        bitrv216(a);
      }
      else {
        cftf081(a, w);
        bitrv208(a);
      }
    }
    else if (n == 8) {
      cftf040(a);
    }
    else if (n == 4) {
      cftx020(a);
    }
  }

  static void dfct(int n, double* a, double* t, int* ip, double* w)
  {
    int j, k, l, m, mh, nw, nc;
    double xr, xi, yr, yi;

    nw = ip[0];
    if (n > (nw << 3)) {
      nw = n >> 3;
      makewt(nw, ip, w);
    }
    nc = ip[1];
    if (n > (nc << 1)) {
      nc = n >> 1;
      makect(nc, ip, w + nw);
    }
    m = n >> 1;
    yi = a[m];
    xi = a[0] + a[n];
    a[0] -= a[n];
    t[0] = xi - yi;
    t[m] = xi + yi;
    if (n > 2) {
      mh = m >> 1;
      for (j = 1; j < mh; j++) {
        k = m - j;
        xr = a[j] - a[n - j];
        xi = a[j] + a[n - j];
        yr = a[k] - a[n - k];
        yi = a[k] + a[n - k];
        a[j] = xr;
        a[k] = yr;
        t[j] = xi - yi;
        t[k] = xi + yi;
      }
      t[mh] = a[mh] + a[n - mh];
      a[mh] -= a[n - mh];
      dctsub(m, a, nc, w + nw);
      if (m > 4) {
        cftfsub(m, a, ip, nw, w);
        rftfsub(m, a, nc, w + nw);
      }
      else if (m == 4) {
        cftfsub(m, a, ip, nw, w);
      }
      a[n - 1] = a[0] - a[1];
      a[1] = a[0] + a[1];
      for (j = m - 2; j >= 2; j -= 2) {
        a[2 * j + 1] = a[j] + a[j + 1];
        a[2 * j - 1] = a[j] - a[j + 1];
      }
      l = 2;
      m = mh;
      while (m >= 2) {
        dctsub(m, t, nc, w + nw);
        if (m > 4) {
          cftfsub(m, t, ip, nw, w);
          rftfsub(m, t, nc, w + nw);
        }
        else if (m == 4) {
          cftfsub(m, t, ip, nw, w);
        }
        a[n - l] = t[0] - t[1];
        a[l] = t[0] + t[1];
        k = 0;
        for (j = 2; j < m; j += 2) {
          k += l << 2;
          a[k - l] = t[j] - t[j + 1];
          a[k + l] = t[j] + t[j + 1];
        }
        l <<= 1;
        mh = m >> 1;
        for (j = 0; j < mh; j++) {
          k = m - j;
          t[j] = t[m + k] - t[m + j];
          t[k] = t[m + k] + t[m + j];
        }
        t[mh] = t[m + mh];
        m = mh;
      }
      a[l] = t[0];
      a[n] = t[2] - t[1];
      a[0] = t[2] + t[1];
    }
    else {
      a[1] = a[0];
      a[2] = t[0];
      a[0] = t[1];
    }
  }

  static void cdft(int n, int isgn, double* a, int* ip, double* w)
  {
    int nw;

    nw = ip[0];
    if (n > (nw << 2)) {
      nw = n >> 2;
      makewt(nw, ip, w);
    }
    if (isgn >= 0) {
      cftfsub(n, a, ip, nw, w);
    }
    else {
      cftbsub(n, a, ip, nw, w);
    }
  }

  static void ddct(int n, int isgn, double* a, int* ip, double* w)
  {
    int j, nw, nc;
    double xr;

    nw = ip[0];
    if (n > (nw << 2)) {
      nw = n >> 2;
      makewt(nw, ip, w);
    }
    nc = ip[1];
    if (n > nc) {
      nc = n;
      makect(nc, ip, w + nw);
    }
    if (isgn < 0) {
      xr = a[n - 1];
      for (j = n - 2; j >= 2; j -= 2) {
        a[j + 1] = a[j] - a[j - 1];
        a[j] += a[j - 1];
      }
      a[1] = a[0] - xr;
      a[0] += xr;
      if (n > 4) {
        rftbsub(n, a, nc, w + nw);
        cftbsub(n, a, ip, nw, w);
      }
      else if (n == 4) {
        cftbsub(n, a, ip, nw, w);
      }
    }
    dctsub(n, a, nc, w + nw);
    if (isgn >= 0) {
      if (n > 4) {
        cftfsub(n, a, ip, nw, w);
        rftfsub(n, a, nc, w + nw);
      }
      else if (n == 4) {
        cftfsub(n, a, ip, nw, w);
      }
      xr = a[0] - a[1];
      a[0] += a[1];
      for (j = 2; j < n; j += 2) {
        a[j - 1] = a[j] - a[j + 1];
        a[j] += a[j + 1];
      }
      a[n - 1] = xr;
    }
  }

  static void ddst(int n, int isgn, double* a, int* ip, double* w)
  {
    int j, nw, nc;
    double xr;

    nw = ip[0];
    if (n > (nw << 2)) {
      nw = n >> 2;
      makewt(nw, ip, w);
    }
    nc = ip[1];
    if (n > nc) {
      nc = n;
      makect(nc, ip, w + nw);
    }
    if (isgn < 0) {
      xr = a[n - 1];
      for (j = n - 2; j >= 2; j -= 2) {
        a[j + 1] = -a[j] - a[j - 1];
        a[j] -= a[j - 1];
      }
      a[1] = a[0] + xr;
      a[0] -= xr;
      if (n > 4) {
        rftbsub(n, a, nc, w + nw);
        cftbsub(n, a, ip, nw, w);
      }
      else if (n == 4) {
        cftbsub(n, a, ip, nw, w);
      }
    }
    dstsub(n, a, nc, w + nw);
    if (isgn >= 0) {
      if (n > 4) {
        cftfsub(n, a, ip, nw, w);
        rftfsub(n, a, nc, w + nw);
      }
      else if (n == 4) {
        cftfsub(n, a, ip, nw, w);
      }
      xr = a[0] - a[1];
      a[0] += a[1];
      for (j = 2; j < n; j += 2) {
        a[j - 1] = -a[j] - a[j + 1];
        a[j] -= a[j + 1];
      }
      a[n - 1] = -xr;
    }
  }

  static void dfst(int n, double* a, double* t, int* ip, double* w)
  {
    int j, k, l, m, mh, nw, nc;
    double xr, xi, yr, yi;

    nw = ip[0];
    if (n > (nw << 3)) {
      nw = n >> 3;
      makewt(nw, ip, w);
    }
    nc = ip[1];
    if (n > (nc << 1)) {
      nc = n >> 1;
      makect(nc, ip, w + nw);
    }
    if (n > 2) {
      m = n >> 1;
      mh = m >> 1;
      for (j = 1; j < mh; j++) {
        k = m - j;
        xr = a[j] + a[n - j];
        xi = a[j] - a[n - j];
        yr = a[k] + a[n - k];
        yi = a[k] - a[n - k];
        a[j] = xr;
        a[k] = yr;
        t[j] = xi + yi;
        t[k] = xi - yi;
      }
      t[0] = a[mh] - a[n - mh];
      a[mh] += a[n - mh];
      a[0] = a[m];
      dstsub(m, a, nc, w + nw);
      if (m > 4) {
        cftfsub(m, a, ip, nw, w);
        rftfsub(m, a, nc, w + nw);
      }
      else if (m == 4) {
        cftfsub(m, a, ip, nw, w);
      }
      a[n - 1] = a[1] - a[0];
      a[1] = a[0] + a[1];
      for (j = m - 2; j >= 2; j -= 2) {
        a[2 * j + 1] = a[j] - a[j + 1];
        a[2 * j - 1] = -a[j] - a[j + 1];
      }
      l = 2;
      m = mh;
      while (m >= 2) {
        dstsub(m, t, nc, w + nw);
        if (m > 4) {
          cftfsub(m, t, ip, nw, w);
          rftfsub(m, t, nc, w + nw);
        }
        else if (m == 4) {
          cftfsub(m, t, ip, nw, w);
        }
        a[n - l] = t[1] - t[0];
        a[l] = t[0] + t[1];
        k = 0;
        for (j = 2; j < m; j += 2) {
          k += l << 2;
          a[k - l] = -t[j] - t[j + 1];
          a[k + l] = t[j] - t[j + 1];
        }
        l <<= 1;
        mh = m >> 1;
        for (j = 1; j < mh; j++) {
          k = m - j;
          t[j] = t[m + k] + t[m + j];
          t[k] = t[m + k] - t[m + j];
        }
        t[0] = t[m + mh];
        m = mh;
      }
      a[l] = t[0];
    }
    a[0] = 0;
  }


  void rdft(int n, int isgn, double* a, int* ip, double* w)
  {
    int nw, nc;
    double xi;

    nw = ip[0];
    if (n > (nw << 2)) {
      nw = n >> 2;
      makewt(nw, ip, w);
    }
    nc = ip[1];
    if (n > (nc << 2)) {
      nc = n >> 2;
      makect(nc, ip, w + nw);
    }
    if (isgn >= 0) {
      if (n > 4) {
        cftfsub(n, a, ip, nw, w);
        rftfsub(n, a, nc, w + nw);
      }
      else if (n == 4) {
        cftfsub(n, a, ip, nw, w);
      }
      xi = a[0] - a[1];
      a[0] += a[1];
      a[1] = xi;
    }
    else {
      a[1] = 0.5 * (a[0] - a[1]);
      a[0] -= a[1];
      if (n > 4) {
        rftbsub(n, a, nc, w + nw);
        cftbsub(n, a, ip, nw, w);
      }
      else if (n == 4) {
        cftbsub(n, a, ip, nw, w);
      }
    }
  }
}





#define BEAT_HISTORY_LENGTH 80
#define FRAND ((rand() % 7381)/7380.0f)
#define projectM_isnan(x) ((x) != (x))
#define FFT_LENGTH 1024
#define FCGSX 32 // final composite gridsize - # verts - should be EVEN.
#define FCGSY 24 // final composite gridsize - # verts - should be EVEN.
#define NOISE_INTERNAL_DATA_FORMAT GL_RGB
#define NUM_BLUR_TEX 6
#define PROJECTM_SUCCESS 1
#define PROJECTM_FAILURE -1
#define PROJECTM_ERROR -1 /* non specific error */
#define PROJECTM_OUTOFMEM_ERROR -7; /* out of memory */
#define PROJECTM_PARSE_ERROR -11
#define MAX_TOKEN_SIZE 512
#define STRING_LINE_SIZE 1024
#define PARSE_DEBUG 0
#define MILKDROP_PRESET_DEBUG 0
#define P_FLAG_NONE 0
#define P_FLAG_READONLY 1
#define P_FLAG_USERDEF (1 << 1)
#define P_FLAG_QVAR (1 << 2)
#define MAX_DOUBLE_SIZE  10000000.0
#define MIN_DOUBLE_SIZE -10000000.0
#define MAX_INT_SIZE 10000000
#define PER_PIXEL_EQN_DEBUG 0
#define EVAL_ERROR -1
#define MAX_SAMPLE_SIZE 4096
#define CUSTOM_WAVE_DEBUG 0
#define GL_TRANSITION // stuff that needs to be ported to newer GL calls


#define DISABLE_NATIVE_PRESETS

static const bool BUILTIN_PARAMS_DEBUG = false;
static const int AUTO_CREATE = 1;
static const int NO_CREATE = 0;

typedef std::vector<int> RatingList;

static const unsigned int NUM_Q_VARIABLES = 32;
static const int kMaxSwitchRetries = 10;



template <class TraverseFunctor, class Container>
static void traverse(Container& container)
{
  TraverseFunctor functor;

  for (typename Container::iterator pos = container.begin(); pos != container.end(); ++pos)
  {
    assert(pos->second);
    functor(pos->second);
  }
}


template <class TraverseFunctor, class Container>
static void traverseVector(Container& container)
{
  TraverseFunctor functor;

  for (typename Container::iterator pos = container.begin(); pos != container.end(); ++pos)
  {
    assert(*pos);
    functor(*pos);
  }

}

template <class TraverseFunctor, class Container>
static void traverse(Container& container, TraverseFunctor& functor)
{

  for (typename Container::iterator pos = container.begin(); pos != container.end(); ++pos)
  {
    assert(pos->second);
    functor(pos->second);
  }

}

namespace TraverseFunctors
{
  template <class Data>
  struct Delete
  {
    void operator() (Data* data)
    {
      assert(data);
      delete(data);
    }
  };
}

static u32 getTicks()
{
#ifdef PLATFORM_WINDOWS
  return GetTickCount();
#endif // PLATFORM_WINDOWS

#ifdef PLATFORM_OPENXR_ANDROID
  struct timespec ts;
  unsigned theTick = 0U;
  clock_gettime(CLOCK_REALTIME, &ts);
  theTick = ts.tv_nsec / 1000000;
  theTick += ts.tv_sec * 1000;
  return theTick;
#endif // PLATFORM_OPENXR_ANDROID

#ifdef SHR3D_WINDOW_SDL
  return SDL_GetTicks();
#endif // SHR3D_WINDOW_SDL

  ASSERT(false);
  return 0;
}

static u32 getTicks(u32 start)
{
  return getTicks() - start;
}

static std::string parseExtension(const std::string& filename) {

  const std::size_t start = filename.find_last_of('.');

  if (start == std::string::npos || start >= (filename.length() - 1))
    return "";
  std::string ext = filename.substr(start + 1, filename.length());
  std::transform(ext.begin(), ext.end(), ext.begin(), tolower);
  return ext;
}

static std::string parseFilename(const std::string& filename) {

  const std::size_t start = filename.find_last_of('/');

  if (start == std::string::npos || start >= (filename.length() - 1))
    return "";
  else
    return filename.substr(start + 1, filename.length());
}

static void* wipe_aligned_alloc(size_t align, size_t size)
{
  void* mem = nullptr;

#if HAVE_ALIGNED_ALLOC==1 && !__APPLE__

  mem = aligned_alloc(align, size);

#elif HAVE_POSIX_MEMALIGN==1

  if (posix_memalign(&mem, align, size))
    mem = nullptr;

#else

  // only support powers of 2 for align
  assert((align & (align - 1)) == 0);
  assert((size % align) == 0);
  void* allocated = malloc(size + align - 1 + sizeof(void*));
  if (allocated)
  {
    mem = (void*)(((size_t)allocated + sizeof(void*) + align - 1) & ~(align - 1));
    ((void**)mem)[-1] = allocated;
  }

#endif

  if (mem)
  {
    memset(mem, 0, size);
  }
  else
  {
    printf("wipe_aligned_alloc() failed to allocate %d bytes\n", (int)size);
  }
  return mem;
}

static void* wipe_aligned_alloc(size_t count)
{
  return wipe_aligned_alloc(16, count);
}

static void* wipemalloc(size_t count)
{
  void* mem = malloc(count);
  if (mem != nullptr)
  {
    memset(mem, 0, count);
  }
  else
  {
    printf("wipemalloc() failed to allocate %d bytes\n", (int)count);
  }
  return mem;
}

static float** alloc_mesh(size_t gx, size_t gy)
{
  // round gy up to multiple 4 (for possible SSE optimization)
  gy = (gy + 3) & ~(size_t)3;

  float** mesh = (float**)wipe_aligned_alloc(gx * sizeof(float*));
  float* m = (float*)wipe_aligned_alloc(gx * gy * sizeof(float));
  for (unsigned int x = 0; x < gx; x++)
    mesh[x] = m + (gy * x);
  return mesh;
}

static void init_mesh(float** mesh, const float value, const int gx, const int gy)
{
  for (int x = 0; x < gx; x++)
    for (int y = 0; y < gy; y++)
      mesh[x][y] = value;
}

enum PresetRatingType {
  FIRST_RATING_TYPE = 0,
  HARD_CUT_RATING_TYPE = FIRST_RATING_TYPE,
  SOFT_CUT_RATING_TYPE,
  LAST_RATING_TYPE = SOFT_CUT_RATING_TYPE,
  TOTAL_RATING_TYPES = SOFT_CUT_RATING_TYPE + 1
};

enum MilkdropWaveformMode
{
  Circle = 0, RadialBlob, Blob2, Blob3, DerivativeLine, Blob5, Line, DoubleLine,
  last // last is a placeholder to find enum size. Please ignore and leave this here.
};

enum ExprClass
{
  TREE, CONSTANT, PARAMETER, FUNCTION, ASSIGN, PROGRAM, JIT, OTHER
};

typedef enum {
  UNSET_LINE_MODE,
  PER_FRAME_LINE_MODE,
  PER_PIXEL_LINE_MODE,
  PER_FRAME_INIT_LINE_MODE,
  INIT_COND_LINE_MODE,
  CUSTOM_WAVE_PER_POINT_LINE_MODE,
  CUSTOM_WAVE_PER_FRAME_LINE_MODE,
  CUSTOM_WAVE_WAVECODE_LINE_MODE,
  CUSTOM_SHAPE_SHAPECODE_LINE_MODE,
  CUSTOM_SHAPE_PER_FRAME_LINE_MODE,
  CUSTOM_SHAPE_PER_FRAME_INIT_LINE_MODE,
  CUSTOM_WAVE_PER_FRAME_INIT_LINE_MODE
} line_mode_t;

typedef enum {
  tEOL,   /* end of a line, usually a '/n' or '/r' */
  tEOF,   /* end of file */
  tLPr,   /* ( */
  tRPr,   /* ) */
  tLBr,   /* [ */
  tRBr,   /* ] */
  tEq,    /* = */
  tPlus,  /* + */
  tMinus, /* - */
  tMult,  /* * */
  tMod,   /* % */
  tDiv,   /* / */
  tOr,    /* | */
  tAnd,   /* & */
  tComma, /* , */
  tPositive, /* + as a prefix operator */
  tNegative, /* - as a prefix operator */
  tSemiColon, /* ; */
  tStringTooLong, /* special token to indicate an invalid string length */
  tStringBufferFilled /* the string buffer for this line is maxed out */
} token_t;

struct PipelineContext
{
  int fps;
  float time;
  float presetStartTime;
  int frame;
  float progress;
};

struct Sampler
{
  GLuint samplerID;
  GLint wrap_mode;
  GLint filter_mode;

  Sampler(const GLint _wrap_mode, const GLint _filter_mode) : wrap_mode(_wrap_mode), filter_mode(_filter_mode)
  {
    glGenSamplers(1, &samplerID);
    glSamplerParameteri(samplerID, GL_TEXTURE_MIN_FILTER, _filter_mode);
    glSamplerParameteri(samplerID, GL_TEXTURE_MAG_FILTER, _filter_mode);
    glSamplerParameteri(samplerID, GL_TEXTURE_WRAP_S, _wrap_mode);
    glSamplerParameteri(samplerID, GL_TEXTURE_WRAP_T, _wrap_mode);
  }
};

struct Texture_
{
  GLuint texID;
  GLenum type;

  int width;
  int height;
  std::string name;
  bool userTexture;
  std::vector<Sampler*> samplers;

  Texture_(const std::string& _name, const GLuint _texID, const GLenum _type, const int _width, const int _height, const bool _userTexture) :
    texID(_texID),
    type(_type),
    name(_name),
    width(_width),
    height(_height),
    userTexture(_userTexture)
  {
  }

  Texture_(const std::string& _name, const int _width, const int _height, const bool _userTexture) :
    type(GL_TEXTURE_2D),
    name(_name),
    width(_width),
    height(_height),
    userTexture(_userTexture)
  {
    glGenTextures(1, &texID);
    glBindTexture(GL_TEXTURE_2D, texID);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, _width, _height, 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr);
    glBindTexture(GL_TEXTURE_2D, 0);
  }

  Sampler* getSampler(const GLint _wrap_mode, const GLint _filter_mode)
  {
    for (std::vector<Sampler*>::const_iterator iter = samplers.begin(); iter != samplers.end(); iter++)
    {
      if ((*iter)->wrap_mode == _wrap_mode && (*iter)->filter_mode == _filter_mode)
      {
        return *iter;
      }
    }

    // Sampler not found -> adding it
    Sampler* sampler = new Sampler(_wrap_mode, _filter_mode);
    samplers.push_back(sampler);

    return sampler;
  }
};

typedef std::pair<Texture_*, Sampler*> TextureSamplerDesc;
struct Shader__
{
  std::map<std::string, TextureSamplerDesc> textures;

  std::string programSource;
};

struct PixelPoint
{
  float x;
  float y;
};

struct PerPixelContext
{
  float x = 0.0f;
  float y = 0.0f;
  float rad = 0.0f;
  float theta = 0.0f;
  int i = 0;
  int j = 0;
};

struct PCM
{
  float** PCMd;
  int start;
  float waveSmoothing;
  int* ip;
  double* w;
  int newsamples;
  int numsamples; //size of new PCM info
  float* pcmdataL;     //holder for most recent pcm data
  float* pcmdataR;     //holder for most recent pcm data
  float vdataL[FFT_LENGTH];  //holders for FFT data (spectrum)
  float vdataR[FFT_LENGTH];

  PCM()
  {
    _initPCM(2048);
  }

  ~PCM()
  {
    free(pcmdataL);
    free(pcmdataR);
    free(w);
    free(ip);

    free(PCMd[0]);
    free(PCMd[1]);
    free(PCMd);
  }

  void _initPCM(int samples)
  {
    int i;

    waveSmoothing = 0;

    //Allocate memory for PCM data buffer
    assert(samples == 2048);
    PCMd = (float**)wipemalloc(2 * sizeof(float*));
    PCMd[0] = (float*)wipemalloc(samples * sizeof(float));
    PCMd[1] = (float*)wipemalloc(samples * sizeof(float));

    //maxsamples=samples;
    newsamples = 0;
    numsamples = Const::audioMaximumPossibleBlockSize;

    //Initialize buffers to 0
    for (i = 0; i < samples; i++)
    {
      PCMd[0][i] = 0;
      PCMd[1][i] = 0;
    }

    start = 0;

    //Allocate FFT workspace
    // per rdft() documentation
    //    length of ip >= 2+sqrt(n) and length of w == n/2
#if FFT_LENGTH > 1024
#error update this code
#endif
    w = (double*)wipemalloc(FFT_LENGTH / 2 * sizeof(double));
    ip = (int*)wipemalloc(34 * sizeof(int));
    ip[0] = 0;

    /** PCM data */
//    this->maxsamples = 2048;
//    this->numsamples = 0;
//    this->pcmdataL = nullptr;
//    this->pcmdataR = nullptr;

    /** Allocate PCM data structures */
    pcmdataL = (float*)wipemalloc(Const::audioMaximumPossibleBlockSize * sizeof(float));
    pcmdataR = (float*)wipemalloc(Const::audioMaximumPossibleBlockSize * sizeof(float));
  }

  void getPCM(float* PCMdata, int samples, int channel, int freq, float smoothing, int derive)
  {
    if (smoothing == 0)
    {
      for (int i = 0; i < samples; i++)
      {
        int index = start - 1 - i;
        if (index < 0)
          index = Const::audioMaximumPossibleBlockSize + index;
        PCMdata[i] = PCMd[channel][index];
      }
    }
    else
    {
      int index = start - 1;

      if (index < 0)
        index = Const::audioMaximumPossibleBlockSize + index;

      PCMdata[0] = PCMd[channel][index];

      for (int i = 1; i < samples; i++)
      {
        index = start - 1 - i;
        if (index < 0)
          index = Const::audioMaximumPossibleBlockSize + index;
        PCMdata[i] = (1 - smoothing) * PCMd[channel][index] + smoothing * PCMdata[i - 1];
      }
    }

    //return derivative of PCM data
    if (derive)
    {
      for (int i = 0; i < samples - 1; i++)
      {
        PCMdata[i] = PCMdata[i] - PCMdata[i + 1];
      }
      PCMdata[samples - 1] = 0;
    }

    //return frequency data instead of PCM (perform FFT)

    if (freq)
    {
      // NOTE some presets set bSpectrum=1 and samples!=2^n, not sure what rdft() does with that
      assert(samples <= 1024);
      samples = std::min(1024, samples);
      double temppcm[1024];
      for (int i = 0; i < samples; i++)
      {
        temppcm[i] = (double)PCMdata[i];
      }
      FFTSG::rdft(samples, 1, temppcm, ip, w);
      for (int j = 0; j < samples; j++)
      {
        PCMdata[j] = (float)temppcm[j];
      }
    }
  }

  int getPCMnew(float* PCMdata, int channel, int freq, float smoothing, int derive, int reset)
  {
    int i, index;

    index = start - 1;

    if (index < 0) index = Const::audioMaximumPossibleBlockSize + index;

    PCMdata[0] = PCMd[channel][index];
    for (i = 1; i < newsamples; i++)
    {
      index = start - 1 - i;
      if (index < 0) index = Const::audioMaximumPossibleBlockSize + index;

      PCMdata[i] = (1 - smoothing) * PCMd[channel][index] + smoothing * PCMdata[i - 1];
    }

    //return derivative of PCM data
    if (derive)
    {
      for (i = 0; i < newsamples - 1; i++)
      {
        PCMdata[i] = PCMdata[i] - PCMdata[i + 1];
      }
      PCMdata[newsamples - 1] = 0;
    }

    //return frequency data instead of PCM (perform FFT)
    //   if (freq) rdft(samples, 1, PCMdata, ip, w);
    i = newsamples;
    if (reset)  newsamples = 0;

    return i;
  }

  void addPCMfloat(const f32* const inBlock[2], const i32 blockSize)
  {
    for (i32 i = 0; i < blockSize; i++)
    {
      i32 j = i + start;

      PCMd[0][j % Const::audioMaximumPossibleBlockSize] = inBlock[0][i];
      PCMd[1][j % Const::audioMaximumPossibleBlockSize] = inBlock[1][i];
    }

    start += blockSize;
    start = start % Const::audioMaximumPossibleBlockSize;

    newsamples += blockSize;
    if (newsamples > Const::audioMaximumPossibleBlockSize) newsamples = Const::audioMaximumPossibleBlockSize;
    numsamples = getPCMnew(pcmdataR, 1, 0, waveSmoothing, 0, 0);
    getPCMnew(pcmdataL, 0, 0, waveSmoothing, 0, 1);
    getPCM(vdataL, FFT_LENGTH, 0, 1, 0, 0);
    getPCM(vdataR, FFT_LENGTH, 1, 1, 0, 0);
  }
};

struct BeatDetect
{
  float treb;
  float mid;
  float bass;
  float vol_old;
  float beatSensitivity;
  float treb_att;
  float mid_att;
  float bass_att;
  float vol;
  float vol_att;
  PCM* pcm;

  int beat_buffer_pos;
  float bass_buffer[BEAT_HISTORY_LENGTH];
  float bass_history;
  float bass_instant;
  float mid_buffer[BEAT_HISTORY_LENGTH];
  float mid_history;
  float mid_instant;
  float treb_buffer[BEAT_HISTORY_LENGTH];
  float treb_history;
  float treb_instant;
  float vol_buffer[BEAT_HISTORY_LENGTH];
  float vol_history;
  float vol_instant;

  BeatDetect(PCM* _pcm)
  {
    this->pcm = _pcm;

    this->vol_instant = 0;
    this->vol_history = 0;
    for (unsigned y = 0; y < BEAT_HISTORY_LENGTH; y++)
      this->vol_buffer[y] = 0;

    this->beat_buffer_pos = 0;

    this->bass_instant = 0;
    this->bass_history = 0;
    for (unsigned y = 0; y < BEAT_HISTORY_LENGTH; y++)
      this->bass_buffer[y] = 0;

    this->mid_instant = 0;
    this->mid_history = 0;
    for (unsigned y = 0; y < BEAT_HISTORY_LENGTH; y++)
      this->mid_buffer[y] = 0;

    this->treb_instant = 0;
    this->treb_history = 0;
    for (unsigned y = 0; y < BEAT_HISTORY_LENGTH; y++)
      this->treb_buffer[y] = 0;

    this->treb = 0;
    this->mid = 0;
    this->bass = 0;
    this->vol_old = 0;
    this->beatSensitivity = 1.00;
    this->treb_att = 0;
    this->mid_att = 0;
    this->bass_att = 0;
    this->vol_att = 0;
    this->vol = 0;
  }

  void reset()
  {
    this->treb = 0;
    this->mid = 0;
    this->bass = 0;
    this->treb_att = 0;
    this->mid_att = 0;
    this->bass_att = 0;
    this->vol_att = 0;
    this->vol_old = 0;
    this->vol_instant = 0;
  }

  void getBeatVals(float samplerate, unsigned fft_length, float* vdataL, float* vdataR)
  {
    assert(512 == fft_length || 1024 == fft_length);    // should be power of 2, expect >= 512

    // TODO: compute ranges based on samplerate
    // roughly aiming or basee-mid cutoff of 220ish and mid-treb cutoff of 2000ish, if I did my math right
    unsigned ranges512[4] = { 0, 3 /* 3*441000/512 = =258 */, 23 /* 23*441000/512 = =1981 */ , 200 };
    unsigned ranges1024[4] = { 0, 5 /* 5*44100/1024 == 215 */, 46 /* 46*44100/1024 == 1981  */, 400 };
    unsigned* ranges = fft_length == 1024 ? ranges1024 : ranges512;

    bass_instant = 0;
    for (unsigned i = ranges[0] + 1; i <= ranges[1]; i++)
    {
      bass_instant += (vdataL[i * 2] * vdataL[i * 2]) + (vdataR[i * 2] * vdataR[i * 2]);
    }
    bass_instant *= 100.0 / (ranges[1] - ranges[0]);
    bass_history -= bass_buffer[beat_buffer_pos] * (1.0 / BEAT_HISTORY_LENGTH);
    bass_buffer[beat_buffer_pos] = bass_instant;
    bass_history += bass_instant * (1.0 / BEAT_HISTORY_LENGTH);

    mid_instant = 0;
    for (unsigned i = ranges[1] + 1; i <= ranges[2]; i++)
    {
      mid_instant += (vdataL[i * 2] * vdataL[i * 2]) + (vdataR[i * 2] * vdataR[i * 2]);
    }
    mid_instant *= 100.0 / (ranges[2] - ranges[1]);
    mid_history -= mid_buffer[beat_buffer_pos] * (1.0 / BEAT_HISTORY_LENGTH);
    mid_buffer[beat_buffer_pos] = mid_instant;
    mid_history += mid_instant * (1.0 / BEAT_HISTORY_LENGTH);

    treb_instant = 0;
    for (unsigned i = ranges[2] + 1; i <= ranges[3]; i++)
    {
      treb_instant += (vdataL[i * 2] * vdataL[i * 2]) + (vdataR[i * 2] * vdataR[i * 2]);
    }
    treb_instant *= 90.0 / (ranges[3] - ranges[2]);
    treb_history -= treb_buffer[beat_buffer_pos] * (1.0 / BEAT_HISTORY_LENGTH);
    treb_buffer[beat_buffer_pos] = treb_instant;
    treb_history += treb_instant * (1.0 / BEAT_HISTORY_LENGTH);

    vol_instant = (bass_instant + mid_instant + treb_instant) / 3.0f;
    vol_history -= (vol_buffer[beat_buffer_pos]) * (1.0 / BEAT_HISTORY_LENGTH);
    vol_buffer[beat_buffer_pos] = vol_instant;
    vol_history += vol_instant * (1.0 / BEAT_HISTORY_LENGTH);

    //    fprintf(stderr, "%6.3f %6.2f %6.3f\n", bass_history/vol_history, mid_history/vol_history, treb_history/vol_history);
    bass = bass_instant / fmax(0.0001, 1.3 * bass_history + 0.2 * vol_history);
    mid = mid_instant / fmax(0.0001, 1.3 * mid_history + 0.2 * vol_history);
    treb = treb_instant / fmax(0.0001, 1.3 * treb_history + 0.2 * vol_history);
    vol = vol_instant / fmax(0.0001, 1.5f * vol_history);

    if (projectM_isnan(treb)) {
      treb = 0.0;
    }
    if (projectM_isnan(mid)) {
      mid = 0.0;
    }
    if (projectM_isnan(bass)) {
      bass = 0.0;
    }

    treb_att = .6f * treb_att + .4f * treb;
    mid_att = .6f * mid_att + .4f * mid;
    bass_att = .6f * bass_att + .4f * bass;
    vol_att = .6f * vol_att + .4f * vol;

    // Use beat sensitivity as a multiplier
    // 0 is "dead"
    // 5 is pretty wild so above that doesn't make sense
    bass_att = bass_att * beatSensitivity;
    bass = bass * beatSensitivity;
    mid_att = mid_att * beatSensitivity;
    mid = mid * beatSensitivity;
    treb_att = treb_att * beatSensitivity;
    treb = treb * beatSensitivity;
    vol_att = vol_att * beatSensitivity;
    vol = vol * beatSensitivity;

    if (bass_att > 100) bass_att = 100;
    if (bass > 100) bass = 100;
    if (mid_att > 100) mid_att = 100;
    if (mid > 100) mid = 100;
    if (treb_att > 100) treb_att = 100;
    if (treb > 100) treb = 100;
    if (vol_att > 100) vol_att = 100;
    if (vol > 100) vol = 100;

    beat_buffer_pos++;
    if (beat_buffer_pos > 79)
      beat_buffer_pos = 0;
  }

  void detectFromSamples()
  {
    vol_old = vol;
    bass = 0;
    mid = 0;
    treb = 0;
    vol = 0;

    // TODO: get sample rate from PCM?  Assume 44100
    getBeatVals(44100.0f, FFT_LENGTH, pcm->pcmdataL, pcm->pcmdataR);
  }

  float getPCMScale()
  {
    // the constant here just depends on the particulars of getBeatVals(), the
    // range of vol_history, and what "looks right".
    // larger value means larger, more jagged waveform.

    // this is also impacted by beatSensitivity.
    return (1.5 / fmax(0.0001f, sqrtf(vol_history))) * beatSensitivity;
  }
};

typedef std::function<void(const std::string& path, const std::string& name)> ScanCallback;
struct FileScanner
{
  std::vector<std::string> _rootDirs;
  std::vector<std::string> _extensions;

  FileScanner()
  {}

  FileScanner(std::vector<std::string>& rootDirs, std::vector<std::string>& extensions) : _rootDirs(rootDirs), _extensions(extensions)
  {}

  void scan(ScanCallback cb)
  {
    for (const auto& dir : _rootDirs)
    {
      if (!File::directoryExists(reinterpret_cast<const char8_t*>(dir.c_str())))
        continue;

      const std::vector<std::u8string> filesInDir = File::filesInDirectory(reinterpret_cast<const char8_t*>(dir.c_str()));
      for (const std::u8string& filePath : filesInDir)
      {
        for (const std::string& extension : _extensions)
        {
          if (extension == reinterpret_cast<const char*>(File::extension(filePath.c_str())))
          {
            const std::string basename = TODOFIX(File::replaceExtension(File::filename(reinterpret_cast<const char8_t*>(filePath.c_str())), u8""));

            cb(TODOFIX(filePath), basename);
          }
        }
      }
    }
  }
};

struct PerlinNoise
{
  float noise_lq[256][256][3];
  float noise_lq_lite[32][32][3];
  float noise_mq[256][256][3];
  float noise_hq[256][256][3];
  float noise_lq_vol[32][32][32][3];
  float noise_hq_vol[32][32][32][3];

  static inline float noise(int x)
  {
    x = (x << 13) ^ x;
    return (((x * (x * x * 15731 + 789221) + 1376312589) & 0x7fffffff) / 2147483648.0);
  }

  static inline float noise(int x, int y)
  {
    int n = x + y * 57;
    return noise(n);
  }

  static inline float noise(int x, int y, int z)
  {
    int n = x + y * 57 + z * 141;
    return noise(n);
  }

  static inline float cubic_interp(float v0, float v1, float v2, float v3, float x)
  {
    float P = (v3 - v2) - (v0 - v1);
    float Q = (v0 - v1) - P;
    float R = v2 - v0;

    return P * pow(x, 3) + Q * pow(x, 2) + R * x + v1;
  }

  static inline float InterpolatedNoise(float x, float y)
  {
    int integer_X = int(x);
    float fractional_X = x - integer_X;

    int integer_Y = int(y);
    float fractional_Y = y - integer_Y;

    float a0 = noise(integer_X - 1, integer_Y - 1);
    float a1 = noise(integer_X, integer_Y - 1);
    float a2 = noise(integer_X + 1, integer_Y - 1);
    float a3 = noise(integer_X + 2, integer_Y - 1);

    float x0 = noise(integer_X - 1, integer_Y);
    float x1 = noise(integer_X, integer_Y);
    float x2 = noise(integer_X + 1, integer_Y);
    float x3 = noise(integer_X + 2, integer_Y);

    float y0 = noise(integer_X + 0, integer_Y + 1);
    float y1 = noise(integer_X, integer_Y + 1);
    float y2 = noise(integer_X + 1, integer_Y + 1);
    float y3 = noise(integer_X + 2, integer_Y + 1);

    float b0 = noise(integer_X - 1, integer_Y + 2);
    float b1 = noise(integer_X, integer_Y + 2);
    float b2 = noise(integer_X + 1, integer_Y + 2);
    float b3 = noise(integer_X + 2, integer_Y + 2);

    float i0 = cubic_interp(a0, a1, a2, a3, fractional_X);
    float i1 = cubic_interp(x0, x1, x2, x3, fractional_X);
    float i2 = cubic_interp(y0, y1, y2, y3, fractional_X);
    float i3 = cubic_interp(b0, b1, b2, b3, fractional_X);

    return cubic_interp(i0, i1, i2, i3, fractional_Y);

  }

  PerlinNoise()
  {
    for (int x = 0; x < 256; x++) {
      for (int y = 0; y < 256; y++) {
        noise_lq[x][y][0] = noise(x, y);
        noise_lq[x][y][1] = noise_lq[x][y][0];
        noise_lq[x][y][2] = noise_lq[x][y][0];
      }
    }

    for (int x = 0; x < 32; x++) {
      for (int y = 0; y < 32; y++) {
        noise_lq_lite[x][y][0] = noise(4 * x, 16 * y);
        noise_lq_lite[x][y][1] = noise_lq_lite[x][y][0];
        noise_lq_lite[x][y][2] = noise_lq_lite[x][y][0];
      }
    }

    for (int x = 0; x < 256; x++) {
      for (int y = 0; y < 256; y++) {
        noise_mq[x][y][0] = InterpolatedNoise((float)x / (float)2.0, (float)y / (float)2.0);
        noise_mq[x][y][1] = noise_mq[x][y][0];
        noise_mq[x][y][2] = noise_mq[x][y][0];
      }
    }

    for (int x = 0; x < 256; x++) {
      for (int y = 0; y < 256; y++) {
        noise_hq[x][y][0] = InterpolatedNoise((float)x / (float)3.0, (float)y / (float)3.0);
        noise_hq[x][y][1] = noise_hq[x][y][0];
        noise_hq[x][y][2] = noise_hq[x][y][0];
      }
    }

    for (int x = 0; x < 32; x++) {
      for (int y = 0; y < 32; y++) {
        for (int z = 0; z < 32; z++) {
          noise_lq_vol[x][y][z][0] = noise(x, y, z);
          noise_lq_vol[x][y][z][1] = noise_lq_vol[x][y][z][0];
          noise_lq_vol[x][y][z][2] = noise_lq_vol[x][y][z][0];
        }
      }
    }

    for (int x = 0; x < 32; x++) {
      for (int y = 0; y < 32; y++) {
        for (int z = 0; z < 32; z++) {
          noise_hq_vol[x][y][z][0] = noise(x, y, z);
          noise_hq_vol[x][y][z][1] = noise_hq_vol[x][y][z][0];
          noise_hq_vol[x][y][z][2] = noise_hq_vol[x][y][z][0];
        }
      }
    }
  }
};

struct TextureManager
{
  std::string presetsURL;
  std::map<std::string, Texture_*> textures;
  std::vector<Texture_*> blurTextures;
  Texture_* mainTexture;
  std::vector<std::string> random_textures;
  std::vector<std::string> extensions;

  void clearRandomTextures()
  {
    for (std::vector<std::string>::iterator pos = random_textures.begin(); pos != random_textures.end(); ++pos)
    {
      textures.erase(*pos);
    }
    random_textures.clear();

  }

  TextureSamplerDesc tryLoadingTexture(const std::string& name)
  {
    TextureSamplerDesc texDesc;
    GLint wrap_mode;
    GLint filter_mode;
    std::string unqualifiedName;

    ExtractTextureSettings(name, wrap_mode, filter_mode, unqualifiedName);

    for (const auto& ext : extensions)
    {
      std::string filename = unqualifiedName + ext;
      std::string fullURL = presetsURL + '\\' + filename;

      texDesc = loadTexture(fullURL, name);

      if (texDesc.first != nullptr)
      {
        std::cerr << "Located texture " << name << std::endl;
        break;
      }
    }

    std::cerr << "Failed to locate texture " << name << std::endl;

    return texDesc;
  }


  TextureSamplerDesc getRandomTextureName(std::string random_id)
  {
    GLint wrap_mode;
    GLint filter_mode;
    std::string unqualifiedName;

    ExtractTextureSettings(random_id, wrap_mode, filter_mode, unqualifiedName);

    std::vector<std::string> user_texture_names;
    size_t separator = unqualifiedName.find("_");
    std::string textureNameFilter;

    if (separator != std::string::npos)
    {
      textureNameFilter = unqualifiedName.substr(separator + 1);
      unqualifiedName = unqualifiedName.substr(0, separator);
    }

    for (std::map<std::string, Texture_*>::const_iterator iter = textures.begin(); iter != textures.end(); iter++)
    {
      if (iter->second->userTexture) {
        if (textureNameFilter.empty() || iter->first.find(textureNameFilter) == 0)
          user_texture_names.push_back(iter->first);
      }
    }

    if (user_texture_names.size() > 0)
    {
      std::string random_name = user_texture_names[rand() % user_texture_names.size()];
      random_textures.push_back(random_id);

      Texture_* randomTexture = new Texture_(*textures[random_name]);
      Sampler* sampler = randomTexture->getSampler(wrap_mode, filter_mode);
      randomTexture->name = unqualifiedName;
      textures[random_id] = randomTexture;

      return TextureSamplerDesc(randomTexture, sampler);
    }

    return TextureSamplerDesc(nullptr, nullptr);
  }

  TextureSamplerDesc getTexture(const std::string fullName, const GLenum defaultWrap, const GLenum defaultFilter)
  {
    std::string fileName = fullName;
    std::string unqualifiedName;
    GLint wrap_mode;
    GLint filter_mode;

    // Remove extension
    std::string lowerCaseFileName(fullName);
    std::transform(lowerCaseFileName.begin(), lowerCaseFileName.end(), lowerCaseFileName.begin(), tolower);
    for (auto ext : extensions)
    {
      size_t found = lowerCaseFileName.find(ext);
      if (found != std::string::npos)
      {
        fileName.replace(int(found), ext.size(), "");
        break;
      }
    }

    ExtractTextureSettings(fileName, wrap_mode, filter_mode, unqualifiedName);
    if (textures.find(unqualifiedName) == textures.end())
    {
      return TextureSamplerDesc(nullptr, nullptr);
    }

    if (fileName == unqualifiedName) {
      // Warp and filter mode not specified in sampler name
      // applying default
      wrap_mode = defaultWrap;
      filter_mode = defaultFilter;
    }

    Texture_* texture = textures[unqualifiedName];
    Sampler* sampler = texture->getSampler(wrap_mode, filter_mode);

    return TextureSamplerDesc(texture, sampler);
  }

  void ExtractTextureSettings(const std::string qualifiedName, GLint& _wrap_mode, GLint& _filter_mode, std::string& name)
  {
    std::string lowerQualifiedName(qualifiedName);
    std::transform(lowerQualifiedName.begin(), lowerQualifiedName.end(), lowerQualifiedName.begin(), tolower);

    _wrap_mode = GL_REPEAT;
    _filter_mode = GL_LINEAR;

    if (lowerQualifiedName.substr(0, 3) == "fc_")
    {
      name = qualifiedName.substr(3);
      _filter_mode = GL_LINEAR;
      _wrap_mode = GL_CLAMP_TO_EDGE;
    }
    else if (lowerQualifiedName.substr(0, 3) == "fw_")
    {
      name = qualifiedName.substr(3);
      _filter_mode = GL_LINEAR;
      _wrap_mode = GL_REPEAT;
    }
    else if (lowerQualifiedName.substr(0, 3) == "pc_")
    {
      name = qualifiedName.substr(3);
      _filter_mode = GL_NEAREST;
      _wrap_mode = GL_CLAMP_TO_EDGE;
    }
    else if (lowerQualifiedName.substr(0, 3) == "pw_")
    {
      name = qualifiedName.substr(3);
      _filter_mode = GL_NEAREST;
      _wrap_mode = GL_REPEAT;
    }
    else
    {
      name = qualifiedName;
    }
  }

  TextureSamplerDesc loadTexture(const std::string fileName, const std::string name)
  {
    int width, height;
    //    std::cout << "Loading texture " << name << " at " << fileName << std::endl;

    unsigned int tex = SOIL_load_OGL_texture(
      fileName.c_str(),
      SOIL_LOAD_AUTO,
      SOIL_CREATE_NEW_ID,
      SOIL_FLAG_MULTIPLY_ALPHA
      , &width, &height);

    if (tex == 0)
    {
      return TextureSamplerDesc(nullptr, nullptr);
    }

    GLint wrap_mode;
    GLint filter_mode;
    std::string unqualifiedName;

    ExtractTextureSettings(name, wrap_mode, filter_mode, unqualifiedName);
    Texture_* newTexture = new Texture_(unqualifiedName, tex, GL_TEXTURE_2D, width, height, true);
    Sampler* sampler = newTexture->getSampler(wrap_mode, filter_mode);

    if (textures.find(name) != textures.end()) {
      // found duplicate.. this could be optimized
      delete textures[name];
    }

    textures[name] = newTexture;
    //    std::cout << "Loaded texture " << name << std::endl;

    return TextureSamplerDesc(newTexture, sampler);
  }

  TextureManager(const std::string _presetsURL, const int texsizeX, const int texsizeY, std::string datadir) : presetsURL(_presetsURL)
  {
    extensions.push_back(".jpg");
    extensions.push_back(".dds");
    extensions.push_back(".png");
    extensions.push_back(".tga");
    extensions.push_back(".bmp");
    extensions.push_back(".dib");

    std::vector<std::string> dirsToScan{ datadir + "/presets", datadir + "/textures", _presetsURL };
    FileScanner fileScanner = FileScanner(dirsToScan, extensions);

    // scan for textures
    fileScanner.scan(std::bind(&TextureManager::loadTexture, this, std::placeholders::_1, std::placeholders::_2));

    Preload();
    // if not data directory specified from user code
    // we use the built-in default directory (unix prefix based)
    if (datadir.empty())
      datadir = ".";

    // Create main texture ans associated samplers
    mainTexture = new Texture_("main", texsizeX, texsizeY, false);
    mainTexture->getSampler(GL_REPEAT, GL_LINEAR);
    mainTexture->getSampler(GL_REPEAT, GL_NEAREST);
    mainTexture->getSampler(GL_CLAMP_TO_EDGE, GL_LINEAR);
    mainTexture->getSampler(GL_CLAMP_TO_EDGE, GL_NEAREST);
    textures["main"] = mainTexture;

    // Initialize blur textures
    int w = texsizeX;
    int h = texsizeY;
    for (int i = 0; i < NUM_BLUR_TEX; i++)
    {
      // main VS = 1024
      // blur0 = 512
      // blur1 = 256  <-  user sees this as "blur1"
      // blur2 = 128
      // blur3 = 128  <-  user sees this as "blur2"
      // blur4 =  64
      // blur5 =  64  <-  user sees this as "blur3"
      if (!(i & 1) || (i < 2))
      {
#if defined WIN32 && defined max
        w = max(16, w / 2);
        h = max(16, h / 2);
#else
        w = std::max(16, w / 2);
        h = std::max(16, h / 2);
#endif /** WIN32 */
      }
      int w2 = ((w + 3) / 16) * 16;
      int h2 = ((h + 3) / 4) * 4;

      std::string texname = "blur" + std::to_string(i / 2 + 1) + ((i % 2) ? "" : "doNOTuseME");
      Texture_* textureBlur = new Texture_(texname, w2, h2, false);
      textureBlur->getSampler(GL_CLAMP_TO_EDGE, GL_LINEAR);
      textures[texname] = textureBlur;
      blurTextures.push_back(textureBlur);
    }

    //#ifdef GL_ES_VERSION_2_0
        //std::unique_ptr<PerlinNoiseWithAlpha> noise(new PerlinNoiseWithAlpha());
    //#else
    std::unique_ptr<PerlinNoise> noise(new PerlinNoise());
    //#endif

    GLuint noise_texture_lq_lite;
    glGenTextures(1, &noise_texture_lq_lite);
    glBindTexture(GL_TEXTURE_2D, noise_texture_lq_lite);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 32, 32, 0, NOISE_INTERNAL_DATA_FORMAT, GL_FLOAT, noise->noise_lq_lite);
    Texture_* textureNoise_lq_lite = new Texture_("noise_lq_lite", noise_texture_lq_lite, GL_TEXTURE_2D, 32, 32, false);
    textureNoise_lq_lite->getSampler(GL_REPEAT, GL_LINEAR);
    textures["noise_lq_lite"] = textureNoise_lq_lite;

    GLuint noise_texture_lq;
    glGenTextures(1, &noise_texture_lq);
    glBindTexture(GL_TEXTURE_2D, noise_texture_lq);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, NOISE_INTERNAL_DATA_FORMAT, GL_FLOAT, noise->noise_lq);
    Texture_* textureNoise_lq = new Texture_("noise_lq", noise_texture_lq, GL_TEXTURE_2D, 256, 256, false);
    textureNoise_lq->getSampler(GL_REPEAT, GL_LINEAR);
    textures["noise_lq"] = textureNoise_lq;

    GLuint noise_texture_mq;
    glGenTextures(1, &noise_texture_mq);
    glBindTexture(GL_TEXTURE_2D, noise_texture_mq);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, NOISE_INTERNAL_DATA_FORMAT, GL_FLOAT, noise->noise_mq);
    Texture_* textureNoise_mq = new Texture_("noise_mq", noise_texture_mq, GL_TEXTURE_2D, 256, 256, false);
    textureNoise_mq->getSampler(GL_REPEAT, GL_LINEAR);
    textures["noise_mq"] = textureNoise_mq;

    GLuint noise_texture_hq;
    glGenTextures(1, &noise_texture_hq);
    glBindTexture(GL_TEXTURE_2D, noise_texture_hq);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, NOISE_INTERNAL_DATA_FORMAT, GL_FLOAT, noise->noise_hq);
    Texture_* textureNoise_hq = new Texture_("noise_hq", noise_texture_hq, GL_TEXTURE_2D, 256, 256, false);
    textureNoise_hq->getSampler(GL_REPEAT, GL_LINEAR);
    textures["noise_hq"] = textureNoise_hq;

    GLuint noise_texture_lq_vol;
    glGenTextures(1, &noise_texture_lq_vol);
    glBindTexture(GL_TEXTURE_3D, noise_texture_lq_vol);
    glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA, 32, 32, 32, 0, NOISE_INTERNAL_DATA_FORMAT, GL_FLOAT, noise->noise_lq_vol);
    Texture_* textureNoise_lq_vol = new Texture_("noisevol_lq", noise_texture_lq_vol, GL_TEXTURE_3D, 32, 32, false);
    textureNoise_lq_vol->getSampler(GL_REPEAT, GL_LINEAR);
    textures["noisevol_lq"] = textureNoise_lq_vol;

    GLuint noise_texture_hq_vol;
    glGenTextures(1, &noise_texture_hq_vol);
    glBindTexture(GL_TEXTURE_3D, noise_texture_hq_vol);
    glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA, 32, 32, 32, 0, NOISE_INTERNAL_DATA_FORMAT, GL_FLOAT, noise->noise_hq_vol);

    Texture_* textureNoise_hq_vol = new Texture_("noisevol_hq", noise_texture_hq_vol, GL_TEXTURE_3D, 32, 32, false);
    textureNoise_hq_vol->getSampler(GL_REPEAT, GL_LINEAR);
    textures["noisevol_hq"] = textureNoise_hq_vol;
  }

  void Preload()
  {
    int width, height;

    unsigned int tex = SOIL_load_OGL_texture_from_memory(
      Data::M_data,
      sizeof(Data::M_data),
      SOIL_LOAD_AUTO,
      SOIL_CREATE_NEW_ID,
      SOIL_FLAG_POWER_OF_TWO
      | SOIL_FLAG_MULTIPLY_ALPHA
      , &width, &height);


    Texture_* newTex = new Texture_("M", tex, GL_TEXTURE_2D, width, height, true);
    newTex->getSampler(GL_CLAMP_TO_EDGE, GL_LINEAR);
    textures["M"] = newTex;

    //    tex = SOIL_load_OGL_texture_from_memory(
    //                project_data,
    //                project_bytes,
    //                SOIL_LOAD_AUTO,
    //                SOIL_CREATE_NEW_ID,
    //                SOIL_FLAG_POWER_OF_TWO
    //                |  SOIL_FLAG_MULTIPLY_ALPHA
    //                ,&width,&height);

    //    newTex = new Texture_("project", tex, GL_TEXTURE_2D, width, height, true);
    //    newTex->getSampler(GL_CLAMP_TO_EDGE, GL_LINEAR);
    //    textures["project"] = newTex;

    tex = SOIL_load_OGL_texture_from_memory(
      Data::headphones_data,
      sizeof(Data::headphones_data),
      SOIL_LOAD_AUTO,
      SOIL_CREATE_NEW_ID,
      SOIL_FLAG_POWER_OF_TWO
      | SOIL_FLAG_MULTIPLY_ALPHA
      , &width, &height);

    newTex = new Texture_("headphones", tex, GL_TEXTURE_2D, width, height, true);
    newTex->getSampler(GL_CLAMP_TO_EDGE, GL_LINEAR);
    textures["headphones"] = newTex;
  }

  const Texture_* getMainTexture() const
  {
    return mainTexture;
  }
  const std::vector<Texture_*>& getBlurTextures() const
  {
    return blurTextures;
  }

  void updateMainTexture()
  {
    glBindTexture(GL_TEXTURE_2D, mainTexture->texID);
    glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, mainTexture->width, mainTexture->height);
    glBindTexture(GL_TEXTURE_2D, 0);
  }
};


struct RenderContext
{
  float time;
  int texsize;
  float aspectRatio;
  bool aspectCorrect;
  BeatDetect* beatDetect;
  TextureManager* textureManager;
  GLuint programID_v2f_c4f;
  GLuint programID_v2f_c4f_t2f;
  GLint uniform_v2f_c4f_vertex_tranformation;
  GLint uniform_v2f_c4f_vertex_point_size;
  GLint uniform_v2f_c4f_t2f_vertex_tranformation;
  GLint uniform_v2f_c4f_t2f_frag_texture_sampler;
  glm::mat4 mat_ortho;
};

struct RenderItem
{
  float masterAlpha = 1.0f;
  GLuint m_vboID;
  GLuint m_vaoID;
  virtual void Draw(RenderContext& context) = 0;

  virtual void InitVertexAttrib() = 0;

  virtual void Init()
  {
    glGenVertexArrays(1, &m_vaoID);
    glGenBuffers(1, &m_vboID);

    glBindVertexArray(m_vaoID);
    glBindBuffer(GL_ARRAY_BUFFER, m_vboID);

    InitVertexAttrib();

    glBindVertexArray(0);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
  }
};

struct Pipeline
{
  bool staticPerPixel;
  int gx;
  int gy;
  float** x_mesh;
  float** y_mesh;

  bool textureWrap;
  float screenDecay;

  float q[NUM_Q_VARIABLES];

  float blur1n;
  float blur2n;
  float blur3n;
  float blur1x;
  float blur2x;
  float blur3x;
  float blur1ed;

  Shader__ compositeShader;
  std::string compositeShaderFilename;

  std::string warpShaderFilename;

  std::vector<RenderItem*> drawables;
  std::vector<RenderItem*> compositeDrawables;

  Shader__ warpShader;

  Pipeline() : staticPerPixel(false), gx(0), gy(0), blur1n(1), blur2n(1), blur3n(1), blur1x(1), blur2x(1), blur3x(1), blur1ed(1)
  {
  }

  PixelPoint PerPixel(PixelPoint p, const PerPixelContext context)
  {
    return p;
  }

  void setStaticPerPixel(int _gx, int _gy)
  {
    staticPerPixel = true;
    this->gx = _gx;
    this->gy = _gy;

    this->x_mesh = alloc_mesh(gx, gy);
    this->y_mesh = alloc_mesh(gx, gy);
  }
};


namespace {
  // Variants of shaders for GLSL1.2
  const std::string kPresetWarpVertexShaderGlsl120 = R"(
attribute vec2 vertex_position;
attribute vec4 vertex_color;
attribute vec2 vertex_texture;

uniform mat4 vertex_transformation;

varying vec4 frag_COLOR;
varying vec4 frag_TEXCOORD0;
varying vec2 frag_TEXCOORD1;

void main(){
    vec4 position = vertex_transformation * vec4(vertex_position, 0.0, 1.0);
    gl_Position = position;
    frag_COLOR = vertex_color;
    frag_TEXCOORD0.xy = vertex_texture;
    frag_TEXCOORD0.zw = position.xy;
    frag_TEXCOORD1 = vec2(0.0, 0.0);
}
)";

  const std::string kPresetCompVertexShaderGlsl120 = R"(
attribute vec2 vertex_position;
attribute vec4 vertex_color;
attribute vec2 vertex_texture;
attribute vec2 vertex_rad_ang;

varying vec4 frag_COLOR;
varying vec2 frag_TEXCOORD0;
varying vec2 frag_TEXCOORD1;

void main(){
    vec4 position = vec4(vertex_position, 0.0, 1.0);
    gl_Position = position;
    frag_COLOR = vertex_color;
    frag_TEXCOORD0 = vertex_texture;
    frag_TEXCOORD1 = vertex_rad_ang;
}
)";

  const std::string kV2fC4fVertexShaderGlsl120 = R"(
attribute vec2 vertex_position;
attribute vec4 vertex_color;

uniform mat4 vertex_transformation;
uniform float vertex_point_size;

varying vec4 fragment_color;

void main(){
    gl_Position = vertex_transformation * vec4(vertex_position, 0.0, 1.0);
    gl_PointSize = vertex_point_size;
    fragment_color = vertex_color;
}
)";

  const std::string kV2fC4fFragmentShaderGlsl120 = R"(
varying vec4 fragment_color;

void main(){
    gl_FragColor = fragment_color;
}
)";

  const std::string kV2fC4fT2fVertexShaderGlsl120 = R"(
attribute vec2 vertex_position;
attribute vec4 vertex_color;
attribute vec2 vertex_texture;

uniform mat4 vertex_transformation;

varying vec4 fragment_color;
varying vec2 fragment_texture;

void main(){
    gl_Position = vertex_transformation * vec4(vertex_position, 0.0, 1.0);
    fragment_color = vertex_color;
    fragment_texture = vertex_texture;
}
)";

  const std::string kV2fC4fT2fFragmentShaderGlsl120 = R"(
varying vec4 fragment_color;
varying vec2 fragment_texture;

uniform sampler2D texture_sampler;

varying vec4 color;

void main(){
    gl_FragColor = fragment_color * texture2D(texture_sampler,
                                              fragment_texture.st);
}
)";

  const std::string kPresetShaderHeaderGlsl120 = R"(
#define  M_PI   3.14159265359
#define  M_PI_2 6.28318530718
#define  M_INV_PI_2  0.159154943091895

uniform float4   rand_frame;    // random float4, updated each frame
uniform float4   rand_preset;   // random float4, updated once per *preset*
uniform float4   _c0;           // .xy: multiplier to use on UV's to paste
                                // an image fullscreen, *aspect-aware*
                                // .zw = inverse.
uniform float4   _c1, _c2, _c3, _c4;
uniform float4   _c5;           // .xy = scale, bias for reading blur1
                                // .zw = scale, bias for reading blur2
uniform float4   _c6;           // .xy = scale, bias for reading blur3
                                // .zw = blur1_min, blur1_max
uniform float4   _c7;           // .xy ~= float2(1024,768)
                                // .zw ~= float2(1/1024.0, 1/768.0)
uniform float4   _c8;           // .xyzw ~= 0.5 + 0.5 * cos(
                                //   time * float4(~0.3, ~1.3, ~5, ~20))
uniform float4   _c9;           // .xyzw ~= same, but using sin()
uniform float4   _c10;          // .xyzw ~= 0.5 + 0.5 * cos(
                                //   time * float4(~0.005, ~0.008, ~0.013,
                                //                 ~0.022))
uniform float4   _c11;          // .xyzw ~= same, but using sin()
uniform float4   _c12;          // .xyz = mip info for main image
                                // (.x=#across, .y=#down, .z=avg)
                                // .w = unused
uniform float4   _c13;          // .xy = blur2_min, blur2_max
                                // .zw = blur3_min, blur3_max
uniform float4   _qa;           // q vars bank 1 [q1-q4]
uniform float4   _qb;           // q vars bank 2 [q5-q8]
uniform float4   _qc;           // q vars ...
uniform float4   _qd;           // q vars
uniform float4   _qe;           // q vars
uniform float4   _qf;           // q vars
uniform float4   _qg;           // q vars
uniform float4   _qh;           // q vars bank 8 [q29-q32]

// note: in general, don't use the current time w/the *dynamic* rotations!

// four random, static rotations, randomized at preset load time.
// minor translation component (<1).
uniform float4x3 rot_s1;
uniform float4x3 rot_s2;
uniform float4x3 rot_s3;
uniform float4x3 rot_s4;

// four random, slowly changing rotations.
uniform float4x3 rot_d1;
uniform float4x3 rot_d2;
uniform float4x3 rot_d3;
uniform float4x3 rot_d4;

// faster-changing.
uniform float4x3 rot_f1;
uniform float4x3 rot_f2;
uniform float4x3 rot_f3;
uniform float4x3 rot_f4;

// very-fast-changing.
uniform float4x3 rot_vf1;
uniform float4x3 rot_vf2;
uniform float4x3 rot_vf3;
uniform float4x3 rot_vf4;

// ultra-fast-changing.
uniform float4x3 rot_uf1;
uniform float4x3 rot_uf2;
uniform float4x3 rot_uf3;
uniform float4x3 rot_uf4;

// Random every frame.
uniform float4x3 rot_rand1;
uniform float4x3 rot_rand2;
uniform float4x3 rot_rand3;
uniform float4x3 rot_rand4;

#define time     _c2.x
#define fps      _c2.y
#define frame    _c2.z
#define progress _c2.w
#define bass _c3.x
#define mid  _c3.y
#define treb _c3.z
#define vol  _c3.w
#define bass_att _c4.x
#define mid_att  _c4.y
#define treb_att _c4.z
#define vol_att  _c4.w
#define q1 _qa.x
#define q2 _qa.y
#define q3 _qa.z
#define q4 _qa.w
#define q5 _qb.x
#define q6 _qb.y
#define q7 _qb.z
#define q8 _qb.w
#define q9 _qc.x
#define q10 _qc.y
#define q11 _qc.z
#define q12 _qc.w
#define q13 _qd.x
#define q14 _qd.y
#define q15 _qd.z
#define q16 _qd.w
#define q17 _qe.x
#define q18 _qe.y
#define q19 _qe.z
#define q20 _qe.w
#define q21 _qf.x
#define q22 _qf.y
#define q23 _qf.z
#define q24 _qf.w
#define q25 _qg.x
#define q26 _qg.y
#define q27 _qg.z
#define q28 _qg.w
#define q29 _qh.x
#define q30 _qh.y
#define q31 _qh.z
#define q32 _qh.w

#define aspect   _c0

// .xy = (w,h); .zw = (1/(float)w, 1/(float)h)
#define texsize  _c7

#define roam_cos _c8
#define roam_sin _c9
#define slow_roam_cos _c10
#define slow_roam_sin _c11
#define mip_x   _c12.x
#define mip_y   _c12.y
#define mip_xy  _c12.xy
#define mip_avg _c12.z
#define blur1_min _c6.z
#define blur1_max _c6.w
#define blur2_min _c13.x
#define blur2_max _c13.y
#define blur3_min _c13.z
#define blur3_max _c13.w

#define sampler_FC_main sampler_fc_main
#define sampler_PC_main sampler_pc_main
#define sampler_FW_main sampler_fw_main
#define sampler_PW_main sampler_pw_main

#define GetMain(uv) (tex2D(sampler_main,uv).xyz)
#define GetPixel(uv) (tex2D(sampler_main,uv).xyz)
#define GetBlur1(uv) (tex2D(sampler_blur1,uv).xyz*_c5.x + _c5.y)
#define GetBlur2(uv) (tex2D(sampler_blur2,uv).xyz*_c5.z + _c5.w)
#define GetBlur3(uv) (tex2D(sampler_blur3,uv).xyz*_c6.x + _c6.y)

#define lum(x) (dot(x,float3(0.32,0.49,0.29)))
#define tex2d tex2D
#define tex3d tex3D
)";

  const std::string kBlurVertexShaderGlsl120 = R"(
attribute vec2 vertex_position;
attribute vec2 vertex_texture;

varying vec2 fragment_texture;

void main(){
    gl_Position = vec4(vertex_position, 0.0, 1.0);
    fragment_texture = vertex_texture;
}
)";

  std::string kBlur1FragmentShaderGlsl120 = R"(
varying vec2 fragment_texture;

uniform sampler2D texture_sampler;
uniform vec4 _c0; // source texsize (.xy), and inverse (.zw)
uniform vec4 _c1; // w1..w4
uniform vec4 _c2; // d1..d4
uniform vec4 _c3; // scale, bias, w_div

void main(){
    // LONG HORIZ. PASS 1:
    #define srctexsize _c0
    #define w1 _c1.x
    #define w2 _c1.y
    #define w3 _c1.z
    #define w4 _c1.w
    #define d1 _c2.x
    #define d2 _c2.y
    #define d3 _c2.z
    #define d4 _c2.w
    #define fscale _c3.x
    #define fbias  _c3.y
    #define w_div  _c3.z

    // note: if you just take one sample at exactly uv.xy, you get an avg of 4
    // pixels.

    // + moves blur UP, LEFT by 1-pixel increments
    vec2 uv2 = fragment_texture.xy + srctexsize.zw*vec2(1.0,1.0);

    vec3 blur =
        (texture2D(texture_sampler, uv2 + vec2(d1 * srctexsize.z, 0)).xyz +
         texture2D(texture_sampler, uv2 + vec2(-d1 * srctexsize.z, 0)).xyz) *
            w1 +
        (texture2D(texture_sampler, uv2 + vec2(d2 * srctexsize.z, 0)).xyz +
         texture2D(texture_sampler, uv2 + vec2(-d2 * srctexsize.z, 0)).xyz) *
            w2 +
        (texture2D(texture_sampler, uv2 + vec2(d3 * srctexsize.z, 0)).xyz +
         texture2D(texture_sampler, uv2 + vec2(-d3 * srctexsize.z, 0)).xyz) *
            w3 +
        (texture2D(texture_sampler, uv2 + vec2(d4 * srctexsize.z, 0)).xyz +
         texture2D(texture_sampler, uv2 + vec2(-d4 * srctexsize.z, 0)).xyz) *
            w4;

    blur.xyz *= w_div;

    blur.xyz = blur.xyz*fscale + fbias;

    gl_FragColor.xyz = blur;
    gl_FragColor.w   = 1.0;
}
)";

  std::string kBlur2FragmentShaderGlsl120 = R"(
varying vec2 fragment_texture;

uniform sampler2D texture_sampler;
uniform vec4 _c0; // source texsize (.xy), and inverse (.zw)
uniform vec4 _c5; // w1,w2,d1,d2
uniform vec4 _c6; // w_div, edge_darken_c1, edge_darken_c2, edge_darken_c3

void main(){
    // SHORT VERTICAL PASS 2:
    #define srctexsize _c0
    #define w1 _c5.x
    #define w2 _c5.y
    #define d1 _c5.z
    #define d2 _c5.w
    #define edge_darken_c1 _c6.y
    #define edge_darken_c2 _c6.z
    #define edge_darken_c3 _c6.w
    #define w_div   _c6.x

    // note: if you just take one sample at exactly uv.xy, you get an avg of 4
    // pixels.

    // + moves blur UP, LEFT by TWO-pixel increments! (since texture is 1/2 the
    // size of blur1_ps)
    vec2 uv2 = fragment_texture.xy + srctexsize.zw*vec2(0,0);

    vec3 blur =
        (texture2D(texture_sampler, uv2 + vec2(0, d1 * srctexsize.w)).xyz +
         texture2D(texture_sampler, uv2 + vec2(0, -d1 * srctexsize.w)).xyz) *
            w1 +
        (texture2D(texture_sampler, uv2 + vec2(0, d2 * srctexsize.w)).xyz +
         texture2D(texture_sampler, uv2 + vec2(0, -d2 * srctexsize.w)).xyz) *
            w2;
    blur.xyz *= w_div;

    // tone it down at the edges (only happens on 1st X pass!)
    float t = min(min(fragment_texture.x, fragment_texture.y),
                  1.0 - max(fragment_texture.x, fragment_texture.y));
    t = sqrt(t);
    t = edge_darken_c1 + edge_darken_c2 * clamp(t * edge_darken_c3, 0.0, 1.0);
    blur.xyz *= t;

    gl_FragColor.xyz = blur;
    gl_FragColor.w = 1.0;
}
)";

  // Variants of shaders for GLSL3.3
  const std::string kPresetWarpVertexShaderGlsl330 = R"(
layout(location = 0) in vec2 vertex_position;
layout(location = 1) in vec4 vertex_color;
layout(location = 2) in vec2 vertex_texture;

uniform mat4 vertex_transformation;

out vec4 frag_COLOR;
out vec4 frag_TEXCOORD0;
out vec2 frag_TEXCOORD1;

void main(){
    vec4 position = vertex_transformation * vec4(vertex_position, 0.0, 1.0);
    gl_Position = position;
    frag_COLOR = vertex_color;
    frag_TEXCOORD0.xy = vertex_texture;
    frag_TEXCOORD0.zw = position.xy;
    frag_TEXCOORD1 = vec2(0.0, 0.0);
}
)";

  const std::string kPresetCompVertexShaderGlsl330 = R"(
layout(location = 0) in vec2 vertex_position;
layout(location = 1) in vec4 vertex_color;
layout(location = 2) in vec2 vertex_texture;
layout(location = 3) in vec2 vertex_rad_ang;

out vec4 frag_COLOR;
out vec2 frag_TEXCOORD0;
out vec2 frag_TEXCOORD1;

void main(){
    vec4 position = vec4(vertex_position, 0.0, 1.0);
    gl_Position = position;
    frag_COLOR = vertex_color;
    frag_TEXCOORD0 = vertex_texture;
    frag_TEXCOORD1 = vertex_rad_ang;
}
)";

  const std::string kV2fC4fVertexShaderGlsl330 = R"(
layout(location = 0) in vec2 vertex_position;
layout(location = 1) in vec4 vertex_color;

uniform mat4 vertex_transformation;
uniform float vertex_point_size;

out vec4 fragment_color;

void main(){
    gl_Position = vertex_transformation * vec4(vertex_position, 0.0, 1.0);
    gl_PointSize = vertex_point_size;
    fragment_color = vertex_color;
}
)";

  const std::string kV2fC4fFragmentShaderGlsl330 = R"(
precision mediump float;

in vec4 fragment_color;
out vec4 color;

void main(){
    color = fragment_color;
}
)";

  const std::string kV2fC4fT2fVertexShaderGlsl330 = R"(
layout(location = 0) in vec2 vertex_position;
layout(location = 1) in vec4 vertex_color;
layout(location = 2) in vec2 vertex_texture;

uniform mat4 vertex_transformation;

out vec4 fragment_color;
out vec2 fragment_texture;

void main(){
    gl_Position = vertex_transformation * vec4(vertex_position, 0.0, 1.0);
    fragment_color = vertex_color;
    fragment_texture = vertex_texture;
}
)";

  const std::string kV2fC4fT2fFragmentShaderGlsl330 = R"(
precision mediump float;

in vec4 fragment_color;
in vec2 fragment_texture;

uniform sampler2D texture_sampler;

out vec4 color;

void main(){
    color = fragment_color * texture(texture_sampler, fragment_texture.st);
}
)";

  const std::string kPresetShaderHeaderGlsl330 = R"(
#define  M_PI   3.14159265359
#define  M_PI_2 6.28318530718
#define  M_INV_PI_2  0.159154943091895

uniform float4   rand_frame;    // random float4, updated each frame
uniform float4   rand_preset;   // random float4, updated once per *preset*
uniform float4   _c0;           // .xy: multiplier to use on UV's to paste
                                // an image fullscreen, *aspect-aware*
                                // .zw = inverse.
uniform float4   _c1, _c2, _c3, _c4;
uniform float4   _c5;           // .xy = scale, bias for reading blur1
                                // .zw = scale, bias for reading blur2
uniform float4   _c6;           // .xy = scale, bias for reading blur3
                                // .zw = blur1_min, blur1_max
uniform float4   _c7;           // .xy ~= float2(1024,768)
                                // .zw ~= float2(1/1024.0, 1/768.0)
uniform float4   _c8;           // .xyzw ~= 0.5 + 0.5 * cos(
                                //   time * float4(~0.3, ~1.3, ~5, ~20))
uniform float4   _c9;           // .xyzw ~= same, but using sin()
uniform float4   _c10;          // .xyzw ~= 0.5 + 0.5 * cos(
                                //   time * float4(~0.005, ~0.008, ~0.013,
                                //                 ~0.022))
uniform float4   _c11;          // .xyzw ~= same, but using sin()
uniform float4   _c12;          // .xyz = mip info for main image
                                // (.x=#across, .y=#down, .z=avg)
                                // .w = unused
uniform float4   _c13;          // .xy = blur2_min, blur2_max
                                // .zw = blur3_min, blur3_max
uniform float4   _qa;           // q vars bank 1 [q1-q4]
uniform float4   _qb;           // q vars bank 2 [q5-q8]
uniform float4   _qc;           // q vars ...
uniform float4   _qd;           // q vars
uniform float4   _qe;           // q vars
uniform float4   _qf;           // q vars
uniform float4   _qg;           // q vars
uniform float4   _qh;           // q vars bank 8 [q29-q32]

// note: in general, don't use the current time w/the *dynamic* rotations!

// four random, static rotations, randomized at preset load time.
// minor translation component (<1).
uniform float4x3 rot_s1;
uniform float4x3 rot_s2;
uniform float4x3 rot_s3;
uniform float4x3 rot_s4;

// four random, slowly changing rotations.
uniform float4x3 rot_d1;
uniform float4x3 rot_d2;
uniform float4x3 rot_d3;
uniform float4x3 rot_d4;

// faster-changing.
uniform float4x3 rot_f1;
uniform float4x3 rot_f2;
uniform float4x3 rot_f3;
uniform float4x3 rot_f4;

// very-fast-changing.
uniform float4x3 rot_vf1;
uniform float4x3 rot_vf2;
uniform float4x3 rot_vf3;
uniform float4x3 rot_vf4;

// ultra-fast-changing.
uniform float4x3 rot_uf1;
uniform float4x3 rot_uf2;
uniform float4x3 rot_uf3;
uniform float4x3 rot_uf4;

// Random every frame.
uniform float4x3 rot_rand1;
uniform float4x3 rot_rand2;
uniform float4x3 rot_rand3;
uniform float4x3 rot_rand4;

#define time     _c2.x
#define fps      _c2.y
#define frame    _c2.z
#define progress _c2.w
#define bass _c3.x
#define mid  _c3.y
#define treb _c3.z
#define vol  _c3.w
#define bass_att _c4.x
#define mid_att  _c4.y
#define treb_att _c4.z
#define vol_att  _c4.w
#define q1 _qa.x
#define q2 _qa.y
#define q3 _qa.z
#define q4 _qa.w
#define q5 _qb.x
#define q6 _qb.y
#define q7 _qb.z
#define q8 _qb.w
#define q9 _qc.x
#define q10 _qc.y
#define q11 _qc.z
#define q12 _qc.w
#define q13 _qd.x
#define q14 _qd.y
#define q15 _qd.z
#define q16 _qd.w
#define q17 _qe.x
#define q18 _qe.y
#define q19 _qe.z
#define q20 _qe.w
#define q21 _qf.x
#define q22 _qf.y
#define q23 _qf.z
#define q24 _qf.w
#define q25 _qg.x
#define q26 _qg.y
#define q27 _qg.z
#define q28 _qg.w
#define q29 _qh.x
#define q30 _qh.y
#define q31 _qh.z
#define q32 _qh.w

#define aspect   _c0

// .xy = (w,h); .zw = (1/(float)w, 1/(float)h)
#define texsize  _c7

#define roam_cos _c8
#define roam_sin _c9
#define slow_roam_cos _c10
#define slow_roam_sin _c11
#define mip_x   _c12.x
#define mip_y   _c12.y
#define mip_xy  _c12.xy
#define mip_avg _c12.z
#define blur1_min _c6.z
#define blur1_max _c6.w
#define blur2_min _c13.x
#define blur2_max _c13.y
#define blur3_min _c13.z
#define blur3_max _c13.w

#define sampler_FC_main sampler_fc_main
#define sampler_PC_main sampler_pc_main
#define sampler_FW_main sampler_fw_main
#define sampler_PW_main sampler_pw_main

#define GetMain(uv) (tex2D(sampler_main,uv).xyz)
#define GetPixel(uv) (tex2D(sampler_main,uv).xyz)
#define GetBlur1(uv) (tex2D(sampler_blur1,uv).xyz*_c5.x + _c5.y)
#define GetBlur2(uv) (tex2D(sampler_blur2,uv).xyz*_c5.z + _c5.w)
#define GetBlur3(uv) (tex2D(sampler_blur3,uv).xyz*_c6.x + _c6.y)

#define lum(x) (dot(x,float3(0.32,0.49,0.29)))
#define tex2d tex2D
#define tex3d tex3D
)";

  const std::string kBlurVertexShaderGlsl330 = R"(
layout(location = 0) in vec2 vertex_position;
layout(location = 1) in vec2 vertex_texture;

out vec2 fragment_texture;

void main(){
    gl_Position = vec4(vertex_position, 0.0, 1.0);
    fragment_texture = vertex_texture;
}
)";

  std::string kBlur1FragmentShaderGlsl330 = R"(
precision mediump float;

in vec2 fragment_texture;

uniform sampler2D texture_sampler;
uniform vec4 _c0; // source texsize (.xy), and inverse (.zw)
uniform vec4 _c1; // w1..w4
uniform vec4 _c2; // d1..d4
uniform vec4 _c3; // scale, bias, w_div

out vec4 color;

void main(){
    // LONG HORIZ. PASS 1:
    #define srctexsize _c0
    #define w1 _c1.x
    #define w2 _c1.y
    #define w3 _c1.z
    #define w4 _c1.w
    #define d1 _c2.x
    #define d2 _c2.y
    #define d3 _c2.z
    #define d4 _c2.w
    #define fscale _c3.x
    #define fbias  _c3.y
    #define w_div  _c3.z

    // note: if you just take one sample at exactly uv.xy, you get an avg of 4
    // pixels.

    // + moves blur UP, LEFT by 1-pixel increments
    vec2 uv2 = fragment_texture.xy + srctexsize.zw*vec2(1.0,1.0);

    vec3 blur =
        (texture(texture_sampler, uv2 + vec2(d1 * srctexsize.z, 0)).xyz +
        texture(texture_sampler, uv2 + vec2(-d1 * srctexsize.z, 0)).xyz) *
            w1 +
        (texture(texture_sampler, uv2 + vec2(d2 * srctexsize.z, 0)).xyz +
        texture(texture_sampler, uv2 + vec2(-d2 * srctexsize.z, 0)).xyz) *
            w2 +
        (texture(texture_sampler, uv2 + vec2(d3 * srctexsize.z, 0)).xyz +
        texture(texture_sampler, uv2 + vec2(-d3 * srctexsize.z, 0)).xyz) *
            w3 +
        (texture(texture_sampler, uv2 + vec2(d4 * srctexsize.z, 0)).xyz +
        texture(texture_sampler, uv2 + vec2(-d4 * srctexsize.z, 0)).xyz) *
            w4;

    blur.xyz *= w_div;

    blur.xyz = blur.xyz*fscale + fbias;

    color.xyz = blur;
    color.w   = 1.0;
}
)";

  std::string kBlur2FragmentShaderGlsl330 = R"(
precision mediump float;

in vec2 fragment_texture;

uniform sampler2D texture_sampler;
uniform vec4 _c0; // source texsize (.xy), and inverse (.zw)
uniform vec4 _c5; // w1,w2,d1,d2
uniform vec4 _c6; // w_div, edge_darken_c1, edge_darken_c2, edge_darken_c3

out vec4 color;

void main(){
    // SHORT VERTICAL PASS 2:
    #define srctexsize _c0
    #define w1 _c5.x
    #define w2 _c5.y
    #define d1 _c5.z
    #define d2 _c5.w
    #define edge_darken_c1 _c6.y
    #define edge_darken_c2 _c6.z
    #define edge_darken_c3 _c6.w
    #define w_div   _c6.x

    // note: if you just take one sample at exactly uv.xy, you get an avg of 4
    // pixels.

    // + moves blur UP, LEFT by TWO-pixel increments! (since texture is 1/2 the
    // size of blur1_ps)
    vec2 uv2 = fragment_texture.xy + srctexsize.zw*vec2(0,0);

    vec3 blur =
        (texture(texture_sampler, uv2 + vec2(0, d1 * srctexsize.w)).xyz +
        texture(texture_sampler, uv2 + vec2(0, -d1 * srctexsize.w)).xyz) *
            w1 +
        (texture(texture_sampler, uv2 + vec2(0, d2 * srctexsize.w)).xyz +
        texture(texture_sampler, uv2 + vec2(0, -d2 * srctexsize.w)).xyz) *
            w2;
    blur.xyz *= w_div;

    // tone it down at the edges (only happens on 1st X pass!)
    float t = min(min(fragment_texture.x, fragment_texture.y),
                  1.0 - max(fragment_texture.x, fragment_texture.y));
    t = sqrt(t);
    t = edge_darken_c1 + edge_darken_c2 * clamp(t * edge_darken_c3, 0.0, 1.0);
    blur.xyz *= t;

    color.xyz = blur;
    color.w = 1.0;
}
)";

}  // namespace

struct StaticGlShaders
{
  struct GlslVersion
  {
    int major, minor;
  };

  bool use_gles_;
  GlslVersion version_;
  std::string version_header_;
  M4::GLSLGenerator::Version glsl_generator_version_;

#define DECLARE_SHADER_ACCESSOR(name)                  \
    std::string Get##name() {         \
        if (use_gles_) {                               \
            return AddVersionHeader(k##name##Glsl330); \
        }                                              \
        if (version_.major < 3) {                      \
            return AddVersionHeader(k##name##Glsl120); \
        }                                              \
        return AddVersionHeader(k##name##Glsl330);     \
    }

#define DECLARE_SHADER_ACCESSOR_NO_HEADER(name) \
    std::string Get##name() {  \
        if (use_gles_) {                        \
            return k##name##Glsl330;            \
        }                                       \
        if (version_.major < 3) {               \
            return k##name##Glsl120;            \
        }                                       \
        return k##name##Glsl330;                \
    }

  DECLARE_SHADER_ACCESSOR(PresetWarpVertexShader);
  DECLARE_SHADER_ACCESSOR(PresetCompVertexShader);
  DECLARE_SHADER_ACCESSOR(V2fC4fVertexShader);
  DECLARE_SHADER_ACCESSOR(V2fC4fFragmentShader);
  DECLARE_SHADER_ACCESSOR(V2fC4fT2fVertexShader);
  DECLARE_SHADER_ACCESSOR(V2fC4fT2fFragmentShader);
  DECLARE_SHADER_ACCESSOR(BlurVertexShader);
  DECLARE_SHADER_ACCESSOR(Blur1FragmentShader);
  DECLARE_SHADER_ACCESSOR(Blur2FragmentShader);
  DECLARE_SHADER_ACCESSOR_NO_HEADER(PresetShaderHeader);

  static std::shared_ptr<StaticGlShaders> Get() {
    bool use_gles = false;
#if defined PLATFORM_EMSCRIPTEN || defined __ANDROID__ || defined SHR3D_OPENGL_ES_FORCE
    use_gles = true;
#endif

    static std::shared_ptr<StaticGlShaders> instance(
      new StaticGlShaders(use_gles));

    return instance;
  }

  std::string AddVersionHeader(std::string shader_text) {
    return version_header_ + "\n" + shader_text;
  }

  M4::GLSLGenerator::Version GetGlslGeneratorVersion()
  {
    return glsl_generator_version_;
  }

  StaticGlShaders::GlslVersion QueryGlslVersion()
  {
    /* In the linux desktop environment with --use-gles configured, the parsing of the GL_SHADING_LANGUAGE_VERSION
    string comes back as "OpenGL ES GLSL ES 3"
    And I think this was supposed to be parsing somethign like
    "3.10 etc etc"
    This caused exceptions to be raised in the std::stoi section;

    So - The parsing will look for <anything> <number> ['.' <number>] [<anything else>]
    and will default to 3.0 for the version in case of errors
    */

    int major = 3;  /* 3.0 is default */
    int minor = 0;

    std::string glsl_version_string = reinterpret_cast<const char*>(
      glGetString(GL_SHADING_LANGUAGE_VERSION));

    size_t version_len = glsl_version_string.length();
    /* make a c version of the string and do the conversion to integers manually just for this case */
    if (version_len) { // find the number
      size_t position = 0;
      char* cstr = new char[version_len + 1];

      strcpy(cstr, glsl_version_string.c_str());

      /* scan the anything before the number */
      while (position < version_len) {
        char ch = cstr[position];
        if ((ch >= '0') && (ch <= '9')) {
          break;
        }
        position++;
      }

      /* scan the first number */
      {
        int possible_major = 0;
        while (position < version_len) {
          char ch = cstr[position];
          if ((ch >= '0') && (ch <= '9')) {
            possible_major = (possible_major * 10) + ch - '0';
          }
          else if (ch == '.') { /* got the minor */
            int possible_minor = 0;
            position++;
            while (position < version_len) {
              ch = cstr[position];
              if ((ch >= '0') && (ch <= '9')) {
                possible_minor = (possible_minor * 10) + ch - '0';
              }
              else break;
              position++;
            }  /* while scanning the minor version */
            if (possible_major) { /* set the minor version only if the major number is valid */
              minor = possible_minor;
            }
            break; // We scanned it
          }
          else { /* not a number or period */
            break;
          }
          position++;
        } /* while scanning the major number */
        if (possible_major) {
          major = possible_major;
        }
      } /* scanning block */
      delete[] cstr;
    } /* if there is a string to parse */

    return GlslVersion{ major, minor };
  }

  StaticGlShaders(bool use_gles) : use_gles_(use_gles) {
    version_ = QueryGlslVersion();

    if (use_gles_) {
      // If GLES is specified, override the version header.
      version_header_ = "#version 300 es";
      glsl_generator_version_ = M4::GLSLGenerator::Version::Version_300_ES;
    }
    else {
      if (version_.major < 3) {
        // For all GLSL versions less than 3.*, use the GLSL1.2 shaders.
        version_header_ = "#version 120";
        glsl_generator_version_ = M4::GLSLGenerator::Version::Version_120;
      }
      else {
        version_header_ = "#version 330";
        glsl_generator_version_ = M4::GLSLGenerator::Version::Version_330;
      }
    }
  }
};


struct ShaderEngine
{
  enum PresentShaderType
  {
    PresentCompositeShader,
    PresentWarpShader,
    PresentBlur1Shader,
    PresentBlur2Shader,
  };

  GLuint programID_v2f_c4f;
  GLuint programID_v2f_c4f_t2f;
  GLint uniform_v2f_c4f_vertex_tranformation;
  GLint uniform_v2f_c4f_vertex_point_size;
  GLint uniform_v2f_c4f_t2f_vertex_tranformation;
  GLint uniform_v2f_c4f_t2f_frag_texture_sampler;
  int texsizeX;
  int texsizeY;
  float aspectX;
  float aspectY;
  BeatDetect* beatDetect;
  TextureManager* textureManager;
  GLint uniform_vertex_transf_warp_shader;
  bool blur1_enabled;
  bool blur2_enabled;
  bool blur3_enabled;
  GLint uniform_blur1_sampler;
  GLint uniform_blur1_c0;
  GLint uniform_blur1_c1;
  GLint uniform_blur1_c2;
  GLint uniform_blur1_c3;
  GLuint programID_blur1;
  GLuint programID_blur2;
  GLint uniform_blur2_sampler;
  GLint uniform_blur2_c0;
  GLint uniform_blur2_c5;
  GLint uniform_blur2_c6;
  GLuint vboBlur;
  GLuint vaoBlur;
  float rand_preset[4];
  glm::vec3 xlate[20];
  glm::vec3 rot_base[20];
  glm::vec3 rot_speed[20];
  GLuint programID_presetWarp;
  GLuint programID_presetComp;
  bool presetCompShaderLoaded;
  bool presetWarpShaderLoaded;
  std::string m_presetName;

  ShaderEngine() : presetCompShaderLoaded(false), presetWarpShaderLoaded(false)
  {
    std::shared_ptr<StaticGlShaders> static_gl_shaders = StaticGlShaders::Get();

    programID_v2f_c4f = CompileShaderProgram(
      static_gl_shaders->GetV2fC4fVertexShader(),
      static_gl_shaders->GetV2fC4fFragmentShader(), "v2f_c4f");
    programID_v2f_c4f_t2f = CompileShaderProgram(
      static_gl_shaders->GetV2fC4fT2fVertexShader(),
      static_gl_shaders->GetV2fC4fT2fFragmentShader(), "v2f_c4f_t2f");

    programID_blur1 = CompileShaderProgram(
      static_gl_shaders->GetBlurVertexShader(),
      static_gl_shaders->GetBlur1FragmentShader(), "blur1");
    programID_blur2 = CompileShaderProgram(
      static_gl_shaders->GetBlurVertexShader(),
      static_gl_shaders->GetBlur2FragmentShader(), "blur2");

    uniform_v2f_c4f_vertex_tranformation = glGetUniformLocation(programID_v2f_c4f, "vertex_transformation");
    uniform_v2f_c4f_vertex_point_size = glGetUniformLocation(programID_v2f_c4f, "vertex_point_size");
    uniform_v2f_c4f_t2f_vertex_tranformation = glGetUniformLocation(programID_v2f_c4f_t2f, "vertex_transformation");
    uniform_v2f_c4f_t2f_frag_texture_sampler = glGetUniformLocation(programID_v2f_c4f_t2f, "texture_sampler");

    uniform_blur1_sampler = glGetUniformLocation(programID_blur1, "texture_sampler");
    uniform_blur1_c0 = glGetUniformLocation(programID_blur1, "_c0");
    uniform_blur1_c1 = glGetUniformLocation(programID_blur1, "_c1");
    uniform_blur1_c2 = glGetUniformLocation(programID_blur1, "_c2");
    uniform_blur1_c3 = glGetUniformLocation(programID_blur1, "_c3");

    uniform_blur2_sampler = glGetUniformLocation(programID_blur2, "texture_sampler");
    uniform_blur2_c0 = glGetUniformLocation(programID_blur2, "_c0");
    uniform_blur2_c5 = glGetUniformLocation(programID_blur2, "_c5");
    uniform_blur2_c6 = glGetUniformLocation(programID_blur2, "_c6");

    // Initialize Blur vao/vbo
    float pointsBlur[16] = {
        -1.0, -1.0,     0.0,    1.0,
         1.0, -1.0,     1.0,    1.0,
        -1.0,  1.0,     0.0,    0.0,
         1.0,  1.0,     1.0,    0.0 };

    glGenBuffers(1, &vboBlur);
    glGenVertexArrays(1, &vaoBlur);

    glBindVertexArray(vaoBlur);
    glBindBuffer(GL_ARRAY_BUFFER, vboBlur);

    glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 8 * 2, pointsBlur, GL_STATIC_DRAW);

    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, (void*)0); // Positions

    glEnableVertexAttribArray(1);
    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, (void*)(sizeof(float) * 2)); // Textures

    glBindVertexArray(0);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
  }

  bool checkCompileStatus(GLuint shader, const std::string& shaderTitle) {
    GLint status;
    glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
    if (status == GL_TRUE)
      return true;  // success

    int InfoLogLength;
    glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &InfoLogLength);
    if (InfoLogLength > 0) {
      std::vector<char> FragmentShaderErrorMessage(InfoLogLength + 1);
      glGetShaderInfoLog(shader, InfoLogLength, nullptr, &FragmentShaderErrorMessage[0]);
      std::cerr << "Failed to compile shader '" << shaderTitle << "'. Error: " << &FragmentShaderErrorMessage[0] << std::endl;
    }

    return false;
  }

  bool linkProgram(GLuint programID)
  {
    glLinkProgram(programID);

    GLint program_linked;
    glGetProgramiv(programID, GL_LINK_STATUS, &program_linked);
    if (program_linked == GL_TRUE) {
      return true;	// success
    }

    int InfoLogLength;
    glGetProgramiv(programID, GL_INFO_LOG_LENGTH, &InfoLogLength);
    if (InfoLogLength > 0) {
      std::vector<char> ProgramErrorMessage(InfoLogLength + 1);
      glGetProgramInfoLog(programID, InfoLogLength, nullptr, &ProgramErrorMessage[0]);
      std::cerr << "Failed to link program: " << &ProgramErrorMessage[0] << std::endl;
    }

    return false;
  }

  GLuint CompileShaderProgram(const std::string& VertexShaderCode, const std::string& FragmentShaderCode, const std::string& shaderTypeString)
  {
    //#if defined(WIN32) && !defined(EYETUNE_WINRT)
    //    GLenum err = glewInit();
    //#endif /** WIN32 */

        // Create the shaders
    GLuint VertexShaderID = glCreateShader(GL_VERTEX_SHADER);
    GLuint FragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER);


    // Compile Vertex Shader__
    char const* VertexSourcePointer = VertexShaderCode.c_str();
    glShaderSource(VertexShaderID, 1, &VertexSourcePointer, nullptr);
    glCompileShader(VertexShaderID);
    if (!checkCompileStatus(VertexShaderID, "Vertex: " + shaderTypeString)) {
      glDeleteShader(VertexShaderID);
      glDeleteShader(FragmentShaderID);
      return GL_FALSE;
    }


    // Compile Fragment Shader__
    char const* FragmentSourcePointer = FragmentShaderCode.c_str();
    glShaderSource(FragmentShaderID, 1, &FragmentSourcePointer, nullptr);
    glCompileShader(FragmentShaderID);
    if (!checkCompileStatus(FragmentShaderID, "Fragment: " + shaderTypeString)) {
      glDeleteShader(VertexShaderID);
      glDeleteShader(FragmentShaderID);
      return GL_FALSE;
    }


    // Link the program
    GLuint programID = glCreateProgram();

    glAttachShader(programID, VertexShaderID);
    glAttachShader(programID, FragmentShaderID);
    bool linkOK = linkProgram(programID);

    glDetachShader(programID, VertexShaderID);
    glDetachShader(programID, FragmentShaderID);

    glDeleteShader(VertexShaderID);
    glDeleteShader(FragmentShaderID);

    return linkOK ? programID : GL_FALSE;
  }

  GLuint compilePresetShader(const PresentShaderType shaderType, Shader__& pmShader, const std::string& shaderFilename) {
    std::string program = pmShader.programSource;

    if (program.length() <= 0)
      return GL_FALSE;

    // replace "}" with return statement (this can probably be optimized for the GLSL conversion...)
    size_t found = program.rfind('}');
    if (found != std::string::npos)
    {
      //std::cout << "last '}' found at: " << int(found) << std::endl;
      program.replace(int(found), 1, "_return_value = float4(ret.xyz, 1.0);\n"
        "}\n");
    }
    else
      return GL_FALSE;

    // replace shader_body with entry point function
    found = program.find("shader_body");
    if (found != std::string::npos)
    {
      //std::cout << "first 'shader_body' found at: " << int(found) << std::endl;
      if (shaderType == PresentWarpShader) {
        program.replace(int(found), 11, "void PS(float4 _vDiffuse : COLOR, float4 _uv : TEXCOORD0, float2 _rad_ang : TEXCOORD1, out float4 _return_value : COLOR)\n");

      }
      else {
        program.replace(int(found), 11, "void PS(float4 _vDiffuse : COLOR, float2 _uv : TEXCOORD0, float2 _rad_ang : TEXCOORD1, out float4 _return_value : COLOR)\n");

      }
    }
    else
      return GL_FALSE;

    // replace "{" with some variable declarations
    found = program.find('{', found);
    if (found != std::string::npos)
    {
      //std::cout << "first '{' found at: " << int(found) << std::endl;
      const char* progMain = \
        "{\n"
        "float3 ret = 0;\n";
      program.replace(int(found), 1, progMain);
    }
    else
      return GL_FALSE;

    pmShader.textures.clear();


    // Add builtin textures
    pmShader.textures["main"] = textureManager->getTexture("main", GL_REPEAT, GL_LINEAR);
    pmShader.textures["fc_main"] = textureManager->getTexture("main", GL_CLAMP_TO_EDGE, GL_LINEAR);
    pmShader.textures["pc_main"] = textureManager->getTexture("main", GL_CLAMP_TO_EDGE, GL_NEAREST);
    pmShader.textures["fw_main"] = textureManager->getTexture("main", GL_REPEAT, GL_LINEAR);
    pmShader.textures["pw_main"] = textureManager->getTexture("main", GL_REPEAT, GL_NEAREST);

    pmShader.textures["noise_lq"] = textureManager->getTexture("noise_lq", GL_CLAMP_TO_EDGE, GL_LINEAR);
    pmShader.textures["noise_lq_lite"] = textureManager->getTexture("noise_lq_lite", GL_CLAMP_TO_EDGE, GL_LINEAR);
    pmShader.textures["noise_mq"] = textureManager->getTexture("noise_mq", GL_CLAMP_TO_EDGE, GL_LINEAR);
    pmShader.textures["noise_hq"] = textureManager->getTexture("noise_hq", GL_CLAMP_TO_EDGE, GL_LINEAR);
    pmShader.textures["noisevol_lq"] = textureManager->getTexture("noisevol_lq", GL_CLAMP_TO_EDGE, GL_LINEAR);
    pmShader.textures["noisevol_hq"] = textureManager->getTexture("noisevol_hq", GL_CLAMP_TO_EDGE, GL_LINEAR);



    // set up texture samplers for all samplers references in the shader program
    found = 0;
    found = program.find("sampler_", found);
    while (found != std::string::npos)
    {
      found += 8;
      size_t end = program.find_first_of(" ;,\n\r)", found);

      if (end != std::string::npos)
      {
        std::string sampler = program.substr((int)found, (int)end - found);
        std::string lowerCaseName(sampler);
        std::transform(lowerCaseName.begin(), lowerCaseName.end(), lowerCaseName.begin(), tolower);

        TextureSamplerDesc texDesc = textureManager->getTexture(sampler, GL_REPEAT, GL_LINEAR);

        if (texDesc.first == nullptr)
        {
          if (lowerCaseName.substr(0, 4) == "rand" || lowerCaseName.substr(2, 5) == "_rand")
          {
            texDesc = textureManager->getRandomTextureName(sampler);
          }
          else
          {
            texDesc = textureManager->tryLoadingTexture(sampler);
          }
        }

        if (texDesc.first == nullptr)
        {
          std::cerr << "Texture_ loading error for: " << sampler << std::endl;
        }
        else
        {
          std::map<std::string, TextureSamplerDesc>::const_iterator iter = pmShader.textures.cbegin();
          for (; iter != pmShader.textures.cend(); ++iter)
          {
            if (iter->first == sampler)
              break;
          }

          if (iter == pmShader.textures.cend())
            pmShader.textures[sampler] = texDesc;
        }
      }

      found = program.find("sampler_", found);
    }

    textureManager->clearRandomTextures();

    found = program.find("GetBlur3");
    if (found != std::string::npos)
    {
      blur1_enabled = blur2_enabled = blur3_enabled = true;
      pmShader.textures["blur3"] = textureManager->getTexture("blur3", GL_CLAMP_TO_EDGE, GL_LINEAR);
      pmShader.textures["blur2"] = textureManager->getTexture("blur2", GL_CLAMP_TO_EDGE, GL_LINEAR);
      pmShader.textures["blur1"] = textureManager->getTexture("blur1", GL_CLAMP_TO_EDGE, GL_LINEAR);
    }
    else
    {
      found = program.find("GetBlur2");
      if (found != std::string::npos)
      {
        blur1_enabled = blur2_enabled = true;
        pmShader.textures["blur2"] = textureManager->getTexture("blur2", GL_CLAMP_TO_EDGE, GL_LINEAR);
        pmShader.textures["blur1"] = textureManager->getTexture("blur1", GL_CLAMP_TO_EDGE, GL_LINEAR);
      }
      else
      {
        found = program.find("GetBlur1");
        if (found != std::string::npos)
        {
          blur1_enabled = true;
          pmShader.textures["blur1"] = textureManager->getTexture("blur1", GL_CLAMP_TO_EDGE, GL_LINEAR);
        }
      }
    }

    std::string fullSource;

    // prepend our HLSL template to the actual program source
    fullSource.append(StaticGlShaders::Get()->GetPresetShaderHeader());

    if (shaderType == PresentWarpShader) {
      fullSource.append("#define rad _rad_ang.x\n"
        "#define ang _rad_ang.y\n"
        "#define uv _uv.xy\n"
        "#define uv_orig _uv.zw\n");
    }
    else {
      fullSource.append("#define rad _rad_ang.x\n"
        "#define ang _rad_ang.y\n"
        "#define uv _uv.xy\n"
        "#define uv_orig _uv.xy\n"
        "#define hue_shader _vDiffuse.xyz\n");
    }


    fullSource.append(program);

    std::string shaderTypeString;
    switch (shaderType) {
    case PresentWarpShader: shaderTypeString = "Warp"; break;
    case PresentCompositeShader: shaderTypeString = "Comp"; break;
    case PresentBlur1Shader: shaderTypeString = "Blur1"; break;
    case PresentBlur2Shader: shaderTypeString = "Blur2"; break;
    default:    shaderTypeString = "Other";
    }

    M4::GLSLGenerator generator;
    M4::Allocator allocator;

    M4::HLSLTree tree(&allocator);
    M4::HLSLParser parser(&allocator, &tree);

    // preprocess define macros
    std::string sourcePreprocessed;
    if (!parser.ApplyPreprocessor(shaderFilename.c_str(), fullSource.c_str(), fullSource.size(), sourcePreprocessed)) {
      std::cerr << "Failed to preprocess HLSL(step1) " << shaderTypeString << " shader" << std::endl;

#if !DUMP_SHADERS_ON_ERROR
      std::cerr << "Source: " << std::endl << fullSource << std::endl;
#else
      std::ofstream out("/tmp/shader_" + shaderTypeString + "_step1.txt");
      out << fullSource;
      out.close();
#endif
      return GL_FALSE;
    }

    // Remove previous shader declarations
    std::smatch matches;
    while (std::regex_search(sourcePreprocessed, matches, std::regex("sampler(2D|3D|)(\\s+|\\().*"))) {
      sourcePreprocessed.replace(matches.position(), matches.length(), "");
    }

    // Remove previous texsize declarations
    while (std::regex_search(sourcePreprocessed, matches, std::regex("float4\\s+texsize_.*"))) {
      sourcePreprocessed.replace(matches.position(), matches.length(), "");
    }

    // Declare samplers
    std::set<std::string> texsizes;
    std::map<std::string, TextureSamplerDesc>::const_iterator iter_samplers = pmShader.textures.cbegin();
    for (; iter_samplers != pmShader.textures.cend(); ++iter_samplers)
    {
      Texture_* texture = iter_samplers->second.first;

      if (texture->type == GL_TEXTURE_3D) {
        sourcePreprocessed.insert(0, "uniform sampler3D sampler_" + iter_samplers->first + ";\n");
      }
      else {
        sourcePreprocessed.insert(0, "uniform sampler2D sampler_" + iter_samplers->first + ";\n");
      }

      texsizes.insert(iter_samplers->first);
      texsizes.insert(texture->name);
    }

    // Declare texsizes
    std::set<std::string>::const_iterator iter_texsizes = texsizes.cbegin();
    for (; iter_texsizes != texsizes.cend(); ++iter_texsizes)
    {
      sourcePreprocessed.insert(0, "uniform float4 texsize_" + *iter_texsizes + ";\n");
    }


    // transpile from HLSL (aka preset shader aka directX shader) to GLSL (aka OpenGL shader lang)

    // parse
    if (!parser.Parse(shaderFilename.c_str(), sourcePreprocessed.c_str(), sourcePreprocessed.size())) {
      std::cerr << "Failed to parse HLSL(step2) " << shaderTypeString << " shader" << std::endl;

#if !DUMP_SHADERS_ON_ERROR
      std::cerr << "Source: " << std::endl << sourcePreprocessed << std::endl;
#else
      std::ofstream out2("/tmp/shader_" + shaderTypeString + "_step2.txt");
      out2 << sourcePreprocessed;
      out2.close();
#endif
      return GL_FALSE;
    }

    // generate GLSL
    if (!generator.Generate(&tree, M4::GLSLGenerator::Target_FragmentShader,
      StaticGlShaders::Get()->GetGlslGeneratorVersion(),
      "PS")) {
      std::cerr << "Failed to transpile HLSL(step3) " << shaderTypeString << " shader to GLSL" << std::endl;
#if !DUMP_SHADERS_ON_ERROR
      std::cerr << "Source: " << std::endl << sourcePreprocessed << std::endl;
#else
      std::ofstream out2("/tmp/shader_" + shaderTypeString + "_step2.txt");
      out2 << sourcePreprocessed;
      out2.close();
#endif
      return GL_FALSE;
    }

    // now we have GLSL source for the preset shader program (hopefully it's
    // valid!) copmile the preset shader fragment shader with the standard
    // vertex shader and cross our fingers
    GLuint ret = 0;
    if (shaderType == PresentWarpShader) {
      ret = CompileShaderProgram(
        StaticGlShaders::Get()->GetPresetWarpVertexShader(),
        generator.GetResult(), shaderTypeString);
    }
    else {
      ret = CompileShaderProgram(
        StaticGlShaders::Get()->GetPresetCompVertexShader(),
        generator.GetResult(), shaderTypeString);
    }

    if (ret != GL_FALSE) {
    }
    else {
      std::cerr << "Compilation error (step3) of " << shaderTypeString << std::endl;

#if !DUMP_SHADERS_ON_ERROR
      std::cerr << "Source:" << std::endl << generator.GetResult() << std::endl;
#else
      std::ofstream out3("/tmp/shader_" + shaderTypeString + "_step3.txt");
      out3 << generator.GetResult();
      out3.close();
#endif
    }

    return ret;
  }

  GLuint loadPresetShader(const ShaderEngine::PresentShaderType shaderType, Shader__& presetShader, std::string& shaderFilename) {
    GLuint program = compilePresetShader(shaderType, presetShader, shaderFilename);

    if (program == GL_FALSE) {
      // failed to compile
      return GL_FALSE;
    }

    return program;
  }

  bool loadPresetShaders(Pipeline& pipeline, const std::string& presetName) {

    bool ok = true;

    // blur programs
    blur1_enabled = false;
    blur2_enabled = false;
    blur3_enabled = false;

    m_presetName = presetName;

    // compile and link warp and composite shaders from pipeline
    if (!pipeline.warpShader.programSource.empty()) {
      programID_presetWarp = loadPresetShader(PresentWarpShader, pipeline.warpShader, pipeline.warpShaderFilename);
      if (programID_presetWarp != GL_FALSE) {
        uniform_vertex_transf_warp_shader = glGetUniformLocation(programID_presetWarp, "vertex_transformation");
        presetWarpShaderLoaded = true;
      }
      else {
        ok = false;
      }
    }

    if (!pipeline.compositeShader.programSource.empty()) {
      programID_presetComp = loadPresetShader(PresentCompositeShader, pipeline.compositeShader, pipeline.compositeShaderFilename);
      if (programID_presetComp != GL_FALSE) {
        presetCompShaderLoaded = true;
      }
      else {
        ok = false;
      }
    }

    //    std::cout << "Preset composite shader active: " << presetCompShaderLoaded << ", preset warp shader active: " << presetWarpShaderLoaded << std::endl;

    return ok;
  }

  void setParams(const int _texsizeX, const int _texsizeY, BeatDetect* _beatDetect, TextureManager* _textureManager)
  {
    this->beatDetect = _beatDetect;
    this->textureManager = _textureManager;

    aspectX = 1;
    aspectY = 1;
    if (_texsizeX > _texsizeY)
      aspectY = (float)_texsizeY / (float)_texsizeX;
    else
      aspectX = (float)_texsizeX / (float)_texsizeY;

    this->texsizeX = _texsizeX;
    this->texsizeY = _texsizeY;
  }

  // deactivate preset shaders
  void disablePresetShaders()
  {
    if (presetCompShaderLoaded)
      glDeleteProgram(programID_presetComp);

    if (presetWarpShaderLoaded)
      glDeleteProgram(programID_presetWarp);

    presetCompShaderLoaded = false;
    presetWarpShaderLoaded = false;
  }

  void reset()
  {
    disablePresetShaders();
    rand_preset[0] = FRAND;
    rand_preset[1] = FRAND;
    rand_preset[2] = FRAND;
    rand_preset[3] = FRAND;

    unsigned int k = 0;
    do
    {
      for (int i = 0; i < 4; i++)
      {
        float xlate_mult = 1;
        float rot_mult = 0.9f * powf(k / 8.0f, 3.2f);
        xlate[k].x = (FRAND * 2 - 1) * xlate_mult;
        xlate[k].y = (FRAND * 2 - 1) * xlate_mult;
        xlate[k].z = (FRAND * 2 - 1) * xlate_mult;
        rot_base[k].x = FRAND * 6.28f;
        rot_base[k].y = FRAND * 6.28f;
        rot_base[k].z = FRAND * 6.28f;
        rot_speed[k].x = (FRAND * 2 - 1) * rot_mult;
        rot_speed[k].y = (FRAND * 2 - 1) * rot_mult;
        rot_speed[k].z = (FRAND * 2 - 1) * rot_mult;
        k++;
      }
    } while (k < sizeof(xlate) / sizeof(xlate[0]));
  }

  void RenderBlurTextures(const Pipeline& pipeline, const PipelineContext& pipelineContext)
  {
    unsigned int passes = 0;
    if (blur3_enabled) {
      passes = 6;
    }
    else if (blur2_enabled) {
      passes = 4;
    }
    else if (blur1_enabled) {
      passes = 2;
    }
    else {
      return;
    }

    const float w[8] = { 4.0f, 3.8f, 3.5f, 2.9f, 1.9f, 1.2f, 0.7f, 0.3f };  //<- user can specify these
    float edge_darken = pipeline.blur1ed;
    float blur_min[3], blur_max[3];

    blur_min[0] = pipeline.blur1n;
    blur_min[1] = pipeline.blur2n;
    blur_min[2] = pipeline.blur3n;
    blur_max[0] = pipeline.blur1x;
    blur_max[1] = pipeline.blur2x;
    blur_max[2] = pipeline.blur3x;

    // check that precision isn't wasted in later blur passes [...min-max gap can't grow!]
    // also, if min-max are close to each other, push them apart:
    const float fMinDist = 0.1f;
    if (blur_max[0] - blur_min[0] < fMinDist) {
      float avg = (blur_min[0] + blur_max[0]) * 0.5f;
      blur_min[0] = avg - fMinDist * 0.5f;
      blur_max[0] = avg - fMinDist * 0.5f;
    }
    blur_max[1] = std::min(blur_max[0], blur_max[1]);
    blur_min[1] = std::max(blur_min[0], blur_min[1]);
    if (blur_max[1] - blur_min[1] < fMinDist) {
      float avg = (blur_min[1] + blur_max[1]) * 0.5f;
      blur_min[1] = avg - fMinDist * 0.5f;
      blur_max[1] = avg - fMinDist * 0.5f;
    }
    blur_max[2] = std::min(blur_max[1], blur_max[2]);
    blur_min[2] = std::max(blur_min[1], blur_min[2]);
    if (blur_max[2] - blur_min[2] < fMinDist) {
      float avg = (blur_min[2] + blur_max[2]) * 0.5f;
      blur_min[2] = avg - fMinDist * 0.5f;
      blur_max[2] = avg - fMinDist * 0.5f;
    }

    float fscale[3];
    float fbias[3];

    // figure out the progressive scale & bias needed, at each step,
    // to go from one [min..max] range to the next.
    float temp_min, temp_max;
    fscale[0] = 1.0f / (blur_max[0] - blur_min[0]);
    fbias[0] = -blur_min[0] * fscale[0];
    temp_min = (blur_min[1] - blur_min[0]) / (blur_max[0] - blur_min[0]);
    temp_max = (blur_max[1] - blur_min[0]) / (blur_max[0] - blur_min[0]);
    fscale[1] = 1.0f / (temp_max - temp_min);
    fbias[1] = -temp_min * fscale[1];
    temp_min = (blur_min[2] - blur_min[1]) / (blur_max[1] - blur_min[1]);
    temp_max = (blur_max[2] - blur_min[1]) / (blur_max[1] - blur_min[1]);
    fscale[2] = 1.0f / (temp_max - temp_min);
    fbias[2] = -temp_min * fscale[2];

    const std::vector<Texture_*>& blurTextures = textureManager->getBlurTextures();
    const Texture_* mainTexture = textureManager->getMainTexture();

    glBlendFunc(GL_ONE, GL_ZERO);
    glBindVertexArray(vaoBlur);

    for (unsigned int i = 0; i < passes; i++)
    {
      // set pixel shader
      if ((i % 2) == 0) {
        glUseProgram(programID_blur1);
        glUniform1i(uniform_blur1_sampler, 0);

      }
      else {
        glUseProgram(programID_blur2);
        glUniform1i(uniform_blur2_sampler, 0);

      }

      glViewport(0, 0, blurTextures[i]->width, blurTextures[i]->height);

      // hook up correct source texture - assume there is only one, at stage 0
      glActiveTexture(GL_TEXTURE0);
      if (i == 0) {
        glBindTexture(GL_TEXTURE_2D, mainTexture->texID);
      }
      else {
        glBindTexture(GL_TEXTURE_2D, blurTextures[i - 1]->texID);
      }

      float srcw = (i == 0) ? mainTexture->width : blurTextures[i - 1]->width;
      float srch = (i == 0) ? mainTexture->height : blurTextures[i - 1]->height;


      float fscale_now = fscale[i / 2];
      float fbias_now = fbias[i / 2];

      // set constants
      if (i % 2 == 0)
      {
        // pass 1 (long horizontal pass)
        //-------------------------------------
        const float w1 = w[0] + w[1];
        const float w2 = w[2] + w[3];
        const float w3 = w[4] + w[5];
        const float w4 = w[6] + w[7];
        const float d1 = 0 + 2 * w[1] / w1;
        const float d2 = 2 + 2 * w[3] / w2;
        const float d3 = 4 + 2 * w[5] / w3;
        const float d4 = 6 + 2 * w[7] / w4;
        const float w_div = 0.5f / (w1 + w2 + w3 + w4);
        //-------------------------------------
        //float4 _c0; // source texsize (.xy), and inverse (.zw)
        //float4 _c1; // w1..w4
        //float4 _c2; // d1..d4
        //float4 _c3; // scale, bias, w_div, 0
        //-------------------------------------
        glUniform4f(uniform_blur1_c0, srcw, srch, 1.0f / srcw, 1.0f / srch);
        glUniform4f(uniform_blur1_c1, w1, w2, w3, w4);
        glUniform4f(uniform_blur1_c2, d1, d2, d3, d4);
        glUniform4f(uniform_blur1_c3, fscale_now, fbias_now, w_div, 0.0);
      }
      else
      {
        // pass 2 (short vertical pass)
        //-------------------------------------
        const float w1 = w[0] + w[1] + w[2] + w[3];
        const float w2 = w[4] + w[5] + w[6] + w[7];
        const float d1 = 0 + 2 * ((w[2] + w[3]) / w1);
        const float d2 = 2 + 2 * ((w[6] + w[7]) / w2);
        const float w_div = 1.0f / ((w1 + w2) * 2);
        //-------------------------------------
        //float4 _c0; // source texsize (.xy), and inverse (.zw)
        //float4 _c5; // w1,w2,d1,d2
        //float4 _c6; // w_div, edge_darken_c1, edge_darken_c2, edge_darken_c3
        //-------------------------------------
        glUniform4f(uniform_blur2_c0, srcw, srch, 1.0f / srcw, 1.0f / srch);
        glUniform4f(uniform_blur2_c5, w1, w2, d1, d2);
        // note: only do this first time; if you do it many times,
        // then the super-blurred levels will have big black lines along the top & left sides.
        if (i == 1)
          glUniform4f(uniform_blur2_c6, w_div, (1 - edge_darken), edge_darken, 5.0f); //darken edges
        else
          glUniform4f(uniform_blur2_c6, w_div, 1.0f, 0.0f, 5.0f); // don't darken
      }

      // draw fullscreen quad
      glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

      // save to blur texture
      glBindTexture(GL_TEXTURE_2D, blurTextures[i]->texID);
      glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, blurTextures[i]->width, blurTextures[i]->height);
    }

    glBindVertexArray(0);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    glBindTexture(GL_TEXTURE_2D, 0);
  }

  void SetupTextures(GLuint program, const Shader__& shader)
  {

    unsigned int texNum = 0;
    std::map<std::string, Texture_*> texsizes;

    // Set samplers
    for (std::map<std::string, TextureSamplerDesc>::const_iterator iter_samplers = shader.textures.begin(); iter_samplers
      != shader.textures.end(); ++iter_samplers)
    {
      std::string texName = iter_samplers->first;
      Texture_* texture = iter_samplers->second.first;
      Sampler* sampler = iter_samplers->second.second;
      std::string samplerName = "sampler_" + texName;

      // https://www.khronos.org/opengl/wiki/Sampler_(GLSL)#Binding_textures_to_samplers
      GLint param = glGetUniformLocation(program, samplerName.c_str());
      if (param < 0) {
        // unused uniform have been optimized out by glsl compiler
        continue;
      }

      texsizes[texName] = texture;
      texsizes[texture->name] = texture;

      glActiveTexture(GL_TEXTURE0 + texNum);
      glBindTexture(texture->type, texture->texID);
      glBindSampler(texNum, sampler->samplerID);

      glUniform1i(param, texNum);
      texNum++;
    }


    // Set texsizes
    std::map<std::string, Texture_*>::const_iterator iter_textures = texsizes.cbegin();
    for (; iter_textures != texsizes.cend(); ++iter_textures)
    {
      Texture_* texture = iter_textures->second;

      std::string texsizeName = "texsize_" + iter_textures->first;
      GLint textSizeParam = glGetUniformLocation(program, texsizeName.c_str());
      if (textSizeParam >= 0) {
        glUniform4f(textSizeParam, texture->width, texture->height,
          1 / (float)texture->width, 1 / (float)texture->height);
      }
      else {
        // unused uniform have been optimized out by glsl compiler
        continue;
      }
    }
  }

  void SetupShaderVariables(GLuint program, const Pipeline& pipeline, const PipelineContext& context)
  {
    // pass info from projectM to the shader uniforms
    // these are the inputs: http://www.geisswerks.com/milkdrop/milkdrop_preset_authoring.html#3f6

    float time_since_preset_start = context.time - context.presetStartTime;
    float time_since_preset_start_wrapped = time_since_preset_start - (int)(time_since_preset_start / 10000) * 10000;
    float mip_x = logf((float)texsizeX) / logf(2.0f);
    float mip_y = logf((float)texsizeX) / logf(2.0f);
    float mip_avg = 0.5f * (mip_x + mip_y);

    glUniform4f(glGetUniformLocation(program, "rand_frame"), (rand() % 100) * .01, (rand() % 100) * .01, (rand() % 100) * .01, (rand() % 100) * .01);
    glUniform4f(glGetUniformLocation(program, "rand_preset"), rand_preset[0], rand_preset[1], rand_preset[2], rand_preset[3]);

    glUniform4f(glGetUniformLocation(program, "_c0"), aspectX, aspectY, 1 / aspectX, 1 / aspectY);
    glUniform4f(glGetUniformLocation(program, "_c1"), 0.0, 0.0, 0.0, 0.0);
    glUniform4f(glGetUniformLocation(program, "_c2"), time_since_preset_start_wrapped, context.fps, context.frame, context.progress);
    glUniform4f(glGetUniformLocation(program, "_c3"), beatDetect->bass / 100, beatDetect->mid / 100, beatDetect->treb / 100, beatDetect->vol / 100);
    glUniform4f(glGetUniformLocation(program, "_c4"), beatDetect->bass_att / 100, beatDetect->mid_att / 100, beatDetect->treb_att / 100, beatDetect->vol_att / 100);
    glUniform4f(glGetUniformLocation(program, "_c5"), pipeline.blur1x - pipeline.blur1n, pipeline.blur1n, pipeline.blur2x - pipeline.blur2n, pipeline.blur2n);
    glUniform4f(glGetUniformLocation(program, "_c6"), pipeline.blur3x - pipeline.blur3n, pipeline.blur3n, pipeline.blur1n, pipeline.blur1x);
    glUniform4f(glGetUniformLocation(program, "_c7"), texsizeX, texsizeY, 1 / (float)texsizeX, 1 / (float)texsizeY);

    glUniform4f(glGetUniformLocation(program, "_c8"), 0.5f + 0.5f * cosf(context.time * 0.329f + 1.2f),
      0.5f + 0.5f * cosf(context.time * 1.293f + 3.9f),
      0.5f + 0.5f * cosf(context.time * 5.070f + 2.5f),
      0.5f + 0.5f * cosf(context.time * 20.051f + 5.4f));

    glUniform4f(glGetUniformLocation(program, "_c9"), 0.5f + 0.5f * sinf(context.time * 0.329f + 1.2f),
      0.5f + 0.5f * sinf(context.time * 1.293f + 3.9f),
      0.5f + 0.5f * sinf(context.time * 5.070f + 2.5f),
      0.5f + 0.5f * sinf(context.time * 20.051f + 5.4f));

    glUniform4f(glGetUniformLocation(program, "_c10"), 0.5f + 0.5f * cosf(context.time * 0.0050f + 2.7f),
      0.5f + 0.5f * cosf(context.time * 0.0085f + 5.3f),
      0.5f + 0.5f * cosf(context.time * 0.0133f + 4.5f),
      0.5f + 0.5f * cosf(context.time * 0.0217f + 3.8f));

    glUniform4f(glGetUniformLocation(program, "_c11"), 0.5f + 0.5f * sinf(context.time * 0.0050f + 2.7f),
      0.5f + 0.5f * sinf(context.time * 0.0085f + 5.3f),
      0.5f + 0.5f * sinf(context.time * 0.0133f + 4.5f),
      0.5f + 0.5f * sinf(context.time * 0.0217f + 3.8f));

    glUniform4f(glGetUniformLocation(program, "_c12"), mip_x, mip_y, mip_avg, 0);
    glUniform4f(glGetUniformLocation(program, "_c13"), pipeline.blur2n, pipeline.blur2x, pipeline.blur3n, pipeline.blur3x);


    glm::mat4 temp_mat[24];

    // write matrices
    for (int i = 0; i < 20; i++)
    {
      glm::mat4 mx, my, mz, mxlate;

      mx = glm::rotate(glm::mat4(1.0f), rot_base[i].x + rot_speed[i].x * context.time, glm::vec3(1.0f, 0.0f, 0.0f));
      my = glm::rotate(glm::mat4(1.0f), rot_base[i].y + rot_speed[i].y * context.time, glm::vec3(0.0f, 1.0f, 0.0f));
      mz = glm::rotate(glm::mat4(1.0f), rot_base[i].z + rot_speed[i].z * context.time, glm::vec3(0.0f, 0.0f, 1.0f));

      mxlate = glm::translate(glm::mat4(1.0f), glm::vec3(xlate[i].x, xlate[i].y, xlate[i].z));

      temp_mat[i] = mxlate * mx;
      temp_mat[i] = mz * temp_mat[i];
      temp_mat[i] = my * temp_mat[i];
    }

    // the last 4 are totally random, each frame
    for (int i = 20; i < 24; i++)
    {
      glm::mat4 mx, my, mz, mxlate;
      mx = glm::rotate(glm::mat4(1.0f), FRAND * 6.28f, glm::vec3(1.0f, 0.0f, 0.0f));
      my = glm::rotate(glm::mat4(1.0f), FRAND * 6.28f, glm::vec3(0.0f, 1.0f, 0.0f));
      mz = glm::rotate(glm::mat4(1.0f), FRAND * 6.28f, glm::vec3(0.0f, 0.0f, 1.0f));

      mxlate = glm::translate(glm::mat4(1.0f), glm::vec3(FRAND, FRAND, FRAND));

      temp_mat[i] = mxlate * mx;
      temp_mat[i] = mz * temp_mat[i];
      temp_mat[i] = my * temp_mat[i];
    }

    glUniformMatrix3x4fv(glGetUniformLocation(program, "rot_s1"), 1, GL_FALSE, glm::value_ptr(glm::mat3x4(temp_mat[0])));
    glUniformMatrix3x4fv(glGetUniformLocation(program, "rot_s2"), 1, GL_FALSE, glm::value_ptr(glm::mat3x4(temp_mat[1])));
    glUniformMatrix3x4fv(glGetUniformLocation(program, "rot_s3"), 1, GL_FALSE, glm::value_ptr(glm::mat3x4(temp_mat[2])));
    glUniformMatrix3x4fv(glGetUniformLocation(program, "rot_s4"), 1, GL_FALSE, glm::value_ptr(glm::mat3x4(temp_mat[3])));
    glUniformMatrix3x4fv(glGetUniformLocation(program, "rot_d1"), 1, GL_FALSE, glm::value_ptr(glm::mat3x4(temp_mat[4])));
    glUniformMatrix3x4fv(glGetUniformLocation(program, "rot_d2"), 1, GL_FALSE, glm::value_ptr(glm::mat3x4(temp_mat[5])));
    glUniformMatrix3x4fv(glGetUniformLocation(program, "rot_d3"), 1, GL_FALSE, glm::value_ptr(glm::mat3x4(temp_mat[6])));
    glUniformMatrix3x4fv(glGetUniformLocation(program, "rot_d4"), 1, GL_FALSE, glm::value_ptr(glm::mat3x4(temp_mat[7])));
    glUniformMatrix3x4fv(glGetUniformLocation(program, "rot_f1"), 1, GL_FALSE, glm::value_ptr(glm::mat3x4(temp_mat[8])));
    glUniformMatrix3x4fv(glGetUniformLocation(program, "rot_f2"), 1, GL_FALSE, glm::value_ptr(glm::mat3x4(temp_mat[9])));
    glUniformMatrix3x4fv(glGetUniformLocation(program, "rot_f3"), 1, GL_FALSE, glm::value_ptr(glm::mat3x4(temp_mat[10])));
    glUniformMatrix3x4fv(glGetUniformLocation(program, "rot_f4"), 1, GL_FALSE, glm::value_ptr(glm::mat3x4(temp_mat[11])));
    glUniformMatrix3x4fv(glGetUniformLocation(program, "rot_vf1"), 1, GL_FALSE, glm::value_ptr(glm::mat3x4(temp_mat[12])));
    glUniformMatrix3x4fv(glGetUniformLocation(program, "rot_vf2"), 1, GL_FALSE, glm::value_ptr(glm::mat3x4(temp_mat[13])));
    glUniformMatrix3x4fv(glGetUniformLocation(program, "rot_vf3"), 1, GL_FALSE, glm::value_ptr(glm::mat3x4(temp_mat[14])));
    glUniformMatrix3x4fv(glGetUniformLocation(program, "rot_vf4"), 1, GL_FALSE, glm::value_ptr(glm::mat3x4(temp_mat[15])));
    glUniformMatrix3x4fv(glGetUniformLocation(program, "rot_uf1"), 1, GL_FALSE, glm::value_ptr(glm::mat3x4(temp_mat[16])));
    glUniformMatrix3x4fv(glGetUniformLocation(program, "rot_uf2"), 1, GL_FALSE, glm::value_ptr(glm::mat3x4(temp_mat[17])));
    glUniformMatrix3x4fv(glGetUniformLocation(program, "rot_uf3"), 1, GL_FALSE, glm::value_ptr(glm::mat3x4(temp_mat[18])));
    glUniformMatrix3x4fv(glGetUniformLocation(program, "rot_uf4"), 1, GL_FALSE, glm::value_ptr(glm::mat3x4(temp_mat[19])));
    glUniformMatrix3x4fv(glGetUniformLocation(program, "rot_rand1"), 1, GL_FALSE, glm::value_ptr(glm::mat3x4(temp_mat[20])));
    glUniformMatrix3x4fv(glGetUniformLocation(program, "rot_rand2"), 1, GL_FALSE, glm::value_ptr(glm::mat3x4(temp_mat[21])));
    glUniformMatrix3x4fv(glGetUniformLocation(program, "rot_rand3"), 1, GL_FALSE, glm::value_ptr(glm::mat3x4(temp_mat[22])));
    glUniformMatrix3x4fv(glGetUniformLocation(program, "rot_rand4"), 1, GL_FALSE, glm::value_ptr(glm::mat3x4(temp_mat[23])));

    // set program uniform "_q[a-h]" values (_qa.x, _qa.y, _qa.z, _qa.w, _qb.x, _qb.y ... ) alias q[1-32]
    for (int i = 0; i < 32; i += 4) {
      std::string varName = "_q";
      varName.push_back('a' + i / 4);
      glUniform4f(glGetUniformLocation(program, varName.c_str()), pipeline.q[i], pipeline.q[i + 1], pipeline.q[i + 2], pipeline.q[i + 3]);
    }
  }

  bool enableWarpShader(Shader__& shader, const Pipeline& pipeline, const PipelineContext& pipelineContext, const glm::mat4& mat_ortho)
  {
    if (presetWarpShaderLoaded)
    {
      glUseProgram(programID_presetWarp);

      SetupTextures(programID_presetWarp, shader);

      SetupShaderVariables(programID_presetWarp, pipeline, pipelineContext);

      glUniformMatrix4fv(uniform_vertex_transf_warp_shader, 1, GL_FALSE, glm::value_ptr(mat_ortho));

      return true;
    }

    glUseProgram(programID_v2f_c4f_t2f);

    glUniformMatrix4fv(uniform_v2f_c4f_t2f_vertex_tranformation, 1, GL_FALSE, glm::value_ptr(mat_ortho));
    glUniform1i(uniform_v2f_c4f_t2f_frag_texture_sampler, 0);

    return false;
  }

  bool enableCompositeShader(Shader__& shader, const Pipeline& pipeline, const PipelineContext& pipelineContext) {
    if (presetCompShaderLoaded) {
      glUseProgram(programID_presetComp);

      SetupTextures(programID_presetComp, shader);

      SetupShaderVariables(programID_presetComp, pipeline, pipelineContext);

#if OGL_DEBUG
      validateProgram(programID_presetComp);
#endif

      return true;
    }

    glUseProgram(programID_v2f_c4f_t2f);

    return false;
  }
};

struct PerPixelMesh
{
  int width;
  int height;
  int size;
  std::vector<PixelPoint> p;
  std::vector<PixelPoint> p_original;
  std::vector<PerPixelContext> identity;

  PerPixelMesh(int _width, int _height) : width(_width), height(_height), size(_width* _height),
    p(_width* _height, PixelPoint{ 0, 0 }),
    p_original(_width* _height, PixelPoint{ 0, 0 }),
    identity(_width* _height, PerPixelContext())
  {
    for (int j = 0; j < height; j++)
      for (int i = 0; i < width; i++)
      {
        int index = j * width + i;

        float xval = i / (float)(width - 1);
        float yval = -((j / (float)(height - 1)) - 1);

        p[index].x = xval;
        p[index].y = yval;

        p_original[index].x = xval;
        p_original[index].y = yval;

        identity[index].x = xval;
        identity[index].y = yval;

        //identity[index].x= (xval-.5)*2;
        //identity[index].y= (yval-.5)*2;

        identity[index].i = i;
        identity[index].j = j;

        identity[index].rad = hypot((xval - .5) * 2, (yval - .5) * 2) * .7071067;
        identity[index].theta = atan2((yval - .5) * 2, (xval - .5) * 2);
      }
  }

  void Reset()
  {
    std::copy(p_original.begin(), p_original.end(), p.begin());
  }
};

struct MilkdropWaveform : public RenderItem
{
  float x;
  float y;

  float r;
  float g;
  float b;
  float a;

  float mystery;
  MilkdropWaveformMode mode;
  bool additive;
  bool dots;
  bool thick;
  bool modulateAlphaByVolume;
  bool maximizeColors;
  float scale;
  float smoothing;
  float modOpacityStart;
  float modOpacityEnd;
  float temp_a;
  float rot;
  float aspectScale;
  int samples;
  bool two_waves;
  bool loop;
  float wavearray[2048][2];
  float wavearray2[2048][2];

  MilkdropWaveform() : RenderItem(),
    x(0.5), y(0.5), r(1), g(0), b(0), a(1), mystery(0), mode(Line), additive(false), dots(false), thick(false),
    modulateAlphaByVolume(false), maximizeColors(false), scale(10), smoothing(0),
    modOpacityStart(0), modOpacityEnd(1), rot(0), samples(512), loop(false) {

    Init();
  }

  void WaveformMath(RenderContext& context)
  {
    const float* pcmdataR = context.beatDetect->pcm->pcmdataR;
    const float* pcmdataL = context.beatDetect->pcm->pcmdataL;
    // scale PCM data based on vol_history to make it more or less independent of the application output volume
    const float  vol_scale = context.beatDetect->getPCMScale();

    float r2, theta;

    float wave_x_temp = 0;
    float wave_y_temp = 0;

    float cos_rot;
    float sin_rot;

    const float temp_y = -1 * (y - 1.0f);

    two_waves = false;
    loop = false;

    switch (mode)
    {

    case Circle:
    {
      loop = true;
      rot = 0;
      aspectScale = 1.0;

      samples = context.beatDetect->pcm->numsamples;

      float inv_nverts_minus_one = 1.0f / (float)(samples);

      float last_value = vol_scale * (pcmdataR[samples - 1] + pcmdataL[samples - 1]);
      float first_value = vol_scale * (pcmdataR[0] + pcmdataL[0]);
      float offset = first_value - last_value;

      for (int i = 0; i < samples; i++)
      {

        float value = vol_scale * (pcmdataR[i] + pcmdataL[i]);
        value += offset * (i / (float)samples);

        r2 = (0.5f + 0.4f * .12f * value * scale + mystery) * .5f;
        theta = i * inv_nverts_minus_one * 6.28f + context.time * 0.2f;

        wavearray[i][0] = (r2 * cos(theta) * (context.aspectCorrect ? context.aspectRatio : 1.0f) + x);
        wavearray[i][1] = (r2 * sin(theta) + temp_y);
      }
    }
    break;

    case RadialBlob://circularly moving waveform
    {
      rot = 0;
      aspectScale = context.aspectRatio;

      samples = 512 - 32;
      for (int i = 0; i < 512 - 32; i++)
      {
        theta = vol_scale * pcmdataL[i + 32] * 0.06f * scale * 1.57f + context.time * 2.3f;
        r2 = (0.53f + 0.43f * vol_scale * pcmdataR[i] * 0.12f * scale + mystery) * .5f;

        wavearray[i][0] = (r2 * cos(theta) * (context.aspectCorrect ? context.aspectRatio : 1.0f) + x);
        wavearray[i][1] = (r2 * sin(theta) + temp_y);
      }
    }
    break;

    case Blob2://EXPERIMENTAL

      rot = 0;
      aspectScale = 1.0;
      samples = 512 - 32;

      for (int i = 0; i < 512 - 32; i++)
      {
        wavearray[i][0] = (vol_scale * pcmdataR[i] * scale * 0.5f * (context.aspectCorrect ? context.aspectRatio : 1.0f) + x);
        wavearray[i][1] = (vol_scale * pcmdataL[i + 32] * scale * 0.5f + temp_y);
      }

      break;

    case Blob3://EXPERIMENTAL

      rot = 0;
      aspectScale = 1.0;

      samples = 512 - 32;

      for (int i = 0; i < 512 - 32; i++)
      {
        wavearray[i][0] = (vol_scale * pcmdataR[i] * scale * 0.5f + x);
        wavearray[i][1] = ((vol_scale * pcmdataL[i + 32] * scale * 0.5f + temp_y));
      }

      break;

    case DerivativeLine://single x-axis derivative waveform
    {
      rot = -mystery * 90;
      aspectScale = 1.0;

      float w1 = 0.45f + 0.5f * (mystery * 0.5f + 0.5f);
      float w2 = 1.0f - w1;
      float xx[512], yy[512];
      samples = 512 - 32;

      for (int i = 0; i < 512 - 32; i++)
      {
        xx[i] = -1.0f + 2.0f * (i / (512.0f - 32.0f)) + x;
        yy[i] = 0.4f * vol_scale * pcmdataL[i] * 0.47f * scale + temp_y;
        xx[i] += 0.4f * vol_scale * pcmdataR[i] * 0.44f * scale;

        if (i > 1)
        {
          xx[i] = xx[i] * w2 + w1 * (xx[i - 1] * 2.0f - xx[i - 2]);
          yy[i] = yy[i] * w2 + w1 * (yy[i - 1] * 2.0f - yy[i - 2]);
        }
        wavearray[i][0] = xx[i];
        wavearray[i][1] = yy[i];
      }											   		}
    break;

    case Blob5://EXPERIMENTAL

      rot = 0;
      aspectScale = 1.0;

      cos_rot = cosf(context.time * 0.3f);
      sin_rot = sinf(context.time * 0.3f);
      samples = 512 - 32;

      for (int i = 0; i < 512 - 32; i++)
      {
        float x0 = (vol_scale * pcmdataR[i] * vol_scale * pcmdataL[i + 32] + vol_scale * pcmdataL[i + 32] * vol_scale * pcmdataR[i]);
        float y0 = (vol_scale * pcmdataR[i] * vol_scale * pcmdataR[i] - vol_scale * pcmdataL[i + 32] * vol_scale * pcmdataL[i + 32]);
        wavearray[i][0] = ((x0 * cos_rot - y0 * sin_rot) * scale * 0.5f * (context.aspectCorrect ? context.aspectRatio : 1.0f) + x);
        wavearray[i][1] = ((x0 * sin_rot + y0 * cos_rot) * scale * 0.5f + temp_y);
      }
      break;

    case Line://single waveform


      wave_x_temp = -2 * 0.4142f * (fabs(fabs(mystery) - .5f) - .5f);

      rot = -mystery * 90;
      aspectScale = 1.0f + wave_x_temp;
      wave_x_temp = -1 * (x - 1.0f);
      samples = context.beatDetect->pcm->numsamples;

      for (int i = 0; i < samples; i++)
      {
        wavearray[i][0] = i / (float)samples;
        wavearray[i][1] = vol_scale * pcmdataR[i] * .04f * scale + wave_x_temp;
      }
      //	  printf("%f %f\n",renderTarget->texsize*wave_y_temp,wave_y_temp);

      break;

    case DoubleLine://dual waveforms
    {

      wave_x_temp = -2 * 0.4142f * (fabs(fabs(mystery) - .5f) - .5f);

      rot = -mystery * 90;
      aspectScale = 1.0f + wave_x_temp;


      samples = context.beatDetect->pcm->numsamples;
      two_waves = true;

      const float y_adj = y * y * .5f;

      wave_y_temp = -1 * (x - 1);

      for (int i = 0; i < samples; i++)
      {
        wavearray[i][0] = i / ((float)samples);
        wavearray[i][1] = vol_scale * pcmdataL[i] * .04f * scale + (wave_y_temp + y_adj);
      }

      for (int i = 0; i < samples; i++)
      {
        wavearray2[i][0] = i / ((float)samples);
        wavearray2[i][1] = vol_scale * pcmdataR[i] * .04f * scale + (wave_y_temp - y_adj);
      }

      break;
    }
    case last:
      break;
    }
  }

  void ModulateOpacityByVolume(RenderContext& context)
  {

    //modulate volume by opacity
    //
    //set an upper and lower bound and linearly
    //calculate the opacity from 0=lower to 1=upper
    //based on current volume

    if (context.beatDetect->vol <= modOpacityStart)  temp_a = 0.0;
    else if (context.beatDetect->vol >= modOpacityEnd) temp_a = a;
    else temp_a = a * ((context.beatDetect->vol - modOpacityStart) / (modOpacityEnd - modOpacityStart));

  }

  void MaximizeColors(RenderContext& context)
  {
    float wave_r_switch = 0, wave_g_switch = 0, wave_b_switch = 0;
    //wave color brightening
    //
    //forces max color value to 1.0 and scales
    // the rest accordingly
    if (mode == Blob2 || mode == Blob5)
      switch (context.texsize)
      {
      case 256:  temp_a *= 0.07f; break;
      case 512:  temp_a *= 0.09f; break;
      case 1024: temp_a *= 0.11f; break;
      case 2048: temp_a *= 0.13f; break;
      }
    else if (mode == Blob3)
    {
      switch (context.texsize)
      {
      case 256:  temp_a *= 0.075f; break;
      case 512:  temp_a *= 0.15f; break;
      case 1024: temp_a *= 0.22f; break;
      case 2048: temp_a *= 0.33f; break;
      }
      temp_a *= 1.3f;
      temp_a *= std::pow(context.beatDetect->treb, 2.0f);
    }

    if (maximizeColors == true)
    {
      if (r >= g && r >= b)   //red brightest
      {
        wave_b_switch = b * (1 / r);
        wave_g_switch = g * (1 / r);
        wave_r_switch = 1.0;
      }
      else if (b >= g && b >= r)         //blue brightest
      {
        wave_r_switch = r * (1 / b);
        wave_g_switch = g * (1 / b);
        wave_b_switch = 1.0;

      }

      else  if (g >= b && g >= r)         //green brightest
      {
        wave_b_switch = b * (1 / g);
        wave_r_switch = r * (1 / g);
        wave_g_switch = 1.0;
      }


      glVertexAttrib4f(1, wave_r_switch, wave_g_switch, wave_b_switch, temp_a * masterAlpha);
    }
    else
    {
      glVertexAttrib4f(1, r, g, b, temp_a * masterAlpha);
    }
  }

  void Draw(RenderContext& context) override
  {
    // wavearray is 2048 so don't exceed that
    // TODO use named constant
    if (samples > 2048)
      samples = 2048;
    if (samples > Const::audioMaximumPossibleBlockSize)
      samples = Const::audioMaximumPossibleBlockSize;

    WaveformMath(context);

    for (int waveno = 1; waveno <= (two_waves ? 2 : 1); waveno++)
    {
      glBindBuffer(GL_ARRAY_BUFFER, m_vboID);

      glBufferData(GL_ARRAY_BUFFER, sizeof(float) * samples * 2, nullptr, GL_DYNAMIC_DRAW);
      if (waveno == 1)
        glBufferData(GL_ARRAY_BUFFER, sizeof(float) * samples * 2, wavearray, GL_DYNAMIC_DRAW);
      else
        glBufferData(GL_ARRAY_BUFFER, sizeof(float) * samples * 2, wavearray2, GL_DYNAMIC_DRAW);

      glBindBuffer(GL_ARRAY_BUFFER, 0);

      glUseProgram(context.programID_v2f_c4f);

      glm::mat4 mat_first_translation = glm::mat4(1.0);
      mat_first_translation[3][0] = -0.5;
      mat_first_translation[3][1] = -0.5;

      glm::mat4 mat_scale = glm::mat4(1.0);
      mat_scale[0][0] = aspectScale;

      float s = glm::sin(glm::radians(-rot));
      float c = glm::cos(glm::radians(-rot));
      glm::mat4 mat_rotation = glm::mat4(c, -s, 0, 0,
        s, c, 0, 0,
        0, 0, 1, 0,
        0, 0, 0, 1);

      glm::mat4 mat_second_translation = glm::mat4(1.0);
      mat_second_translation[3][0] = 0.5;
      mat_second_translation[3][1] = 0.5;

      glm::mat4 mat_vertex = context.mat_ortho;
      mat_vertex = mat_first_translation * mat_vertex;
      mat_vertex = mat_scale * mat_vertex;
      mat_vertex = mat_rotation * mat_vertex;
      mat_vertex = mat_second_translation * mat_vertex;
      glUniformMatrix4fv(context.uniform_v2f_c4f_vertex_tranformation, 1, GL_FALSE, glm::value_ptr(mat_vertex));

      if (modulateAlphaByVolume)
        ModulateOpacityByVolume(context);
      else
        temp_a = a;
      MaximizeColors(context);

#ifndef GL_TRANSITION
      if (dots == 1) glEnable(GL_LINE_STIPPLE);
#endif

      //Thick wave drawing
      if (thick == 1)
      {
        glLineWidth((context.texsize < 512) ? 2 : 2 * context.texsize / 512);
        ASSERT(glGetError() == GL_NO_ERROR);
      }
      else
      {
        glLineWidth((context.texsize < 512) ? 1 : context.texsize / 512);
        ASSERT(glGetError() == GL_NO_ERROR);
      }

      //Additive wave drawing (vice overwrite)
      if (additive == 1)
        glBlendFunc(GL_SRC_ALPHA, GL_ONE);
      else
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

      glBindVertexArray(m_vaoID);

      if (loop)
        glDrawArrays(GL_LINE_LOOP, 0, samples);
      else
        glDrawArrays(GL_LINE_STRIP, 0, samples);

      glBindVertexArray(0);
    }

#ifndef GL_TRANSITION
    if (dots == 1) glDisable(GL_LINE_STIPPLE);
#endif
  }

  void InitVertexAttrib() override
  {
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, (void*)0);
    glDisableVertexAttribArray(1);
  }
};

typedef struct
{
  float x, y;     // screen position + Z-buffer depth
  float Diffuse[4];     // diffuse color
  float tu, tv;           // DYNAMIC
  float rad, ang;         // STATIC
} composite_shader_vertex;

struct Renderer_
{
  bool noSwitch;
  RenderContext renderContext;
  ShaderEngine shaderEngine;
  std::string m_presetName;
  TextureManager* textureManager;
  PerPixelMesh mesh;
  BeatDetect* beatDetect;
  Pipeline* currentPipe;
  std::string m_datadir;
  std::string presetURL;
  GLuint m_vbo_Interpolation;
  GLuint m_vao_Interpolation;
  GLuint m_vbo_CompositeOutput;
  GLuint m_vao_CompositeOutput;
  GLuint m_vbo_CompositeShaderOutput;
  GLuint m_vao_CompositeShaderOutput;
  i32 totalframes = 1;
  float* p;
  int vstartx; /* view start x position - normally 0, but could be different if doing a subset of the window - like
                for virtual reality */
  int vstarty; /* view start y position - normally 0, but could be different if doing a subset of the window - like
                  for virtual reality */
                  /* these are currently set only for rendering to the screen, not to the textbuffer */
  int vw;
  int vh;
  float aspect;
  bool correction;
  std::vector<MilkdropWaveform> waveformList;
  int texsizeX;
  int texsizeY;
  float m_fAspectX;
  float m_fAspectY;
  float m_fInvAspectX;
  float m_fInvAspectY;
  GLuint textureRenderToTexture;
  composite_shader_vertex m_comp_verts[FCGSX * FCGSY];
  int m_comp_indices[(FCGSX - 2) * (FCGSY - 2) * 6];



  Renderer_(int width, int height, int gx, int gy, BeatDetect* _beatDetect, std::string _presetURL,
    std::string _titlefontURL, std::string _menufontURL, const std::string& datadir) :
    mesh(gx, gy), m_presetName("None"), m_datadir(datadir), vw(width), vh(height), presetURL(_presetURL)
  {
    this->totalframes = 1;
    //this->lastTimeFPS = nowMilliseconds();
    //this->currentTimeFPS = nowMilliseconds();
    //this->lastTimeToast = nowMilliseconds();
    //this->currentTimeToast = nowMilliseconds();
    this->noSwitch = false;
    //this->showfps = false;
    //this->showtoast = false;
    //this->showtitle = false;
    //this->showpreset = false;
    //this->showhelp = false;
    //this->showsearch = false;
    //this->showmenu = false;
    //this->showstats = false;
    //this->studio = false;
    //this->m_activePresetID = 0;
    //this->realfps = 0;
    /* Set up the v xoffset and vy offset to 0 which is normal Only used for VR */
    this->vstartx = 0;
    this->vstarty = 0;

    //this->drawtitle = 0;

    // This is the default help menu for applications that have not defined any custom menu.
    const char* defaultHelpMenu = "\n"
      "F1: This help menu""\n"
      "F3: Show preset name""\n"
      "F5: Show FPS""\n"
      "L: Lock/Unlock Preset""\n"
      "R: Random preset""\n"
      "N/P: [N]ext+ or [P]revious-reset""\n"
      "M: Preset Menu (Arrow Up/Down & Page Up/Down to Navigate)""\n"
      "Arrow Up/Down: Increase or Decrease Beat Sensitivity""\n"
      "CTRL-F: Fullscreen";

    //this->setHelpText(defaultHelpMenu);

    //this->title = "Unknown";

    /** Other stuff... */
    this->correction = true;

    /// @bug put these on member init list
    this->textureManager = nullptr;
    this->beatDetect = _beatDetect;

    textureRenderToTexture = 0;

    int size = (mesh.height - 1) * mesh.width * 4 * 2;
    p = static_cast<float*>(wipemalloc(size * sizeof(float)));


    for (int j = 0; j < mesh.height - 1; j++)
    {
      int base = j * mesh.width * 4 * 2;


      for (int i = 0; i < mesh.width; i++)
      {
        int index = j * mesh.width + i;
        int index2 = (j + 1) * mesh.width + i;

        int strip = base + i * 8;
        p[strip + 0] = mesh.identity[index].x;
        p[strip + 1] = mesh.identity[index].y;

        p[strip + 4] = mesh.identity[index2].x;
        p[strip + 5] = mesh.identity[index2].y;
      }
    }

    renderContext.programID_v2f_c4f = shaderEngine.programID_v2f_c4f;
    renderContext.programID_v2f_c4f_t2f = shaderEngine.programID_v2f_c4f_t2f;

    renderContext.uniform_v2f_c4f_vertex_tranformation = shaderEngine.uniform_v2f_c4f_vertex_tranformation;
    renderContext.uniform_v2f_c4f_vertex_point_size = shaderEngine.uniform_v2f_c4f_vertex_point_size;
    renderContext.uniform_v2f_c4f_t2f_vertex_tranformation = shaderEngine.uniform_v2f_c4f_t2f_vertex_tranformation;
    renderContext.uniform_v2f_c4f_t2f_frag_texture_sampler = shaderEngine.uniform_v2f_c4f_t2f_frag_texture_sampler;

    // Interpolation VAO/VBO's
    glGenBuffers(1, &m_vbo_Interpolation);
    glGenVertexArrays(1, &m_vao_Interpolation);

    glBindVertexArray(m_vao_Interpolation);
    glBindBuffer(GL_ARRAY_BUFFER, m_vbo_Interpolation);

    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, static_cast<void*>(nullptr)); // Positions

    glDisableVertexAttribArray(1);

    glEnableVertexAttribArray(2);
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, (void*)(sizeof(float) * 2)); // Textures

    glBindVertexArray(0);
    glBindBuffer(GL_ARRAY_BUFFER, 0);

    // CompositeOutput VAO/VBO's
    glGenBuffers(1, &m_vbo_CompositeOutput);
    glGenVertexArrays(1, &m_vao_CompositeOutput);

    float composite_buffer_data[8][2] =
    {
      {-0.5, -0.5},
      {0, 1},
      {-0.5, 0.5},
      {0, 0},
      {0.5, 0.5},
      {1, 0},
      {0.5, -0.5},
      {1, 1}
    };


    glBindVertexArray(m_vao_CompositeOutput);
    glBindBuffer(GL_ARRAY_BUFFER, m_vbo_CompositeOutput);

    glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 8 * 2, composite_buffer_data, GL_STATIC_DRAW);

    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, static_cast<void*>(nullptr)); // Positions

    glDisableVertexAttribArray(1);

    glEnableVertexAttribArray(2);
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, (void*)(sizeof(float) * 2)); // Textures

    glBindVertexArray(0);
    glBindBuffer(GL_ARRAY_BUFFER, 0);


    // CompositeShaderOutput VAO/VBO's
    glGenBuffers(1, &m_vbo_CompositeShaderOutput);
    glGenVertexArrays(1, &m_vao_CompositeShaderOutput);

    glBindVertexArray(m_vao_CompositeShaderOutput);
    glBindBuffer(GL_ARRAY_BUFFER, m_vbo_CompositeShaderOutput);

    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(composite_shader_vertex), static_cast<void*>(nullptr));
    // Positions

    glEnableVertexAttribArray(1);
    glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, sizeof(composite_shader_vertex), (void*)(sizeof(float) * 2));
    // Colors

    glEnableVertexAttribArray(2);
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(composite_shader_vertex), (void*)(sizeof(float) * 6)); // UV

    glEnableVertexAttribArray(3);
    glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, sizeof(composite_shader_vertex), (void*)(sizeof(float) * 8));
    // RAD ANG

    glBindVertexArray(0);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
  }



  std::string SetPipeline(Pipeline& pipeline)
  {
    currentPipe = &pipeline;
    shaderEngine.reset();
    if (!shaderEngine.loadPresetShaders(pipeline, m_presetName))
    {
      return "Shader__ compilation error";
    }

    return std::string();
  }

  void setPresetName(const std::string& theValue)
  {
    m_presetName = theValue;
  }

  float SquishToCenter(float x, float fExp)
  {
    if (x > 0.5f)
      return powf(x * 2 - 1, fExp) * 0.5f + 0.5f;

    return (1 - powf(1 - x * 2, fExp)) * 0.5f;
  }

  void UvToMathSpace(float u, float v, float* rad, float* ang)
  {
    // (screen space = -1..1 on both axes; corresponds to UV space)
    // uv space = [0..1] on both axes
    // "math" space = what the preset authors are used to:
    //      upper left = [0,0]
    //      bottom right = [1,1]
    //      rad == 1 at corners of screen
    //      ang == 0 at three o'clock, and increases counter-clockwise (to 6.28).
    float px = (u * 2 - 1) * m_fAspectX; // probably 1.0
    float py = (v * 2 - 1) * m_fAspectY; // probably <1

    *rad = sqrtf(px * px + py * py) / sqrtf(m_fAspectX * m_fAspectX + m_fAspectY * m_fAspectY);
    *ang = atan2f(py, px);
    if (*ang < 0)
      *ang += 6.2831853071796f;
  }

  void InitCompositeShaderVertex()
  {
    // BUILD VERTEX LIST for final composite blit
    memset(m_comp_verts, 0, sizeof(composite_shader_vertex) * FCGSX * FCGSY);
    float fDivX = 1.0f / static_cast<float>(FCGSX - 2);
    float fDivY = 1.0f / static_cast<float>(FCGSY - 2);
    for (int j = 0; j < FCGSY; j++)
    {
      int j2 = j - j / (FCGSY / 2);
      float v = j2 * fDivY;
      v = SquishToCenter(v, 3.0f);
      float sy = -((v) * 2 - 1);
      for (int i = 0; i < FCGSX; i++)
      {
        int i2 = i - i / (FCGSX / 2);
        float u = i2 * fDivX;
        u = SquishToCenter(u, 3.0f);
        float sx = (u) * 2 - 1;
        composite_shader_vertex* pComp = &m_comp_verts[i + j * FCGSX];
        pComp->x = sx;
        pComp->y = sy;

        float rad, ang;
        UvToMathSpace(u, v, &rad, &ang);
        // fix-ups:
        if (i == FCGSX / 2 - 1)
        {
          if (j < FCGSY / 2 - 1)
            ang = 3.1415926535898f * 1.5f;
          else if (j == FCGSY / 2 - 1)
            ang = 3.1415926535898f * 1.25f;
          else if (j == FCGSY / 2)
            ang = 3.1415926535898f * 0.75f;
          else
            ang = 3.1415926535898f * 0.5f;
        }
        else if (i == FCGSX / 2)
        {
          if (j < FCGSY / 2 - 1)
            ang = 3.1415926535898f * 1.5f;
          else if (j == FCGSY / 2 - 1)
            ang = 3.1415926535898f * 1.75f;
          else if (j == FCGSY / 2)
            ang = 3.1415926535898f * 0.25f;
          else
            ang = 3.1415926535898f * 0.5f;
        }
        else if (j == FCGSY / 2 - 1)
        {
          if (i < FCGSX / 2 - 1)
            ang = 3.1415926535898f * 1.0f;
          else if (i == FCGSX / 2 - 1)
            ang = 3.1415926535898f * 1.25f;
          else if (i == FCGSX / 2)
            ang = 3.1415926535898f * 1.75f;
          else
            ang = 3.1415926535898f * 2.0f;
        }
        else if (j == FCGSY / 2)
        {
          if (i < FCGSX / 2 - 1)
            ang = 3.1415926535898f * 1.0f;
          else if (i == FCGSX / 2 - 1)
            ang = 3.1415926535898f * 0.75f;
          else if (i == FCGSX / 2)
            ang = 3.1415926535898f * 0.25f;
          else
            ang = 3.1415926535898f * 0.0f;
        }
        pComp->tu = u;
        pComp->tv = v;

        pComp->rad = rad;
        pComp->ang = ang;
      }
    }

    // build index list for final composite blit -
    // order should be friendly for interpolation of 'ang' value!
    int* cur_index = &m_comp_indices[0];
    for (int y = 0; y < FCGSY - 1; y++)
    {
      if (y == FCGSY / 2 - 1)
        continue;
      for (int x = 0; x < FCGSX - 1; x++)
      {
        if (x == FCGSX / 2 - 1)
          continue;
        bool left_half = (x < FCGSX / 2);
        bool top_half = (y < FCGSY / 2);
        bool center_4 = ((x == FCGSX / 2 || x == FCGSX / 2 - 1) && (y == FCGSY / 2 || y == FCGSY / 2 - 1));

        if ((static_cast<int>(left_half) + static_cast<int>(top_half) + static_cast<int>(center_4)) % 2)
        {
          *(cur_index + 0) = (y)*FCGSX + (x);
          *(cur_index + 1) = (y)*FCGSX + (x + 1);
          *(cur_index + 2) = (y + 1) * FCGSX + (x + 1);
          *(cur_index + 3) = (y + 1) * FCGSX + (x + 1);
          *(cur_index + 4) = (y + 1) * FCGSX + (x);
          *(cur_index + 5) = (y)*FCGSX + (x);
        }
        else
        {
          *(cur_index + 0) = (y + 1) * FCGSX + (x);
          *(cur_index + 1) = (y)*FCGSX + (x);
          *(cur_index + 2) = (y)*FCGSX + (x + 1);
          *(cur_index + 3) = (y)*FCGSX + (x + 1);
          *(cur_index + 4) = (y + 1) * FCGSX + (x + 1);
          *(cur_index + 5) = (y + 1) * FCGSX + (x);
        }

        cur_index += 6;
      }
    }
  }

  void reset(int w, int h)
  {
    aspect = static_cast<float>(h) / static_cast<float>(w);
    this->vw = w;
    this->vh = h;

    glCullFace(GL_BACK);

#ifndef GL_TRANSITION
    glEnable(GL_LINE_SMOOTH);
#endif

    glClearColor(0, 0, 0, 0);

    glViewport(vstartx, vstarty, w, h);

    glEnable(GL_BLEND);

    texsizeX = w;
    texsizeY = h;

    // snap to 16x16 blocks
    texsizeX = ((texsizeX - 15) / 16) * 16;
    texsizeY = ((texsizeY - 15) / 16) * 16;

    m_fAspectX = (texsizeY > texsizeX) ? static_cast<float>(texsizeX) / static_cast<float>(texsizeY) : 1.0f;
    m_fAspectY = (texsizeX > texsizeY) ? static_cast<float>(texsizeY) / static_cast<float>(texsizeX) : 1.0f;
    m_fInvAspectX = 1.0f / m_fAspectX;
    m_fInvAspectY = 1.0f / m_fAspectY;

    InitCompositeShaderVertex();

    if (textureManager != nullptr)
    {
      delete textureManager;
    }
    textureManager = new TextureManager(presetURL, texsizeX, texsizeY, m_datadir);

    shaderEngine.setParams(texsizeX, texsizeY, beatDetect, textureManager);
    shaderEngine.reset();
    shaderEngine.loadPresetShaders(*currentPipe, m_presetName);

    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    glClear(GL_COLOR_BUFFER_BIT);

#ifdef USE_TEXT_MENU
    // When the renderer resets, do a check to find out what the maximum number of lines we could display are.
    int r_textMenuPageSize = 0;
    int yOffset = textMenuYOffset;
    while (true) { // infinite loop, only satisifed when we have found the max lines of text on the screen.
      if (yOffset < vh - textMenuLineHeight) { // if yOffset could be displayed on the screen (vh), then we have room for the next line.
        r_textMenuPageSize++;
        yOffset = yOffset + textMenuLineHeight;
      }
      else { // if we reached the end of the screen, set textMenuPageSize and move on.
        textMenuPageSize = r_textMenuPageSize;
        break;
      }
    }
#endif
  }

  void SetupPass1(const Pipeline& pipeline, const PipelineContext& pipelineContext)
  {
    totalframes++;

    glViewport(0, 0, texsizeX, texsizeY);

    renderContext.mat_ortho = glm::ortho(0.0f, 1.0f, 0.0f, 1.0f, -40.0f, 40.0f);
  }

  void Interpolation(const Pipeline& pipeline, const PipelineContext& pipelineContext)
  {
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, textureManager->getMainTexture()->texID);

    //Texture_ wrapping( clamp vs. wrap)
    if (pipeline.textureWrap == 0)
    {
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    }
    else
    {
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    }

    int size = (mesh.height - 1) * mesh.width * 4 * 2;

    if (pipeline.staticPerPixel)
    {
      for (int j = 0; j < mesh.height - 1; j++)
      {
        int base = j * mesh.width * 2 * 4;

        for (int i = 0; i < mesh.width; i++)
        {
          int strip = base + i * 8;
          p[strip + 2] = pipeline.x_mesh[i][j];
          p[strip + 3] = pipeline.y_mesh[i][j];

          p[strip + 6] = pipeline.x_mesh[i][j + 1];
          p[strip + 7] = pipeline.y_mesh[i][j + 1];
        }
      }
    }
    else
    {
      mesh.Reset();
      Pipeline* cp = currentPipe;
      std::transform(mesh.p.begin(), mesh.p.end(), mesh.identity.begin(), mesh.p.begin(),
        [cp](PixelPoint p, PerPixelContext& context) {
          return cp->PerPixel(p, context);
        });

      for (int j = 0; j < mesh.height - 1; j++)
      {
        int base = j * mesh.width * 2 * 4;

        for (int i = 0; i < mesh.width; i++)
        {
          int strip = base + i * 8;
          int index = j * mesh.width + i;
          int index2 = (j + 1) * mesh.width + i;

          p[strip + 2] = mesh.p[index].x;
          p[strip + 3] = mesh.p[index].y;

          p[strip + 6] = mesh.p[index2].x;
          p[strip + 7] = mesh.p[index2].y;
        }
      }
    }

    glBindBuffer(GL_ARRAY_BUFFER, m_vbo_Interpolation);

    glBufferData(GL_ARRAY_BUFFER, sizeof(float) * size, nullptr, GL_DYNAMIC_DRAW);
    glBufferData(GL_ARRAY_BUFFER, sizeof(float) * size, p, GL_DYNAMIC_DRAW);

    glBindBuffer(GL_ARRAY_BUFFER, 0);

    shaderEngine.enableWarpShader(currentPipe->warpShader, pipeline, pipelineContext, renderContext.mat_ortho);

    glVertexAttrib4f(1, 1.0, 1.0, 1.0, pipeline.screenDecay);

    glBlendFunc(GL_SRC_ALPHA, GL_ZERO);

    glBindVertexArray(m_vao_Interpolation);

    for (int j = 0; j < mesh.height - 1; j++)
      glDrawArrays(GL_TRIANGLE_STRIP, j * mesh.width * 2, mesh.width * 2);

    glBindVertexArray(0);

    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    glBindTexture(GL_TEXTURE_2D, 0);
  }

  void RenderTouch(const Pipeline& pipeline, const PipelineContext& pipelineContext)
  {
    Pipeline pipelineTouch;
    MilkdropWaveform wave;
    for (std::size_t x = 0; x < waveformList.size(); x++) {
      pipelineTouch.drawables.push_back(&wave);
      wave = waveformList[x];

      // Render waveform
      for (std::vector<RenderItem*>::const_iterator pos = pipelineTouch.drawables.begin(); pos != pipelineTouch.drawables.end(); ++pos)
      {
        if (*pos != nullptr)
          (*pos)->Draw(renderContext);
      }
    }
  }

  int nearestPower2(int value)
  {
    int x = value;
    int power = 0;

    if (x == 0)
    {
      return 0;
    }

    while ((x & 0x01) != 1)
    {
      x >>= 1;
    }

    if (x == 1)
    {
      return value;
    }
    x = value;
    while (x != 0)
    {
      x >>= 1;
      power++;
    }
    if (((1 << power) - value) <= (value - (1 << (power - 1))))
    {
      return 1 << power;
    }
    return 1 << (power - 1);
    return 0;
  }

  void RenderItems(const Pipeline& pipeline, const PipelineContext& pipelineContext)
  {
    renderContext.time = pipelineContext.time;
    renderContext.texsize = nearestPower2(std::max(texsizeX, texsizeY));
    renderContext.aspectCorrect = correction;
    renderContext.aspectRatio = aspect;
    renderContext.textureManager = textureManager;
    renderContext.beatDetect = beatDetect;

    for (std::vector<RenderItem*>::const_iterator pos = pipeline.drawables.begin(); pos != pipeline.drawables.end(); ++pos)
    {
      if (*pos != nullptr)
        (*pos)->Draw(renderContext);
    }

    // If we have touch waveforms, render them.
    if (waveformList.size() >= 1) {
      RenderTouch(pipeline, pipelineContext);
    }
  }

  void draw_title_to_texture()
  {
#ifdef USE_TEXT_MENU
    if (this->drawtitle > 100)
    {
      draw_title_to_screen(true);
      this->drawtitle = 0;
    }
#endif /** USE_TEXT_MENU */
  }

  void FinishPass1()
  {
    draw_title_to_texture();

    textureManager->updateMainTexture();
  }

  void RenderFrameOnlyPass1(const Pipeline& pipeline, const PipelineContext& pipelineContext)
  {
    shaderEngine.RenderBlurTextures(pipeline, pipelineContext);

    SetupPass1(pipeline, pipelineContext);

    Interpolation(pipeline, pipelineContext);

    RenderItems(pipeline, pipelineContext);

    FinishPass1();
  }

  void CompositeOutput(const Pipeline& pipeline, const PipelineContext& pipelineContext)
  {
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, textureManager->getMainTexture()->texID);

    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

    renderContext.mat_ortho = glm::ortho(-0.5f, 0.5f, -0.5f, 0.5f, -40.0f, 40.0f);

    shaderEngine.enableCompositeShader(currentPipe->compositeShader, pipeline, pipelineContext);

    glUniformMatrix4fv(shaderEngine.uniform_v2f_c4f_t2f_vertex_tranformation, 1, GL_FALSE,
      value_ptr(renderContext.mat_ortho));
    glUniform1i(shaderEngine.uniform_v2f_c4f_t2f_frag_texture_sampler, 0);

    //Overwrite anything on the screen
    glBlendFunc(GL_ONE, GL_ZERO);
    glVertexAttrib4f(1, 1.0, 1.0, 1.0, 1.0);

    glBindVertexArray(m_vao_CompositeOutput);

    glDrawArrays(GL_TRIANGLE_FAN, 0, 4);

    glBindVertexArray(0);

    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    for (auto drawable : pipeline.compositeDrawables)
      drawable->Draw(renderContext);

    glBindTexture(GL_TEXTURE_2D, 0);
  }


  void CompositeShaderOutput(const Pipeline& pipeline, const PipelineContext& pipelineContext)
  {
    // hue shader
    float shade[4][3] = {
      {1.0f, 1.0f, 1.0f},
      {1.0f, 1.0f, 1.0f},
      {1.0f, 1.0f, 1.0f},
      {1.0f, 1.0f, 1.0f}
    }; // for each vertex, then each comp.

    // pick 4 colors for the 4 corners
    for (int i = 0; i < 4; i++)
    {
      shade[i][0] = 0.6f + 0.3f * sinf(pipelineContext.time * 30.0f * 0.0143f + 3 + i * 21);
      shade[i][1] = 0.6f + 0.3f * sinf(pipelineContext.time * 30.0f * 0.0107f + 1 + i * 13);
      shade[i][2] = 0.6f + 0.3f * sinf(pipelineContext.time * 30.0f * 0.0129f + 6 + i * 9);
      float max = ((shade[i][0] > shade[i][1]) ? shade[i][0] : shade[i][1]);
      if (shade[i][2] > max) max = shade[i][2];
      for (int k = 0; k < 3; k++)
      {
        shade[i][k] /= max;
        shade[i][k] = 0.5f + 0.5f * shade[i][k];
      }
    }

    // interpolate the 4 colors & apply to all the verts
    for (int j = 0; j < FCGSY; j++)
    {
      for (int i = 0; i < FCGSX; i++)
      {
        composite_shader_vertex* pComp = &m_comp_verts[i + j * FCGSX];
        float x = pComp->x * 0.5f + 0.5f;
        float y = pComp->y * 0.5f + 0.5f;

        float col[3] = { 1, 1, 1 };
        for (int c = 0; c < 3; c++)
          col[c] = shade[0][c] * (x) * (y)+
          shade[1][c] * (1 - x) * (y)+
          shade[2][c] * (x) * (1 - y) +
          shade[3][c] * (1 - x) * (1 - y);

        pComp->Diffuse[0] = col[0];
        pComp->Diffuse[1] = col[1];
        pComp->Diffuse[2] = col[2];
        pComp->Diffuse[3] = 1.0;
      }
    }


    const int primCount = (FCGSX - 2) * (FCGSY - 2) * 6;
    composite_shader_vertex tempv[primCount];
    memset(tempv, 0, sizeof(composite_shader_vertex) * primCount);
    int src_idx = 0;
    for (int i = 0; i < primCount; i++)
    {
      tempv[i] = m_comp_verts[m_comp_indices[src_idx++]];
    }

    glBindBuffer(GL_ARRAY_BUFFER, m_vbo_CompositeShaderOutput);

    glBufferData(GL_ARRAY_BUFFER, sizeof(composite_shader_vertex) * primCount, nullptr, GL_DYNAMIC_DRAW);
    glBufferData(GL_ARRAY_BUFFER, sizeof(composite_shader_vertex) * primCount, tempv, GL_DYNAMIC_DRAW);

    glBindBuffer(GL_ARRAY_BUFFER, 0);

    glBlendFunc(GL_ONE, GL_ZERO);

    glBindVertexArray(m_vao_CompositeShaderOutput);

    // Now do the final composite blit, fullscreen;
    glDrawArrays(GL_TRIANGLES, 0, primCount);

    glBindVertexArray(0);

    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  }

  void Pass2(const Pipeline& pipeline, const PipelineContext& pipelineContext)
  {
    //BEGIN PASS 2
    //
    //end of texture rendering
    //now we copy the texture from the FBO or framebuffer to
    //video texture memory and render fullscreen.

    /** Reset the viewport size */
    if (textureRenderToTexture)
    {
      glViewport(0, 0, texsizeX, texsizeY);
    }
    else
      glViewport(vstartx, vstarty, this->vw, this->vh);

    if (shaderEngine.enableCompositeShader(currentPipe->compositeShader, pipeline, pipelineContext))
    {
      CompositeShaderOutput(pipeline, pipelineContext);
    }
    else
    {
      CompositeOutput(pipeline, pipelineContext);
    }


    // TODO:
    //if (this->showhelp == true)
    //  draw_help();
    //if (this->showtitle == true)
    //  draw_title();
    //if (this->showfps == true)
    //  draw_fps();
    //// this->realfps
    //if (this->showsearch == true)
    //  draw_search();
    //if (this->showmenu == true)
    //  draw_menu();
    //if (this->showpreset == true)
    //  draw_preset();
    //if (this->showstats == true)
    //  draw_stats();
    //// We should always draw toasts last so they are on top of other text (lp/menu).
    //if (this->showtoast == true)
    //  draw_toast();
  }

  void RenderFrameOnlyPass2(const Pipeline& pipeline, const PipelineContext& pipelineContext, int xoffset, int yoffset, int eye)
  {
    /* draw in a certain range of the screen */
    vstartx = xoffset;
    vstarty = yoffset;
    // ignore eye
    Pass2(pipeline, pipelineContext);
    vstartx = 0;
    vstarty = 0;
  }
};


namespace RandomNumberGenerators
{
  std::size_t uniformInteger(std::size_t upperBound = 1) {


    /// @bug there was a man entry about how this leads to a lousy uniform
    /// @bug distribution in practice. should probably review
    assert(upperBound > 0);
    return ((rand()) % ((int)upperBound));
  }

#define WEIGHTED_RANDOM_DEBUG 0
  std::size_t weightedRandom(const std::vector<int>& weights, unsigned int weightTotalHint = 0) {


    if (weightTotalHint == 0) {
      for (std::size_t i = 0; i < weights.size(); i++)
        weightTotalHint += weights[i];
    }

    const int sampledSum = uniformInteger(weightTotalHint);
    int sum = 0;
    if (WEIGHTED_RANDOM_DEBUG) std::cout << "[RNG::weightedRandom()] weightTotal = " << weightTotalHint <<
      std::endl;

    for (std::size_t i = 0; i < weights.size(); i++) {
      if (WEIGHTED_RANDOM_DEBUG)
        std::cout << "[RNG::weightedRandom()] weight[" << i << "] = " << weights[i] <<
        std::endl;

      sum += weights[i];
      if (sampledSum <= sum) {
        if (WEIGHTED_RANDOM_DEBUG)
          std::cout << "[RNG::weightedRandom()] sampled index " << i << "(" <<
          "running sum = " << sum << ", sampled sum = " << sampledSum << std::endl;
        return i;
      }
    }

    return weights.size() - 1;
  }
}


struct TimeKeeper
{
  u32 startTime;
  double _easterEgg;
  double _presetDuration;
  double _presetDurationA;
  double _presetDurationB;
  double _smoothDuration;
  double _hardcutDuration;
  double _currentTime;
  double _presetTimeA;
  double _presetTimeB;
  int _presetFrameA = 0;
  int _presetFrameB = 0;
  bool _isSmoothing;

  TimeKeeper(double presetDuration, double smoothDuration, double hardcutDuration, double easterEgg)
  {
    _smoothDuration = smoothDuration;
    _presetDuration = presetDuration;
    _hardcutDuration = hardcutDuration;
    _easterEgg = easterEgg;

    startTime = getTicks();

    UpdateTimers();
  }

  void UpdateTimers()
  {
    _currentTime = getTicks(startTime) * 0.001 * Settings::environmentMilkFrameSpeed;

    {
      static i32 skip = 0;
      if (skip == 0)
      {
        ++_presetFrameA;
        ++_presetFrameB;
      }
      skip = (skip + 1) % (Settings::environmentMilkFrameSkip + 1);
    }
  }

  void StartPreset()
  {
    _isSmoothing = false;
    _presetTimeA = _currentTime;
    _presetFrameA = 1;
    _presetDurationA = sampledPresetDuration();
  }

  void StartSmoothing()
  {
    _isSmoothing = true;
    _presetTimeB = _currentTime;
    _presetFrameB = 1;
    _presetDurationB = sampledPresetDuration();
  }

  void EndSmoothing()
  {
    _isSmoothing = false;
    _presetTimeA = _presetTimeB;
    _presetFrameA = _presetFrameB;
    _presetDurationA = _presetDurationB;
  }

  bool CanHardCut()
  {
    return ((_currentTime - _presetTimeA) > _hardcutDuration);
  }

  double SmoothRatio()
  {
    return (_currentTime - _presetTimeB) / _smoothDuration;
  }

  bool IsSmoothing()
  {
    return _isSmoothing;
  }

  double GetRunningTime()
  {
    return _currentTime;
  }

  double PresetProgressA()
  {
    if (_isSmoothing) return 1.0;
    else return (_currentTime - _presetTimeA) / _presetDurationA;
  }

  double PresetProgressB()
  {
    return (_currentTime - _presetTimeB) / _presetDurationB;
  }

  int PresetFrameB()
  {
    return _presetFrameB;
  }

  int PresetFrameA()
  {
    return _presetFrameA;
  }

  int PresetTimeB()
  {
    return _presetTimeB;
  }


  int PresetTimeA()
  {
    return _presetTimeA;
  }

  double sampledPresetDuration()
  {
    return _presetDuration;
  }

  void ChangePresetDuration(int seconds)
  {
    _presetDuration = seconds;
  }
};

struct Preset
{
  std::string _name;
  std::string _author;

  Preset(const std::string& presetName = std::string(), const std::string& presetAuthor = {}) : _name(presetName), _author(presetAuthor) {}

  const std::string& name() const
  {
    return _name;
  }

  virtual Pipeline& pipeline() = 0;
  virtual void Render(const BeatDetect& music, const PipelineContext& context) = 0;
};

struct PresetFactory
{
  static const std::string IDLE_PRESET_PROTOCOL;

  virtual std::unique_ptr<Preset> allocate(const std::string& url, const std::string& name = std::string(), const std::string& author = std::string()) = 0;
  virtual std::string supportedExtensions() const = 0;

  static std::string protocol(const std::string& url, std::string& path) {

    std::size_t pos = url.find_first_of("://");
    if (pos == std::string::npos)
      return std::string();
    else {
      path = url.substr(pos + 3, url.length());
      //        std::cout << "[PresetFactory] path is " << path << std::endl;
      return url.substr(0, pos);
    }
  }
};

const std::string PresetFactory::IDLE_PRESET_PROTOCOL("idle");

struct PresetFactoryException : public std::exception
{
  std::string _message;

  PresetFactoryException(const std::string& message) : _message(message)
  {}

  const std::string& message() const { return _message; }
};

namespace FuncWrappers
{
  float int_wrapper(float* arg_list)
  {
    return floor(arg_list[0]);
  }

  float abs_wrapper(float* arg_list)
  {
    return (fabs(arg_list[0]));
  }

  float sin_wrapper(float* arg_list)
  {
    const float d = sinf(*arg_list);
    return d;
  }

  float cos_wrapper(float* arg_list)
  {
    return (cos(arg_list[0]));
  }

  float tan_wrapper(float* arg_list)
  {
    return (tan(arg_list[0]));
  }

  float asin_wrapper(float* arg_list)
  {
    return (asin(arg_list[0]));
  }

  float acos_wrapper(float* arg_list)
  {
    return (acos(arg_list[0]));
  }

  float atan_wrapper(float* arg_list)
  {
    return (atan(arg_list[0]));
  }

  float sqr_wrapper(float* arg_list)
  {
    return pow(arg_list[0], 2);
  }
  float sqrt_wrapper(float* arg_list)
  {
    return (sqrt(arg_list[0]));
  }
  float pow_wrapper(float* arg_list)
  {
    return (pow(arg_list[0], arg_list[1]));
  }
  float exp_wrapper(float* arg_list)
  {
    return (exp(arg_list[0]));
  }
  float log_wrapper(float* arg_list)
  {
    return (log(arg_list[0]));
  }
  float log10_wrapper(float* arg_list)
  {
    return (log10(arg_list[0]));
  }
  float sign_wrapper(float* arg_list)
  {
    return -arg_list[0];
  }
  float min_wrapper(float* arg_list)
  {
    if (arg_list[0] > arg_list[1])
      return arg_list[1];
    return arg_list[0];
  }

  float max_wrapper(float* arg_list)
  {
    if (arg_list[0] > arg_list[1])
      return arg_list[0];
    return arg_list[1];
  }
  float sigmoid_wrapper(float* arg_list)
  {
    const double t = (1 + exp(-arg_list[0] * arg_list[1]));
    return (fabs(t) > 0.00001) ? 1.0 / t : 0;
  }
  float atan2_wrapper(float* arg_list)
  {
    return (atan2(arg_list[0], arg_list[1]));
  }
  float rand_wrapper(float* arg_list)
  {
    float l = 1;

    //  printf("RAND ARG:(%d)\n", (int)arg_list[0]);
    if ((int)arg_list[0] > 0)
      l = (float)RandomNumberGenerators::uniformInteger((int)arg_list[0]);

    return l;
  }
  float band_wrapper(float* arg_list)
  {
    return (float)((int)arg_list[0] && (int)arg_list[1]);
  }
  float bor_wrapper(float* arg_list)
  {
    return (float)((int)arg_list[0] || (int)arg_list[1]);
  }
  float bnot_wrapper(float* arg_list)
  {
    return (float)(!(int)arg_list[0]);
  }
  float if_wrapper(float* arg_list)
  {
    if ((int)arg_list[0] == 0)
      return arg_list[2];
    //std::cout <<"NOT ZERO: " << arg_list[0] << std::endl;
    return arg_list[1];
  }
  float equal_wrapper(float* arg_list)
  {
    return (arg_list[0] == arg_list[1]);
  }
  float above_wrapper(float* arg_list)
  {
    return (arg_list[0] > arg_list[1]);
  }
  float below_wrapper(float* arg_list)
  {
    return (arg_list[0] < arg_list[1]);
  }
  float nchoosek_wrapper(float* arg_list)
  {
    unsigned long cnm = 1UL;
    int i, f;
    int n, m;
    n = (int)arg_list[0];
    m = (int)arg_list[1];
    if (m * 2 > n) m = n - m;
    for (i = 1; i <= m; n--, i++)
    {
      if ((f = n) % i == 0)
        f /= i;
      else  cnm /= i;
      cnm *= f;
    }
    return (float)cnm;
  }
  float fact_wrapper(float* arg_list)
  {
    int result = 1;
    int n = (int)arg_list[0];
    while (n > 1) {
      result = result * n;
      n--;
    }
    return (float)result;
  }
  float print_wrapper(float* arg_list)
  {
    int len = 1;
    for (int i = 0; i < len; i++)
    {
      std::cout << arg_list[i];
      if (i != (len - 1))
        std::cout << " ";
    }
    if (len > 0)
      std::cout << std::endl;
    if (len > 0)
      return arg_list[0];
    else
      return 0;
  }
}

struct Func
{
  float (*func_ptr)(float*);
  std::string name;
  int num_args;
  int llvm_intrinsic;

  Func(const std::string& _name, float (*_func_ptr)(float*), int _num_args, int llvm_id) :
    func_ptr(_func_ptr), name(_name), num_args(_num_args), llvm_intrinsic(llvm_id) {}

  const std::string& getName() const
  {
    return name;
  }

  int getNumArgs() const
  {
    return num_args;
  }
};

namespace BuiltinFuncs
{
  std::map<std::string, Func*> builtin_func_tree;

  int insert_func(Func* func)
  {
    assert(func);

    if (func == 0) {
      std::cerr << "Received a null function object, ignoring...." << std::endl;
      return PROJECTM_ERROR;
    }

    //   //std::cout << "inserting function " << func->getName() << std::endl;

    const std::pair<std::string, Func*> pair = std::make_pair(std::string(func->getName()), func);

    assert(pair.second);

    const std::pair<std::map<std::string, Func*>::iterator, bool> inserteePair =
      builtin_func_tree.insert(pair);

    if (!inserteePair.second) {
      std::cerr << "Failed to insert builtin function \"" << func->getName() << "\" into collection! Bailing..." << std::endl;
      abort();
    }

    return PROJECTM_SUCCESS;
  }

  int load_builtin_func(const std::string& name, float (*func_ptr)(float*), int num_args, int id = 0)
  {
    Func* func;
    int retval;

    /* Create new function */
    func = new Func(name, func_ptr, num_args, id);

    if (func == 0)
      return PROJECTM_OUTOFMEM_ERROR;

    retval = insert_func(func);

    return retval;

  }

  Func* find_func(const std::string& name) {

    std::map<std::string, Func*>::iterator pos = builtin_func_tree.find(name);

    // Case: function not found, return null
    if (pos == builtin_func_tree.end())
      return 0;

    // Case: function found, return a pointer to it
    return pos->second;

  }

#if HAVE_LLVM
#include "llvm/IR/Intrinsics.h"
#define INTRINSIC(id) llvm::Intrinsic::id
#else
#define INTRINSIC(id) 0
#endif

  int load_all_builtin_func()
  {
    if (load_builtin_func("int", FuncWrappers::int_wrapper, 1) < 0)
      return PROJECTM_ERROR;
    if (load_builtin_func("abs", FuncWrappers::abs_wrapper, 1, INTRINSIC(fabs)) < 0)
      return PROJECTM_ERROR;
    if (load_builtin_func("sin", FuncWrappers::sin_wrapper, 1, INTRINSIC(sin)) < 0)
      return PROJECTM_ERROR;
    if (load_builtin_func("cos", FuncWrappers::cos_wrapper, 1, INTRINSIC(cos)) < 0)
      return PROJECTM_ERROR;
    if (load_builtin_func("tan", FuncWrappers::tan_wrapper, 1) < 0)
      return PROJECTM_ERROR;
    if (load_builtin_func("asin", FuncWrappers::asin_wrapper, 1) < 0)
      return PROJECTM_ERROR;
    if (load_builtin_func("acos", FuncWrappers::acos_wrapper, 1) < 0)
      return PROJECTM_ERROR;
    if (load_builtin_func("atan", FuncWrappers::atan_wrapper, 1) < 0)
      return PROJECTM_ERROR;
    if (load_builtin_func("sqr", FuncWrappers::sqr_wrapper, 1) < 0)
      return PROJECTM_ERROR;
    if (load_builtin_func("sqrt", FuncWrappers::sqrt_wrapper, 1, INTRINSIC(sqrt)) < 0)
      return PROJECTM_ERROR;
    if (load_builtin_func("pow", FuncWrappers::pow_wrapper, 2, INTRINSIC(pow)) < 0)
      return PROJECTM_ERROR;
    if (load_builtin_func("exp", FuncWrappers::exp_wrapper, 1, INTRINSIC(exp)) < 0)
      return PROJECTM_ERROR;
    if (load_builtin_func("log", FuncWrappers::log_wrapper, 1, INTRINSIC(log)) < 0)
      return PROJECTM_ERROR;
    if (load_builtin_func("log10", FuncWrappers::log10_wrapper, 1, INTRINSIC(log10)) < 0)
      return PROJECTM_ERROR;
    if (load_builtin_func("sign", FuncWrappers::sign_wrapper, 1) < 0)
      return PROJECTM_ERROR;
    if (load_builtin_func("min", FuncWrappers::min_wrapper, 2, INTRINSIC(minnum)) < 0)
      return PROJECTM_ERROR;
    if (load_builtin_func("max", FuncWrappers::max_wrapper, 2, INTRINSIC(maxnum)) < 0)
      return PROJECTM_ERROR;
    if (load_builtin_func("sigmoid", FuncWrappers::sigmoid_wrapper, 2) < 0)
      return PROJECTM_ERROR;
    if (load_builtin_func("atan2", FuncWrappers::atan2_wrapper, 2) < 0)
      return PROJECTM_ERROR;
    if (load_builtin_func("rand", FuncWrappers::rand_wrapper, 1) < 0)
      return PROJECTM_ERROR;
    if (load_builtin_func("band", FuncWrappers::band_wrapper, 2) < 0)
      return PROJECTM_ERROR;
    if (load_builtin_func("bor", FuncWrappers::bor_wrapper, 2) < 0)
      return PROJECTM_ERROR;
    if (load_builtin_func("bnot", FuncWrappers::bnot_wrapper, 1) < 0)
      return PROJECTM_ERROR;
    if (load_builtin_func("if", FuncWrappers::if_wrapper, 3) < 0)
      return PROJECTM_ERROR;
    if (load_builtin_func("equal", FuncWrappers::equal_wrapper, 2) < 0)
      return PROJECTM_ERROR;
    if (load_builtin_func("above", FuncWrappers::above_wrapper, 2) < 0)
      return PROJECTM_ERROR;
    if (load_builtin_func("below", FuncWrappers::below_wrapper, 2) < 0)
      return PROJECTM_ERROR;
    if (load_builtin_func("nchoosek", FuncWrappers::nchoosek_wrapper, 2) < 0)
      return PROJECTM_ERROR;
    if (load_builtin_func("fact", FuncWrappers::fact_wrapper, 1) < 0)
      return PROJECTM_ERROR;
    if (load_builtin_func("print", FuncWrappers::print_wrapper, 1) < 0)
      return PROJECTM_ERROR;
    return PROJECTM_SUCCESS;
  }

  volatile bool initialized = false;
  int init_builtin_func_db()
  {
    int retval;

    if (initialized) {
      return 0;
    }
    else
      initialized = true;

    retval = load_all_builtin_func();
    return retval;
  }
}

enum Orientation
{
  Normal = 0, FlipX, FlipY, FlipXY
};

struct VideoEcho : public RenderItem
{
  float a;
  float zoom;
  Orientation orientation;

  VideoEcho() : a(0), zoom(1), orientation(Normal)
  {
    Init();
  }

  void Draw(RenderContext& context) override
  {
    int flipx = 1, flipy = 1;
    switch (orientation)
    {
    case Normal: flipx = 1; flipy = 1; break;
    case FlipX: flipx = -1; flipy = 1; break;
    case FlipY: flipx = 1; flipy = -1; break;
    case FlipXY: flipx = -1; flipy = -1; break;
    default: flipx = 1; flipy = 1; break;
    }

    float buffer_data[8][2] = {
        {-0.5f * flipx, -0.5f * flipy},
        {0.0, 1.0},

        {-0.5f * flipx,  0.5f * flipy},
        {0.0, 0.0},

        { 0.5f * flipx,  0.5f * flipy},
        {1.0, 0.0},

        { 0.5f * flipx, -0.5f * flipy},
        {1.0, 1.0}
    };

    glm::mat4 mat_first_translation = glm::mat4(1.0);
    mat_first_translation[3][0] = -0.5;
    mat_first_translation[3][1] = -0.5;

    glm::mat4  mat_scale = glm::mat4(1.0);
    mat_scale[0][0] = 1.0 / zoom;
    mat_scale[1][1] = 1.0 / zoom;

    glm::mat4  mat_second_translation = glm::mat4(1.0);
    mat_second_translation[3][0] = 0.5;
    mat_second_translation[3][1] = 0.5;

    for (int i = 1; i < 8; i += 2) {
      glm::vec4 texture = glm::vec4(buffer_data[i][0], buffer_data[i][1], 0, 1);
      texture = mat_first_translation * texture;
      texture = mat_scale * texture;
      texture = mat_second_translation * texture;

      buffer_data[i][0] = texture[0];
      buffer_data[i][1] = texture[1];
    }


    glBindBuffer(GL_ARRAY_BUFFER, m_vboID);

    glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 16, nullptr, GL_DYNAMIC_DRAW);
    glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 16, buffer_data, GL_DYNAMIC_DRAW);

    glBindBuffer(GL_ARRAY_BUFFER, 0);

    glUseProgram(context.programID_v2f_c4f_t2f);

    glUniformMatrix4fv(context.uniform_v2f_c4f_t2f_vertex_tranformation, 1, GL_FALSE, glm::value_ptr(context.mat_ortho));
    glUniform1i(context.uniform_v2f_c4f_t2f_frag_texture_sampler, 0);


    //Now Blend the Video Echo
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    glVertexAttrib4f(1, 1.0, 1.0, 1.0, a * masterAlpha);

    glBindVertexArray(m_vaoID);

    //draw video echo
    glDrawArrays(GL_TRIANGLE_FAN, 0, 4);

    glBindVertexArray(0);

    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  }

  void InitVertexAttrib() override
  {
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, (void*)0);   // Positions

    glDisableVertexAttribArray(1);

    glEnableVertexAttribArray(2);
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, (void*)(sizeof(float) * 2)); // Textures
  }
};


struct Border : public RenderItem
{
  float outer_size;
  float outer_r;
  float outer_g;
  float outer_b;
  float outer_a;

  float inner_size;
  float inner_r;
  float inner_g;
  float inner_b;
  float inner_a;

  Border() : RenderItem()
  {
    Init();
  }

  void Draw(RenderContext& context) override
  {
    //Draw Borders
    float of = outer_size * .5;
    float iff = inner_size * .5;
    float texof = 1.0 - of;

    float points[40] = {
      // Outer
      0,0,            of,0,
      0,1,            of,texof,
      1,1,            texof,texof,
      1,0,            texof,of,
      of,0,           of,of,

      // Inner
      of,of,          of + iff,of,
      of,texof,       of + iff,texof - iff,
      texof,texof,    texof - iff,texof - iff,
      texof,of,       texof - iff,of + iff,
      of + iff,of,      of + iff,of + iff,
    };

    glBindBuffer(GL_ARRAY_BUFFER, m_vboID);

    glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 40, nullptr, GL_DYNAMIC_DRAW);
    glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 40, points, GL_DYNAMIC_DRAW);

    glBindBuffer(GL_ARRAY_BUFFER, 0);

    glUseProgram(context.programID_v2f_c4f);

    glUniformMatrix4fv(context.uniform_v2f_c4f_vertex_tranformation, 1, GL_FALSE, glm::value_ptr(context.mat_ortho));

    glVertexAttrib4f(1, outer_r, outer_g, outer_b, outer_a * masterAlpha);

    //no additive drawing for borders
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    glBindVertexArray(m_vaoID);

    glDrawArrays(GL_TRIANGLE_STRIP, 0, 10);

    glVertexAttrib4f(1, inner_r, inner_g, inner_b, inner_a * masterAlpha);

    // 1st pass for inner
    glDrawArrays(GL_TRIANGLE_STRIP, 10, 10);

    // 2nd pass for inner
    glDrawArrays(GL_TRIANGLE_STRIP, 10, 10);

    glBindVertexArray(0);
  }

  void InitVertexAttrib() override
  {
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, (void*)0);
    glDisableVertexAttribArray(1);
  }
};


typedef float floatPair[2];

struct MotionVectors : public RenderItem
{
  float r;
  float g;
  float b;
  float a;
  float length;
  float x_num;
  float y_num;
  float x_offset;
  float y_offset;

  MotionVectors() : RenderItem()
  {
    Init();
  }

  void Draw(RenderContext& context) override
  {
    float  intervalx = 1.0 / x_num;
    float  intervaly = 1.0 / y_num;

    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    if (x_num + y_num < 600)
    {
      int size = x_num * y_num;

      floatPair* points = new float[size][2];

      for (int x = 0; x < (int)x_num; x++)
      {
        for (int y = 0; y < (int)y_num; y++)
        {
          float lx, ly;
          lx = x_offset + x * intervalx;
          ly = y_offset + y * intervaly;

          points[(x * (int)y_num) + y][0] = lx;
          points[(x * (int)y_num) + y][1] = ly;
        }
      }

      glBindBuffer(GL_ARRAY_BUFFER, m_vboID);

      glBufferData(GL_ARRAY_BUFFER, sizeof(floatPair) * size, nullptr, GL_DYNAMIC_DRAW);
      glBufferData(GL_ARRAY_BUFFER, sizeof(floatPair) * size, points, GL_DYNAMIC_DRAW);

      glBindBuffer(GL_ARRAY_BUFFER, 0);

      delete[] points;


      glUseProgram(context.programID_v2f_c4f);

      glUniformMatrix4fv(context.uniform_v2f_c4f_vertex_tranformation, 1, GL_FALSE, glm::value_ptr(context.mat_ortho));

#ifndef GL_TRANSITION
      if (length <= 0.0) {
        glPointSize(1.0);
      }
      else {
        glPointSize(length);
      }
#endif

      glUniform1f(context.uniform_v2f_c4f_vertex_point_size, length);
      glVertexAttrib4f(1, r, g, b, a * masterAlpha);

      glBindVertexArray(m_vaoID);

      glDrawArrays(GL_POINTS, 0, size);

      glBindVertexArray(0);
    }
  }

  void InitVertexAttrib() override
  {
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, (void*)0);
    glDisableVertexAttribArray(1);
  }
};

typedef union CValue_t {
  bool bool_val;
  int int_val;
  float float_val;
} CValue;

struct Param;
struct InitCond
{
  Param* param;
  CValue init_val;

  InitCond(Param* _param, CValue _init_val) :param(_param), init_val(_init_val)
  {
    assert(param);
  }

  void evaluate(bool evalUser);

  void evaluate()
  {
    evaluate(false);
  }
};

namespace InitCondUtils {
  struct LoadUnspecInitCond
  {
    std::map<std::string, InitCond*>& m_initCondTree;
    std::map<std::string, InitCond*>& m_perFrameInitEqnTree;

    LoadUnspecInitCond(std::map<std::string, InitCond*>& initCondTree, std::map<std::string, InitCond*>& perFrameInitEqnTree) :
      m_initCondTree(initCondTree), m_perFrameInitEqnTree(perFrameInitEqnTree) {}

    void operator() (Param* param);
  };
}

struct PerFrameEqn;

struct Waveform : public RenderItem
{
  struct ColoredPoint
  {
    float x = 0.5f;
    float y = 0.5f;
    float r = 1.0f;
    float g = 1.0f;
    float b = 1.0f;
    float a = 1.0f;
  };

  struct WaveformContext
  {
    float sample;
    int samples;
    int sample_int;
    float left;
    float right;
    BeatDetect* music;

    WaveformContext(int _samples, BeatDetect* _music) :samples(_samples), music(_music) {}
  };

  int samples;	 /* number of samples associated with this wave form. Usually powers of 2 */
  bool spectrum; /* spectrum data or pcm data */
  bool dots; /* draw wave as dots or lines */
  bool thick; /* draw thicker lines */
  bool additive; /* add color values together */

  float scaling; /* scale factor of waveform */
  float smoothing; /* smooth factor of waveform */
  int sep;  /* no idea what this is yet... */

  std::vector<ColoredPoint> points;
  std::vector<float> pointContext;

  Waveform(int _samples) : RenderItem(), samples(_samples), points(_samples), pointContext(_samples)
  {

    spectrum = false; /* spectrum data or pcm data */
    dots = false; /* draw wave as dots or lines */
    thick = false; /* draw thicker lines */
    additive = false; /* add color values together */

    scaling = 1; /* scale factor of waveform */
    smoothing = 0; /* smooth factor of waveform */
    sep = 0;

    Init();
  }

  void Draw(RenderContext& context) override
  {
    // scale PCM data based on vol_history to make it more or less independent of the application output volume
    const float  vol_scale = context.beatDetect->getPCMScale();

    // Make sure samples<=points.size().  We could reallocate points, but 512 is probably big enough.
    size_t samples_count = this->samples;
    if (samples_count > this->points.size())
      samples_count = this->points.size();

    float* value1 = new float[samples_count];
    float* value2 = new float[samples_count];
    context.beatDetect->pcm->getPCM(value1, samples_count, 0, spectrum, smoothing, 0);
    context.beatDetect->pcm->getPCM(value2, samples_count, 1, spectrum, smoothing, 0);

    float mult = scaling * (spectrum ? 0.015f : 1.0f);
    std::transform(&value1[0], &value1[samples_count], &value1[0], std::bind(std::multiplies<float>(), std::placeholders::_1, mult));
    std::transform(&value2[0], &value2[samples_count], &value2[0], std::bind(std::multiplies<float>(), std::placeholders::_1, mult));

    WaveformContext waveContext(samples_count, context.beatDetect);

    for (size_t x = 0; x < samples_count; x++)
    {
      waveContext.sample = x / (float)(samples_count - 1);
      waveContext.sample_int = x;
      waveContext.left = vol_scale * value1[x];
      waveContext.right = vol_scale * value2[x];

      points[x] = PerPoint(points[x], waveContext);
    }

    std::vector<ColoredPoint> points_transf = points;

    for (std::vector<ColoredPoint>::iterator iter = points_transf.begin(); iter != points_transf.end(); ++iter) {
      (*iter).y = -((*iter).y - 1);
      (*iter).a *= masterAlpha;
    }

    glBindBuffer(GL_ARRAY_BUFFER, m_vboID);

    glBufferData(GL_ARRAY_BUFFER, sizeof(ColoredPoint) * samples_count, nullptr, GL_DYNAMIC_DRAW);
    glBufferData(GL_ARRAY_BUFFER, sizeof(ColoredPoint) * samples_count, &points_transf[0], GL_DYNAMIC_DRAW);

    glBindBuffer(GL_ARRAY_BUFFER, 0);

    glUseProgram(context.programID_v2f_c4f);

    glUniformMatrix4fv(context.uniform_v2f_c4f_vertex_tranformation, 1, GL_FALSE, glm::value_ptr(context.mat_ortho));

    if (additive)  glBlendFunc(GL_SRC_ALPHA, GL_ONE);
    else glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    if (thick)
    {
      glLineWidth(context.texsize <= 512 ? 2 : 2 * context.texsize / 512);
      ASSERT(glGetError() == GL_NO_ERROR);

#ifndef GL_TRANSITION
      glPointSize(context.texsize <= 512 ? 2 : 2 * context.texsize / 512);
#endif
      glUniform1f(context.uniform_v2f_c4f_vertex_point_size, context.texsize <= 512 ? 2 : 2 * context.texsize / 512);
    }
    else
    {
#ifndef GL_TRANSITION
      glPointSize(context.texsize <= 512 ? 1 : context.texsize / 512);
#endif
      glUniform1f(context.uniform_v2f_c4f_vertex_point_size, context.texsize <= 512 ? 1 : context.texsize / 512);
    }

    glBindVertexArray(m_vaoID);

    if (dots)	glDrawArrays(GL_POINTS, 0, samples_count);
    else  	glDrawArrays(GL_LINE_STRIP, 0, samples_count);

    glBindVertexArray(0);

    glLineWidth(context.texsize < 512 ? 1 : context.texsize / 512);
    ASSERT(glGetError() == GL_NO_ERROR);

    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    delete[] value1;
    delete[] value2;
  }

  void InitVertexAttrib() override
  {
    glEnableVertexAttribArray(0);
    glEnableVertexAttribArray(1);

    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(ColoredPoint), (void*)0);    // points
    glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, sizeof(ColoredPoint), (void*)(sizeof(float) * 2));    // colors
  }

  virtual ColoredPoint PerPoint(ColoredPoint p, const WaveformContext context) = 0;
};

struct Expr
{
  ExprClass clazz;
  explicit Expr(ExprClass c) : clazz(c)
  {
  }

  virtual ~Expr() = default;

  virtual bool isConstant()
  {
    return false;
  };

  virtual float eval(int mesh_i, int mesh_j) = 0;

  static Expr* create_program_expr(std::vector<Expr*>& steps_, bool ownSteps);

  static Expr* create_matrix_assignment(class LValue* lhs, Expr* rhs);

  virtual Expr* _optimize()
  {
    return this;
  };

  virtual void _delete_from_tree()
  {
    delete this;
  }

  static void delete_expr(Expr* expr)
  {
    if (nullptr != expr)
      expr->_delete_from_tree();
  }

  static Expr* const_to_expr(float val);

  static Expr* optimize(Expr* expr)
  {
    Expr* optimized = expr->_optimize();
    if (optimized != expr)
      Expr::delete_expr(expr);
    return optimized;
  }

  static Expr* param_to_expr(Param* param);

  static Expr* prefun_to_expr(Func* function, Expr** expr_list);
};

struct LValue : public Expr
{
  explicit LValue(ExprClass c) : Expr(c)
  {
  }

  virtual void set(float value) = 0;
  virtual void set_matrix(int mesh_i, int mesh_j, float value) = 0;
};


#define P_TYPE_BOOL 0
#define P_TYPE_INT 1
#define P_TYPE_DOUBLE 2
#define P_TYPE_STRING 3
#define P_FLAG_NONE 0
#define P_FLAG_TVAR (1 << 3)
#define P_FLAG_ALWAYS_MATRIX (1 << 4)
#define P_FLAG_PER_PIXEL (1 << 6)
#define P_FLAG_PER_POINT (1 << 7)
#define DEFAULT_DOUBLE_IV 0.0
#define DEFAULT_DOUBLE_LB MIN_DOUBLE_SIZE
#define DEFAULT_DOUBLE_UB MAX_DOUBLE_SIZE

struct Param : public LValue
{
  std::string name; /* name of the parameter, not necessary but useful neverthless */
  short int type; /* parameter number type (int, bool, or float) */
  short int flags; /* read, write, user defined, etc */
  short int matrix_flag; /* for optimization purposes */
  void* engine_val; /* pointer to the engine variable */
  void* matrix; /* per pixel / per point matrix for this variable */
  CValue default_init_val; /* a default initial condition value */
  CValue upper_bound; /* this parameter's upper bound */
  CValue lower_bound; /* this parameter's lower bound */
  float local_value;

  Param(const std::string& _name, short int _type, short int _flags, void* _engine_val, void* _matrix,
    CValue _default_init_val, CValue _upper_bound, CValue _lower_bound) :
    LValue(PARAMETER),
    name(_name),
    type(_type),
    flags(_flags),
    matrix_flag(0),
    engine_val(_engine_val),
    matrix(_matrix),
    default_init_val(_default_init_val),
    upper_bound(_upper_bound),
    lower_bound(_lower_bound),
    local_value(0.0)
  {
  }

  Param(const std::string& _name) : LValue(PARAMETER),
    name(_name),
    type(P_TYPE_DOUBLE),
    flags(P_FLAG_USERDEF),
    matrix_flag(0),
    matrix(0),
    local_value(0.0)
  {
    engine_val = (float*)&local_value;

    default_init_val.float_val = DEFAULT_DOUBLE_IV;
    upper_bound.float_val = DEFAULT_DOUBLE_UB;
    lower_bound.float_val = DEFAULT_DOUBLE_LB;

    /// @note may have fixed a recent bug. testing
    *((float*)engine_val) = default_init_val.float_val;
  }

  static Param* new_param_float(const char* name, short int flags, void* engine_val, void* matrix,
    float upper_bound, float lower_bound, float init_val) {

    Param* param;
    CValue iv, ub, lb;
    assert(engine_val);

    iv.float_val = init_val;
    ub.float_val = upper_bound;
    lb.float_val = lower_bound;

    if ((param = Param::create(name, P_TYPE_DOUBLE, flags, engine_val, matrix, iv, ub, lb)) == nullptr)
      return nullptr;


    /* Finished, return success */
    return param;
  }

  static Param* new_param_int(const char* name, short int flags, void* engine_val, int upper_bound, int lower_bound, int init_val)
  {
    Param* param;
    CValue iv, ub, lb;
    assert(engine_val);

    iv.int_val = init_val;
    ub.int_val = upper_bound;
    lb.int_val = lower_bound;

    if ((param = Param::create(name, P_TYPE_INT, flags, engine_val, nullptr, iv, ub, lb)) == nullptr)
      return nullptr;

    /* Finished, return success */
    return param;
  }

  static Param* new_param_bool(const char* name, short int flags, void* engine_val, bool upper_bound, bool lower_bound, bool init_val)
  {
    Param* param;
    CValue iv, ub, lb;
    assert(engine_val);

    iv.bool_val = init_val;
    ub.bool_val = upper_bound;
    lb.bool_val = lower_bound;

    if ((param = Param::create(name, P_TYPE_BOOL, flags, engine_val, nullptr, iv, ub, lb)) == nullptr)
      return nullptr;

    /* Finished, return success */
    return param;
  }

  static Param* new_param_string(const char* name, short int flags, void* engine_val)
  {
    Param* param;
    CValue iv, ub, lb;
    assert(engine_val);

    iv.bool_val = 0;
    ub.bool_val = 0;
    lb.bool_val = 0;

    if ((param = Param::create(name, P_TYPE_STRING, flags, engine_val, nullptr, iv, ub, lb)) == nullptr)
      return nullptr;

    /* Finished, return success */
    return param;
  }

  static Param* createUser(const std::string& name);

  static bool is_valid_param_string(const char* string)
  {
    if (string == nullptr)
      return false;

    /* This ensures the first character is non numeric */
    if (((*string) >= 48) && ((*string) <= 57))
      return false;

    /* These probably should never happen */
    if (*string == '.')
      return false;

    if (*string == '+')
      return false;

    if (*string == '-')
      return false;

    /* Could also add checks for other symbols. May do later */

    return true;
  }

  static Param* create(const std::string& name, short int type, short int flags, void* eqn_val, void* matrix, CValue default_init_val, CValue upper_bound, CValue lower_bound);

  void set_param(float val)
  {
    matrix_flag = false;
    switch (type)
    {
    case P_TYPE_BOOL:
      if (val < 0)
        *((bool*)engine_val) = false;
      else if (val > 0)
        *((bool*)engine_val) = true;
      else
        *((bool*)engine_val) = false;
      break;
    case P_TYPE_INT:
      /* Make sure value is an integer */
      val = floor(val);
      if (val < lower_bound.int_val)
        *((int*)engine_val) = lower_bound.int_val;
      else if (val > upper_bound.int_val)
        *((int*)engine_val) = upper_bound.int_val;
      else
        *((int*)engine_val) = (int)val;
      break;
    case P_TYPE_DOUBLE:
      /* Make sure value is an integer */
      if (val < lower_bound.float_val)
        *((float*)engine_val) = lower_bound.float_val;
      else if (val > upper_bound.float_val)
        *((float*)engine_val) = upper_bound.float_val;
      else
        *((float*)engine_val) = val;
      break;
    default:
      //abort();
      break;
    }
  }

  void set_param(CValue val)
  {
    matrix_flag = false;
    switch (type)
    {
    case P_TYPE_BOOL:
      set_param(val.bool_val);
      break;
    case P_TYPE_INT:
      set_param(val.int_val);
      break;
    case P_TYPE_DOUBLE:
      set_param(val.float_val);
      break;
    default:
      //abort();
      break;
    }
  }

  void set_param(std::string& text)
  {
    *((std::string*)engine_val) = text;
  }
};

struct PerPointEqn
{
  int index;
  Expr* assign_expr;

  PerPointEqn(int _index, Param* param, Expr* gen_expr) : index(_index)
  {
    assign_expr = Expr::create_matrix_assignment(param, gen_expr);
  }
};

struct CustomWave : public Waveform
{
  int id;
  int per_frame_count;
  std::map<std::string, Param*> param_tree;
  float x; /* x position for per point equations */
  float y; /* y position for per point equations */
  float r; /* red color value */
  float g; /* green color value */
  float b; /* blue color value */
  float a; /* alpha color value */
  float* x_mesh;
  float* y_mesh;
  float* r_mesh;
  float* b_mesh;
  float* g_mesh;
  float* a_mesh;
  bool enabled;
  float sample;
  float t1;
  float t2;
  float t3;
  float t4;
  float t5;
  float t6;
  float t7;
  float t8;
  float q[NUM_Q_VARIABLES];
  float v1;
  float v2;
  std::map<std::string, InitCond*> init_cond_tree;
  std::vector<PerFrameEqn*> per_frame_eqn_tree;
  std::vector<PerPointEqn*> per_point_eqn_tree;
  Expr* per_point_program;
  std::map<std::string, InitCond*> per_frame_init_eqn_tree;

  CustomWave(int id);

  ~CustomWave()
  {
    for (std::vector<PerPointEqn*>::iterator pos = per_point_eqn_tree.begin(); pos != per_point_eqn_tree.end(); ++pos)
      delete(*pos);

    for (std::vector<PerFrameEqn*>::iterator pos = per_frame_eqn_tree.begin(); pos != per_frame_eqn_tree.end(); ++pos)
      delete(*pos);

    for (std::map<std::string, InitCond*>::iterator pos = init_cond_tree.begin(); pos != init_cond_tree.end(); ++pos)
      delete(pos->second);

    for (std::map<std::string, InitCond*>::iterator pos = per_frame_init_eqn_tree.begin(); pos != per_frame_init_eqn_tree.end(); ++pos)
      delete(pos->second);

    for (std::map<std::string, Param*>::iterator pos = param_tree.begin(); pos != param_tree.end(); ++pos)
      delete(pos->second);

    free(r_mesh);
    free(g_mesh);
    free(b_mesh);
    free(a_mesh);
    free(x_mesh);
    free(y_mesh);
  }

  int add_per_point_eqn(char* name, Expr* gen_expr);

  void loadUnspecInitConds()
  {
    InitCondUtils::LoadUnspecInitCond fun(this->init_cond_tree, this->per_frame_init_eqn_tree);
    traverse(param_tree, fun);
  }

  void evalInitConds()
  {
    for (std::map<std::string, InitCond*>::iterator pos = per_frame_init_eqn_tree.begin(); pos != per_frame_init_eqn_tree.end(); ++pos)
    {
      assert(pos->second);
      pos->second->evaluate();
    }
  }

  ColoredPoint PerPoint(ColoredPoint p, const WaveformContext context) override
  {
    if (nullptr == per_point_program)
    {
      // see comment in MilkdropPreset, collect a list of assignments into one ProgramExpr
      // which (theoretically) could be compiled together.
      std::vector<Expr*> steps;
      for (auto pos = per_point_eqn_tree.begin(); pos != per_point_eqn_tree.end(); ++pos)
        steps.push_back((*pos)->assign_expr);
      Expr* program_expr = Expr::create_program_expr(steps, false);
      Expr* jit = nullptr;
#if HAVE_LLVM
      char buffer[100];
      sprintf(buffer, "wave_%d", id);
      if (!steps.empty())
        jit = Expr::jit(program_expr, buffer);
#endif
      per_point_program = jit ? jit : program_expr;
    }

    r_mesh[context.sample_int] = r;
    g_mesh[context.sample_int] = g;
    b_mesh[context.sample_int] = b;
    a_mesh[context.sample_int] = a;
    x_mesh[context.sample_int] = x;
    y_mesh[context.sample_int] = y;
    sample = context.sample;
    v1 = context.left;
    v2 = context.right;

    per_point_program->eval(context.sample_int, -1);

    p.a = a_mesh[context.sample_int];
    p.r = r_mesh[context.sample_int];
    p.g = g_mesh[context.sample_int];
    p.b = b_mesh[context.sample_int];
    p.x = x_mesh[context.sample_int];
    p.y = y_mesh[context.sample_int];

    return p;
  }

};

struct Shape : public RenderItem
{
  struct struct_data {
    float point_x;
    float point_y;
    float color_r;
    float color_g;
    float color_b;
    float color_a;
    float tex_x;
    float tex_y;
  };

  std::string imageUrl;
  int sides;
  bool thickOutline;
  bool enabled;
  bool additive;
  bool textured;

  float tex_zoom;
  float tex_ang;

  float radius;
  float ang;

  float x; /* x position  */
  float y; /* y position  */

  float r; /* red color value */
  float g; /* green color value */
  float b; /* blue color value */
  float a; /* alpha color value */

  float border_r; /* red color value */
  float border_g; /* green color value */
  float border_b; /* blue color value */
  float border_a; /* alpha color value */

  float r2; /* red color value */
  float g2; /* green color value */
  float b2; /* blue color value */
  float a2; /* alpha color value */

  GLuint m_vboID_texture;
  GLuint m_vaoID_texture;

  GLuint m_vboID_not_texture;
  GLuint m_vaoID_not_texture;

  Shape() : RenderItem()
  {
    sides = 4;
    thickOutline = false;
    enabled = true;
    additive = false;
    textured = false;

    tex_zoom = 1.0;
    tex_ang = 0.0;

    x = 0.5;
    y = 0.5;
    radius = 1.0;
    ang = 0.0;

    r = 0.0; /* red color value */
    g = 0.0; /* green color value */
    b = 0.0; /* blue color value */
    a = 0.0; /* alpha color value */

    r2 = 0.0; /* red color value */
    g2 = 0.0; /* green color value */
    b2 = 0.0; /* blue color value */
    a2 = 0.0; /* alpha color value */

    border_r = 0.0; /* red color value */
    border_g = 0.0; /* green color value */
    border_b = 0.0; /* blue color value */
    border_a = 0.0; /* alpha color value */


    glGenVertexArrays(1, &m_vaoID_texture);
    glGenBuffers(1, &m_vboID_texture);

    glGenVertexArrays(1, &m_vaoID_not_texture);
    glGenBuffers(1, &m_vboID_not_texture);

    glBindVertexArray(m_vaoID_texture);
    glBindBuffer(GL_ARRAY_BUFFER, m_vboID_texture);

    glEnableVertexAttribArray(0);
    glEnableVertexAttribArray(1);
    glEnableVertexAttribArray(2);

    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(struct_data), (void*)0);     // Positions
    glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, sizeof(struct_data), (void*)(sizeof(float) * 2));     // Colors
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(struct_data), (void*)(sizeof(float) * 6)); // Textures

    glBindVertexArray(m_vaoID_not_texture);
    glBindBuffer(GL_ARRAY_BUFFER, m_vboID_not_texture);

    glEnableVertexAttribArray(0);
    glEnableVertexAttribArray(1);

    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(struct_data), (void*)0);   // points
    glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, sizeof(struct_data), (void*)(sizeof(float) * 2));     // Colors

    Init();
  }

  void Draw(RenderContext& context) override
  {
    float xval, yval;
    float t;

    float temp_radius = radius * (.707 * .707 * .707 * 1.04);

    //Additive Drawing or Overwrite
    if (additive == 0)  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    else    glBlendFunc(GL_SRC_ALPHA, GL_ONE);

    xval = x;
    yval = -(y - 1);

    struct_data* buffer_data = new struct_data[sides + 2];

    if (textured)
    {
      if (imageUrl != "")
      {
        TextureSamplerDesc tex = context.textureManager->getTexture(imageUrl, GL_CLAMP_TO_EDGE, GL_LINEAR);
        if (tex.first != nullptr)
        {
          glActiveTexture(GL_TEXTURE0);
          glBindTexture(GL_TEXTURE_2D, tex.first->texID);
          glBindSampler(0, tex.second->samplerID);

          context.aspectRatio = 1.0;
        }
      }
      else
      {
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, context.textureManager->getMainTexture()->texID);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
      }

      //Define the center point of the shape
      buffer_data[0].color_r = r;
      buffer_data[0].color_g = g;
      buffer_data[0].color_b = b;
      buffer_data[0].color_a = a * masterAlpha;
      buffer_data[0].tex_x = 0.5;
      buffer_data[0].tex_y = 0.5;
      buffer_data[0].point_x = xval;
      buffer_data[0].point_y = yval;

      for (int i = 1; i < sides + 2; i++)
      {
        buffer_data[i].color_r = r2;
        buffer_data[i].color_g = g2;
        buffer_data[i].color_b = b2;
        buffer_data[i].color_a = a2 * masterAlpha;

        t = (i - 1) / (float)sides;
        buffer_data[i].tex_x = 0.5f + 0.5f * cosf(t * 3.1415927f * 2 + tex_ang + 3.1415927f * 0.25f) * (context.aspectCorrect ? context.aspectRatio : 1.0) / tex_zoom;
        buffer_data[i].tex_y = 0.5f + 0.5f * sinf(t * 3.1415927f * 2 + tex_ang + 3.1415927f * 0.25f) / tex_zoom;
        buffer_data[i].point_x = temp_radius * cosf(t * 3.1415927f * 2 + ang + 3.1415927f * 0.25f) * (context.aspectCorrect ? context.aspectRatio : 1.0) + xval;
        buffer_data[i].point_y = temp_radius * sinf(t * 3.1415927f * 2 + ang + 3.1415927f * 0.25f) + yval;

      }

      glBindBuffer(GL_ARRAY_BUFFER, m_vboID_texture);

      glBufferData(GL_ARRAY_BUFFER, sizeof(struct_data) * (sides + 2), nullptr, GL_DYNAMIC_DRAW);
      glBufferData(GL_ARRAY_BUFFER, sizeof(struct_data) * (sides + 2), buffer_data, GL_DYNAMIC_DRAW);

      glUseProgram(context.programID_v2f_c4f_t2f);

      glUniformMatrix4fv(context.uniform_v2f_c4f_t2f_vertex_tranformation, 1, GL_FALSE, glm::value_ptr(context.mat_ortho));
      glUniform1i(context.uniform_v2f_c4f_t2f_frag_texture_sampler, 0);

      glBindVertexArray(m_vaoID_texture);
      glDrawArrays(GL_TRIANGLE_FAN, 0, sides + 2);
      glBindVertexArray(0);
    }
    else
    {//Untextured (use color values)

      //Define the center point of the shape
      buffer_data[0].color_r = r;
      buffer_data[0].color_g = g;
      buffer_data[0].color_b = b;
      buffer_data[0].color_a = a * masterAlpha;
      buffer_data[0].point_x = xval;
      buffer_data[0].point_y = yval;


      for (int i = 1; i < sides + 2; i++)
      {
        buffer_data[i].color_r = r2;
        buffer_data[i].color_g = g2;
        buffer_data[i].color_b = b2;
        buffer_data[i].color_a = a2 * masterAlpha;
        t = (i - 1) / (float)sides;
        buffer_data[i].point_x = temp_radius * cosf(t * 3.1415927f * 2 + ang + 3.1415927f * 0.25f) * (context.aspectCorrect ? context.aspectRatio : 1.0) + xval;
        buffer_data[i].point_y = temp_radius * sinf(t * 3.1415927f * 2 + ang + 3.1415927f * 0.25f) + yval;
      }

      glBindBuffer(GL_ARRAY_BUFFER, m_vboID_not_texture);

      glBufferData(GL_ARRAY_BUFFER, sizeof(struct_data) * (sides + 2), nullptr, GL_DYNAMIC_DRAW);
      glBufferData(GL_ARRAY_BUFFER, sizeof(struct_data) * (sides + 2), buffer_data, GL_DYNAMIC_DRAW);

      glUseProgram(context.programID_v2f_c4f);

      glUniformMatrix4fv(context.uniform_v2f_c4f_vertex_tranformation, 1, GL_FALSE, glm::value_ptr(context.mat_ortho));

      glBindVertexArray(m_vaoID_not_texture);
      glDrawArrays(GL_TRIANGLE_FAN, 0, sides + 2);
      glBindVertexArray(0);
    }


    floatPair* points = new float[sides + 1][2];

    for (int i = 0; i < sides; i++)
    {
      t = (i - 1) / (float)sides;
      points[i][0] = temp_radius * cosf(t * 3.1415927f * 2 + ang + 3.1415927f * 0.25f) * (context.aspectCorrect ? context.aspectRatio : 1.0) + xval;
      points[i][1] = temp_radius * sinf(t * 3.1415927f * 2 + ang + 3.1415927f * 0.25f) + yval;
    }

    glBindBuffer(GL_ARRAY_BUFFER, m_vboID);

    glBufferData(GL_ARRAY_BUFFER, sizeof(floatPair) * (sides), nullptr, GL_DYNAMIC_DRAW);
    glBufferData(GL_ARRAY_BUFFER, sizeof(floatPair) * (sides), points, GL_DYNAMIC_DRAW);

    glBindBuffer(GL_ARRAY_BUFFER, 0);

    glUseProgram(context.programID_v2f_c4f);

    glUniformMatrix4fv(context.uniform_v2f_c4f_vertex_tranformation, 1, GL_FALSE, glm::value_ptr(context.mat_ortho));

    glVertexAttrib4f(1, border_r, border_g, border_b, border_a * masterAlpha);

    if (thickOutline == 1)
    {
      glLineWidth(context.texsize < 512 ? 1 : 2 * context.texsize / 512);
      ASSERT(glGetError() == GL_NO_ERROR);
    }

    glBindVertexArray(m_vaoID);
    glDrawArrays(GL_LINE_LOOP, 0, sides);
    glBindVertexArray(0);

    glBindTexture(GL_TEXTURE_2D, 0);
    glBindSampler(0, 0);

    if (thickOutline == 1)
    {
      glLineWidth(context.texsize < 512 ? 1 : context.texsize / 512);
      ASSERT(glGetError() == GL_NO_ERROR);
    }

    delete[] buffer_data;
    delete[] points;
  }

  void InitVertexAttrib() override
  {
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, (void*)0);   // points
    glDisableVertexAttribArray(1);
  }
};

struct CustomShape : public Shape
{
  int id;
  int per_frame_count;
  std::map<std::string, Param*> param_tree;
  bool enabled;

  /* stupid t variables */
  float t1;
  float t2;
  float t3;
  float t4;
  float t5;
  float t6;
  float t7;
  float t8;

  float q[NUM_Q_VARIABLES];

  std::map<std::string, InitCond*>  init_cond_tree;
  std::vector<PerFrameEqn*>  per_frame_eqn_tree;
  std::map<std::string, InitCond*>  per_frame_init_eqn_tree;

  std::map<std::string, Param*> text_properties_tree;

  CustomShape(int _id = 0);

  ~CustomShape()
  {
    traverseVector<TraverseFunctors::Delete<PerFrameEqn> >(per_frame_eqn_tree);
    traverse<TraverseFunctors::Delete<InitCond> >(init_cond_tree);
    traverse<TraverseFunctors::Delete<Param> >(param_tree);
    traverse<TraverseFunctors::Delete<InitCond> >(per_frame_init_eqn_tree);
    traverse<TraverseFunctors::Delete<Param> >(text_properties_tree);
  }


  void loadUnspecInitConds()
  {
    InitCondUtils::LoadUnspecInitCond fun(this->init_cond_tree, this->per_frame_init_eqn_tree);
    traverse(param_tree, fun);
  }

  void evalInitConds()
  {
    // NOTE: This is verified to be same behavior as trunk
    for (std::map<std::string, InitCond*>::iterator pos = per_frame_init_eqn_tree.begin(); pos != per_frame_init_eqn_tree.end(); ++pos)
      pos->second->evaluate();
  }
};

struct DarkenCenter : public RenderItem
{
  DarkenCenter() : RenderItem()
  {
    Init();
  }

  void Draw(RenderContext& context) override
  {
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    glUseProgram(context.programID_v2f_c4f);

    glUniformMatrix4fv(context.uniform_v2f_c4f_vertex_tranformation, 1, GL_FALSE, glm::value_ptr(context.mat_ortho));

    glBindVertexArray(m_vaoID);

    glDrawArrays(GL_TRIANGLE_FAN, 0, 6);

    glBindVertexArray(0);
  }

  void InitVertexAttrib() override
  {
    float points_colors[6][6] = {
        { 0.5,  0.5,      0, 0, 0, (3.0f / 32.0f) * masterAlpha},
        { 0.45, 0.5,      0, 0, 0, 0},
        { 0.5,  0.45,     0, 0, 0, 0},
        { 0.55, 0.5,      0, 0, 0, 0},
        { 0.5,  0.55,     0, 0, 0, 0},
        { 0.45, 0.5,      0, 0, 0, 0} };

    glEnableVertexAttribArray(0);
    glEnableVertexAttribArray(1);

    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 6, (void*)0); // points
    glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, sizeof(float) * 6, (void*)(sizeof(float) * 2)); // colors

    glBufferData(GL_ARRAY_BUFFER, sizeof(points_colors), points_colors, GL_STATIC_DRAW);
  }
};

struct Brighten : public RenderItem
{
  Brighten()
  {
    Init();
  }

  void Draw(RenderContext& context) override
  {
    glUseProgram(context.programID_v2f_c4f);

    glUniformMatrix4fv(context.uniform_v2f_c4f_vertex_tranformation, 1, GL_FALSE, glm::value_ptr(context.mat_ortho));

    glBindVertexArray(m_vaoID);

    glVertexAttrib4f(1, 1.0, 1.0, 1.0, 1.0);
    glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO);
    glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
    glBlendFunc(GL_ZERO, GL_DST_COLOR);
    glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
    glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO);
    glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    glBindVertexArray(0);
  }

  void InitVertexAttrib() override
  {
    float points[4][2] = { {-0.5, -0.5},
                      {-0.5,  0.5},
                      { 0.5,  0.5},
                      { 0.5,  -0.5} };

    glBufferData(GL_ARRAY_BUFFER, sizeof(points), points, GL_STATIC_DRAW);

    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, (void*)0);
    glDisableVertexAttribArray(1);
  }
};

struct Darken : public RenderItem
{
  Darken()
  {
    Init();
  }

  void Draw(RenderContext& context) override
  {
    glUseProgram(context.programID_v2f_c4f);

    glUniformMatrix4fv(context.uniform_v2f_c4f_vertex_tranformation, 1, GL_FALSE, glm::value_ptr(context.mat_ortho));

    glVertexAttrib4f(1, 1.0, 1.0, 1.0, 1.0);

    glBlendFunc(GL_ZERO, GL_DST_COLOR);

    glBindVertexArray(m_vaoID);
    glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
    glBindVertexArray(0);

    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  }

  void InitVertexAttrib() override
  {
    float points[4][2] = { {-0.5, -0.5},
                      {-0.5,  0.5},
                      { 0.5,  0.5},
                      { 0.5,  -0.5} };

    glBufferData(GL_ARRAY_BUFFER, sizeof(points), points, GL_STATIC_DRAW);

    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, (void*)0);
    glDisableVertexAttribArray(1);
  }
};

struct Invert : public RenderItem
{
  Invert()
  {
    Init();
  }

  void Draw(RenderContext& context) override
  {
    glUseProgram(context.programID_v2f_c4f);

    glUniformMatrix4fv(context.uniform_v2f_c4f_vertex_tranformation, 1, GL_FALSE, glm::value_ptr(context.mat_ortho));

    glVertexAttrib4f(1, 1.0, 1.0, 1.0, 1.0);

    glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO);

    glBindVertexArray(m_vaoID);
    glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
    glBindVertexArray(0);

    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  }

  void InitVertexAttrib() override
  {
    float points[4][2] = { {-0.5, -0.5},
                      {-0.5,  0.5},
                      { 0.5,  0.5},
                      { 0.5,  -0.5} };

    glBufferData(GL_ARRAY_BUFFER, sizeof(points), points, GL_STATIC_DRAW);

    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, (void*)0);
    glDisableVertexAttribArray(1);
  }
};

struct Solarize : public RenderItem
{
  Solarize()
  {
    Init();
  }

  void Draw(RenderContext& context) override
  {
    glUseProgram(context.programID_v2f_c4f);

    glUniformMatrix4fv(context.uniform_v2f_c4f_vertex_tranformation, 1, GL_FALSE, glm::value_ptr(context.mat_ortho));

    glVertexAttrib4f(1, 1.0, 1.0, 1.0, 1.0);

    glBlendFunc(GL_ZERO, GL_ONE_MINUS_DST_COLOR);

    glBindVertexArray(m_vaoID);
    glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
    glBlendFunc(GL_DST_COLOR, GL_ONE);
    glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
    glBindVertexArray(0);

    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  }

  void InitVertexAttrib() override
  {
    float points[4][2] = { {-0.5, -0.5},
                          {-0.5,  0.5},
                          { 0.5,  0.5},
                          { 0.5,  -0.5} };

    glBufferData(GL_ARRAY_BUFFER, sizeof(points), points, GL_STATIC_DRAW);

    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, (void*)0);
    glDisableVertexAttribArray(1);
  }
};

struct PresetOutputs : public Pipeline
{
  typedef std::vector<CustomWave*> cwave_container;
  typedef std::vector<CustomShape*> cshape_container;

  cwave_container customWaves;
  cshape_container customShapes;

  float zoom;
  float zoomexp;
  float rot;
  float warp;

  float sx;
  float sy;
  float dx;
  float dy;
  float cx;
  float cy;

  VideoEcho videoEcho;

  MilkdropWaveform wave;
  Border border;
  MotionVectors mv;

  DarkenCenter darkenCenter;
  Brighten brighten;
  Darken darken;
  Invert invert;
  Solarize solarize;

  int gy;
  int gx;

  float fRating;
  float fGammaAdj;

  bool bDarkenCenter;
  bool bRedBlueStereo;
  bool bBrighten;
  bool bDarken;
  bool bSolarize;
  bool bInvert;
  bool bMotionVectorsOn;

  float fWarpAnimSpeed;
  float fWarpScale;
  float fShader;

  float** zoom_mesh;
  float** zoomexp_mesh;
  float** rot_mesh;

  float** sx_mesh;
  float** sy_mesh;
  float** dx_mesh;
  float** dy_mesh;
  float** cx_mesh;
  float** cy_mesh;
  float** warp_mesh;

  float** orig_x;  //original mesh
  float** orig_y;
  float** rad_mesh;

  PresetOutputs() : Pipeline()
  {
  }

  // N.B. The more optimization that can be done on this method, the better! This is called a lot and can probably be improved.
  void PerPixelMath_c(const PipelineContext& context)
  {
    for (int x = 0; x < gx; x++)
    {
      for (int y = 0; y < gy; y++)
      {
        const float fZoom2 = std::pow(this->zoom_mesh[x][y], std::pow(this->zoomexp_mesh[x][y],
          rad_mesh[x][y] * 2.0f - 1.0f));
        const float fZoom2Inv = 1.0f / fZoom2;
        this->x_mesh[x][y] = this->orig_x[x][y] * 0.5f * fZoom2Inv + 0.5f;
        this->x_mesh[x][y] = (this->x_mesh[x][y] - this->cx_mesh[x][y]) / this->sx_mesh[x][y] + this->cx_mesh[x][y];
        this->y_mesh[x][y] = this->orig_y[x][y] * 0.5f * fZoom2Inv + 0.5f;
        this->y_mesh[x][y] = (this->y_mesh[x][y] - this->cy_mesh[x][y]) / this->sy_mesh[x][y] + this->cy_mesh[x][y];
      }
    }

    const float fWarpTime = context.time * this->fWarpAnimSpeed;
    const float fWarpScaleInv = 1.0f / this->fWarpScale;
    float f[4];
    f[0] = 11.68f + 4.0f * cosf(fWarpTime * 1.413f + 10);
    f[1] = 8.77f + 3.0f * cosf(fWarpTime * 1.113f + 7);
    f[2] = 10.54f + 3.0f * cosf(fWarpTime * 1.233f + 3);
    f[3] = 11.49f + 4.0f * cosf(fWarpTime * 0.933f + 5);

    for (int x = 0; x < gx; x++)
    {
      for (int y = 0; y < gy; y++)
      {
        const float orig_x2 = this->orig_x[x][y];
        const float orig_y2 = this->orig_y[x][y];
        const float warp_mesh2 = this->warp_mesh[x][y] * 0.0035f;

        this->x_mesh[x][y] +=
          (warp_mesh2 * sinf(fWarpTime * 0.333f + fWarpScaleInv * (orig_x2 * f[0] - orig_y2 * f[3]))) +
          (warp_mesh2 * cosf(fWarpTime * 0.753f - fWarpScaleInv * (orig_x2 * f[1] - orig_y2 * f[2])));

        this->y_mesh[x][y] +=
          (warp_mesh2 * cosf(fWarpTime * 0.375f - fWarpScaleInv * (orig_x2 * f[2] + orig_y2 * f[1]))) +
          (warp_mesh2 * sinf(fWarpTime * 0.825f + fWarpScaleInv * (orig_x2 * f[0] + orig_y2 * f[3])));
      }
    }

    for (int x = 0; x < gx; x++)
    {
      for (int y = 0; y < gy; y++)
      {
        const float u2 = this->x_mesh[x][y] - this->cx_mesh[x][y];
        const float v2 = this->y_mesh[x][y] - this->cy_mesh[x][y];

        const float rot2 = this->rot_mesh[x][y];
        const float cos_rot = cosf(rot2);
        const float sin_rot = sinf(rot2);

        this->x_mesh[x][y] = u2 * cos_rot - v2 * sin_rot + this->cx_mesh[x][y] - this->dx_mesh[x][y];
        this->y_mesh[x][y] = u2 * sin_rot + v2 * cos_rot + this->cy_mesh[x][y] - this->dy_mesh[x][y];
      }
    }
  }

  void PerPixelMath(const PipelineContext& context)
  {
    PerPixelMath_c(context);
  }

  void Render(const BeatDetect& music, const PipelineContext& context)
  {
    PerPixelMath(context);

    drawables.clear();

    drawables.push_back(&mv);

    for (PresetOutputs::cshape_container::iterator pos = customShapes.begin();
      pos != customShapes.end(); ++pos)
    {
      if ((*pos)->enabled == 1)
        drawables.push_back((*pos));
    }

    for (PresetOutputs::cwave_container::iterator pos = customWaves.begin();
      pos != customWaves.end(); ++pos)
    {
      if ((*pos)->enabled == 1)
        drawables.push_back((*pos));
    }

    drawables.push_back(&wave);

    if (bDarkenCenter == 1)
      drawables.push_back(&darkenCenter);
    drawables.push_back(&border);

    compositeDrawables.clear();
    compositeDrawables.push_back(&videoEcho);

    if (bBrighten == 1)
      compositeDrawables.push_back(&brighten);

    if (bDarken == 1)
      compositeDrawables.push_back(&darken);

    if (bSolarize == 1)
      compositeDrawables.push_back(&solarize);

    if (bInvert == 1)
      compositeDrawables.push_back(&invert);
  }

  void Initialize(int _gx, int _gy)
  {
    assert(_gx > 0);

    this->gx = _gx;
    this->gy = _gy;

    staticPerPixel = true;

    assert(this->gx > 0);
    int x;
    this->x_mesh = alloc_mesh(gx, gy);
    this->y_mesh = alloc_mesh(gx, gy);
    this->sx_mesh = alloc_mesh(gx, gy);
    this->sy_mesh = alloc_mesh(gx, gy);
    this->dx_mesh = alloc_mesh(gx, gy);
    this->dy_mesh = alloc_mesh(gx, gy);
    this->cx_mesh = alloc_mesh(gx, gy);
    this->cy_mesh = alloc_mesh(gx, gy);
    this->zoom_mesh = alloc_mesh(gx, gy);
    this->zoomexp_mesh = alloc_mesh(gx, gy);
    this->rot_mesh = alloc_mesh(gx, gy);

    this->warp_mesh = alloc_mesh(gx, gy);
    this->rad_mesh = alloc_mesh(gx, gy);
    this->orig_x = alloc_mesh(gx, gy);
    this->orig_y = alloc_mesh(gx, gy);

    //initialize reference grid values
    for (x = 0; x < gx; x++)
    {
      for (int y = 0; y < gy; y++)
      {
        float origx = x / (float)(gx - 1);
        float origy = -((y / (float)(gy - 1)) - 1);

        rad_mesh[x][y] = hypot((origx - .5) * 2, (origy - .5) * 2) * .7071067;
        orig_x[x][y] = (origx - .5) * 2;
        orig_y[x][y] = (origy - .5) * 2;
      }
    }
  }
};

class InfixOp
{
public:
  int type;
  int precedence;

  InfixOp(int _type, int _precedence)
  {
    this->type = _type;
    this->precedence = _precedence;
  }
};

#define INFIX_ADD 0
#define INFIX_MINUS 1
#define INFIX_MOD 2
#define INFIX_DIV 3
#define INFIX_MULT 4
#define INFIX_OR 5
#define INFIX_AND 6

namespace Eval
{
  InfixOp* infix_add = nullptr;
  InfixOp* infix_minus = nullptr;
  InfixOp* infix_div = nullptr;
  InfixOp* infix_mult = nullptr;
  InfixOp* infix_or = nullptr;
  InfixOp* infix_and = nullptr;
  InfixOp* infix_mod = nullptr;
  InfixOp* infix_negative = nullptr;
  InfixOp* infix_positive = nullptr;

  int init_infix_ops()
  {
    if (nullptr == Eval::infix_add)
    {
      Eval::infix_add = new InfixOp(INFIX_ADD, 4);
      Eval::infix_minus = new InfixOp(INFIX_MINUS, 3);
      Eval::infix_div = new InfixOp(INFIX_DIV, 2);
      Eval::infix_or = new InfixOp(INFIX_OR, 5);
      Eval::infix_and = new InfixOp(INFIX_AND, 4);
      Eval::infix_mod = new InfixOp(INFIX_MOD, 1);
      Eval::infix_mult = new InfixOp(INFIX_MULT, 2);

      /* Prefix operators */
      Eval::infix_positive = new InfixOp(INFIX_ADD, 0);
      Eval::infix_negative = new InfixOp(INFIX_MINUS, 0);
    }
    return PROJECTM_SUCCESS;
  }
};

static void resetPresetOutputs(PresetOutputs* presetOutputs)
{
  presetOutputs->zoom = 1.0;
  presetOutputs->zoomexp = 1.0;
  presetOutputs->rot = 0.0;
  presetOutputs->warp = 0.0;

  presetOutputs->sx = 1.0;
  presetOutputs->sy = 1.0;
  presetOutputs->dx = 0.0;
  presetOutputs->dy = 0.0;
  presetOutputs->cx = 0.5;
  presetOutputs->cy = 0.5;

  presetOutputs->screenDecay = .98;

  presetOutputs->wave.r = 1.0;
  presetOutputs->wave.g = 0.2;
  presetOutputs->wave.b = 0.0;
  presetOutputs->wave.x = 0.5;
  presetOutputs->wave.y = 0.5;
  presetOutputs->wave.mystery = 0.0;

  presetOutputs->border.outer_size = 0.0;
  presetOutputs->border.outer_r = 0.0;
  presetOutputs->border.outer_g = 0.0;
  presetOutputs->border.outer_b = 0.0;
  presetOutputs->border.outer_a = 0.0;

  presetOutputs->border.inner_size = 0.0;
  presetOutputs->border.inner_r = 0.0;
  presetOutputs->border.inner_g = 0.0;
  presetOutputs->border.inner_b = 0.0;
  presetOutputs->border.inner_a = 0.0;

  presetOutputs->mv.a = 0.0;
  presetOutputs->mv.r = 0.0;
  presetOutputs->mv.g = 0.0;
  presetOutputs->mv.b = 0.0;
  presetOutputs->mv.length = 1.0;
  presetOutputs->mv.x_num = 16.0;
  presetOutputs->mv.y_num = 12.0;
  presetOutputs->mv.x_offset = 0.02;
  presetOutputs->mv.y_offset = 0.02;


  /* PER_FRAME CONSTANTS END */
  presetOutputs->fRating = 0;
  presetOutputs->fGammaAdj = 1.0;
  presetOutputs->videoEcho.zoom = 1.0;
  presetOutputs->videoEcho.a = 0;
  presetOutputs->videoEcho.orientation = Normal;

  presetOutputs->wave.additive = false;
  presetOutputs->wave.dots = false;
  presetOutputs->wave.thick = false;
  presetOutputs->wave.modulateAlphaByVolume = 0;
  presetOutputs->wave.maximizeColors = 0;
  presetOutputs->textureWrap = 0;
  presetOutputs->bDarkenCenter = 0;
  presetOutputs->bRedBlueStereo = 0;
  presetOutputs->bBrighten = 0;
  presetOutputs->bDarken = 0;
  presetOutputs->bSolarize = 0;
  presetOutputs->bInvert = 0;
  presetOutputs->bMotionVectorsOn = 1;

  presetOutputs->wave.a = 1.0;
  presetOutputs->wave.scale = 1.0;
  presetOutputs->wave.smoothing = 0;
  presetOutputs->wave.mystery = 0;
  presetOutputs->wave.modOpacityEnd = 0;
  presetOutputs->wave.modOpacityStart = 0;
  presetOutputs->fWarpAnimSpeed = 0;
  presetOutputs->fWarpScale = 0;
  presetOutputs->fShader = 0;

  /* PER_PIXEL CONSTANT END */
  /* Q VARIABLES START */

  for (int i = 0; i < 32; i++)
    presetOutputs->q[i] = 0;

  //	for ( std::vector<CustomWave*>::iterator pos = presetOutputs->customWaves.begin();
  //	        pos != presetOutputs->customWaves.end(); ++pos )
  //		if ( *pos != 0 ) delete ( *pos );

  //	for ( std::vector<CustomShape*>::iterator pos = presetOutputs->customShapes.begin();
  //	        pos != presetOutputs->customShapes.end(); ++pos )
  //		if ( *pos != 0 ) delete ( *pos );

  presetOutputs->customWaves.clear();
  presetOutputs->customShapes.clear();

  /* Q VARIABLES END */

}

struct IdlePresets
{
  static const std::string IDLE_PRESET_NAME;

  static std::unique_ptr<Preset> allocate(struct MilkdropPresetFactory* factory, const std::string& name, PresetOutputs& presetOutputs);

  static std::string presetText()
  {
    std::ostringstream out;

    out << "[preset00]\n" <<
      "fRating=2.000000\n" <<
      "fGammaAdj=1.700000\n" <<
      "fDecay=0.940000\n" <<
      "fVideoEchoZoom=1.000000\n" <<
      "fVideoEchoAlpha=0.000000\n" <<
      "nVideoEchoOrientation=0\n" <<
      "nWaveMode=0\n" <<
      "bAdditiveWaves=1\n" <<
      "bWaveDots=0\n" <<
      "bWaveThick=0\n" <<
      "bModWaveAlphaByVolume=0\n" <<
      "bMaximizeWaveColor=0\n" <<
      "bTexWrap=1\n" <<
      "bDarkenCenter=0\n" <<
      "bRedBlueStereo=0\n" <<
      "bBrighten=0\n" <<
      "bDarken=0\n" <<
      "bSolarize=0\n" <<
      "bInvert=0\n" <<
      "fWaveAlpha=0.001000\n" <<
      "fWaveScale=0.010000\n" <<
      "fWaveSmoothing=0.630000\n" <<
      "fWaveParam=-1.000000\n" <<
      "fModWaveAlphaStart=0.710000\n" <<
      "fModWaveAlphaEnd=1.300000\n" <<
      "fWarpAnimSpeed=1.000000\n" <<
      "fWarpScale=1.331000\n" <<
      "fZoomExponent=1.000000\n" <<
      "fShader=0.000000\n" <<
      "zoom=13.290894\n" <<
      "rot=-0.020000\n" <<
      "cx=0.500000\n" <<
      "cy=0.500000\n" <<
      "dx=-0.280000\n" <<
      "dy=-0.320000\n" <<
      "warp=0.010000\n" <<
      "sx=1.000000\n" <<
      "sy=1.000000\n" <<
      "wave_r=0.650000\n" <<
      "wave_g=0.650000\n" <<
      "wave_b=0.650000\n" <<
      "wave_x=0.500000\n" <<
      "wave_y=0.500000\n" <<
      "ob_size=0.000000\n" <<
      "ob_r=0.010000\n" <<
      "ob_g=0.000000\n" <<
      "ob_b=0.000000\n" <<
      "ob_a=1.000000\n" <<
      "ib_size=0.000000\n" <<
      "ib_r=0.950000\n" <<
      "ib_g=0.850000\n" <<
      "ib_b=0.650000\n" <<
      "ib_a=1.000000\n" <<
      "nMotionVectorsX=64.000000\n" <<
      "nMotionVectorsY=0.000000\n" <<
      "mv_dx=0.000000\n" <<
      "mv_dy=0.000000\n" <<
      "mv_l=0.900000\n" <<
      "mv_r=1.000000\n" <<
      "mv_g=1.000000\n" <<
      "mv_b=1.000000\n" <<
      "mv_a=0.000000\n" <<
      "shapecode_3_enabled=1\n" <<
      "shapecode_3_sides=20\n" <<
      "shapecode_3_additive=0\n" <<
      "shapecode_3_thickOutline=0\n" <<
      "shapecode_3_textured=1\n" <<
      "shapecode_3_ImageURL=M.tga\n" <<
      "shapecode_3_x=0.68\n" <<
      "shapecode_3_y=0.5\n" <<
      "shapecode_3_rad=0.41222\n" <<
      "shapecode_3_ang=0\n" <<
      "shapecode_3_tex_ang=0\n" <<
      "shapecode_3_tex_zoom=0.71\n" <<
      "shapecode_3_r=1\n" <<
      "shapecode_3_g=1\n" <<
      "shapecode_3_b=1\n" <<
      "shapecode_3_a=1\n" <<
      "shapecode_3_r2=1\n" <<
      "shapecode_3_g2=1\n" <<
      "shapecode_3_b2=1\n" <<
      "shapecode_3_a2=1\n" <<
      "shapecode_3_border_r=0\n" <<
      "shapecode_3_border_g=0\n" <<
      "shapecode_3_border_b=0\n" <<
      "shapecode_3_border_a=0\n" <<
      "shape_3_per_frame1=x = x + q1;\n" <<
      "shape_3_per_frame2=y = y + q2;\n" <<
      "shape_3_per_frame3=r =0.5 + 0.5*sin(q8*0.613 + 1);\n" <<
      "shape_3_per_frame4=g = 0.5 + 0.5*sin(q8*0.763 + 2);\n" <<
      "shape_3_per_frame5=b = 0.5 + 0.5*sin(q8*0.771 + 5);\n" <<
      "shape_3_per_frame6=r2 = 0.5 + 0.5*sin(q8*0.635 + 4);\n" <<
      "shape_3_per_frame7=g2 = 0.5 + 0.5*sin(q8*0.616+ 1);\n" <<
      "shape_3_per_frame8=b2 = 0.5 + 0.5*sin(q8*0.538 + 3);\n" <<
      "shapecode_4_enabled=1\n" <<
      "shapecode_4_sides=4\n" <<
      "shapecode_4_additive=0\n" <<
      "shapecode_4_thickOutline=0\n" <<
      "shapecode_4_textured=1\n" <<
      "shapecode_4_ImageURL=headphones.tga\n" <<
      "shapecode_4_x=0.68\n" <<
      "shapecode_4_y=0.58\n" <<
      "shapecode_4_rad=0.6\n" <<
      "shapecode_4_ang=0\n" <<
      "shapecode_4_tex_ang=0\n" <<
      "shapecode_4_tex_zoom=0.71\n" <<
      "shapecode_4_r=1\n" <<
      "shapecode_4_g=1\n" <<
      "shapecode_4_b=1\n" <<
      "shapecode_4_a=1\n" <<
      "shapecode_4_r2=1\n" <<
      "shapecode_4_g2=1\n" <<
      "shapecode_4_b2=1\n" <<
      "shapecode_4_a2=1\n" <<
      "shapecode_4_border_r=0\n" <<
      "shapecode_4_border_g=0\n" <<
      "shapecode_4_border_b=0\n" <<
      "shapecode_4_border_a=0\n" <<
      "shape_4_per_frame1=x = x + q1;\n" <<
      "shape_4_per_frame2=y = y + q2;\n" <<
      "shape_4_per_frame3=rad = rad + bass * 0.1;\n" <<
      "shape_4_per_frame4=a = q3;\n" <<
      "shape_4_per_frame5=a2 = q3;\n" <<
      // disabling projectM logo thingey
      // "shapecode_6_enabled=1\n" <<
      // "shapecode_6_sides=4\n" <<
      // "shapecode_6_additive=0\n" <<
      // "shapecode_6_thickOutline=0\n" <<
      // "shapecode_6_textured=1\n" <<
      // "shapecode_6_ImageURL=project.tga\n" <<
      // "shapecode_6_x=0.38\n" <<
      // "shapecode_6_y=0.435\n" <<
      // "shapecode_6_rad=0.8\n" <<
      // "shapecode_6_ang=0\n" <<
      // "shapecode_6_tex_ang=0\n" <<
      // "shapecode_6_tex_zoom=0.71\n" <<
      // "shapecode_6_r=1\n" <<
      // "shapecode_6_g=1\n" <<
      // "shapecode_6_b=1\n" <<
      // "shapecode_6_a=1\n" <<
      // "shapecode_6_r2=1\n" <<
      // "shapecode_6_g2=1\n" <<
      // "shapecode_6_b2=1\n" <<
      // "shapecode_6_a2=1\n" <<
      // "shapecode_6_border_r=0\n" <<
      // "shapecode_6_border_g=0\n" <<
      // "shapecode_6_border_b=0\n" <<
      // "shapecode_6_border_a=0\n" <<
      // "shape_6_per_frame1=x = x + q1;\n" <<
      // "shape_6_per_frame2=y = y + q2;\n" <<
      // "shape_6_per_frame3=a = q3;\n" <<
      // "shape_6_per_frame4=a2 = q3;\n" <<
      "per_frame_1=ob_r = 0.5 + 0.4*sin(time*1.324);\n" <<
      "per_frame_2=ob_g = 0.5 + 0.4*cos(time*1.371);\n" <<
      "per_frame_3=ob_b = 0.5+0.4*sin(2.332*time);\n" <<
      "per_frame_4=ib_r = 0.5 + 0.25*sin(time*1.424);\n" <<
      "per_frame_5=ib_g = 0.25 + 0.25*cos(time*1.871);\n" <<
      "per_frame_6=ib_b = 1-ob_b;\n" <<
      "per_frame_7=volume = 0.15*(bass+bass_att+treb+treb_att+mid+mid_att);\n" <<
      "per_frame_8=xamptarg = if(equal(frame%15,0),min(0.5*volume*bass_att,0.5),xamptarg);\n" <<
      "per_frame_9=xamp = xamp + 0.5*(xamptarg-xamp);\n" <<
      "per_frame_10=xdir = if(above(abs(xpos),xamp),-sign(xpos),if(below(abs(xspeed),0.1),2*above(xpos,0)-1,xdir));\n" <<
      "per_frame_11=xaccel = xdir*xamp - xpos - xspeed*0.055*below(abs(xpos),xamp);\n" <<
      "per_frame_12=xspeed = xspeed + xdir*xamp - xpos - xspeed*0.055*below(abs(xpos),xamp);\n" <<
      "per_frame_13=xpos = xpos + 0.001*xspeed;\n" <<
      "per_frame_14=dx = xpos*0.05;\n" <<
      "per_frame_15=yamptarg = if(equal(frame%15,0),min(0.3*volume*treb_att,0.5),yamptarg);\n" <<
      "per_frame_16=yamp = yamp + 0.5*(yamptarg-yamp);\n" <<
      "per_frame_17=ydir = if(above(abs(ypos),yamp),-sign(ypos),if(below(abs(yspeed),0.1),2*above(ypos,0)-1,ydir));\n" <<
      "per_frame_18=yaccel = ydir*yamp - ypos - yspeed*0.055*below(abs(ypos),yamp);\n" <<
      "per_frame_19=yspeed = yspeed + ydir*yamp - ypos - yspeed*0.055*below(abs(ypos),yamp);\n" <<
      "per_frame_20=ypos = ypos + 0.001*yspeed;\n" <<
      "per_frame_21=dy = ypos*0.05;\n" <<
      "per_frame_22=wave_a = 0;\n" <<
      "per_frame_23=q8 = oldq8 + 0.0003*(pow(1+1.2*bass+0.4*bass_att+0.1*treb+0.1*treb_att+0.1*mid+0.1*mid_att,6)/fps);\n" <<
      "per_frame_24=oldq8 = q8;\n" <<
      "per_frame_25=q7 = 0.003*(pow(1+1.2*bass+0.4*bass_att+0.1*treb+0.1*treb_att+0.1*mid+0.1*mid_att,6)/fps);\n" <<
      "per_frame_26=rot = 0.4 + 1.5*sin(time*0.273) + 0.4*sin(time*0.379+3);\n" <<
      "per_frame_27=q1 = 0.05*sin(time*1.14);\n" <<
      "per_frame_28=q2 = 0.03*sin(time*0.93+2);\n" <<
      "per_frame_29=q3 = if(above(frame,60),1, frame/60.0);\n" <<
      "per_frame_30=oldq8 = if(above(oldq8,1000),0,oldq8);\n" <<
      "per_pixel_1=zoom =( log(sqrt(2)-rad) -0.24)*1;\n";

    return out.str();
  }
};
const std::string IdlePresets::IDLE_PRESET_NAME("Geiss & Sperl - Feedback (projectM idle HDR mix).milk");

struct Expr;
struct PerPixelEqn;

struct MilkdropPresetFactory : public PresetFactory
{
  int gx;
  int gy;
  std::map<int, PerPixelEqn*>  per_pixel_eqn_tree; /* per pixel equation tree */
  PresetOutputs* _presetOutputsCache;

  MilkdropPresetFactory(int gx_, int gy_) : gx(gx_), gy(gy_), _presetOutputsCache(nullptr)
  {
    /* Initializes the builtin function database */
    BuiltinFuncs::init_builtin_func_db();

    /* Initializes all infix operators */
    Eval::init_infix_ops();
  }

  PresetOutputs* createPresetOutputs(int gx, int gy)
  {

    PresetOutputs* presetOutputs = new PresetOutputs();

    presetOutputs->Initialize(gx, gy);

    /* PER FRAME CONSTANTS BEGIN */
    presetOutputs->zoom = 1.0;
    presetOutputs->zoomexp = 1.0;
    presetOutputs->rot = 0.0;
    presetOutputs->warp = 0.0;

    presetOutputs->sx = 1.0;
    presetOutputs->sy = 1.0;
    presetOutputs->dx = 0.0;
    presetOutputs->dy = 0.0;
    presetOutputs->cx = 0.5;
    presetOutputs->cy = 0.5;

    presetOutputs->screenDecay = .98;


    //_presetInputs.meshx = 0;
    //_presetInputs.meshy = 0;


      /* PER_FRAME CONSTANTS END */
    presetOutputs->fRating = 0;
    presetOutputs->fGammaAdj = 1.0;
    presetOutputs->videoEcho.zoom = 1.0;
    presetOutputs->videoEcho.a = 0;
    presetOutputs->videoEcho.orientation = Normal;

    presetOutputs->textureWrap = 0;
    presetOutputs->bDarkenCenter = 0;
    presetOutputs->bRedBlueStereo = 0;
    presetOutputs->bBrighten = 0;
    presetOutputs->bDarken = 0;
    presetOutputs->bSolarize = 0;
    presetOutputs->bInvert = 0;
    presetOutputs->bMotionVectorsOn = 1;
    presetOutputs->fWarpAnimSpeed = 0;
    presetOutputs->fWarpScale = 0;
    presetOutputs->fShader = 0;

    /* PER_PIXEL CONSTANTS BEGIN */

    /* PER_PIXEL CONSTANT END */

    /* Q AND T VARIABLES START */

    for (unsigned int i = 0; i < NUM_Q_VARIABLES; i++)
      presetOutputs->q[i] = 0;

    /* Q AND T VARIABLES END */
    return presetOutputs;
  }

  std::unique_ptr<Preset> allocate(const std::string& url, const std::string& name, const std::string& author) override;

  std::string supportedExtensions() const override
  {
    return ".milk .prjm";
  }
};

struct PresetInputs : public PipelineContext
{
  float x_per_pixel;
  float y_per_pixel;
  float rad_per_pixel;
  float ang_per_pixel;

  float bass;
  float mid;
  float treb;
  float bass_att;
  float mid_att;
  float treb_att;

  int gx;
  int gy;

  float** x_mesh;
  float** y_mesh;
  float** rad_mesh;
  float** theta_mesh;

  float** origtheta;  //grid containing interpolated mesh reference values
  float** origrad;
  float** origx;  //original mesh
  float** origy;

  void update(const BeatDetect& music, const PipelineContext& context)
  {
    // Reflect new values form the beat detection unit
    this->bass = music.bass;
    this->mid = music.mid;
    this->treb = music.treb;
    this->bass_att = music.bass_att;
    this->mid_att = music.mid_att;
    this->treb_att = music.treb_att;

    // Reflect new values from the pipeline context
    this->fps = context.fps;
    this->time = context.time;

    this->frame = context.frame;
    this->progress = context.progress;
  }

  void Initialize(int _gx, int _gy)
  {
    int x, y;

    this->gx = _gx;
    this->gy = _gy;


    /// @bug no clue if this block belongs here
    // ***
    progress = 0;
    frame = 1;

    x_per_pixel = 0;
    y_per_pixel = 0;
    rad_per_pixel = 0;
    ang_per_pixel = 0;
    // ***

    this->x_mesh = alloc_mesh(gx, gy);
    this->y_mesh = alloc_mesh(gx, gy);
    this->rad_mesh = alloc_mesh(gx, gy);
    this->theta_mesh = alloc_mesh(gx, gy);
    this->origtheta = alloc_mesh(gx, gy);
    this->origrad = alloc_mesh(gx, gy);
    this->origx = alloc_mesh(gx, gy);
    this->origy = alloc_mesh(gx, gy);

    for (x = 0; x < gx; x++)
    {
      for (y = 0; y < gy; y++)
      {
        this->origx[x][y] = x / (float)(gx - 1);
        this->origy[x][y] = -((y / (float)(gy - 1)) - 1);
        this->origrad[x][y] = hypot((this->origx[x][y] - .5) * 2, (this->origy[x][y] - .5) * 2) * .7071067;
        this->origtheta[x][y] = atan2(((this->origy[x][y] - .5) * 2), ((this->origx[x][y] - .5) * 2));
      }
    }
  }
};

struct ConstantExpr : Expr
{
  float constant;
  explicit ConstantExpr(float value) : Expr(CONSTANT), constant(value)
  {
  }

  bool isConstant() override
  {
    return true;
  }

  float eval(int mesh_i, int mesh_j) override
  {
    return constant;
  }
};

struct MultAndAddExpr : public Expr
{
  Expr* a, * b, * c;

  MultAndAddExpr(Expr* _a, Expr* _b, Expr* _c) : Expr(OTHER),
    a(_a), b(_b), c(_c)
  {
  }

  ~MultAndAddExpr() override {
    Expr::delete_expr(a);
    Expr::delete_expr(b);
    Expr::delete_expr(c);
  }

  float eval(int mesh_i, int mesh_j) override
  {
    float a_value = a->eval(mesh_i, mesh_j);
    float b_value = b->eval(mesh_i, mesh_j);
    float c_value = c->eval(mesh_i, mesh_j);
    return a_value * b_value + c_value;
  }
};

struct MultConstExpr : public Expr
{
  Expr* expr;
  float c;

  MultConstExpr(Expr* _expr, float _c) : Expr(OTHER),
    expr(_expr), c(_c)
  {
  }

  ~MultConstExpr() override
  {
    Expr::delete_expr(expr);
  }

  float eval(int mesh_i, int mesh_j) override
  {
    float value = expr->eval(mesh_i, mesh_j);
    return value * c;
  }
};

static bool isConstantFn(float (*fn)(float*))
{
  return fn != FuncWrappers::print_wrapper && fn != FuncWrappers::rand_wrapper;
}

struct PrefunExpr : public Expr
{
  Func* function;
  float (*func_ptr)(float*);
  int num_args;
  Expr** expr_list;

  PrefunExpr(Func* func, Expr** expr_list_) : Expr(FUNCTION),
    function(func), func_ptr(func->func_ptr), num_args(func->getNumArgs()), expr_list(expr_list_)
  {
    assert(func_ptr);
  }

  PrefunExpr() : Expr(FUNCTION)
  {
  }

  ~PrefunExpr() override
  {
    int i;

    /* Free every element in expression list */
    for (i = 0; i < num_args; i++)
    {
      Expr::delete_expr(expr_list[i]);
    }
    free(expr_list);
  }

  float eval(int mesh_i, int mesh_j) override
  {
    assert(func_ptr);
#ifdef WIN32
    float arg_list[3];	// variable length array not supported by MSVC
#else
    float arg_list[num_args];	// variable length array supported by GCC
#endif /** WIN32 */

    //printf("numargs %d", num_args);

  /* Evaluate each argument before calling the function itself */
    for (int i = 0; i < num_args; i++)
    {
      arg_list[i] = expr_list[i]->eval(mesh_i, mesh_j);
      //printf("numargs %x", arg_list[i]);
    }
    /* Now we call the function, passing a list of
       floats as its argument */

    const float value = (func_ptr)(arg_list);
    return value;
  }

  Expr* _optimize() override
  {
    bool constant_args = true;
    for (int i = 0; i < num_args; i++)
    {
      Expr* orig = expr_list[i];
      expr_list[i] = orig->_optimize();
      if (orig != expr_list[i])
        Expr::delete_expr(orig);
      constant_args = constant_args && expr_list[i]->isConstant();
    }
    if (constant_args && isConstantFn(func_ptr))
    {
      return Expr::const_to_expr(eval(-1, -1));
    }
    return this;
  }

};

struct ProgramExpr : public Expr
{
  std::vector<Expr*> steps;
  bool own;

  ProgramExpr(std::vector<Expr*>& steps_, bool ownSteps) : Expr(PROGRAM), steps(steps_), own(ownSteps)
  {
  }

  ~ProgramExpr() override
  {
    if (!own)
      return;
    for (auto it = steps.begin(); it < steps.end(); it++)
      Expr::delete_expr(*it);
  }

  float eval(int mesh_i, int mesh_j) override
  {
    float f = 0.0f;
    for (auto it = steps.begin(); it < steps.end(); it++)
      f = (*it)->eval(mesh_i, mesh_j);
    return f;
  }
};

struct IfAboveExpr : public PrefunExpr
{
  IfAboveExpr(Expr* a, Expr* b, Expr* t, Expr* e) : PrefunExpr()
  {
    num_args = 4;
    expr_list = (Expr**)malloc(num_args * sizeof(Expr*));
    expr_list[0] = a;
    expr_list[1] = b;
    expr_list[2] = t;
    expr_list[3] = e;
  }

  float eval(int mesh_i, int mesh_j) override
  {
    // see above_wrapper(), below_wrapper()
    float aval = expr_list[0]->eval(mesh_i, mesh_j);
    float bval = expr_list[1]->eval(mesh_i, mesh_j);
    if (aval > bval)
      return expr_list[2]->eval(mesh_i, mesh_j);
    else
      return expr_list[3]->eval(mesh_i, mesh_j);
  }
};

struct IfEqualExpr : public PrefunExpr
{
  IfEqualExpr(Expr* a, Expr* b, Expr* t, Expr* e) : PrefunExpr()
  {
    num_args = 4;
    expr_list = (Expr**)malloc(num_args * sizeof(Expr*));
    expr_list[0] = a;
    expr_list[1] = b;
    expr_list[2] = t;
    expr_list[3] = e;
  }

  float eval(int mesh_i, int mesh_j) override
  {
    // see above_wrapper(), below_wrapper()
    float aval = expr_list[0]->eval(mesh_i, mesh_j);
    float bval = expr_list[1]->eval(mesh_i, mesh_j);
    if (aval == bval)
      return expr_list[2]->eval(mesh_i, mesh_j);
    else
      return expr_list[3]->eval(mesh_i, mesh_j);
  }
};

struct IfExpr : public PrefunExpr
{
  IfExpr(Func* fun, Expr** expr_list_) : PrefunExpr(fun, expr_list_) {}

  float eval(int mesh_i, int mesh_j) override
  {
    // see if_wrapper()
    float val = expr_list[0]->eval(mesh_i, mesh_j);
    if (val == 0)
      return expr_list[2]->eval(mesh_i, mesh_j);
    return expr_list[1]->eval(mesh_i, mesh_j);
  }

  Expr* _optimize() override
  {
    Expr* opt = PrefunExpr::_optimize();
    if (opt != this)
      return opt;
    if (expr_list[0]->clazz != FUNCTION)
      return this;
    auto* compExpr = (PrefunExpr*)expr_list[0];
    if (compExpr->func_ptr == FuncWrappers::above_wrapper || compExpr->func_ptr == FuncWrappers::below_wrapper ||
      compExpr->func_ptr == FuncWrappers::equal_wrapper)
    {
      Expr* ret;
      if (compExpr->func_ptr == FuncWrappers::above_wrapper)
        ret = new IfAboveExpr(compExpr->expr_list[0], compExpr->expr_list[1], expr_list[1], expr_list[2]);
      else if (compExpr->func_ptr == FuncWrappers::below_wrapper)
        ret = new IfAboveExpr(compExpr->expr_list[1], compExpr->expr_list[0], expr_list[1], expr_list[2]);
      else
        ret = new IfEqualExpr(compExpr->expr_list[0], compExpr->expr_list[1], expr_list[1], expr_list[2]);
      compExpr->expr_list[0] = nullptr;
      compExpr->expr_list[1] = nullptr;
      expr_list[1] = nullptr;
      expr_list[2] = nullptr;
      return ret;
    }
    return this;
  }
};

struct PrefunExprOne : public PrefunExpr
{
  PrefunExprOne(Func* fun, Expr** expr_list_) : PrefunExpr(fun, expr_list_) {}

  float eval(int mesh_i, int mesh_j) override
  {
    float val = expr_list[0]->eval(mesh_i, mesh_j);
    return (func_ptr)(&val);
  }
};

struct SinExpr : public PrefunExpr
{
  SinExpr(Func* fun, Expr** expr_list_) : PrefunExpr(fun, expr_list_) {}

  float eval(int mesh_i, int mesh_j) override
  {
    float val = expr_list[0]->eval(mesh_i, mesh_j);
    return sinf(val);
  }
};

struct CosExpr : public PrefunExpr
{
  CosExpr(Func* fun, Expr** expr_list_) : PrefunExpr(fun, expr_list_) {}

  float eval(int mesh_i, int mesh_j) override
  {
    float val = expr_list[0]->eval(mesh_i, mesh_j);
    return cosf(val);
  }
};

struct LogExpr : public PrefunExpr
{
  LogExpr(Func* fun, Expr** expr_list_) : PrefunExpr(fun, expr_list_) {}

  float eval(int mesh_i, int mesh_j) override
  {
    float val = expr_list[0]->eval(mesh_i, mesh_j);
    return logf(val);
  }
};

Expr* Expr::const_to_expr(float val)
{
  return new ConstantExpr(val);
}

Expr* Expr::create_program_expr(std::vector<Expr*>& steps_, bool ownSteps)
{
  return new ProgramExpr(steps_, ownSteps);
}

Expr* Expr::prefun_to_expr(Func* function, Expr** expr_list)
{
  float (*func_ptr)(float*) = function->func_ptr;
  int num_args = function->getNumArgs();
  PrefunExpr* prefun_expr;
  // NOTE: all these PrefunExpr subclasses are a only helpful if we don't JIT
  if (num_args == 1)
  {
    if (func_ptr == FuncWrappers::sin_wrapper)
      prefun_expr = new SinExpr(function, expr_list);
    else if (func_ptr == (float (*)(float*)) FuncWrappers::cos_wrapper)
      prefun_expr = new CosExpr(function, expr_list);
    else if (func_ptr == (float (*)(float*)) FuncWrappers::log_wrapper)
      prefun_expr = new LogExpr(function, expr_list);
    else
      prefun_expr = new PrefunExprOne(function, expr_list);
  }
  else if (num_args == 3)
  {
    if (func_ptr == FuncWrappers::if_wrapper)
      prefun_expr = new IfExpr(function, expr_list);
    else
      prefun_expr = new PrefunExpr(function, expr_list);
  }
  else
  {
    prefun_expr = new PrefunExpr(function, expr_list);
  }
  return prefun_expr;
}

struct AssignExpr : public Expr
{
  LValue* lhs;
  Expr* rhs;

  AssignExpr(LValue* lhs_, Expr* rhs_) : Expr(ASSIGN), lhs(lhs_), rhs(rhs_)
  {
  }

  ~AssignExpr() override
  {
    Expr::delete_expr(lhs);
    Expr::delete_expr(rhs);
  }

  Expr* _optimize() override
  {
    Expr* t = rhs->_optimize();
    if (t != rhs)
      Expr::delete_expr(rhs);
    rhs = t;
    return this;
  }

  float eval(int mesh_i, int mesh_j) override
  {
    float v = rhs->eval(mesh_i, mesh_j);
    lhs->set(v);
    return v;
  }
};

struct AssignMatrixExpr : public AssignExpr
{
  AssignMatrixExpr(LValue* lhs_, Expr* rhs_) : AssignExpr(lhs_, rhs_) {}

  float eval(int mesh_i, int mesh_j) override
  {
    float v = rhs->eval(mesh_i, mesh_j);
    lhs->set_matrix(mesh_i, mesh_j, v);
    return v;
  }
};

Expr* Expr::create_matrix_assignment(LValue* lhs, Expr* rhs)
{
  return new AssignMatrixExpr(lhs, rhs);
}

Expr* Expr::param_to_expr(Param* param)
{
  if (param == nullptr)
    return nullptr;
  return (LValue*)param;
}

struct PerPixelEqn
{
  int index; /* used for splay tree ordering. */
  Expr* assign_expr;


  PerPixelEqn(unsigned long _index, Param* param, Expr* gen_expr) :index(_index)
  {
    assert(index >= 0);
    assert(param != 0);
    assert(gen_expr != 0);
    assign_expr = Expr::create_matrix_assignment(param, gen_expr);
  }
};


void InitCond::evaluate(bool evalUser)
{
  assert(this);
  assert(param);

  if (param->flags & P_FLAG_USERDEF && !evalUser)
    return;

  /* Has side-effect of also setting matrix flag to zero. This ensures
     its constant value will be used rather than a matrix value
  */
  param->set_param(init_val);
}

struct _Param : public Param
{
  _Param(const std::string& name_, short int type_, short int flags_,
    void* eqn_val_, void* matrix_,
    CValue default_init_val_, CValue upper_bound_,
    CValue lower_bound_) :
    Param(name_, type_, flags_, eqn_val_, matrix_, default_init_val_, upper_bound_, lower_bound_)
  {
    if (flags & P_FLAG_ALWAYS_MATRIX)
      matrix_flag = true;
  }
  explicit _Param(const std::string& name_) : Param(name_)
  {
  }

  void _delete_from_tree() override
  {
    /* do nothing, as the param isn't owned by the expresion tree */
  }

  void set(float value) override
  {
    set_param(value);
  }

  void set_matrix(int mesh_i, int mesh_j, float value) override
  {
    set_param(value);
  }
};

struct _BoolParam : public _Param
{
  _BoolParam(const std::string& name_, short int type_, short int flags_,
    void* eqn_val_, void* matrix_,
    CValue default_init_val_, CValue upper_bound_,
    CValue lower_bound_) :
    _Param(name_, type_, flags_, eqn_val_, matrix_, default_init_val_, upper_bound_, lower_bound_) {}

  float eval(int mesh_i, int mesh_j) override
  {
    return *(bool*)engine_val ? 1 : 0;
  }
};

struct _IntParam : public _Param
{
  _IntParam(const std::string& name_, short int type_, short int flags_,
    void* eqn_val_, void* matrix_,
    CValue default_init_val_, CValue upper_bound_,
    CValue lower_bound_) :
    _Param(name_, type_, flags_, eqn_val_, matrix_, default_init_val_, upper_bound_, lower_bound_) {}

  float eval(int mesh_i, int mesh_j) override
  {
    return *(int*)engine_val;
  }
};

struct _StringParam : public _Param
{
  _StringParam(const std::string& name_, short int type_, short int flags_,
    void* eqn_val_, void* matrix_,
    CValue default_init_val_, CValue upper_bound_,
    CValue lower_bound_) :
    _Param(name_, type_, flags_, eqn_val_, matrix_, default_init_val_, upper_bound_, lower_bound_) {}

  float eval(int mesh_i, int mesh_j) override
  {
    return 0;
  }
};

struct _FloatParam : public _Param
{
public:
  _FloatParam(const std::string& name_, short int type_, short int flags_,
    void* eqn_val_, void* matrix_,
    CValue default_init_val_, CValue upper_bound_,
    CValue lower_bound_) :
    _Param(name_, type_, flags_, eqn_val_, matrix_, default_init_val_, upper_bound_, lower_bound_)
  {
  }
  explicit _FloatParam(const std::string& name_) : _Param(name_)
  {
  }

  float eval(int mesh_i, int mesh_j) override
  {
    return *(float*)engine_val;
  }
#if HAVE_LLVM
  llvm::Value* _llvm(JitContext& jitx) override
  {
    llvm::Constant* ptr = jitx.CreateFloatPtr((float*)engine_val);
    return jitx.builder.CreateLoad(ptr, name);
  }
  virtual llvm::Value* _llvm_set_matrix(JitContext& jitx, llvm::Value* rhs)
  {
    llvm::Constant* ptr = jitx.CreateFloatPtr((float*)engine_val);
    jitx.builder.CreateStore(rhs, ptr, false);
    return rhs;
  }
#endif
};

Param* Param::createUser(const std::string& name)
{
  return new _FloatParam(name);
}

struct _MeshParam : public _Param
{
  _MeshParam(const std::string& name_, short int type_, short int flags_,
    void* eqn_val_, void* matrix_,
    CValue default_init_val_, CValue upper_bound_,
    CValue lower_bound_) :
    _Param(name_, type_, flags_, eqn_val_, matrix_, default_init_val_, upper_bound_, lower_bound_) {}

  float eval(int mesh_i, int mesh_j) override
  {
    assert(type == P_TYPE_DOUBLE);
    // see issue 64: There are presets with per_point equations that read from pre_frame/per_pixel parameters,
    //    e.g. per_point1=dx=dx*1.01
    // any this means that we get called with (i>=0,j==-1)
    if (matrix_flag && mesh_i >= 0 && mesh_j >= 0)
      return (((float**)matrix)[mesh_i][mesh_j]);
    return *((float*)(engine_val));
  }

  void set_matrix(int mesh_i, int mesh_j, float value) override
  {
    if (nullptr == matrix)
    {
      *(float*)engine_val = value;
    }
    else
    {
      ((float**)matrix)[mesh_i][mesh_j] = value;
      matrix_flag = true;
    }
  }
};

// TODO merge PointsParam/MeshParam
struct _PointsParam : public _Param
{
  _PointsParam(const std::string& name_, short int type_, short int flags_,
    void* eqn_val_, void* matrix_,
    CValue default_init_val_, CValue upper_bound_,
    CValue lower_bound_) :
    _Param(name_, type_, flags_, eqn_val_, matrix_, default_init_val_, upper_bound_, lower_bound_) {}

  float eval(int mesh_i, int mesh_j) override
  {
    assert(mesh_j == -1);
    assert(type == P_TYPE_DOUBLE);
    if (matrix_flag && mesh_i >= 0)
      return (((float*)matrix)[mesh_i]);
    return *((float*)(engine_val));
  }

  void set_matrix(int mesh_i, int mesh_j, float value) override
  {
    if (nullptr == matrix)
    {
      *(float*)engine_val = value;
    }
    else
    {
      ((float*)matrix)[mesh_i] = value;
      matrix_flag = true;
    }
  }
};

Param* Param::create(const std::string& name, short int type, short int flags, void* eqn_val, void* matrix, CValue default_init_val, CValue upper_bound, CValue lower_bound)
{
  if (type == P_TYPE_BOOL)
  {
    assert(nullptr == matrix);
    assert(0 == (flags & (P_FLAG_PER_PIXEL | P_FLAG_PER_POINT)));
    return new _BoolParam(name, type, flags, eqn_val, matrix, default_init_val, upper_bound, lower_bound);
  }
  if (type == P_TYPE_INT)
  {
    assert(nullptr == matrix);
    assert(0 == (flags & (P_FLAG_PER_PIXEL | P_FLAG_PER_POINT)));
    return new _IntParam(name, type, flags, eqn_val, matrix, default_init_val, upper_bound, lower_bound);
  }
  if (type == P_TYPE_STRING)
  {
    assert(0 == (flags & (P_FLAG_PER_PIXEL | P_FLAG_PER_POINT)));
    return new _StringParam(name, type, flags, eqn_val, matrix, default_init_val, upper_bound, lower_bound);
  }
  assert(type == P_TYPE_DOUBLE);
  if (matrix == nullptr)
  {
    assert(0 == (flags & (P_FLAG_PER_PIXEL | P_FLAG_PER_POINT)));
    return new _FloatParam(name, type, flags, eqn_val, matrix, default_init_val, upper_bound, lower_bound);
  }
  assert(flags & (P_FLAG_PER_PIXEL | P_FLAG_PER_POINT));
  if (flags & P_FLAG_PER_PIXEL)
  {
    return new _MeshParam(name, type, flags, eqn_val, matrix, default_init_val, upper_bound, lower_bound);
  }
  else
  {
    return new _PointsParam(name, type, flags, eqn_val, matrix, default_init_val, upper_bound, lower_bound);
  }
}

struct BuiltinParams
{
  typedef std::map<std::string, std::string> AliasMap;

  AliasMap aliasMap;
  std::map<std::string, Param*> builtin_param_tree;

  BuiltinParams(PresetInputs& presetInputs, PresetOutputs& presetOutputs)
  {
    presetInputs.Initialize(presetOutputs.gx, presetOutputs.gy);

    int ret;
    if ((ret = init_builtin_param_db(presetInputs, presetOutputs)) != PROJECTM_SUCCESS)
    {
      std::cout << "failed to allocate builtin parameter database with error " << ret << std::endl;
      throw ret;
    }
  }

  int init_builtin_param_db(const PresetInputs& presetInputs, PresetOutputs& presetOutputs)
  {

    if (BUILTIN_PARAMS_DEBUG)
    {
      printf("init_builtin_param: loading database...");
      fflush(stdout);
    }

    /* Loads all builtin parameters into the database */
    if (load_all_builtin_param(presetInputs, presetOutputs) < 0)
    {
      if (BUILTIN_PARAMS_DEBUG) printf("failed loading builtin parameters (FATAL)\n");
      return PROJECTM_ERROR;
    }

    if (BUILTIN_PARAMS_DEBUG) printf("success!\n");

    /* Finished, no errors */
    return PROJECTM_SUCCESS;
  }

  /* Inserts a parameter into the builtin database */
  int insert_builtin_param(Param* param)
  {
    std::pair<std::map<std::string, Param*>::iterator, bool> inserteePos = builtin_param_tree.insert(std::make_pair(param->name, param));

    return inserteePos.second;
  }

  int load_builtin_param_float(const std::string& name, void* engine_val, void* matrix, short int flags,
    float init_val, float upper_bound, float lower_bound, const std::string& alt_name)
  {

    Param* param = nullptr;
    CValue iv, ub, lb;

    iv.float_val = init_val;
    ub.float_val = upper_bound;
    lb.float_val = lower_bound;

    /* Create new parameter of type float */
    if (BUILTIN_PARAMS_DEBUG == 2)
    {
      printf("load_builtin_param_float: (name \"%s\") (alt_name = \"%s\") ", name.c_str(), alt_name.c_str());
      fflush(stdout);
    }

    std::string lowerName(name);
    std::transform(lowerName.begin(), lowerName.end(), lowerName.begin(), tolower);

    if ((param = Param::create(lowerName, P_TYPE_DOUBLE, flags, engine_val, matrix, iv, ub, lb)) == nullptr)
    {
      return PROJECTM_OUTOFMEM_ERROR;
    }

    if (BUILTIN_PARAMS_DEBUG == 2)
    {
      printf("created...");
      fflush(stdout);
    }

    /* Insert the paremeter into the database */

    if (insert_builtin_param(param) < 0)
    {
      delete param;
      return PROJECTM_ERROR;
    }

    if (BUILTIN_PARAMS_DEBUG == 2)
    {
      printf("inserted...");
      fflush(stdout);
    }

    /* If this parameter has an alternate name, insert it into the database as link */

    if (alt_name != "")
    {
      std::string alt_lower_name(alt_name);
      std::transform(alt_lower_name.begin(), alt_lower_name.end(), alt_lower_name.begin(), tolower);
      insert_param_alt_name(param, alt_lower_name);

      if (BUILTIN_PARAMS_DEBUG == 2)
      {
        printf("alt_name inserted...");
        fflush(stdout);
      }


    }

    if (BUILTIN_PARAMS_DEBUG == 2) printf("finished\n");

    /* Finished, return success */
    return PROJECTM_SUCCESS;
  }

  /* Insert a parameter into the database with an alternate name */
  int insert_param_alt_name(Param* param, const std::string& alt_name)
  {

    assert(param);

    aliasMap.insert(std::make_pair(alt_name, param->name));

    return PROJECTM_SUCCESS;
  }

  /* Loads a integer parameter into the builtin database */
  int load_builtin_param_int(const std::string& name, void* engine_val, short int flags,
    int init_val, int upper_bound, int lower_bound, const std::string& alt_name)
  {

    Param* param;
    CValue iv, ub, lb;

    iv.int_val = init_val;
    ub.int_val = upper_bound;
    lb.int_val = lower_bound;

    // normalize to lower case as milkdrop scripts depend on this
    std::string lowerName(name);
    std::transform(lowerName.begin(), lowerName.end(), lowerName.begin(), tolower);

    param = Param::create(lowerName, P_TYPE_INT, flags, engine_val, nullptr, iv, ub, lb);

    if (param == nullptr)
    {
      return PROJECTM_OUTOFMEM_ERROR;
    }

    if (insert_builtin_param(param) < 0)
    {
      delete param;
      return PROJECTM_ERROR;
    }

    if (alt_name != "")
    {
      std::string alt_lower_name(alt_name);
      std::transform(alt_lower_name.begin(), alt_lower_name.end(), alt_lower_name.begin(), tolower);
      insert_param_alt_name(param, alt_lower_name);

    }

    return PROJECTM_SUCCESS;

  }

  /* Loads a boolean parameter */
  int load_builtin_param_bool(const std::string& name, void* engine_val, short int flags,
    int init_val, const std::string& alt_name)
  {

    Param* param;
    CValue iv, ub, lb;

    iv.int_val = init_val;
    ub.int_val = true;
    lb.int_val = false;

    std::string lowerName(name);
    std::transform(lowerName.begin(), lowerName.end(), lowerName.begin(), tolower);

    param = Param::create(lowerName, P_TYPE_BOOL, flags, engine_val, nullptr, iv, ub, lb);

    if (param == nullptr)
    {
      return PROJECTM_OUTOFMEM_ERROR;
    }

    if (insert_builtin_param(param) < 0)
    {
      delete param;
      return PROJECTM_ERROR;
    }

    if (alt_name != "")
    {
      std::string alt_lower_name(alt_name);
      std::transform(alt_lower_name.begin(), alt_lower_name.end(), alt_lower_name.begin(), tolower);
      insert_param_alt_name(param, alt_lower_name);
    }

    return PROJECTM_SUCCESS;

  }

  int load_all_builtin_param(const PresetInputs& presetInputs, PresetOutputs& presetOutputs)
  {
    load_builtin_param_float("frating", (void*)&presetOutputs.fRating, nullptr, P_FLAG_NONE, 0.0, 5.0, 0.0, "");
    // 0 will turn off all waviness in our waves... 1 seems better
    load_builtin_param_float("fwavescale", (void*)&presetOutputs.wave.scale, nullptr, P_FLAG_NONE, 1.0, MAX_DOUBLE_SIZE, -MAX_DOUBLE_SIZE, "");
    load_builtin_param_float("gamma", (void*)&presetOutputs.fGammaAdj, nullptr, P_FLAG_NONE, 0.0, MAX_DOUBLE_SIZE, 0, "fGammaAdj");
    load_builtin_param_float("echo_zoom", (void*)&presetOutputs.videoEcho.zoom, nullptr, P_FLAG_NONE, 0.0, MAX_DOUBLE_SIZE, 0, "fVideoEchoZoom");
    load_builtin_param_float("echo_alpha", (void*)&presetOutputs.videoEcho.a, nullptr, P_FLAG_NONE, 0.0, MAX_DOUBLE_SIZE, 0, "fvideoechoalpha");
    load_builtin_param_float("wave_a", (void*)&presetOutputs.wave.a, nullptr, P_FLAG_NONE, 1.0, 1.0, 0, "fwavealpha");
    load_builtin_param_float("fwavesmoothing", (void*)&presetOutputs.wave.smoothing, nullptr, P_FLAG_NONE, 0.0, 1.0, -1.0, "");
    load_builtin_param_float("fmodwavealphastart", (void*)&presetOutputs.wave.modOpacityStart, nullptr, P_FLAG_NONE, 0.0, 1.0, -1.0, "");
    load_builtin_param_float("fmodwavealphaend", (void*)&presetOutputs.wave.modOpacityEnd, nullptr, P_FLAG_NONE, 0.0, 1.0, -1.0, "");
    load_builtin_param_float("fWarpAnimSpeed", (void*)&presetOutputs.fWarpAnimSpeed, nullptr, P_FLAG_NONE, 1.0, 1.0, -1.0, "");
    load_builtin_param_float("fWarpScale", (void*)&presetOutputs.fWarpScale, nullptr, P_FLAG_NONE, 1.0, 1.0, -1.0, "");

    load_builtin_param_float("fshader", (void*)&presetOutputs.fShader, nullptr, P_FLAG_NONE, 0.0, 1.0, -1.0, "");
    // 0.98 seems close to milkdrop2 default
    load_builtin_param_float("decay", (void*)&presetOutputs.screenDecay, nullptr, P_FLAG_NONE, 0.98, 1.0, 0, "fdecay");

    load_builtin_param_int("echo_orient", (void*)&presetOutputs.videoEcho.orientation, P_FLAG_NONE, 0, 3, 0, "nVideoEchoOrientation");
    load_builtin_param_int("wave_mode", (void*)&presetOutputs.wave.mode, P_FLAG_NONE, 0, 7, 0, "nwavemode");

    load_builtin_param_bool("wave_additive", (void*)&presetOutputs.wave.additive, P_FLAG_NONE, false, "bAdditiveWaves");
    load_builtin_param_bool("bmodwavealphabyvolume", (void*)&presetOutputs.wave.modulateAlphaByVolume, P_FLAG_NONE, false, "");
    load_builtin_param_bool("wave_brighten", (void*)&presetOutputs.wave.maximizeColors, P_FLAG_NONE, false, "bMaximizeWaveColor");
    load_builtin_param_bool("wrap", (void*)&presetOutputs.textureWrap, P_FLAG_NONE, true, "btexwrap");
    load_builtin_param_bool("darken_center", (void*)&presetOutputs.bDarkenCenter, P_FLAG_NONE, false, "bdarkencenter");
    load_builtin_param_bool("bredbluestereo", (void*)&presetOutputs.bRedBlueStereo, P_FLAG_NONE, false, "");
    load_builtin_param_bool("brighten", (void*)&presetOutputs.bBrighten, P_FLAG_NONE, false, "bbrighten");
    load_builtin_param_bool("darken", (void*)&presetOutputs.bDarken, P_FLAG_NONE, false, "bdarken");
    load_builtin_param_bool("solarize", (void*)&presetOutputs.bSolarize, P_FLAG_NONE, false, "bsolarize");
    load_builtin_param_bool("invert", (void*)&presetOutputs.bInvert, P_FLAG_NONE, false, "binvert");
    load_builtin_param_bool("bmotionvectorson", (void*)&presetOutputs.bMotionVectorsOn, P_FLAG_NONE, false, "");
    load_builtin_param_bool("wave_dots", (void*)&presetOutputs.wave.dots, P_FLAG_NONE, false, "bwavedots");
    load_builtin_param_bool("wave_thick", (void*)&presetOutputs.wave.thick, P_FLAG_NONE, false, "bwavethick");
    // warp is turned on by default in milkdrop2
    load_builtin_param_float("warp", (void*)&presetOutputs.warp, presetOutputs.warp_mesh, P_FLAG_PER_PIXEL | P_FLAG_NONE, 1.0, MAX_DOUBLE_SIZE, MIN_DOUBLE_SIZE, "");
    // zoom=1 is the 'do nothing' value, 0 causes Inf values in PresetOutputs::PerPixelMath()
    load_builtin_param_float("zoom", (void*)&presetOutputs.zoom, presetOutputs.zoom_mesh, P_FLAG_PER_PIXEL | P_FLAG_NONE, 1.0, MAX_DOUBLE_SIZE, MIN_DOUBLE_SIZE, "");
    load_builtin_param_float("rot", (void*)&presetOutputs.rot, presetOutputs.rot_mesh, P_FLAG_PER_PIXEL | P_FLAG_NONE, 0.0, MAX_DOUBLE_SIZE, MIN_DOUBLE_SIZE, "");
    // zoomexp=1 is the 'do nothing' value, 0 effectively forces zoom=1
    load_builtin_param_float("zoomexp", (void*)&presetOutputs.zoomexp, presetOutputs.zoomexp_mesh, P_FLAG_PER_PIXEL | P_FLAG_NONE, 1.0, MAX_DOUBLE_SIZE, 0, "fzoomexponent");

    load_builtin_param_float("cx", (void*)&presetOutputs.cx, presetOutputs.cx_mesh, P_FLAG_PER_PIXEL | P_FLAG_NONE, 0.0, MAX_DOUBLE_SIZE, MIN_DOUBLE_SIZE, "");
    load_builtin_param_float("cy", (void*)&presetOutputs.cy, presetOutputs.cy_mesh, P_FLAG_PER_PIXEL | P_FLAG_NONE, 0.0, MAX_DOUBLE_SIZE, MIN_DOUBLE_SIZE, "");
    load_builtin_param_float("dx", (void*)&presetOutputs.dx, presetOutputs.dx_mesh, P_FLAG_PER_PIXEL | P_FLAG_NONE, 0.0, MAX_DOUBLE_SIZE, MIN_DOUBLE_SIZE, "");
    load_builtin_param_float("dy", (void*)&presetOutputs.dy, presetOutputs.dy_mesh, P_FLAG_PER_PIXEL | P_FLAG_NONE, 0.0, MAX_DOUBLE_SIZE, MIN_DOUBLE_SIZE, "");
    // sx=1 and sy=1 are the 'do nothing' values, 0 causes Inf values in PresetOutputs::PerPixelMath()
    load_builtin_param_float("sx", (void*)&presetOutputs.sx, presetOutputs.sx_mesh, P_FLAG_PER_PIXEL | P_FLAG_NONE, 1.0, MAX_DOUBLE_SIZE, MIN_DOUBLE_SIZE, "");
    load_builtin_param_float("sy", (void*)&presetOutputs.sy, presetOutputs.sy_mesh, P_FLAG_PER_PIXEL | P_FLAG_NONE, 1.0, MAX_DOUBLE_SIZE, MIN_DOUBLE_SIZE, "");


    load_builtin_param_float("b1n", (void*)&presetOutputs.blur1n, nullptr, P_FLAG_NONE, 0.0, 1.0, 0.0, "");
    load_builtin_param_float("b2n", (void*)&presetOutputs.blur2n, nullptr, P_FLAG_NONE, 0.0, 1.0, 0.0, "");
    load_builtin_param_float("b3n", (void*)&presetOutputs.blur3n, nullptr, P_FLAG_NONE, 0.0, 1.0, 0.0, "");
    load_builtin_param_float("b1x", (void*)&presetOutputs.blur1x, nullptr, P_FLAG_NONE, 0.0, 1.0, 0.0, "");
    load_builtin_param_float("b2x", (void*)&presetOutputs.blur2x, nullptr, P_FLAG_NONE, 0.0, 1.0, 0.0, "");
    load_builtin_param_float("b3x", (void*)&presetOutputs.blur3x, nullptr, P_FLAG_NONE, 0.0, 1.0, 0.0, "");
    load_builtin_param_float("b1ed", (void*)&presetOutputs.blur1ed, nullptr, P_FLAG_NONE, 0.0, 1.0, 0.0, "");

    load_builtin_param_float("wave_r", (void*)&presetOutputs.wave.r, nullptr, P_FLAG_NONE, 0.0, 1.0, 0.0, "");
    load_builtin_param_float("wave_g", (void*)&presetOutputs.wave.g, nullptr, P_FLAG_NONE, 0.0, 1.0, 0.0, "");
    load_builtin_param_float("wave_b", (void*)&presetOutputs.wave.b, nullptr, P_FLAG_NONE, 0.0, 1.0, 0.0, "");
    load_builtin_param_float("wave_x", (void*)&presetOutputs.wave.x, nullptr, P_FLAG_NONE, 0.0, 1.0, 0.0, "");
    load_builtin_param_float("wave_y", (void*)&presetOutputs.wave.y, nullptr, P_FLAG_NONE, 0.0, 1.0, 0.0, "");
    load_builtin_param_float("wave_mystery", (void*)&presetOutputs.wave.mystery, nullptr, P_FLAG_NONE, 0.0, 1.0, -1.0, "fWaveParam");

    load_builtin_param_float("ob_size", (void*)&presetOutputs.border.outer_size, nullptr, P_FLAG_NONE, 0.0, 0.5, 0, "");
    load_builtin_param_float("ob_r", (void*)&presetOutputs.border.outer_r, nullptr, P_FLAG_NONE, 0.0, 1.0, 0.0, "");
    load_builtin_param_float("ob_g", (void*)&presetOutputs.border.outer_g, nullptr, P_FLAG_NONE, 0.0, 1.0, 0.0, "");
    load_builtin_param_float("ob_b", (void*)&presetOutputs.border.outer_b, nullptr, P_FLAG_NONE, 0.0, 1.0, 0.0, "");
    load_builtin_param_float("ob_a", (void*)&presetOutputs.border.outer_a, nullptr, P_FLAG_NONE, 0.0, 1.0, 0.0, "");

    load_builtin_param_float("ib_size", (void*)&presetOutputs.border.inner_size, nullptr, P_FLAG_NONE, 0.0, .5, 0.0, "");
    load_builtin_param_float("ib_r", (void*)&presetOutputs.border.inner_r, nullptr, P_FLAG_NONE, 0.0, 1.0, 0.0, "");
    load_builtin_param_float("ib_g", (void*)&presetOutputs.border.inner_g, nullptr, P_FLAG_NONE, 0.0, 1.0, 0.0, "");
    load_builtin_param_float("ib_b", (void*)&presetOutputs.border.inner_b, nullptr, P_FLAG_NONE, 0.0, 1.0, 0.0, "");
    load_builtin_param_float("ib_a", (void*)&presetOutputs.border.inner_a, nullptr, P_FLAG_NONE, 0.0, 1.0, 0.0, "");

    load_builtin_param_float("mv_r", (void*)&presetOutputs.mv.r, nullptr, P_FLAG_NONE, 0.0, 1.0, 0.0, "");
    load_builtin_param_float("mv_g", (void*)&presetOutputs.mv.g, nullptr, P_FLAG_NONE, 0.0, 1.0, 0.0, "");
    load_builtin_param_float("mv_b", (void*)&presetOutputs.mv.b, nullptr, P_FLAG_NONE, 0.0, 1.0, 0.0, "");
    load_builtin_param_float("mv_x", (void*)&presetOutputs.mv.x_num, nullptr, P_FLAG_NONE, 0.0, 64.0, 0.0, "nmotionvectorsx");
    load_builtin_param_float("mv_y", (void*)&presetOutputs.mv.y_num, nullptr, P_FLAG_NONE, 0.0, 48.0, 0.0, "nmotionvectorsy");
    load_builtin_param_float("mv_l", (void*)&presetOutputs.mv.length, nullptr, P_FLAG_NONE, 0.0, 5.0, 0.0, "");
    load_builtin_param_float("mv_dx", (void*)&presetOutputs.mv.x_offset, nullptr, P_FLAG_NONE, 0.0, 1.0, -1.0, "");
    load_builtin_param_float("mv_dy", (void*)&presetOutputs.mv.y_offset, nullptr, P_FLAG_NONE, 0.0, 1.0, -1.0, "");
    load_builtin_param_float("mv_a", (void*)&presetOutputs.mv.a, nullptr, P_FLAG_NONE, 0.0, 1.0, 0.0, "");

    load_builtin_param_float("time", (void*)&presetInputs.time, nullptr, P_FLAG_READONLY, 0.0, MAX_DOUBLE_SIZE, 0.0, "");
    load_builtin_param_float("bass", (void*)&presetInputs.bass, nullptr, P_FLAG_READONLY, 0.0, MAX_DOUBLE_SIZE, 0.0, "");
    load_builtin_param_float("mid", (void*)&presetInputs.mid, nullptr, P_FLAG_READONLY, 0.0, MAX_DOUBLE_SIZE, 0, "");

    load_builtin_param_float("treb", (void*)&presetInputs.treb, nullptr, P_FLAG_READONLY, 0.0, MAX_DOUBLE_SIZE, 0, "");


    load_builtin_param_float("bass_att", (void*)&presetInputs.bass_att, nullptr, P_FLAG_READONLY, 0.0, MAX_DOUBLE_SIZE, 0, "");
    load_builtin_param_float("mid_att", (void*)&presetInputs.mid_att, nullptr, P_FLAG_READONLY, 0.0, MAX_DOUBLE_SIZE, 0, "");
    load_builtin_param_float("treb_att", (void*)&presetInputs.treb_att, nullptr, P_FLAG_READONLY, 0.0, MAX_DOUBLE_SIZE, 0, "");
    load_builtin_param_int("frame", (void*)&presetInputs.frame, P_FLAG_READONLY, 0, MAX_INT_SIZE, 0, "");
    load_builtin_param_float("progress", (void*)&presetInputs.progress, nullptr, P_FLAG_READONLY, 0.0, 1, 0, "");
    load_builtin_param_int("fps", (void*)&presetInputs.fps, P_FLAG_READONLY, 15, MAX_INT_SIZE, 0, "");

    load_builtin_param_float("x", (void*)&presetInputs.x_per_pixel, presetInputs.origx, P_FLAG_PER_PIXEL | P_FLAG_ALWAYS_MATRIX | P_FLAG_READONLY | P_FLAG_NONE,
      0, MAX_DOUBLE_SIZE, -MAX_DOUBLE_SIZE, "");
    load_builtin_param_float("y", (void*)&presetInputs.y_per_pixel, presetInputs.origy, P_FLAG_PER_PIXEL | P_FLAG_ALWAYS_MATRIX | P_FLAG_READONLY | P_FLAG_NONE,
      0, MAX_DOUBLE_SIZE, -MAX_DOUBLE_SIZE, "");
    load_builtin_param_float("ang", (void*)&presetInputs.ang_per_pixel, presetInputs.origtheta, P_FLAG_PER_PIXEL | P_FLAG_ALWAYS_MATRIX | P_FLAG_READONLY | P_FLAG_NONE,
      0, MAX_DOUBLE_SIZE, -MAX_DOUBLE_SIZE, "");
    load_builtin_param_float("rad", (void*)&presetInputs.rad_per_pixel, presetInputs.origrad, P_FLAG_PER_PIXEL | P_FLAG_ALWAYS_MATRIX | P_FLAG_READONLY | P_FLAG_NONE,
      0, MAX_DOUBLE_SIZE, -MAX_DOUBLE_SIZE, "");

    for (unsigned int i = 0; i < NUM_Q_VARIABLES; i++) {
      std::ostringstream os;
      os << "q" << i + 1;
      load_builtin_param_float(os.str().c_str(), (void*)&presetOutputs.q[i], nullptr, P_FLAG_QVAR, 0, MAX_DOUBLE_SIZE, -MAX_DOUBLE_SIZE, "");

    }

    /* variables added in 1.04 */
    load_builtin_param_int("meshx", (void*)&presetInputs.gx, P_FLAG_READONLY, 32, 96, 8, "");
    load_builtin_param_int("meshy", (void*)&presetInputs.gy, P_FLAG_READONLY, 24, 72, 6, "");

    return PROJECTM_SUCCESS;
  }

  Param* find_builtin_param(const std::string& name)
  {
    AliasMap::iterator pos = aliasMap.find(name);
    Param* param = 0;
    //std::cerr << "[BuiltinParams] find_builtin_param: name is " << name << std::endl;
    if (pos == aliasMap.end())
    {
      std::map<std::string, Param*>::iterator builtinPos = builtin_param_tree.find(name);

      if (builtinPos != builtin_param_tree.end()) {
        //  std::cerr << "[BuiltinParams] find_builtin_param: found it directly." << std::endl;
        param = builtinPos->second;
      }
    }
    else
    {

      std::map<std::string, Param*>::iterator builtinPos = builtin_param_tree.find(pos->second);

      if (builtinPos != builtin_param_tree.end()) {
        //std::cerr << "[BuiltinParams] find_builtin_param: found it indirectly." << std::endl;
        param = builtinPos->second;

      }
    }
    return param;
  }

  template <class Fun>
  void apply(Fun& fun) {
    traverse(builtin_param_tree, fun);
  }
};

#define PER_FRAME_EQN_DEBUG 0
struct PerFrameEqn
{
  int index; /* a unique id for each per frame eqn (generated by order in preset files) */
  Param* param; /* parameter to be assigned a value */
  Expr* gen_expr;   /* expression that paremeter is equal to */

  PerFrameEqn(int _index, Param* _param, Expr* _gen_expr) :
    index(_index), param(_param), gen_expr(_gen_expr)
  {
    gen_expr;
  }

  void evaluate()
  {
    if (PER_FRAME_EQN_DEBUG) {
      printf("per_frame_%d=%s= ", index, param->name.c_str());
      fflush(stdout);
    }

    //*((float*)per_frame_eqn->param->engine_val) = eval(per_frame_eqn->gen_expr);
    assert(gen_expr);
    assert(param);
    float v = gen_expr->eval(-1, -1);
    param->set_param(v);

    if (PER_FRAME_EQN_DEBUG) printf(" = %.4f\n", v);
  }
};

struct MilkdropPreset;
struct TreeExpr;

namespace Parser
{
  int parse_top_comment(std::istream& fs);
  int parse_preset_name(std::istream& fs, char* name);
  int parse_line(std::istream& fs, MilkdropPreset* preset);
  token_t parseToken(std::istream& fs, char* string);
  Expr* parse_infix_op(std::istream& fs, token_t token, TreeExpr* tree_expr, MilkdropPreset* preset);
  Expr** parse_prefix_args(std::istream& fs, int num_args, MilkdropPreset* preset);
}

namespace ParamUtils
{
  bool insert(Param* param, std::map<std::string, Param*>* paramTree)
  {
    assert(param);
    assert(paramTree);

    return ((paramTree->insert(std::make_pair(param->name, param))).second);
  }

  template <int FLAGS>
  Param* find(std::string name, std::map<std::string, Param*>* paramTree)
  {
    assert(paramTree);

    Param* param;

    /* First look in the suggested database */
    std::map<std::string, Param*>::iterator pos = paramTree->find(name);

    if ((FLAGS == AUTO_CREATE) && ((pos == paramTree->end())))
    {
      /* Check if string is valid */
      if (!Param::is_valid_param_string(name.c_str()))
        return nullptr;

      /* Now, create the user defined parameter given the passed name */
      if ((param = Param::createUser(name)) == nullptr)
        return nullptr;

      /* Finally, insert the new parameter into this preset's parameter tree */
      std::pair<std::map<std::string, Param*>::iterator, bool>  insertRetPair =
        paramTree->insert(std::make_pair(param->name, param));

      assert(insertRetPair.second);

    }
    else if (pos != paramTree->end())
      param = pos->second;
    else
      param = nullptr;

    /* Return the found (or created) parameter. Note that this could be null */
    return param;
  }

  Param* find(const std::string& name, BuiltinParams* builtinParams, std::map<std::string, Param*>* insertionTree)
  {
    Param* param;

    // Check first db
    if ((param = builtinParams->find_builtin_param(name)) != 0)
      return param;

    // Check second db, create if necessary
    return find<AUTO_CREATE>(name, insertionTree);
  }
};

int CustomWave::add_per_point_eqn(char* name, Expr* gen_expr)
{
  PerPointEqn* per_point_eqn;
  int index;
  Param* param = NULL;

  /* Argument checks */
  if (gen_expr == NULL)
    return PROJECTM_FAILURE;
  if (name == NULL)
    return PROJECTM_FAILURE;

  if (CUSTOM_WAVE_DEBUG) printf("add_per_point_eqn: per pixel equation (name = \"%s\")\n", name);

  /* Search for the parameter so we know what matrix the per pixel equation is referencing */

  if ((param = ParamUtils::find<AUTO_CREATE>(name, &param_tree)) == NULL)
  {
    if (CUSTOM_WAVE_DEBUG) printf("add_per_point_eqn: failed to allocate a new parameter!\n");
    return PROJECTM_FAILURE;
  }

  /* Get largest index in the tree */
  index = per_point_eqn_tree.size();

  /* Create the per point equation given the index, parameter, and general expression */
  if ((per_point_eqn = new PerPointEqn(index, param, gen_expr)) == NULL)
    return PROJECTM_FAILURE;
  if (CUSTOM_WAVE_DEBUG)
    printf("add_per_point_eqn: created new equation (index = %d) (name = \"%s\")\n", per_point_eqn->index, param->name.c_str());

  /* Insert the per pixel equation into the preset per pixel database */

  per_point_eqn_tree.push_back(per_point_eqn);

  /* Done */
  return PROJECTM_SUCCESS;
}

struct MilkdropPreset : public Preset
{
  BuiltinParams builtinParams;

  int per_frame_eqn_count;
  int per_frame_init_eqn_count;

  PresetOutputs::cwave_container customWaves;
  PresetOutputs::cshape_container customShapes;

  std::vector<PerFrameEqn*>  per_frame_eqn_tree;   /* per frame equations */
  std::map<int, PerPixelEqn*>  per_pixel_eqn_tree; /* per pixel equation tree */
  Expr* per_pixel_program;
  std::map<std::string, InitCond*> per_frame_init_eqn_tree; /* per frame initial equations */
  std::map<std::string, InitCond*> init_cond_tree; /* initial conditions */
  std::map<std::string, Param*> user_param_tree; /* user parameter splay tree */

  std::string _filename;
  PresetInputs _presetInputs;
  std::string _absoluteFilePath;
  MilkdropPresetFactory* _factory;
  PresetOutputs& _presetOutputs;

  MilkdropPreset(MilkdropPresetFactory* factory, std::istream& in, const std::string& presetName, PresetOutputs& presetOutputs) :
    Preset(presetName),
    builtinParams(_presetInputs, presetOutputs),
    per_pixel_program(nullptr),
    _factory(factory),
    _presetOutputs(presetOutputs)
  {
    initialize(in);
  }

  MilkdropPreset(MilkdropPresetFactory* factory, const std::string& absoluteFilePath, const std::string& presetName, PresetOutputs& presetOutputs) :
    Preset(presetName),
    builtinParams(_presetInputs, presetOutputs),
    per_pixel_program(nullptr),
    _filename(parseFilename(absoluteFilePath)),
    _absoluteFilePath(absoluteFilePath),
    _factory(factory),
    _presetOutputs(presetOutputs)
  {

    initialize(absoluteFilePath);
  }

  const PresetInputs& presetInputs() const
  {
    return _presetInputs;
  }

  PresetOutputs& presetOutputs() const
  {
    return _presetOutputs;
  }

  int add_per_pixel_eqn(char* name, Expr* gen_expr)
  {
    PerPixelEqn* per_pixel_eqn = nullptr;
    Param* param = nullptr;

    assert(gen_expr);
    assert(name);

    if (PER_PIXEL_EQN_DEBUG) printf("add_per_pixel_eqn: per pixel equation (name = \"%s\")\n", name);

    /* Search for the parameter so we know what matrix the per pixel equation is referencing */

    param = ParamUtils::find(name, &this->builtinParams, &this->user_param_tree);
    if (!param)
    {
      if (PER_PIXEL_EQN_DEBUG) printf("add_per_pixel_eqn: failed to allocate a new parameter!\n");
      return PROJECTM_FAILURE;
    }

    auto index = per_pixel_eqn_tree.size();

    /* Create the per pixel equation given the index, parameter, and general expression */
    if ((per_pixel_eqn = new PerPixelEqn(index, param, gen_expr)) == nullptr)
    {
      if (PER_PIXEL_EQN_DEBUG) printf("add_per_pixel_eqn: failed to create new per pixel equation!\n");
      return PROJECTM_FAILURE;
    }

    /* Insert the per pixel equation into the preset per pixel database */
    std::pair<std::map<int, PerPixelEqn*>::iterator, bool> inserteeOption = per_pixel_eqn_tree.insert
    (std::make_pair(per_pixel_eqn->index, per_pixel_eqn));

    if (!inserteeOption.second)
    {
      printf("failed to add per pixel eqn!\n");
      delete(per_pixel_eqn);
      return PROJECTM_FAILURE;
    }

    /* Done */
    return PROJECTM_SUCCESS;
  }

  int readIn(std::istream& fs)
  {
    presetOutputs().compositeShader.programSource.clear();
    presetOutputs().warpShader.programSource.clear();

    /* Parse any comments (aka "[preset00]") */
    /* We don't do anything with this info so it's okay if it's missing */
    if (Parser::parse_top_comment(fs) == PROJECTM_SUCCESS)
    {
      /* Parse the preset name and a left bracket */
      char tmp_name[MAX_TOKEN_SIZE];

      if (Parser::parse_preset_name(fs, tmp_name) < 0)
      {
        std::cerr << "[Preset::readIn] loading of preset name failed" << std::endl;
        fs.seekg(0);
      }
      /// @note  We ignore the preset name because [preset00] is just not so useful
    }
    else {
      // no comment found. whatever
      if (MILKDROP_PRESET_DEBUG)
        std::cerr << "[Preset::readIn] no left bracket found..." << std::endl;
      fs.seekg(0);
    }

    // Loop through each line in file, trying to successfully parse the file.
    // If a line does not parse correctly, keep trucking along to next line.
    int retval;
    while ((retval = Parser::parse_line(fs, this)) != EOF)
    {
      if (retval == PROJECTM_PARSE_ERROR)
      {
        // std::cerr << "[Preset::readIn()] parse error in file \"" << this->absoluteFilePath() << "\"" << std::endl;
      }
    }

    //  std::cerr << "loadPresetFile: finished line parsing successfully" << std::endl;

      /* Now the preset has been loaded.
         Evaluation calls can be made at appropiate
         times in the frame loop */

    return PROJECTM_SUCCESS;
  }


  void loadBuiltinParamsUnspecInitConds() {

    InitCondUtils::LoadUnspecInitCond loadUnspecInitCond(this->init_cond_tree, this->per_frame_init_eqn_tree);

    this->builtinParams.apply(loadUnspecInitCond);
    traverse(user_param_tree, loadUnspecInitCond);

  }

  void loadCustomWaveUnspecInitConds()
  {
    for (PresetOutputs::cwave_container::iterator pos = customWaves.begin(); pos != customWaves.end(); ++pos)
    {
      assert(*pos);
      (*pos)->loadUnspecInitConds();
    }

  }

  void loadCustomShapeUnspecInitConds()
  {
    for (PresetOutputs::cshape_container::iterator pos = customShapes.begin();
      pos != customShapes.end(); ++pos)
    {
      assert(*pos);
      (*pos)->loadUnspecInitConds();
    }
  }

  void postloadInitialize()
  {
    /* It's kind of ugly to reset these values here. Should definitely be placed in the parser somewhere */
    this->per_frame_eqn_count = 0;
    this->per_frame_init_eqn_count = 0;

    this->loadBuiltinParamsUnspecInitConds();
    this->loadCustomWaveUnspecInitConds();
    this->loadCustomShapeUnspecInitConds();
  }

  int loadPresetFile(const std::string& pathname)
  {
    /* Open the file corresponding to pathname */
    std::ifstream fs(pathname.c_str());
    if (!fs || fs.eof()) {

      std::ostringstream oss;
      oss << "Problem reading file from path: \"" << pathname << "\"";

      throw PresetFactoryException(oss.str());

    }

    return readIn(fs);

  }

  void initialize(const std::string& pathname)
  {
    if (MILKDROP_PRESET_DEBUG)
      std::cerr << "[Preset] loading file \"" << pathname << "\"..." << std::endl;

    loadPresetFile(pathname);

    postloadInitialize();

    if (!presetOutputs().compositeShader.programSource.empty())
      pipeline().compositeShaderFilename = pathname;
    if (!presetOutputs().warpShader.programSource.empty())
      pipeline().warpShaderFilename = pathname;
  }

  void initialize(std::istream& in)
  {
    int retval;

    if ((retval = readIn(in)) < 0)
    {

      if (MILKDROP_PRESET_DEBUG)
        std::cerr << "[Preset] failed to load from stream " << std::endl;

      /// @bug how should we handle this problem? a well define exception?
      throw PresetFactoryException("failed to read from input stream");
    }

    postloadInitialize();
  }

  PresetOutputs& pipeline() override
  {
    return _presetOutputs;
  }

  void evalPerFrameInitEquations()
  {

    for (std::map<std::string, InitCond*>::iterator pos = per_frame_init_eqn_tree.begin(); pos != per_frame_init_eqn_tree.end(); ++pos)
    {
      assert(pos->second);
      pos->second->evaluate();
    }

  }

  void evalPerFrameEquations()
  {

    for (std::map<std::string, InitCond*>::iterator pos = init_cond_tree.begin(); pos != init_cond_tree.end(); ++pos)
    {
      assert(pos->second);
      pos->second->evaluate();
    }

    for (std::vector<PerFrameEqn*>::iterator pos = per_frame_eqn_tree.begin(); pos != per_frame_eqn_tree.end(); ++pos)
    {
      (*pos)->evaluate();
    }

  }

  template <class CustomObject>
  void transfer_q_variables(std::vector<CustomObject*>& customObjects)
  {
    CustomObject* custom_object;

    for (typename std::vector<CustomObject*>::iterator pos = customObjects.begin(); pos != customObjects.end(); ++pos) {

      custom_object = *pos;
      for (unsigned int i = 0; i < NUM_Q_VARIABLES; i++)
        custom_object->q[i] = _presetOutputs.q[i];
    }
  }

  void initialize_PerPixelMeshes()
  {
    int gx = presetInputs().gx;
    int gy = presetInputs().gy;

    init_mesh(_presetOutputs.cx_mesh, presetOutputs().cx, gx, gy);
    init_mesh(_presetOutputs.cy_mesh, presetOutputs().cy, gx, gy);
    init_mesh(_presetOutputs.sx_mesh, presetOutputs().sx, gx, gy);
    init_mesh(_presetOutputs.sy_mesh, presetOutputs().sy, gx, gy);
    init_mesh(_presetOutputs.dx_mesh, presetOutputs().dx, gx, gy);
    init_mesh(_presetOutputs.dy_mesh, presetOutputs().dy, gx, gy);
    init_mesh(_presetOutputs.zoom_mesh, presetOutputs().zoom, gx, gy);
    init_mesh(_presetOutputs.zoomexp_mesh, presetOutputs().zoomexp, gx, gy);
    init_mesh(_presetOutputs.rot_mesh, presetOutputs().rot, gx, gy);
    init_mesh(_presetOutputs.warp_mesh, presetOutputs().warp, gx, gy);
  }

  void evalPerPixelEqns()
  {
    if (nullptr == per_pixel_program)
    {
      // This is a little forward looking, but if we want to JIT assignments expressions, we might
      // as well JIT the batch all together rather than one at a time.  At the moment ProgramExpr is
      // just a different place to loop over the individual steps, but the idea is that this encapsulates
      // an optimizable chunk of work.
      // See also CustomWave which does the same for PerPointEqn
      std::vector<Expr*> steps;
      for (std::map<int, PerPixelEqn*>::iterator pos = per_pixel_eqn_tree.begin(); pos != per_pixel_eqn_tree.end(); ++pos)
        steps.push_back(pos->second->assign_expr);
      Expr* program_expr = Expr::create_program_expr(steps, false);
      Expr* jit = nullptr;
#if HAVE_LLVM
      std::string module_name = this->_filename + "_per_pixel";
      if (!steps.empty())
        jit = Expr::jit(program_expr, module_name);
#endif
      per_pixel_program = jit ? jit : program_expr;
    }

    for (int mesh_x = 0; mesh_x < presetInputs().gx; mesh_x++)
      for (int mesh_y = 0; mesh_y < presetInputs().gy; mesh_y++)
        per_pixel_program->eval(mesh_x, mesh_y);
  }

  void evalCustomWaveInitConditions()
  {

    for (PresetOutputs::cwave_container::iterator pos = customWaves.begin(); pos != customWaves.end(); ++pos) {
      assert(*pos);
      (*pos)->evalInitConds();
    }
  }

  void evalCustomWavePerFrameEquations()
  {

    for (PresetOutputs::cwave_container::iterator pos = customWaves.begin(); pos != customWaves.end(); ++pos)
    {

      std::map<std::string, InitCond*>& init_cond_tree2 = (*pos)->init_cond_tree;
      for (std::map<std::string, InitCond*>::iterator _pos = init_cond_tree2.begin(); _pos != init_cond_tree2.end(); ++_pos)
      {
        assert(_pos->second);
        _pos->second->evaluate();
      }

      std::vector<PerFrameEqn*>& per_frame_eqn_tree2 = (*pos)->per_frame_eqn_tree;
      for (std::vector<PerFrameEqn*>::iterator _pos = per_frame_eqn_tree2.begin(); _pos != per_frame_eqn_tree2.end(); ++_pos)
      {
        (*_pos)->evaluate();
      }
    }
  }

  void evalCustomShapeInitConditions()
  {

    for (PresetOutputs::cshape_container::iterator pos = customShapes.begin(); pos != customShapes.end(); ++pos) {
      assert(*pos);
      (*pos)->evalInitConds();
    }
  }

  void evalCustomShapePerFrameEquations()
  {
    for (PresetOutputs::cshape_container::iterator pos = customShapes.begin(); pos != customShapes.end(); ++pos)
    {
      std::map<std::string, InitCond*>& init_cond_tree2 = (*pos)->init_cond_tree;
      for (std::map<std::string, InitCond*>::iterator _pos = init_cond_tree2.begin(); _pos != init_cond_tree2.end(); ++_pos)
      {
        assert(_pos->second);
        _pos->second->evaluate();
      }

      std::vector<PerFrameEqn*>& per_frame_eqn_tree2 = (*pos)->per_frame_eqn_tree;
      for (std::vector<PerFrameEqn*>::iterator _pos = per_frame_eqn_tree2.begin(); _pos != per_frame_eqn_tree2.end(); ++_pos)
      {
        (*_pos)->evaluate();
      }
    }
  }

  void evaluateFrame()
  {
    // Evaluate all equation objects according to milkdrop flow diagram

    evalPerFrameInitEquations();
    evalPerFrameEquations();

    // Important step to ensure custom shapes and waves don't stamp on the q variable values
    // calculated by the per frame (init) and per pixel equations.
    transfer_q_variables(customWaves);
    transfer_q_variables(customShapes);

    initialize_PerPixelMeshes();

    evalPerPixelEqns();

    evalCustomWaveInitConditions();
    evalCustomWavePerFrameEquations();

    evalCustomShapeInitConditions();
    evalCustomShapePerFrameEquations();

    // Setup pointers of the custom waves and shapes to the preset outputs instance
    /// @slow an extra O(N) per frame, could do this during eval
    _presetOutputs.customWaves = PresetOutputs::cwave_container(customWaves);
    _presetOutputs.customShapes = PresetOutputs::cshape_container(customShapes);

  }

  void Render(const BeatDetect& music, const PipelineContext& context) override
  {
    _presetInputs.update(music, context);

    evaluateFrame();
    pipeline().Render(music, context);
  }

  template <class CustomObject>
  static CustomObject* find_custom_object(int id, std::vector<CustomObject*>& customObjects)
  {
    CustomObject* custom_object = nullptr;

    for (typename std::vector<CustomObject*>::iterator pos = customObjects.begin(); pos != customObjects.end(); ++pos) {
      if ((*pos)->id == id) {
        custom_object = *pos;
        break;
      }
    }

    if (custom_object == nullptr)
    {

      if ((custom_object = new CustomObject(id)) == nullptr)
      {
        return nullptr;
      }

      customObjects.push_back(custom_object);

    }

    assert(custom_object);
    return custom_object;
  }
};

std::unique_ptr<Preset> MilkdropPresetFactory::allocate(const std::string& url, const std::string& name, const std::string& author)
{
  PresetOutputs* presetOutputs;
  // use cached PresetOutputs if there is one, otherwise allocate
  if (_presetOutputsCache)
  {
    presetOutputs = _presetOutputsCache;
    _presetOutputsCache = nullptr;
  }
  else
  {
    presetOutputs = createPresetOutputs(gx, gy);
  }

  resetPresetOutputs(presetOutputs);

  std::string path;
  if (PresetFactory::protocol(url, path) == PresetFactory::IDLE_PRESET_PROTOCOL) {
    return IdlePresets::allocate(this, path, *presetOutputs);
  }
  else
    return std::unique_ptr<Preset>(new MilkdropPreset(this, url, name, *presetOutputs));
}

std::unique_ptr<Preset> IdlePresets::allocate(MilkdropPresetFactory* factory, const std::string& name, PresetOutputs& presetOutputs)
{

  if (name == IDLE_PRESET_NAME) {
    std::istringstream in(presetText());
    return std::unique_ptr<Preset>(new MilkdropPreset(factory, in, IDLE_PRESET_NAME, presetOutputs));
  }
  else
    return std::unique_ptr<Preset>();
}


struct TreeExpr : public Expr
{
  InfixOp* infix_op; /* null if leaf */
  Expr* gen_expr;
  Expr* left;
  Expr* right;

  TreeExpr(InfixOp* _infix_op, Expr* _gen_expr, Expr* _left, Expr* _right) : Expr(TREE), infix_op(_infix_op), gen_expr(_gen_expr), left(_left), right(_right)
  {
  }

  ~TreeExpr() override
  {

    /* free left tree */
    if (left != nullptr)
    {
      Expr::delete_expr(left);
    }

    /* free general expression object */
    if (gen_expr != nullptr)
    {
      Expr::delete_expr(gen_expr);
    }

    /* Note that infix operators are always
       stored in memory unless the program
       exits, so we don't remove them here */

       /* free right tree */
    if (right != nullptr)
    {
      Expr::delete_expr(right);
    }
  }

  float eval(int mesh_i, int mesh_j) override
  {
    float left_arg, right_arg;

    /* shouldn't be null if we've called _optimize() */
    assert(nullptr != infix_op);

    left_arg = left->eval(mesh_i, mesh_j);
    right_arg = right->eval(mesh_i, mesh_j);

    switch (infix_op->type)
    {
    case INFIX_ADD:
      return (left_arg + right_arg);
    case INFIX_MINUS:
      return (left_arg - right_arg);
    case INFIX_MULT:
      return (left_arg * right_arg);
    case INFIX_MOD:
      if ((int)right_arg == 0)
      {
        return 0;
      }
      return ((int)left_arg % (int)right_arg);
    case INFIX_OR:
      return ((int)left_arg | (int)right_arg);
    case INFIX_AND:
      return ((int)left_arg & (int)right_arg);
    case INFIX_DIV:
      if (right_arg == 0)
      {
        return MAX_DOUBLE_SIZE;
      }
      return (left_arg / right_arg);
    default:
      return EVAL_ERROR;
    }
  }

  Expr* _optimize() override
  {
    if (infix_op == nullptr)
    {
      Expr* opt = gen_expr->_optimize();
      if (opt != gen_expr)
        Expr::delete_expr(gen_expr);
      gen_expr = nullptr;
      return opt;
    }
    if (left != nullptr)
    {
      Expr* l = left->_optimize();
      if (l != left)
        Expr::delete_expr(left);
      left = l;
    }
    if (right != nullptr)
    {
      Expr* r = right->_optimize();
      if (r != right)
        Expr::delete_expr(right);
      right = r;
    }
    if (left == nullptr)
    {
      Expr* opt = right;
      right = nullptr;
      return opt;
    }
    if (right == nullptr)
    {
      Expr* opt = left;
      left = nullptr;
      return opt;
    }
    if (left->isConstant() && right->isConstant())
      return Expr::const_to_expr(eval(-1, -1));

    // this is gratuitious, but a*b+c is super common, so as proof-of-concept, let's make a special Expr
    if (infix_op->type == INFIX_ADD &&
      ((left->clazz == TREE && ((TreeExpr*)left)->infix_op->type == INFIX_MULT) ||
        (right->clazz == TREE && ((TreeExpr*)right)->infix_op->type == INFIX_MULT)))
    {
      Expr* a, * b, * c;
      if (left->clazz == TREE && ((TreeExpr*)left)->infix_op->type == INFIX_MULT)
        std::swap(left, right);
      a = ((TreeExpr*)right)->left;
      b = ((TreeExpr*)right)->right;
      c = left;
      ((TreeExpr*)right)->left = nullptr;
      ((TreeExpr*)right)->right = nullptr;
      left = nullptr;
      return new MultAndAddExpr(a, b, c);
    }

    if (infix_op->type == INFIX_MULT && (left->isConstant() || right->isConstant()))
    {
      if (right->isConstant())
        std::swap(left, right);
      float c = left->eval(-1, -1);
      Expr* expr = right;
      right = left = nullptr;
      return new MultConstExpr(expr, c);
    }

    return this;
  }

  static TreeExpr* create(InfixOp* _infix_op, Expr* _gen_expr, TreeExpr* _left, TreeExpr* _right);
  static TreeExpr* create(InfixOp* infix_op, Expr* left, Expr* right);

  TreeExpr* leftTree()
  {
    return dynamic_cast<TreeExpr*>(left);
  }

  TreeExpr* rightTree()
  {
    return dynamic_cast<TreeExpr*>(right);
  }
};

struct TreeExprAdd : public TreeExpr
{
  TreeExprAdd(Expr* _left, Expr* _right)
    : TreeExpr(Eval::infix_add, nullptr, _left, _right) {}

  float eval(int mesh_i, int mesh_j) override
  {
    return left->eval(mesh_i, mesh_j) + right->eval(mesh_i, mesh_j);
  }
};

struct TreeExprMinus : public TreeExpr
{
  TreeExprMinus(Expr* _left, Expr* _right) :
    TreeExpr(Eval::infix_minus, nullptr, _left, _right) {}

  float eval(int mesh_i, int mesh_j) override
  {
    return left->eval(mesh_i, mesh_j) - right->eval(mesh_i, mesh_j);
  }
};

struct TreeExprMult : public TreeExpr
{
public:
  TreeExprMult(Expr* _left, Expr* _right) :
    TreeExpr(Eval::infix_mult, nullptr, (TreeExpr*)_left, (TreeExpr*)_right) {}        // TODO fix TreeExpr constructor to avoid bogus cast

  float eval(int mesh_i, int mesh_j) override
  {
    return left->eval(mesh_i, mesh_j) * right->eval(mesh_i, mesh_j);
  }
};

TreeExpr* TreeExpr::create(InfixOp* _infix_op, Expr* _gen_expr, TreeExpr* _left, TreeExpr* _right)
{
  if (nullptr != _infix_op)
  {
    // TODO move to TreeExpr::optimize()
    if (_infix_op->type == INFIX_ADD)
      return new TreeExprAdd(_left, _right);
    if (_infix_op->type == INFIX_MINUS)
      return new TreeExprMinus(_left, _right);
    if (_infix_op->type == INFIX_MULT)
      return new TreeExprMult(_left, _right);
  }
  return new TreeExpr(_infix_op, _gen_expr, _left, _right);
}

TreeExpr* TreeExpr::create(InfixOp* _infix_op, Expr* _left, Expr* _right)
{
  assert(nullptr != _infix_op);
  // TODO move to TreeExpr::optimize()
  if (_infix_op->type == INFIX_ADD)
    return new TreeExprAdd(_left, _right);
  if (_infix_op->type == INFIX_MINUS)
    return new TreeExprMinus(_left, _right);
  if (_infix_op->type == INFIX_MULT)
    return new TreeExprMult(_left, _right);
  return new TreeExpr(_infix_op, nullptr, _left, _right);
}

#define PER_FRAME_STRING "per_frame_"
#define PER_FRAME_STRING_LENGTH 10
#define PER_FRAME_INIT_STRING "per_frame_init_"
#define PER_FRAME_INIT_STRING_LENGTH 15
#define WAVE_STRING_LENGTH 5
#define WAVE_INIT_STRING "init"
#define WAVE_INIT_STRING_LENGTH 4
#define PER_PIXEL_STRING "per_pixel_"
#define PER_PIXEL_STRING_LENGTH 10
#define PER_FRAME_STRING_NO_UNDERSCORE "per_frame"
#define PER_FRAME_STRING_NO_UNDERSCORE_LENGTH 9
#define PER_POINT_STRING "per_point"
#define PER_POINT_STRING_LENGTH 9
#define WARP_STRING "warp_"
#define WARP_STRING_LENGTH 5
#define COMPOSITE_STRING "comp_"
#define COMPOSITE_STRING_LENGTH 5
#define SHAPE_INIT_STRING "init"
#define SHAPE_INIT_STRING_LENGTH 4
#define SHAPE_STRING "shape_"
#define SHAPE_STRING_LENGTH 6
#define SHAPECODE_STRING "shapecode_"
#define SHAPECODE_STRING_LENGTH 10
#define WAVE_STRING "wave_"
#define WAVE_STRING_LENGTH 5
#define WAVECODE_STRING "wavecode_"
#define WAVECODE_STRING_LENGTH 9

namespace Parser
{
  line_mode_t line_mode;
  CustomWave* current_wave;
  CustomShape* current_shape;
  int last_token_size;
  int string_line_buffer_index;
  char string_line_buffer[STRING_LINE_SIZE];
  int per_frame_eqn_count;
  int per_frame_init_eqn_count;
  unsigned int line_count;
  std::string lastLinePrefix("");
  bool tokenWrapAroundEnabled(false);
  int last_custom_wave_id;
  int last_custom_shape_id;
  char last_eqn_type[MAX_TOKEN_SIZE + 1];


  int insert_gen_rec(Expr* gen_expr, TreeExpr* root)
  {
    /* Trivial Case: root is null */

    if (root == nullptr)
    {
      if (PARSE_DEBUG) printf("insert_gen_rec: root is null, returning failure\n");
      return PROJECTM_FAILURE;
    }


    /* The current node's left pointer is null, and this
       current node is an infix operator, so insert the
       general expression at the left pointer */

    if ((root->left == nullptr) && (root->infix_op != nullptr))
    {
      root->left = TreeExpr::create(nullptr, gen_expr, nullptr, nullptr);
      return PROJECTM_SUCCESS;
    }

    /* The current node's right pointer is null, and this
       current node is an infix operator, so insert the
       general expression at the right pointer */

    if ((root->right == nullptr) && (root->infix_op != nullptr))
    {
      root->right = TreeExpr::create(nullptr, gen_expr, nullptr, nullptr);
      return PROJECTM_SUCCESS;
    }

    /* Otherwise recurse down to the left. If
       this succeeds then return. If it fails, try
       recursing down to the right */

    if (insert_gen_rec(gen_expr, root->leftTree()) == PROJECTM_FAILURE)
      return insert_gen_rec(gen_expr, root->rightTree());

    /* Impossible for control flow to reach here, but in
       the world of C programming, who knows... */
    if (PARSE_DEBUG) printf("insert_gen_rec: should never reach here!\n");
    return PROJECTM_FAILURE;
  }

  int insert_infix_rec(InfixOp* infix_op, TreeExpr* root)
  {
    /* Shouldn't happen, implies a parse error */

    if (root == nullptr)
      return PROJECTM_FAILURE;

    /* Also shouldn't happen, also implies a (different) parse error */

    if (root->infix_op == nullptr)
      return PROJECTM_FAILURE;

    /* Left tree is empty, attach this operator to it.
       I don't think this will ever happen */
    if (root->left == nullptr)
    {
      root->left = TreeExpr::create(infix_op, nullptr, root->leftTree(), nullptr);
      return PROJECTM_SUCCESS;
    }

    /* Right tree is empty, attach this operator to it */
    if (root->right == nullptr)
    {
      root->right = TreeExpr::create(infix_op, nullptr, root->rightTree(), nullptr);
      return PROJECTM_SUCCESS;
    }

    /* The left element can now be ignored, since there is no way for this
       operator to use those expressions */

       /* If the right element is not an infix operator,
          then insert the expression here, attaching the old right branch
          to the left of the new expression */

    if (root->rightTree()->infix_op == nullptr)
    {
      root->right = TreeExpr::create(infix_op, nullptr, root->rightTree(), nullptr);
      return PROJECTM_SUCCESS;
    }

    /* Traverse deeper if the inserting operator precedence is less than the
       the root's right operator precedence */
    if (infix_op->precedence < root->rightTree()->infix_op->precedence)
      return insert_infix_rec(infix_op, root->rightTree());

    /* Otherwise, insert the operator here */

    root->right = TreeExpr::create(infix_op, nullptr, root->rightTree(), nullptr);
    return PROJECTM_SUCCESS;
  }

  TreeExpr* insert_gen_expr(Expr* gen_expr, TreeExpr** root)
  {
    TreeExpr* new_root;

    /* If someone foolishly passes a null
       pointer to insert, return the original tree */

    if (gen_expr == nullptr)
    {
      return *root;
    }

    /* If the root is null, generate a new expression tree,
       using the passed expression as the root element */

    if (*root == nullptr)
    {
      new_root = TreeExpr::create(nullptr, gen_expr, nullptr, nullptr);
      *root = new_root;
      return new_root;
    }


    /* Otherwise. the new element definitely will not replace the current root.
       Use a recursive helper function to do insertion */

    insert_gen_rec(gen_expr, *root);
    return *root;
  }

  TreeExpr* insert_infix_op(InfixOp* infix_op, TreeExpr** root)
  {
    TreeExpr* new_root;

    /* Sanity check */
    if (infix_op == nullptr)
      return nullptr;

    /* The root is null, so make this operator
       the new root */

    if (*root == nullptr)
    {
      new_root = TreeExpr::create(infix_op, nullptr, nullptr, nullptr);
      *root = new_root;
      return new_root;
    }

    /* The root node is not an infix function,
       so we make this infix operator the new root  */

    if ((*root)->infix_op == nullptr)
    {
      new_root = TreeExpr::create(infix_op, nullptr, *root, nullptr);
      (*root) = new_root;
      return new_root;
    }

    /* The root is an infix function. If the precedence
       of the item to be inserted is greater than the root's
       precedence, then make gen_expr the root */

    if (infix_op->precedence >= (*root)->infix_op->precedence)
    {
      new_root = TreeExpr::create(infix_op, nullptr, *root, nullptr);
      (*root) = new_root;
      return new_root;
    }

    /* If control flow reaches here, use a recursive helper
       with the knowledge that the root is higher precedence
       than the item to be inserted */

    insert_infix_rec(infix_op, *root);
    return *root;
  }

  int parse_int(std::istream& fs, int* int_ptr)
  {
    char string[MAX_TOKEN_SIZE];
    token_t token;
    int sign;
    char* end_ptr = (char*)" ";

    token = parseToken(fs, string);


    switch (token)
    {
    case tMinus:
      sign = -1;
      parseToken(fs, string);
      break;
    case tPlus:
      sign = 1;
      parseToken(fs, string);
      break;
    default:
      sign = 1;
      break;
    }


    if (string[0] == 0)
      return PROJECTM_PARSE_ERROR;

    /* Convert the string to an integer. *end_ptr
       should end up pointing to null terminator of 'string'
       if the conversion was successful. */
       //  printf("STRING: \"%s\"\n", string);

    (*int_ptr) = sign * strtol(string, &end_ptr, 10);

    /* If end pointer is a return character or null terminator, all is well */
    if ((*end_ptr == '\r') || (*end_ptr == '\0'))
      return PROJECTM_SUCCESS;

    return PROJECTM_PARSE_ERROR;
  }

  /* Parses a floating point number */
  int string_to_float(char* string, float* float_ptr)
  {
    if (*string == 0)
      return PROJECTM_PARSE_ERROR;

    std::istringstream iss(string);
    iss.imbue(std::locale("C"));
    iss >> (*float_ptr);
    if (!iss.fail()) {
      return PROJECTM_SUCCESS;
    }

    (*float_ptr) = 0;
    return PROJECTM_PARSE_ERROR;
  }

  int parse_float(std::istream& fs, float* float_ptr)
  {
    char string[MAX_TOKEN_SIZE];
    token_t token;
    int sign;

    token = parseToken(fs, string);

    switch (token)
    {
    case tMinus:
      sign = -1;
      parseToken(fs, string);
      break;
    case tPlus:
      sign = 1;
      parseToken(fs, string);
      break;
    default:
      sign = 1;
    }

    if (string[0] == 0)
    {
      return PROJECTM_PARSE_ERROR;
    }

    std::istringstream iss(string);
    iss.imbue(std::locale("C"));
    iss >> (*float_ptr);
    if (!iss.fail()) {
      (*float_ptr) *= sign;
      return PROJECTM_SUCCESS;
    }

    if (PARSE_DEBUG) printf("parse_float: float conversion failed for string \"%s\"\n", string);

    (*float_ptr) = 0;
    return PROJECTM_PARSE_ERROR;
  }

  InitCond* parse_init_cond(std::istream& fs, char* name, MilkdropPreset* preset)
  {
    Param* param;
    CValue init_val;
    InitCond* init_cond;

    if (name == nullptr)
      return nullptr;
    if (preset == nullptr)
      return nullptr;

    /* Search for the paramater in the database, creating it if necessary */
    if ((param = ParamUtils::find(name, &preset->builtinParams, &preset->user_param_tree)) == nullptr)
    {
      return nullptr;
    }

    if (PARSE_DEBUG) printf("parse_init_cond: parameter = \"%s\" (LINE %d)\n", param->name.c_str(), line_count);

    if (param->flags & P_FLAG_READONLY)
    {
      if (PARSE_DEBUG) printf("parse_init_cond: builtin parameter \"%s\" marked as read only!\n", param->name.c_str());
      return nullptr;
    }

    /* At this point, a parameter has been created or was found
       in the database. */

    if (PARSE_DEBUG) printf("parsed_init_cond: parsing initial condition value... (LINE %d)\n", line_count);

    /* integer value (boolean is an integer in C) */
    if (param->type == P_TYPE_BOOL)
    {
      int bool_test;
      if ((parse_int(fs, &bool_test)) == PROJECTM_PARSE_ERROR)
      {
        if (PARSE_DEBUG) printf("parse_init_cond: error parsing integer!\n");
        return nullptr;
      }
      init_val.bool_val = bool_test;
    }

    else if (param->type == P_TYPE_INT)
    {
      if ((parse_int(fs, (int*)&init_val.int_val)) == PROJECTM_PARSE_ERROR)
      {
        if (PARSE_DEBUG) printf("parse_init_cond: error parsing integer!\n");
        return nullptr;
      }
    }

    /* float value */
    else if (param->type == P_TYPE_DOUBLE)
    {
      if ((parse_float(fs, (float*)&init_val.float_val)) == PROJECTM_PARSE_ERROR)
      {
        if (PARSE_DEBUG) printf("parse_init_cond: error parsing float!\n");
        return nullptr;
      }
    }

    /* Unknown value */
    else
    {
      if (PARSE_DEBUG) printf("parse_init_cond: unknown parameter type!\n");
      return nullptr;
    }

    /* Create new initial condition */
    if ((init_cond = new InitCond(param, init_val)) == nullptr)
    {
      if (PARSE_DEBUG) printf("parse_init_cond: new_init_cond failed!\n");
      return nullptr;
    }

    /* Finished */
    return init_cond;
  }

  int parse_wave_prefix(char* token, int* id, char** eqn_string)
  {
    int len, i, j;

    if (token == nullptr)
      return PROJECTM_FAILURE;
    if (eqn_string == nullptr)
      return PROJECTM_FAILURE;
    if (id == nullptr)
      return PROJECTM_FAILURE;

    len = strlen(token);

    if (len <= WAVE_STRING_LENGTH)
      return PROJECTM_FAILURE;


    i = WAVE_STRING_LENGTH;
    j = 0;
    (*id) = 0;

    /* This loop grabs the integer id for this custom wave */
    while ((i < len) && (token[i] >= 48) && (token[i] <= 57))
    {
      if (j >= MAX_TOKEN_SIZE)
        return PROJECTM_FAILURE;

      (*id) = 10 * (*id) + (token[i] - 48);
      j++;
      i++;
    }

    if (i > (len - 2))
      return PROJECTM_FAILURE;

    *eqn_string = token + i + 1;

    if (PARSE_DEBUG) printf("parse_wave_prefix: prefix = %s\n (LINE %d)", *eqn_string, line_count);
    return PROJECTM_SUCCESS;
  }

  Expr* _parse_gen_expr(std::istream& fs, TreeExpr* tree_expr, MilkdropPreset* preset);
  Expr* parse_gen_expr(std::istream& fs, TreeExpr* tree_expr, MilkdropPreset* preset)
  {
    Expr* gen_expr = _parse_gen_expr(fs, tree_expr, preset);
    if (nullptr == gen_expr)
      return nullptr;
    //std::cout << gen_expr << std::endl;
    Expr* opt = Expr::optimize(gen_expr);
    gen_expr = nullptr;
    //std::cout << opt << std::endl << std::endl;
    return opt;
  }

  Expr* _parse_gen_expr(std::istream& fs, TreeExpr* tree_expr, MilkdropPreset* preset)
  {
    int i;
    char string[MAX_TOKEN_SIZE];
    token_t token;
    Expr* gen_expr;
    float val;
    Param* param = nullptr;
    Func* func;
    Expr** expr_list;

    switch (token = parseToken(fs, string))
    {
      /* Left Parentice Case */
    case tLPr:
      //std::cerr << "token before tLPr:" << string << std::endl;
      /* CASE 1 (Left Parentice): See if the previous string before this parentice is a function name */
      if ((func = BuiltinFuncs::find_func(string)) != nullptr)
      {
        if (PARSE_DEBUG)
        {
          std::cerr << "parse_gen_expr: found prefix function (name = \""
            << func->getName() << "\") (LINE " << line_count << ")" << std::endl;
        }

        /* Parse the functions arguments */
        if ((expr_list = parse_prefix_args(fs, func->getNumArgs(), preset)) == nullptr)
        {
          if (PARSE_DEBUG)
          {
            std::cerr << "parse_prefix_args: failed to generate an expresion list! (LINE "
              << line_count << ")" << std::endl;
          }
          if (tree_expr != nullptr)
          {
            Expr::delete_expr(tree_expr);
          }
          return nullptr;
        }

        /* Convert function to expression */
        if ((gen_expr = Expr::prefun_to_expr(func, expr_list)) == nullptr)
        {
          if (PARSE_DEBUG) printf("parse_prefix_args: failed to convert prefix function to general expression (LINE %d) \n",
            line_count);
          if (tree_expr)
            Expr::delete_expr(tree_expr);
          for (i = 0; i < func->getNumArgs(); i++)
            Expr::delete_expr(expr_list[i]);
          free(expr_list);
          return nullptr;
        }

        token = parseToken(fs, string);

        if (*string != 0)
        {
          if (PARSE_DEBUG) printf("parse_prefix_args: empty string expected, but not found...(LINE %d)\n", line_count);
          /* continue anyway for now, could be implicit multiplication */
        }

        return parse_infix_op(fs, token, insert_gen_expr(gen_expr, &tree_expr), preset);
      }


      /* Case 2: (Left Parentice), a string coupled with a left parentice. Either an error or implicit
         multiplication operator. For now treat it as an error */
      if (*string != 0)
      {
        std::cerr << "token prefix is " << *string << std::endl;
        if (PARSE_DEBUG) printf("parse_gen_expr: implicit multiplication case unimplemented!\n");
        if (tree_expr)
          Expr::delete_expr(tree_expr);
        return nullptr;
      }

      /* CASE 3 (Left Parentice): the following is enclosed parentices to change order
         of operations. So we create a new expression tree */

      if ((gen_expr = parse_gen_expr(fs, nullptr, preset)) == nullptr)
      {
        if (PARSE_DEBUG) printf("parse_gen_expr:  found left parentice, but failed to create new expression tree \n");
        if (tree_expr)
          Expr::delete_expr(tree_expr);
        return nullptr;
      }

      if (PARSE_DEBUG) printf("parse_gen_expr: finished enclosed expression tree...\n");
      token = parseToken(fs, string);
      return parse_infix_op(fs, token, insert_gen_expr(gen_expr, &tree_expr), preset);

      /* Plus is a prefix operator check */
    case tPlus:
      if (*string == 0)
      {

        if (PARSE_DEBUG) printf("parse_gen_expr: plus used as prefix (LINE %d)\n", line_count);

        /* Treat prefix plus as implict 0 preceding operator */
        gen_expr = Expr::const_to_expr(0);

        return parse_infix_op(fs, tPositive, insert_gen_expr(gen_expr, &tree_expr), preset);
      }

      /* Minus is a prefix operator check */
    case tMinus:
      if (*string == 0)
      {

        /* Use the negative infix operator, but first add an implicit zero to the operator tree */
        gen_expr = Expr::const_to_expr(0);
        //return parse_gen_expr(fs, insert_gen_expr(gen_expr, &tree_expr), preset);
        return parse_infix_op(fs, tNegative, insert_gen_expr(gen_expr, &tree_expr), preset);
      }

      /* All the following cases are strings followed by an infix operator or terminal */
    case tRPr:
    case tEOL:
    case tEOF:
    case tSemiColon:
    case tComma:

      /* CASE 1 (terminal): string is empty, but not null. Not sure if this will actually happen
         any more. */
      if (*string == 0)
      {
        if (PARSE_DEBUG) printf("parse_gen_expr: empty string coupled with terminal (LINE %d) \n", line_count);
        if (nullptr == tree_expr && (token == tEOF || token == tEOL))
        {
          // we will get here if we have a completely empty line e.g. "shape_1_per_frame1=shpt ="
          // we return 0 because returning nullptr would indicate an error
          return Expr::const_to_expr(0.0f);
        }
        return parse_infix_op(fs, token, tree_expr, preset);
      }

    default:

      /* CASE 0: Empty string, parse error */
      if (*string == 0)
      {
        if (tree_expr)
          Expr::delete_expr(tree_expr);
        return nullptr;
      }

      /* CASE 1: Check if string is a just a floating point number */
      if (string_to_float(string, &val) != PROJECTM_PARSE_ERROR)
      {
        if ((gen_expr = Expr::const_to_expr(val)) == nullptr)
        {
          if (tree_expr)
            Expr::delete_expr(tree_expr);
          return nullptr;
        }

        /* Parse the rest of the line */
        return parse_infix_op(fs, token, insert_gen_expr(gen_expr, &tree_expr), preset);

      }


      /* CASE 4: custom shape variable */
      if (current_shape != nullptr)
      {
        if ((param = ParamUtils::find<NO_CREATE>(std::string(string), &current_shape->param_tree)) == nullptr)
        {
          if ((param = preset->builtinParams.find_builtin_param(std::string(string))) == nullptr)
            if ((param = ParamUtils::find<AUTO_CREATE>(std::string(string), &current_shape->param_tree)) == nullptr)
            {
              if (tree_expr)
                Expr::delete_expr(tree_expr);
              return nullptr;
            }
        }

        if (PARSE_DEBUG)
        {
          std::cerr << "parse_gen_expr: custom shape parameter (name = "
            << param->name << ")" << std::endl;
        }


        /* Convert parameter to an expression */
        if ((gen_expr = Expr::param_to_expr(param)) == nullptr)
        {
          Expr::delete_expr(tree_expr);
          return nullptr;
        }

        //if (PARSE_DEBUG) printf("converted to expression (LINE %d)\n", line_count);

        /* Parse the rest of the line */
        return parse_infix_op(fs, token, insert_gen_expr(gen_expr, &tree_expr), preset);
      }

      /* CASE 5: custom wave variable */
      if (current_wave != nullptr)
      {
        if ((param = ParamUtils::find<NO_CREATE>(std::string(string), &current_wave->param_tree)) == nullptr)
        {
          if ((param = preset->builtinParams.find_builtin_param(std::string(string))) == nullptr)
            if ((param = ParamUtils::find<AUTO_CREATE>(std::string(string), &current_wave->param_tree)) == nullptr)
            {
              if (tree_expr)
                Expr::delete_expr(tree_expr);
              return nullptr;
            }
        }
        assert(param);

        if (PARSE_DEBUG)
        {
          std::cerr << "parse_gen_expr: custom wave parameter (name = " << param->name << ")" << std::endl;

        }

        /* Convert parameter to an expression */
        if ((gen_expr = Expr::param_to_expr(param)) == nullptr)
        {
          Expr::delete_expr(tree_expr);
          return nullptr;
        }

        //if (PARSE_DEBUG) printf("converted to expression (LINE %d)\n", line_count);

        /* Parse the rest of the line */
        return parse_infix_op(fs, token, insert_gen_expr(gen_expr, &tree_expr), preset);

      }

      /* CASE 6: regular parameter. Will be created if necessary and the string has no invalid characters */
      if ((param = ParamUtils::find(string, &preset->builtinParams, &preset->user_param_tree)) != nullptr)
      {

        if (PARSE_DEBUG)
        {
          std::cerr << "parse_gen_expr: parameter (name = \"" << param->name << "\")..." << std::endl;
        }

        /* Convert parameter to an expression */
        if ((gen_expr = Expr::param_to_expr(param)) == nullptr)
        {
          Expr::delete_expr(tree_expr);
          return nullptr;
        }

        //if (PARSE_DEBUG) printf("converted to expression (LINE %d)\n", line_count);

        /* Parse the rest of the line */
        return parse_infix_op(fs, token, insert_gen_expr(gen_expr, &tree_expr), preset);

      }

      /* CASE 7: Bad string, give up */
      if (PARSE_DEBUG)
      {
        printf("parse_gen_expr: syntax error [string = \"%s\"] (LINE %d)\n", string, line_count);

      }
      if (tree_expr)
        Expr::delete_expr(tree_expr);
      return nullptr;
    }
  }

  Expr** parse_prefix_args(std::istream& fs, int num_args, MilkdropPreset* preset)
  {
    int i, j;
    Expr** expr_list; /* List of arguments to function */
    Expr* gen_expr;

    /* Malloc the expression list */
    expr_list = (Expr**)wipemalloc(sizeof(Expr*) * num_args);

    /* Malloc failed */
    if (expr_list == nullptr)
      return nullptr;


    i = 0;

    while (i < num_args)
    {
      //if (PARSE_DEBUG) printf("parse_prefix_args: parsing argument %d...\n", i+1);
      /* Parse the ith expression in the list */
      if ((gen_expr = parse_gen_expr(fs, nullptr, preset)) == nullptr)
      {
        //if (PARSE_DEBUG) printf("parse_prefix_args: failed to get parameter # %d for function (LINE %d)\n", i+1, line_count);
        for (j = 0; j < i; j++)
          Expr::delete_expr(expr_list[j]);
        free(expr_list);
        return nullptr;
      }
      /* Assign entry in expression list */
      expr_list[i++] = gen_expr;
    }

    //if (PARSE_DEBUG) printf("parse_prefix_args: finished parsing %d arguments (LINE %d)\n", num_args, line_count);
    /* Finally, return the resulting expression list */
    return expr_list;
  }

  /* Parses an infix operator */
  Expr* parse_infix_op(std::istream& fs, token_t token, TreeExpr* tree_expr, MilkdropPreset* preset)
  {
    Expr* gen_expr;

    switch (token)
    {
      /* All the infix operators */
    case tPlus:
      if (PARSE_DEBUG) printf("parse_infix_op: found addition operator (LINE %d)\n", line_count);
      if (PARSE_DEBUG) std::cerr << "WRAP AROUND IS " << tokenWrapAroundEnabled << std::endl;

      return _parse_gen_expr(fs, insert_infix_op(Eval::infix_add, &tree_expr), preset);
    case tMinus:
      if (PARSE_DEBUG) printf("parse_infix_op: found subtraction operator (LINE %d)\n", line_count);
      return _parse_gen_expr(fs, insert_infix_op(Eval::infix_minus, &tree_expr), preset);
    case tMult:
      if (PARSE_DEBUG) printf("parse_infix_op: found multiplication operator (LINE %d)\n", line_count);
      return _parse_gen_expr(fs, insert_infix_op(Eval::infix_mult, &tree_expr), preset);
    case tDiv:
      if (PARSE_DEBUG) printf("parse_infix_op: found division operator (LINE %d)\n", line_count);
      return _parse_gen_expr(fs, insert_infix_op(Eval::infix_div, &tree_expr), preset);
    case tMod:
      if (PARSE_DEBUG) printf("parse_infix_op: found modulo operator (LINE %d)\n", line_count);
      return _parse_gen_expr(fs, insert_infix_op(Eval::infix_mod, &tree_expr), preset);
    case tOr:
      if (PARSE_DEBUG) printf("parse_infix_op: found bitwise or operator (LINE %d)\n", line_count);
      return _parse_gen_expr(fs, insert_infix_op(Eval::infix_or, &tree_expr), preset);
    case tAnd:
      if (PARSE_DEBUG) printf("parse_infix_op: found bitwise and operator (LINE %d)\n", line_count);
      return _parse_gen_expr(fs, insert_infix_op(Eval::infix_and, &tree_expr), preset);
    case tPositive:
      if (PARSE_DEBUG) printf("parse_infix_op: found positive operator (LINE %d)\n", line_count);
      return _parse_gen_expr(fs, insert_infix_op(Eval::infix_positive, &tree_expr), preset);
    case tNegative:
      if (PARSE_DEBUG) printf("parse_infix_op: found negative operator (LINE %d)\n", line_count);
      return _parse_gen_expr(fs, insert_infix_op(Eval::infix_negative, &tree_expr), preset);

    case tEOL:
    case tEOF:
    case tSemiColon:
    case tRPr:
    case tComma:
      if (PARSE_DEBUG) printf("parse_infix_op: terminal found (LINE %d)\n", line_count);
      gen_expr = tree_expr;
      assert(gen_expr);
      return gen_expr;
    default:
      if (PARSE_DEBUG) printf("parse_infix_op: operator or terminal expected, but not found (LINE %d)\n", line_count);
      Expr::delete_expr(tree_expr);
      return nullptr;
    }

    /* Will never happen */
    return nullptr;
  }

  InitCond* parse_per_frame_init_eqn(std::istream& fs, MilkdropPreset* preset, std::map<std::string, Param*>* database)
  {
    char name[MAX_TOKEN_SIZE];
    Param* param = nullptr;
    CValue init_val;
    InitCond* init_cond;
    Expr* gen_expr;
    float val;
    token_t token;


    if (preset == nullptr)
      return nullptr;
    if (fs.fail())
      return nullptr;

    if ((token = parseToken(fs, name)) != tEq)
      return nullptr;


    /* If a database was specified,then use ParamUtils::find_db instead */
    if ((database != nullptr) && ((param = ParamUtils::find<AUTO_CREATE>(name, database)) == nullptr))
    {
      return nullptr;
    }

    /* Otherwise use the builtin parameter and user databases. This is confusing. Sorry. */
    if ((param == nullptr) && ((param = ParamUtils::find(name, &preset->builtinParams, &preset->user_param_tree)) == nullptr))
    {
      return nullptr;
    }

    if (PARSE_DEBUG) printf("parse_per_frame_init_eqn: parameter = \"%s\" (LINE %d)\n", param->name.c_str(), line_count);

    if (param->flags & P_FLAG_READONLY)
    {
      if (PARSE_DEBUG) printf("pars_per_frame_init_eqn: builtin parameter \"%s\" marked as read only!\n", param->name.c_str());
      return nullptr;
    }

    /* At this point, a parameter has been created or was found
       in the database. */

    if (PARSE_DEBUG) printf("parse_per_frame_init_eqn: parsing right hand side of per frame init equation.. (LINE %d)\n", line_count);

    if ((gen_expr = parse_gen_expr(fs, nullptr, preset)) == nullptr)
    {
      if (PARSE_DEBUG) printf("parse_per_frame_init_eqn: failed to parse general expresion!\n");
      return nullptr;
    }

    /* Compute initial condition value */
    val = gen_expr->eval(-1, -1);

    /* Free the general expression now that we are done with it */
    Expr::delete_expr(gen_expr);

    /* integer value (boolean is an integer in C) */
    if (param->type == P_TYPE_BOOL)
    {
      init_val.bool_val = (bool)val;
    }

    else if (param->type == P_TYPE_INT)
    {
      init_val.int_val = (int)val;
    }

    /* float value */
    else if (param->type == P_TYPE_DOUBLE)
    {
      init_val.float_val = val;
    }

    /* Unknown value */
    else
    {
      if (PARSE_DEBUG) printf("pase_per_frame_init_eqn: unknown parameter type!\n");
      return nullptr;
    }


    /* Create new initial condition */
    if ((init_cond = new InitCond(param, init_val)) == nullptr)
    {
      if (PARSE_DEBUG) printf("parse_per_frame_init_eqn: new_init_cond failed!\n");
      return nullptr;
    }

    init_cond->evaluate(true);

    /* Finished */
    return init_cond;
  }


  int parse_shapecode_prefix(char* token, int* id, char** var_string)
  {
    int len, i, j;

    if (token == nullptr)
      return PROJECTM_FAILURE;
    /*
    if (*var_string == nullptr)
      return PROJECTM_FAILURE;
    */
    if (id == nullptr)
      return PROJECTM_FAILURE;

    len = strlen(token);

    /* Move pointer passed "shapecode_" prefix */
    if (len <= SHAPECODE_STRING_LENGTH)
      return PROJECTM_FAILURE;
    i = SHAPECODE_STRING_LENGTH;
    j = 0;
    (*id) = 0;

    /* This loop grabs the integer id for this custom shape */
    while ((i < len) && (token[i] >= 48) && (token[i] <= 57))
    {
      if (j >= MAX_TOKEN_SIZE)
        return PROJECTM_FAILURE;

      (*id) = 10 * (*id) + (token[i] - 48);
      j++;
      i++;
    }


    if (i > (len - 2))
      return PROJECTM_FAILURE;

    *var_string = token + i + 1;

    return PROJECTM_SUCCESS;
  }

  int parse_shapecode(char* token, std::istream& fs, MilkdropPreset* preset)
  {
    char* var_string;
    InitCond* init_cond;
    CustomShape* custom_shape;
    int id;
    CValue init_val;
    Param* param;

    /* Null argument checks */
    if (preset == nullptr)
      return PROJECTM_FAILURE;
    if (fs.fail())
      return PROJECTM_FAILURE;
    if (token == nullptr)
      return PROJECTM_FAILURE;

    /* token should be in the form shapecode_N_var, such as shapecode_1_samples */

    /* Get id and variable name from token string */
    if (parse_shapecode_prefix(token, &id, &var_string) < 0)
      return PROJECTM_PARSE_ERROR;

    last_custom_shape_id = id;

    if (PARSE_DEBUG) printf("parse_shapecode: shapecode id = %d, parameter = \"%s\"\n", id, var_string);


    /* Retrieve custom shape information from preset. The 3rd argument
       if true creates a custom shape if one does not exist */

    if ((custom_shape = MilkdropPreset::find_custom_object(id, preset->customShapes)) == nullptr)
    {
      if (PARSE_DEBUG) printf("parse_shapecode: failed to load (or create) custom shape (id = %d)!\n", id);
      return PROJECTM_FAILURE;
    }
    if (PARSE_DEBUG) printf("parse_shapecode: custom shape found (id = %d)\n", custom_shape->id);

    if ((param = ParamUtils::find<NO_CREATE>(var_string, &custom_shape->text_properties_tree)) != nullptr)
    {

      std::string text;//[MAX_TOKEN_SIZE];
      //token_t token = parseToken(fs, text);

      fs >> text;

      param->set_param(text);
      if (PARSE_DEBUG)
        std::cerr << "parse_shapecode: found image url, text is \""
        << text << "\"" << std::endl;

      return PROJECTM_SUCCESS;
    }

    /* Retrieve parameter from this custom shapes parameter db */


    if ((param = ParamUtils::find<AUTO_CREATE>(var_string, &custom_shape->param_tree)) == nullptr)
    {
      if (PARSE_DEBUG) printf("parse_shapecode: failed to create parameter.\n");
      return PROJECTM_FAILURE;
    }
    if (PARSE_DEBUG) printf("parse_shapecode: custom shape parameter found (name = %s)\n", param->name.c_str());

    /* integer value (boolean is an integer in C) */


    if (param->type == P_TYPE_BOOL)
    {
      int bool_test;
      if ((parse_int(fs, &bool_test)) == PROJECTM_PARSE_ERROR)
      {
        if (PARSE_DEBUG) printf("parse_shapecode: error parsing integer!\n");
        return PROJECTM_PARSE_ERROR;
      }
      init_val.bool_val = bool_test;
    }
    else if (param->type == P_TYPE_INT)
    {
      if ((parse_int(fs, (int*)&init_val.int_val)) == PROJECTM_PARSE_ERROR)
      {
        if (PARSE_DEBUG) printf("parse_shapecode: error parsing integer!\n");
        return PROJECTM_PARSE_ERROR;
      }
    }

    /* float value */
    else if (param->type == P_TYPE_DOUBLE)
    {
      if ((parse_float(fs, (float*)&init_val.float_val)) == PROJECTM_PARSE_ERROR)
      {
        if (PARSE_DEBUG) printf("parse_shapecode: error parsing float!\n");
        return PROJECTM_PARSE_ERROR;
      }
    }

    /* Unknown value */
    else
    {
      if (PARSE_DEBUG) printf("parse_shapecode: unknown parameter type!\n");
      return PROJECTM_PARSE_ERROR;
    }

    /* Create new initial condition */
    if ((init_cond = new InitCond(param, init_val)) == nullptr)
    {
      if (PARSE_DEBUG) printf("parse_shapecode: new_init_cond failed!\n");
      return PROJECTM_FAILURE;
    }

    custom_shape->init_cond_tree.insert(std::make_pair(param->name, init_cond));
    line_mode = CUSTOM_SHAPE_SHAPECODE_LINE_MODE;

    if (PARSE_DEBUG) printf("parse_shapecode: [success]\n");
    return PROJECTM_SUCCESS;
  }

  int parse_wave_helper(std::istream& fs, MilkdropPreset* preset, int id, char* eqn_type, char* init_string)
  {
    Param* param;
    Expr* gen_expr;
    char string[MAX_TOKEN_SIZE];
    PerFrameEqn* per_frame_eqn;
    CustomWave* custom_wave;
    InitCond* init_cond;

    /* Retrieve custom wave associated with this id */
    if ((custom_wave = MilkdropPreset::find_custom_object(id, preset->customWaves)) == nullptr)
    {
      if (PARSE_DEBUG) printf("parse_wave_helper: custom wave id %d not found!\n", id);
      return PROJECTM_FAILURE;
    }

    /* per frame init equation case */
    if (!strncmp(eqn_type, WAVE_INIT_STRING, WAVE_INIT_STRING_LENGTH))
    {

      if (PARSE_DEBUG) printf("parse_wave_helper (per frame init): [begin] (LINE %d)\n", line_count);

      /* Parse the per frame init equation */
      if ((init_cond = parse_per_frame_init_eqn(fs, preset, &custom_wave->param_tree)) == nullptr)
      {
        if (PARSE_DEBUG) printf("parse_wave_helper (per frame init): equation parsing failed (LINE %d)\n", line_count);
        return PROJECTM_PARSE_ERROR;
      }

      /* Insert the equation in the per frame equation tree */
      custom_wave->per_frame_init_eqn_tree.insert(std::make_pair(init_cond->param->name, init_cond));

      line_mode = CUSTOM_WAVE_PER_FRAME_INIT_LINE_MODE;
      init_cond->evaluate(true);
      return PROJECTM_SUCCESS;

    }

    /* per frame equation case */
    if (!strncmp(eqn_type, PER_FRAME_STRING_NO_UNDERSCORE, PER_FRAME_STRING_NO_UNDERSCORE_LENGTH))
    {

      if (PARSE_DEBUG) printf("parse_wave_helper (per_frame): [start] (custom wave id = %d)\n", custom_wave->id);

      if (parseToken(fs, string) != tEq)
      {
        if (PARSE_DEBUG) printf("parse_wave (per_frame): no equal sign after string \"%s\" (LINE %d)\n", string, line_count);
        return PROJECTM_PARSE_ERROR;
      }

      /* Find the parameter associated with the string in the custom wave database */
      if ((param = ParamUtils::find<AUTO_CREATE>(string, &custom_wave->param_tree)) == nullptr)
      {
        if (PARSE_DEBUG) printf("parse_wave (per_frame): parameter \"%s\" not found or cannot be wipemalloc'ed!!\n", string);
        return PROJECTM_FAILURE;
      }


      /* Make sure parameter is writable */
      if (param->flags & P_FLAG_READONLY)
      {
        if (PARSE_DEBUG) printf("parse_wave (per_frame): parameter %s is marked as read only (LINE %d)\n", param->name.c_str(), line_count);
        return PROJECTM_FAILURE;
      }

      /* Parse right side of equation as an expression */

      current_wave = custom_wave;
      if ((gen_expr = parse_gen_expr(fs, nullptr, preset)) == nullptr)
      {
        if (PARSE_DEBUG) printf("parse_wave (per_frame): equation evaluated to null (LINE %d)\n", line_count);
        current_wave = nullptr;
        return PROJECTM_PARSE_ERROR;

      }

      current_wave = nullptr;

      if (PARSE_DEBUG) printf("parse_wave (per_frame): [finished parsing equation] (LINE %d)\n", line_count);

      /* Create a new per frame equation */
      if ((per_frame_eqn = new PerFrameEqn(custom_wave->per_frame_count++, param, gen_expr)) == nullptr)
      {
        if (PARSE_DEBUG) printf("parse_wave (per_frame): failed to create a new per frame eqn, out of memory?\n");
        Expr::delete_expr(gen_expr);
        return PROJECTM_FAILURE;
      }

      custom_wave->per_frame_eqn_tree.push_back(per_frame_eqn);
      if (PARSE_DEBUG) printf("parse_wave (per_frame): equation %d associated with custom wave %d [success]\n",
        per_frame_eqn->index, custom_wave->id);


      /* Need to add stuff to string buffer so the editor can read the equations.
         Why not make a nice little helper function for this? - here it is: */

      line_mode = CUSTOM_WAVE_PER_FRAME_LINE_MODE;
      return PROJECTM_SUCCESS;
    }


    /* per point equation case */
    if (!strncmp(eqn_type, PER_POINT_STRING, PER_POINT_STRING_LENGTH))
    {

      if (PARSE_DEBUG) printf("parse_wave_helper (per_point): per_pixel equation parsing start...(LINE %d)\n", line_count);

      /// HACK the parse_line code already parsed the per_pixel variable name. This handles that case
      /// Parser needs reworked. Don't have time for it. So this is the result.
      if (init_string)
        memcpy(string, init_string, strlen(init_string) + 1);
      else
      {
        if (parseToken(fs, string) != tEq)
        { /* parse per pixel operator  name */
          if (PARSE_DEBUG) printf("parse_wave_helper (per_point): equal operator missing after per pixel operator. Last token = \"%s\"  (LINE %d)\n", string, line_count);

          return PROJECTM_PARSE_ERROR;
        }
      }

      /* Parse right side of equation as an expression, First tell parser we are parsing a custom wave */
      current_wave = custom_wave;
      if ((gen_expr = parse_gen_expr(fs, nullptr, preset)) == nullptr)
      {
        if (PARSE_DEBUG) printf("parse_wave_helper (per_point): equation evaluated to null? (LINE %d)\n", line_count);
        current_wave = nullptr;
        return PROJECTM_PARSE_ERROR;
      }


      /* Add the per point equation */
      if (custom_wave->add_per_point_eqn(string, gen_expr) < 0)
      {
        Expr::delete_expr(gen_expr);
        current_wave = nullptr;
        return PROJECTM_PARSE_ERROR;
      }
      // This tells the parser we are no longer parsing a custom wave
      current_wave = nullptr;



      line_mode = CUSTOM_WAVE_PER_POINT_LINE_MODE;
      if (PARSE_DEBUG) printf("parse_wave_helper (per_point): [finished] (custom wave id = %d)\n", custom_wave->id);
      return PROJECTM_SUCCESS;
    }

    return PROJECTM_FAILURE;
  }

  int parse_wave(char* token, std::istream& fs, MilkdropPreset* preset)
  {
    int id;
    char* eqn_type;

    if (PARSE_DEBUG) printf("parse_wave:begin\n");

    if (token == nullptr)
      return PROJECTM_FAILURE;
    if (fs.fail())
      return PROJECTM_FAILURE;
    if (preset == nullptr)
      return PROJECTM_FAILURE;

    /* Grab custom wave id and equation type (per frame or per point) from string token */
    if (parse_wave_prefix(token, &id, &eqn_type) < 0)
    {
      if (PARSE_DEBUG) printf("parse_wave: syntax error in custom wave prefix!\n");
      return PROJECTM_FAILURE;
    }

    strncpy(last_eqn_type, eqn_type, MAX_TOKEN_SIZE);

    return parse_wave_helper(fs, preset, id, eqn_type, 0);
  }

  int parse_wavecode_prefix(char* token, int* id, char** var_string)
  {
    int len, i, j;

    if (token == nullptr)
      return PROJECTM_FAILURE;
    /*
    if (*var_string == nullptr)
      return PROJECTM_FAILURE;
    */
    if (id == nullptr)
      return PROJECTM_FAILURE;

    len = strlen(token);

    /* Move pointer passed "wavecode_" prefix */
    if (len <= WAVECODE_STRING_LENGTH)
      return PROJECTM_FAILURE;
    i = WAVECODE_STRING_LENGTH;
    j = 0;
    (*id) = 0;

    /* This loop grabs the integer id for this custom wave */
    while ((i < len) && (token[i] >= 48) && (token[i] <= 57))
    {
      if (j >= MAX_TOKEN_SIZE)
        return PROJECTM_FAILURE;

      (*id) = 10 * (*id) + (token[i] - 48);
      j++;
      i++;
    }


    if (i > (len - 2))
      return PROJECTM_FAILURE;

    *var_string = token + i + 1;

    return PROJECTM_SUCCESS;
  }

  int parse_wavecode(char* token, std::istream& fs, MilkdropPreset* preset)
  {
    char* var_string;
    InitCond* init_cond;
    CustomWave* custom_wave;
    int id;
    CValue init_val;
    Param* param;

    assert(preset);
    assert(fs);
    assert(token);

    /* token should be in the form wavecode_N_var, such as wavecode_1_samples */

    /* Get id and variable name from token string */
    if (parse_wavecode_prefix(token, &id, &var_string) < 0)
      return PROJECTM_PARSE_ERROR;

    last_custom_wave_id = id;

    if (PARSE_DEBUG) printf("parse_wavecode: wavecode id = %d, parameter = \"%s\"\n", id, var_string);

    /* Retrieve custom wave information from preset, allocating new one if necessary */
    if ((custom_wave = MilkdropPreset::find_custom_object(id, preset->customWaves)) == nullptr)
    {
      std::cerr << "parse_wavecode: failed to load (or create) custom wave (id = "
        << id << ")!\n" << std::endl;

      return PROJECTM_FAILURE;
    }

    if (PARSE_DEBUG) printf("parse_wavecode: custom wave found (id = %d)\n", custom_wave->id);

    /* Retrieve parameter from this custom waves parameter db */
    if ((param = ParamUtils::find<AUTO_CREATE>(var_string, &custom_wave->param_tree)) == nullptr)
      return PROJECTM_FAILURE;

    if (PARSE_DEBUG) printf("parse_wavecode: custom wave parameter found (name = %s)\n", param->name.c_str());

    /* integer value (boolean is an integer in C) */

    if (param->type == P_TYPE_BOOL)
    {
      int bool_test;
      if ((parse_int(fs, &bool_test)) == PROJECTM_PARSE_ERROR)
      {

        if (PARSE_DEBUG) printf("parse_wavecode: error parsing integer!\n");
        return PROJECTM_PARSE_ERROR;
      }
      init_val.bool_val = bool_test;
    }
    else if (param->type == P_TYPE_INT)
    {
      if ((parse_int(fs, (int*)&init_val.int_val)) == PROJECTM_PARSE_ERROR)
      {

        if (PARSE_DEBUG) printf("parse_wavecode: error parsing integer!\n");
        return PROJECTM_PARSE_ERROR;
      }
    }

    /* float value */
    else if (param->type == P_TYPE_DOUBLE)
    {
      if ((parse_float(fs, (float*)&init_val.float_val)) == PROJECTM_PARSE_ERROR)
      {
        if (PARSE_DEBUG) printf("parse_wavecode: error parsing float!\n");
        return PROJECTM_PARSE_ERROR;
      }
    }

    /* Unknown value */
    else
    {
      if (PARSE_DEBUG) printf("parse_wavecode: unknown parameter type!\n");
      return PROJECTM_PARSE_ERROR;
    }

    /* Create new initial condition */
    init_cond = new InitCond(param, init_val);

    if (init_cond == nullptr)
    {
      if (PARSE_DEBUG) printf("parse_wavecode: new_init_cond failed!\n");
      return PROJECTM_FAILURE;
    }

    custom_wave->init_cond_tree.insert(std::make_pair(init_cond->param->name, init_cond));

    line_mode = CUSTOM_WAVE_WAVECODE_LINE_MODE;

    if (PARSE_DEBUG) printf("parse_wavecode: [success]\n");
    return PROJECTM_SUCCESS;
  }

  int parse_preset_name(std::istream& fs, char* name)
  {
    token_t token;

    if (name == nullptr)
      return PROJECTM_FAILURE;

    if ((token = parseToken(fs, name)) != tRBr)
      return PROJECTM_PARSE_ERROR;

    return PROJECTM_SUCCESS;
  }

  bool scanForComment(std::istream& fs)
  {
    int c;
    c = fs.get();

    if (c == '/') {
      while (true)
      {
        if (!fs || fs.eof())
          return true;
        else
          c = fs.get();
        if (c == EOF)
          return true;

        if (c == '\n')
        {
          return true;
        }
      }
    }
    else {
      fs.unget();
      return false;
    }
  }

  bool wrapsToNextLine(const std::string& str)
  {
    std::size_t lastLineEndIndex = lastLinePrefix.find_last_not_of("0123456789");

    std::size_t thisLineEndIndex = str.find_last_not_of("0123456789");

    std::size_t startIndex = 0;
    if ((str.compare(startIndex, lastLineEndIndex, lastLinePrefix.c_str(), thisLineEndIndex)) == 0)
      return true;
    else
      return false;
  }

  void readStringUntil(std::istream& fs, std::string* out_buffer, bool wrapAround, const std::set<char>& skipList)
  {
    int c;

    /* Loop until a delimiter is found, or the maximum string size is found */
    while (true)
    {

      if (!fs || fs.eof())
        c = EOF;
      else
        c = fs.get();

      /* Now interpret the character */
      switch (c)
      {
      case '/':
      {
        bool commentExisted = scanForComment(fs);
        if (!commentExisted) {
          out_buffer->push_back(c);
          break;
        }
        else {
          line_count++;
          out_buffer->push_back('\n');
          return;
        }
      }
      case '\n':
        if (!out_buffer->empty() && ((*out_buffer)[out_buffer->length() - 1] == '\n'))
          return;

        line_count++;
        if (wrapAround)
        {
          std::ostringstream buffer;

          //			if (PARSE_DEBUG) std::cerr << "token wrap! line " << line_count << std::endl;
          while (c != '=')
          {

            if (!fs || fs.eof())
            {
              line_count = 1;
              line_mode = UNSET_LINE_MODE;
              //					if (PARSE_DEBUG)     std::cerr << "token wrap: end of file" << std::endl;
              return;
            }

            else {
              c = fs.get();
              if (c != '=')
                buffer << c;
            }

          }


          if (!wrapsToNextLine(buffer.str())) {
            int buf_size = (int)buffer.str().length();
            // <= to also remove equal sign parsing from stream
            for (int k = 0; k <= buf_size; k++) {
              if (fs)
                fs.unget();
              else
                abort();
            }
            return;
          }
          break;
        }
        else
          out_buffer->push_back(c);
        return;
      case EOF:
        line_count = 1;
        return;
      default:

        if (out_buffer != nullptr)
        {
          if (skipList.find(c) == skipList.end())
            out_buffer->push_back(c);

        }
      }
    }
  }

  void parse_string_block(std::istream& fs, std::string* out_string)
  {
    std::set<char> skipList;
    skipList.insert('`');
    readStringUntil(fs, out_string, false, skipList);

    //std::cout << "out_string:\n " << *out_string << "\n" << std::endl;
  }

  int parse_shape_per_frame_eqn(std::istream& fs, CustomShape* custom_shape, MilkdropPreset* preset)
  {
    Param* param;
    Expr* gen_expr;
    PerFrameEqn* per_frame_eqn;

    char string[MAX_TOKEN_SIZE];

    if (PARSE_DEBUG) printf("parse_shape (per_frame): [start] (custom shape id = %d)\n", custom_shape->id);

    if (parseToken(fs, string) != tEq)
    {
      if (PARSE_DEBUG) printf("parse_shape (per_frame): no equal sign after string \"%s\" (LINE %d)\n", string, line_count);
      return PROJECTM_PARSE_ERROR;
    }

    /* Find the parameter associated with the string in the custom shape database */
    if ((param = ParamUtils::find<AUTO_CREATE>(string, &custom_shape->param_tree)) == nullptr)
    {
      if (PARSE_DEBUG) printf("parse_shape (per_frame): parameter \"%s\" not found or cannot be wipemalloc'ed!!\n", string);
      return PROJECTM_FAILURE;
    }


    /* Make sure parameter is writable */
    if (param->flags & P_FLAG_READONLY)
    {
      if (PARSE_DEBUG) printf("parse_shape (per_frame): parameter %s is marked as read only (LINE %d)\n", param->name.c_str(), line_count);
      return PROJECTM_PARSE_ERROR;
    }

    /* Parse right side of equation as an expression */

    current_shape = custom_shape;
    if ((gen_expr = parse_gen_expr(fs, nullptr, preset)) == nullptr)
    {
      if (PARSE_DEBUG) printf("parse_shape (per_frame): equation evaluated to null (LINE %d)\n", line_count);
      current_shape = nullptr;
      return PROJECTM_PARSE_ERROR;
    }

    current_shape = nullptr;

    if (PARSE_DEBUG) printf("parse_shape (per_frame): [finished parsing equation] (LINE %d)\n", line_count);

    /* Create a new per frame equation */
    if ((per_frame_eqn = new PerFrameEqn(custom_shape->per_frame_count++, param, gen_expr)) == nullptr)
    {
      if (PARSE_DEBUG) printf("parse_shape (per_frame): failed to create a new per frame eqn, out of memory?\n");
      Expr::delete_expr(gen_expr);
      return PROJECTM_FAILURE;
    }

    custom_shape->per_frame_eqn_tree.push_back(per_frame_eqn);

    /// \idea add string buffer update for easy >> and <<

    line_mode = CUSTOM_SHAPE_PER_FRAME_LINE_MODE;
    return PROJECTM_SUCCESS;
  }

  /* Parses per pixel equations */
  int parse_per_pixel_eqn(std::istream& fs, MilkdropPreset* preset, char* init_string)
  {
    char string[MAX_TOKEN_SIZE];
    Expr* gen_expr;

    if (init_string != 0)
    {
      memcpy(string, init_string, strlen(init_string) + 1);
    }
    else
    {

      if (parseToken(fs, string) != tEq)
      { /* parse per pixel operator name */
        return PROJECTM_PARSE_ERROR;
      }
    }

    /* Parse right side of equation as an expression */
    if ((gen_expr = parse_gen_expr(fs, nullptr, preset)) == nullptr)
    {
      return PROJECTM_PARSE_ERROR;
    }

    /* Add the per pixel equation */
    if (preset->add_per_pixel_eqn(string, gen_expr) < 0)
    {
      if (PARSE_DEBUG)
      {

      }
      Expr::delete_expr(gen_expr);
      return PROJECTM_PARSE_ERROR;
    }

    return PROJECTM_SUCCESS;
  }

  int parse_shape_prefix(char* token, int* id, char** eqn_string)
  {
    int len, i, j;

    if (token == nullptr)
      return PROJECTM_FAILURE;
    if (eqn_string == nullptr)
      return PROJECTM_FAILURE;
    if (id == nullptr)
      return PROJECTM_FAILURE;

    len = strlen(token);

    if (len <= SHAPE_STRING_LENGTH)
      return PROJECTM_FAILURE;


    i = SHAPE_STRING_LENGTH;
    j = 0;
    (*id) = 0;

    /* This loop grabs the integer id for this custom wave */
    while ((i < len) && (token[i] >= 48) && (token[i] <= 57))
    {
      if (j >= MAX_TOKEN_SIZE)
        return PROJECTM_FAILURE;

      (*id) = 10 * (*id) + (token[i] - 48);
      j++;
      i++;
    }

    if (i > (len - 2))
      return PROJECTM_FAILURE;

    *eqn_string = token + i + 1;

    return PROJECTM_SUCCESS;
  }

  int parse_shape_per_frame_init_eqn(std::istream& fs, CustomShape* custom_shape, MilkdropPreset* preset)
  {
    InitCond* init_cond;

    if (PARSE_DEBUG) printf("parse_shape (per frame init): [begin] (LINE %d)\n", line_count);

    /* Parse the per frame equation */
    if ((init_cond = parse_per_frame_init_eqn(fs, preset, &custom_shape->param_tree)) == nullptr)
    {
      if (PARSE_DEBUG) printf("parse_shape (per frame init): equation parsing failed (LINE %d)\n", line_count);
      return PROJECTM_PARSE_ERROR;
    }

    /// \idea possibly a good place to update a string buffer;

    line_mode = CUSTOM_SHAPE_PER_FRAME_INIT_LINE_MODE;
    init_cond->evaluate(true);

    delete init_cond;
    return PROJECTM_SUCCESS;
  }

  int parse_shape(char* token, std::istream& fs, MilkdropPreset* preset)
  {
    int id;
    char* eqn_type;
    CustomShape* custom_shape;


    if (token == nullptr)

      return PROJECTM_FAILURE;
    if (fs.fail())
      return PROJECTM_FAILURE;
    if (preset == nullptr)
      return PROJECTM_FAILURE;

    /* Grab custom shape id and equation type (per frame or per point) from string token */
    if (parse_shape_prefix(token, &id, &eqn_type) < 0)
    {
      if (PARSE_DEBUG) printf("parse_shape: syntax error in custom shape prefix!\n");
      return PROJECTM_PARSE_ERROR;
    }

    /* Retrieve custom shape associated with this id */
    if ((custom_shape = MilkdropPreset::find_custom_object(id, preset->customShapes)) == nullptr)
      return PROJECTM_FAILURE;


    /* per frame init equation case */
    if (!strncmp(eqn_type, SHAPE_INIT_STRING, SHAPE_INIT_STRING_LENGTH))
    {
      return parse_shape_per_frame_init_eqn(fs, custom_shape, preset);
    }

    /* per frame equation case */
    if (!strncmp(eqn_type, PER_FRAME_STRING_NO_UNDERSCORE, PER_FRAME_STRING_NO_UNDERSCORE_LENGTH))
    {
      return parse_shape_per_frame_eqn(fs, custom_shape, preset);
    }


    /* Syntax error, return parse error */
    return PROJECTM_PARSE_ERROR;
  }

  PerFrameEqn* parse_implicit_per_frame_eqn(std::istream& fs, char* param_string, int index, MilkdropPreset* preset)
  {
    Param* param;
    PerFrameEqn* per_frame_eqn;
    Expr* gen_expr;

    if (fs.fail())
      return nullptr;
    if (param_string == nullptr)
      return nullptr;
    if (preset == nullptr)
      return nullptr;

    //rintf("param string: %s\n", param_string);
    /* Find the parameter associated with the string, create one if necessary */
    if ((param = ParamUtils::find(param_string, &preset->builtinParams, &preset->user_param_tree)) == nullptr)
    {
      return nullptr;
    }

    //printf("parse_implicit_per_frame_eqn: param is %s\n", param->name);

    /* Make sure parameter is writable */
    if (param->flags & P_FLAG_READONLY)
    {
      if (PARSE_DEBUG) printf("parse_implicit_per_frame_eqn: parameter %s is marked as read only (LINE %d)\n", param->name.c_str(), line_count);
      return nullptr;
    }

    /* Parse right side of equation as an expression */
    if ((gen_expr = parse_gen_expr(fs, nullptr, preset)) == nullptr)
    {
      if (PARSE_DEBUG) printf("parse_implicit_per_frame_eqn: equation evaluated to null (LINE %d)\n", line_count);
      return nullptr;
    }

    if (PARSE_DEBUG) printf("parse_implicit_per_frame_eqn: finished per frame equation evaluation (LINE %d)\n", line_count);

    /* Create a new per frame equation */
    if ((per_frame_eqn = new PerFrameEqn(index, param, gen_expr)) == nullptr)
    {
      if (PARSE_DEBUG) printf("parse_implicit_per_frame_eqn: failed to create a new per frame eqn, out of memory?\n");
      Expr::delete_expr(gen_expr);
      return nullptr;
    }

    if (PARSE_DEBUG) printf("parse_implicit_per_frame_eqn: per_frame eqn parsed succesfully\n");

    return per_frame_eqn;
  }

  int parse_wave_per_frame_eqn(std::istream& fs, CustomWave* custom_wave, MilkdropPreset* preset)
  {
    Param* param;
    Expr* gen_expr;
    PerFrameEqn* per_frame_eqn;

    char string[MAX_TOKEN_SIZE];

    if (PARSE_DEBUG) printf("parse_wave (per_frame): [start] (custom shape id = %d)\n", custom_wave->id);

    if (parseToken(fs, string) != tEq)
    {
      if (PARSE_DEBUG) printf("parse_wave (per_frame): no equal sign after string \"%s\" (LINE %d)\n", string, line_count);
      return PROJECTM_PARSE_ERROR;
    }

    /* Find the parameter associated with the string in the custom shape database */
    if ((param = ParamUtils::find<AUTO_CREATE>(string, &custom_wave->param_tree)) == nullptr)
    {
      if (PARSE_DEBUG) printf("parse_wave (per_frame): parameter \"%s\" not found or cannot be wipemalloc'ed!!\n", string);
      return PROJECTM_FAILURE;
    }


    /* Make sure parameter is writable */
    if (param->flags & P_FLAG_READONLY)
    {
      if (PARSE_DEBUG) printf("parse_wave (per_frame): parameter %s is marked as read only (LINE %d)\n", param->name.c_str(), line_count);
      return PROJECTM_FAILURE;
    }

    /* Parse right side of equation as an expression */

    current_wave = custom_wave;
    if ((gen_expr = parse_gen_expr(fs, nullptr, preset)) == nullptr)
    {
      if (PARSE_DEBUG) printf("parse_wave (per_frame): equation evaluated to null (LINE %d)\n", line_count);
      current_wave = nullptr;
      return PROJECTM_PARSE_ERROR;
    }

    current_wave = nullptr;

    if (PARSE_DEBUG) printf("parse_wave (per_frame): [finished parsing equation] (LINE %d)\n", line_count);

    /* Create a new per frame equation */
    if ((per_frame_eqn = new PerFrameEqn(custom_wave->per_frame_count++, param, gen_expr)) == nullptr)
    {
      if (PARSE_DEBUG) printf("parse_wave (per_frame): failed to create a new per frame eqn, out of memory?\n");
      Expr::delete_expr(gen_expr);
      return PROJECTM_FAILURE;
    }

    custom_wave->per_frame_eqn_tree.push_back(per_frame_eqn);
    if (PARSE_DEBUG) printf("parse_wave (per_frame): equation %d associated with custom wave %d [success]\n",
      per_frame_eqn->index, custom_wave->id);


    /* Need to add stuff to string buffer so the editor can read the equations.
       Why not make a nice little helper function for this? - here it is: */

    line_mode = CUSTOM_WAVE_PER_FRAME_LINE_MODE;
    return PROJECTM_SUCCESS;
  }

  token_t parseToken(std::istream& fs, char* string)
  {
    int c;
    int i;

    if (string != nullptr)
      memset(string, 0, MAX_TOKEN_SIZE);


    /* Loop until a delimiter is found, or the maximum string size is found */
    for (i = 0; i < MAX_TOKEN_SIZE; i++)
    {
      //c = fgetc(fs);
      if (!fs || fs.eof())
        c = EOF;
      else
        c = fs.get();

      last_token_size++;
      /* If the string line buffer is full, quit */
      if (string_line_buffer_index == (STRING_LINE_SIZE - 1)) {
        if (PARSE_DEBUG) {
          string_line_buffer[string_line_buffer_index++] = '\0';
          std::cout << "ERROR String line buffer full. Buffer: " << string_line_buffer << std::endl;
        }
        return tStringBufferFilled;
      }

      /* Otherwise add this character to the string line buffer */
      string_line_buffer[string_line_buffer_index++] = tolower(c);
      /* Now interpret the character */
      switch (c)
      {
      case '+':
        return tPlus;
      case '-':
        return tMinus;
      case '%':
        return tMod;
      case '/':

        /* check for line comment here */
        if (!fs || fs.eof())
          c = EOF;
        else
          c = fs.get();
        if (c == '/')
        {
          while (true)
          {
            if (!fs || fs.eof())
              c = EOF;
            else
              c = fs.get();
            if (c == EOF)
            {
              line_mode = UNSET_LINE_MODE;
              return tEOF;
            }
            if (c == '\n')
            {
              line_mode = UNSET_LINE_MODE;
              return tEOL;
            }
          }

        }

        /* Otherwise, just a regular division operator */
        fs.unget();
        return tDiv;
      case '*':
        return tMult;
      case '|':
        return tOr;
      case '&':
        return tAnd;
      case '(':
        return tLPr;
      case ')':
        return tRPr;
      case '[':
        return tLBr;
      case ']':
        return tRBr;
      case '=':
        return tEq;
      case '\n':
        line_count++;
        /// @note important hack implemented here to handle "wrap around tokens"
        // In particular: "per_frame_1=x=t+1.\nper_frame_2=37 + 5;" implies
        // "per_frame_1=x=t+1.37 + 5;". Thus, we have a global flag to determine
        // if "token wrap mode" is enabled. If so, we consider a token *continuing*
        // to the next line, but only attaching to the token buffer anything following
        // the first equals sign on that line.
        //
        // We can safely assume the next line must be part of the token if we allow the
        // semi colon to be a guaranteed delimiter in the grammar for all equation-based lines.
        // This IS NO LONGER assumed here. Instead, we check if the next line prefix
        // matches with the previous line prefix. If it passes a simple comparison, we wrap around.

        if (tokenWrapAroundEnabled)
        {
          std::ostringstream buffer;

          if (PARSE_DEBUG) std::cerr << "token wrap! line " << line_count << std::endl;
          while (c != '=')
          {

            if (!fs || fs.eof())
            {
              line_count = 1;
              line_mode = UNSET_LINE_MODE;
              if (PARSE_DEBUG)     std::cerr << "token wrap: end of file" << std::endl;
              return tEOF;
            }

            else {
              c = fs.get();
              if (c != '=')
                buffer << (char)c;
            }

          }
          if (PARSE_DEBUG) std::cerr << "parseToken: parsed away equal sign, line prefix is \"" << buffer.str()
            << "\"" << std::endl;
          --i;

          if (!wrapsToNextLine(buffer.str())) {
            tokenWrapAroundEnabled = false;
            int buf_size = (int)buffer.str().length();
            // <= to also remove equal sign parsing from stream
            for (int k = 0; k <= buf_size; k++) {
              if (fs)
                fs.unget();
              else {
                std::cerr << "about to abort!  buf_size was " << buf_size << std::endl;
                //	aborting kills the process, so instead of that we return a constant indicating an error, and hope that the lib bails gracefully
                //abort();
                return tStringTooLong;
              }
            }
            return tEOL;
          }


          break;
        }


        line_mode = UNSET_LINE_MODE;
        return tEOL;
      case ',':
        return tComma;
      case ';':
        tokenWrapAroundEnabled = false;
        if (PARSE_DEBUG) std::cerr << "token wrap around = false (LINE " << line_count << ")" << std::endl;
        return tSemiColon;
      case ' ': /* space, skip the character */
        i--;
        break;
      case EOF:
        line_count = 1;
        line_mode = UNSET_LINE_MODE;
        return tEOF;

      case '\r':
        i--;
        break;
      default:
        if (c == EOF)
          std::cerr << "shouldn't happen: " << c << "(LINE " << line_count << ")" << std::endl;
        string[i] = tolower(c);
        //string[i+1] = 0;
        //std::cerr << "string is \n\"" << string << "\"" << std::endl;
      }
    }

    /* String reached maximum length, return special token error */
    return tStringTooLong;
  }

  int parse_top_comment(std::istream& fs)
  {
    char string[MAX_TOKEN_SIZE];
    token_t token;

    /* Process tokens until left bracket is found */
    while ((token = parseToken(fs, string)) != tLBr)
    {
      if (token == tEOF || token == tStringBufferFilled)
        // filled up the buffer and didn't find a '[' 
        return PROJECTM_PARSE_ERROR;
    }

    /* Done, return success */
    return PROJECTM_SUCCESS;
  }

  /* Parses a per frame equation. That is, interprets a stream of data as a per frame equation */
  PerFrameEqn* parse_per_frame_eqn(std::istream& fs, int index, MilkdropPreset* preset)
  {
    char string[MAX_TOKEN_SIZE];
    Param* param;
    PerFrameEqn* per_frame_eqn;
    Expr* gen_expr;


    if (parseToken(fs, string) != tEq)
    {
      if (PARSE_DEBUG) printf("parse_per_frame_eqn: no equal sign after string \"%s\" (LINE %d)\n", string, line_count);
      return nullptr;
    }

    /* Find the parameter associated with the string, create one if necessary */
    if ((param = ParamUtils::find(string, &preset->builtinParams, &preset->user_param_tree)) == nullptr)
    {
      return nullptr;
    }
    if (PARSE_DEBUG) std::cerr << "parse_per_frame_eqn: parameter \"" << param->name << "\" retrieved (LINE" << line_count << ")" << std::endl;
    /* Make sure parameter is writable */
    if (param->flags & P_FLAG_READONLY)
    {
      if (PARSE_DEBUG) std::cerr << "parse_per_frame_eqn: parameter \"" << param->name << "\" %s is marked as read only (LINE " << line_count << ")" << std::endl;
      return nullptr;
    }

    /* Parse right side of equation as an expression */
    if ((gen_expr = parse_gen_expr(fs, nullptr, preset)) == nullptr)
    {
      if (PARSE_DEBUG) printf("parse_per_frame_eqn: equation evaluated to null (LINE %d)\n", line_count);
      return nullptr;
    }

    if (PARSE_DEBUG) printf("parse_per_frame_eqn: finished per frame equation evaluation (LINE %d)\n", line_count);

    /* Create a new per frame equation */
    if ((per_frame_eqn = new PerFrameEqn(index, param, gen_expr)) == nullptr)
    {
      if (PARSE_DEBUG) printf("parse_per_frame_eqn: failed to create a new per frame eqn, out of memory?\n");
      Expr::delete_expr(gen_expr);
      return nullptr;
    }

    if (PARSE_DEBUG) printf("parse_per_frame_eqn: per_frame eqn parsed succesfully\n");

    return per_frame_eqn;
  }

  int parse_line(std::istream& fs, MilkdropPreset* preset)
  {
    char eqn_string[MAX_TOKEN_SIZE];
    token_t token;
    InitCond* init_cond;
    PerFrameEqn* per_frame_eqn;

    /* Clear the string line buffer */
    memset(string_line_buffer, 0, STRING_LINE_SIZE);
    string_line_buffer_index = 0;

    tokenWrapAroundEnabled = false;

    token = parseToken(fs, eqn_string);
    switch (token)
    {

      /* Invalid Cases */
    case tRBr:
    case tLPr:
    case tRPr:
    case tComma:
    case tLBr:
    case tPlus:
    case tMinus:
    case tMod:
    case tMult:
    case tOr:
    case tAnd:
    case tDiv:

      if (PARSE_DEBUG) std::cerr << "parse_line: invalid token found at start of line (LINE "
        << line_count << ")" << std::endl;

      /* Invalid token found, return a parse error */
      return PROJECTM_PARSE_ERROR;


    case tEOL:  /* Empty line */
      line_mode = UNSET_LINE_MODE;
      return PROJECTM_SUCCESS;

    case tEOF: /* End of File */
      line_mode = UNSET_LINE_MODE;
      line_count = 1;
      tokenWrapAroundEnabled = false;
      return EOF;

    case tSemiColon: /* Indicates end of expression */
      tokenWrapAroundEnabled = false;
      return PROJECTM_SUCCESS;

      /* Valid Case, either an initial condition or equation should follow */
    case tEq:
      lastLinePrefix = std::string(eqn_string);
      if (PARSE_DEBUG) std::cout << "last line prefix = \"" << eqn_string << "\"" << std::endl;
      // std::cerr << "parse_line: tEQ case, fs.peek()=\'" << fs.peek() << "\'" << std::endl;
      if (!fs)
        return PROJECTM_PARSE_ERROR;

      //	char z = fs.get();
      char tmpChar;
      if ((tmpChar = fs.get()) == '\n') {
        tokenWrapAroundEnabled = false;
        return PROJECTM_PARSE_ERROR;
      }
      else if (tmpChar == '\r') {
        tokenWrapAroundEnabled = false;
        return PROJECTM_PARSE_ERROR;
      }
      else
        fs.unget();



      /* CASE: WARP CODE */
      if (!strncmp(eqn_string, WARP_STRING, WARP_STRING_LENGTH))
      {
        //std::cout << "parsing warp string block\n" << std::endl;
        parse_string_block(fs, &preset->presetOutputs().warpShader.programSource);
        return PROJECTM_SUCCESS;
      }


      /* CASE: COMPOSITE CODE */
      if (!strncmp(eqn_string, COMPOSITE_STRING, COMPOSITE_STRING_LENGTH))
      {
        //std::cout << "parsing composite string block\n" << std::endl;
        parse_string_block(fs, &preset->presetOutputs().compositeShader.programSource);
        return PROJECTM_SUCCESS;
      }

      /* CASE: PER FRAME INIT EQUATION */
      if (!strncmp(eqn_string, PER_FRAME_INIT_STRING, PER_FRAME_INIT_STRING_LENGTH))
      {
        tokenWrapAroundEnabled = true;
        //if (PARSE_DEBUG) printf("parse_line: per frame init equation found...(LINE %d)\n", line_count);

        /* Parse the per frame equation */
        if ((init_cond = parse_per_frame_init_eqn(fs, preset, nullptr)) == nullptr)
        {
          //if (PARSE_DEBUG) printf("parse_line: per frame init equation parsing failed (LINE %d)\n", line_count);
          tokenWrapAroundEnabled = false;
          return PROJECTM_PARSE_ERROR;
        }

        /* Insert the equation in the per frame equation tree */
        preset->per_frame_init_eqn_tree.insert(std::make_pair(init_cond->param->name, init_cond));

        line_mode = PER_FRAME_INIT_LINE_MODE;
        return PROJECTM_SUCCESS;
      }

      /* Per frame equation case */
      if (!strncmp(eqn_string, PER_FRAME_STRING, PER_FRAME_STRING_LENGTH))
      {
        tokenWrapAroundEnabled = true;
        /* Sometimes per frame equations are implicitly defined without the
        per_frame_ prefix. This informs the parser that one could follow */
        line_mode = PER_FRAME_LINE_MODE;

        //if (PARSE_DEBUG) printf("parse_line: per frame equation found...(LINE %d)\n", line_count);

        /* Parse the per frame equation */
        if ((per_frame_eqn = parse_per_frame_eqn(fs, ++per_frame_eqn_count, preset)) == nullptr)
        {
          if (PARSE_DEBUG) printf("parse_line: per frame equation parsing failed (LINE %d)\n", line_count);
          tokenWrapAroundEnabled = false;
          return PROJECTM_PARSE_ERROR;
        }

        /* Insert the equation in the per frame equation tree */
        preset->per_frame_eqn_tree.push_back(per_frame_eqn);

        return PROJECTM_SUCCESS;

      }

      /* Wavecode initial condition case */
      if (!strncmp(eqn_string, WAVECODE_STRING, WAVECODE_STRING_LENGTH))
      {

        line_mode = CUSTOM_WAVE_WAVECODE_LINE_MODE;

        return parse_wavecode(eqn_string, fs, preset);
      }

      /* Custom Wave Prefix */
      if ((!strncmp(eqn_string, WAVE_STRING, WAVE_STRING_LENGTH)) &&
        ((eqn_string[5] >= 48) && (eqn_string[5] <= 57)))
      {
        tokenWrapAroundEnabled = true;
        //    if (PARSE_DEBUG) printf("parse_line wave prefix found: \"%s\"\n", eqn_string);

        return parse_wave(eqn_string, fs, preset);

      }


      /* Shapecode initial condition case */
      if (!strncmp(eqn_string, SHAPECODE_STRING, SHAPECODE_STRING_LENGTH))
      {

        line_mode = CUSTOM_SHAPE_SHAPECODE_LINE_MODE;

        if (PARSE_DEBUG) printf("parse_line: shapecode prefix found: \"%s\"\n", eqn_string);

        return parse_shapecode(eqn_string, fs, preset);
      }

      /* Custom Shape Prefix */
      if ((!strncmp(eqn_string, SHAPE_STRING, SHAPE_STRING_LENGTH)) &&
        ((eqn_string[6] >= 48) && (eqn_string[6] <= 57)))
      {
        tokenWrapAroundEnabled = true;
        if (PARSE_DEBUG) printf("parse_line shape prefix found: \"%s\"\n", eqn_string);
        return parse_shape(eqn_string, fs, preset);

      }

      /* Per pixel equation case */
      if (!strncmp(eqn_string, PER_PIXEL_STRING, PER_PIXEL_STRING_LENGTH))
      {
        tokenWrapAroundEnabled = true;

        line_mode = PER_PIXEL_LINE_MODE;

        if (parse_per_pixel_eqn(fs, preset, 0) < 0)
        {
          tokenWrapAroundEnabled = false;
          return PROJECTM_PARSE_ERROR;
        }


        if (PARSE_DEBUG) printf("parse_line: finished parsing per pixel equation (LINE %d)\n", line_count);
        return PROJECTM_SUCCESS;
      }

      /* Sometimes equations are written implicitly in milkdrop files, in the form

      per_frame_1 = p1 = eqn1; p2 = eqn2; p3 = eqn3;..;

      which is analagous to:

      per_frame_1 = p1 = eqn1; per_frame_2 = p2 = eqn2; per_frame_3 = p3 = eqn3; ...;

      The following line mode hack allows such implicit declaration of the
      prefix that specifies the equation type. An alternative method
      may be to associate each equation line as list of equations separated
      by semicolons (and a new line ends the list). Instead, however, a global
      variable called "line_mode" specifies the last type of equation found,
      and bases any implicitly typed input on this fact

      Note added by Carmelo Piccione (carmelo.piccione@gmail.com) 10/19/03
      */

      /* Per frame line mode previously, try to parse the equation implicitly */
      if (line_mode == PER_FRAME_LINE_MODE)
      {
        tokenWrapAroundEnabled = true;
        if ((per_frame_eqn = parse_implicit_per_frame_eqn(fs, eqn_string, ++per_frame_eqn_count, preset)) == nullptr)
        {
          tokenWrapAroundEnabled = false;
          return PROJECTM_PARSE_ERROR;
        }

        /* Insert the equation in the per frame equation tree */
        preset->per_frame_eqn_tree.push_back(per_frame_eqn);


        return PROJECTM_SUCCESS;

      }
      else if (line_mode == PER_FRAME_INIT_LINE_MODE)
      {
        tokenWrapAroundEnabled = true;
        if (PARSE_DEBUG) printf("parse_line: parsing implicit per frame init eqn)\n");
        if ((init_cond = parse_per_frame_init_eqn(fs, preset, nullptr)) == nullptr)
        {
          tokenWrapAroundEnabled = false;
          return PROJECTM_PARSE_ERROR;
        }

        ++per_frame_init_eqn_count;

        /* Insert the equation in the per frame equation tree */
        preset->per_frame_init_eqn_tree.insert(std::make_pair(init_cond->param->name, init_cond));

        return PROJECTM_SUCCESS;
      }
      else if (line_mode == PER_PIXEL_LINE_MODE)
      {
        tokenWrapAroundEnabled = true;
        if (PARSE_DEBUG) printf("parse_line: implicit per pixel eqn (LINE %d)\n", line_count);
        return parse_per_pixel_eqn(fs, preset, eqn_string);


      }
      else if (line_mode == CUSTOM_WAVE_PER_POINT_LINE_MODE)
      {
        tokenWrapAroundEnabled = true;
        if (PARSE_DEBUG) printf("parse_line: implicit cwave ppoint eqn found (LINE %d)\n", line_count);
        //int len = strlen(eqn_string);

        if (parse_wave_helper(fs, preset, last_custom_wave_id, last_eqn_type, eqn_string) < 0)
        {
          if (PARSE_DEBUG) printf("parse_line: failed to parse an implicit custom wave per point eqn\n");
          return PROJECTM_FAILURE;
        }
        return PROJECTM_SUCCESS;
      }
      else if (line_mode == CUSTOM_WAVE_PER_FRAME_LINE_MODE)
      {
        tokenWrapAroundEnabled = true;
        //Added by PJS. I hope I did it right
        CustomWave* custom_wave;

        /* Retrieve custom shape associated with this id */
        if ((custom_wave = MilkdropPreset::find_custom_object(last_custom_wave_id, preset->customWaves)) == nullptr)
          return PROJECTM_FAILURE;
        return parse_wave_per_frame_eqn(fs, custom_wave, preset);

      }
      else if (line_mode == CUSTOM_WAVE_WAVECODE_LINE_MODE)
      {
        if (PARSE_DEBUG) printf("unsupported line mode: CUSTOM_WAVE_WAVECODE_LINE_MODE\n");
        return PROJECTM_FAILURE;
      }
      else if (line_mode == CUSTOM_SHAPE_SHAPECODE_LINE_MODE)
      {
        if (PARSE_DEBUG) printf("unsupported line mode: CUSTOM_SHAPE_SHAPECODE_LINE_MODE\n");
        return PROJECTM_FAILURE;
      }
      else if (line_mode == CUSTOM_SHAPE_PER_FRAME_LINE_MODE)
      {
        tokenWrapAroundEnabled = true;

        CustomShape* custom_shape;

        /* Retrieve custom shape associated with this id */
        if ((custom_shape = MilkdropPreset::find_custom_object(last_custom_shape_id, preset->customShapes)) == nullptr)
          return PROJECTM_FAILURE;

        return parse_shape_per_frame_eqn(fs, custom_shape, preset);

      }
      else if (line_mode == CUSTOM_SHAPE_PER_FRAME_INIT_LINE_MODE)
      {
        tokenWrapAroundEnabled = true;
        CustomShape* custom_shape;

        /* Retrieve custom shape associated with this id */
        if ((custom_shape = preset->find_custom_object(last_custom_shape_id, preset->customShapes)) == nullptr)
          return PROJECTM_FAILURE;

        return parse_shape_per_frame_init_eqn(fs, custom_shape, preset);

      }

      if (PARSE_DEBUG) printf("parse_line: found initial condition: name = \"%s\" (LINE %d)\n", eqn_string, line_count);
      /* Evaluate the initial condition */
      if ((init_cond = parse_init_cond(fs, eqn_string, preset)) == nullptr)
      {
        if (PARSE_DEBUG) printf("parse_line: failed to parse initial condition (LINE %d)\n", line_count);
        return PROJECTM_PARSE_ERROR;
      }

      /* Add equation to initial condition tree */
      preset->init_cond_tree.insert(std::make_pair(init_cond->param->name, init_cond));

      /* Finished with initial condition line */
      //    if (PARSE_DEBUG) printf("parse_line: initial condition parsed successfully\n");

      return PROJECTM_SUCCESS;

      /* END INITIAL CONDITIONING PARSING */

    default: /* an uncaught type or an error has occurred */
      if (PARSE_DEBUG) printf("parse_line: uncaught case, token val = %d\n", token);
      return PROJECTM_PARSE_ERROR;
    }

    /* Because of the default in the case statement,
       control flow should never actually reach here */
    return PROJECTM_PARSE_ERROR;
  }
}

struct PresetFactoryManager
{
  mutable std::map<std::string, PresetFactory*> _factoryMap;
  int _gx = 0;
  int _gy = 0;
  mutable std::vector<PresetFactory*> _factoryList;
  volatile bool initialized = false;

  void registerFactory(const std::string& extensions, PresetFactory* factory) {

    std::stringstream ss(extensions);
    std::string extension;

    _factoryList.push_back(factory);

    while (ss >> extension) {
      if (_factoryMap.count(extension)) {
        std::cerr << "[PresetFactoryManager] Warning: extension \"" << extension <<
          "\" already has a factory. New factory handler ignored." << std::endl;
      }
      else {
        _factoryMap.insert(std::make_pair(extension, factory));
      }
    }
  }

  void initialize(int gx, int gy)
  {
    _gx = gx;
    _gy = gy;

    if (!initialized) {
      initialized = true;
    }
    else {
      std::cout << "already initialized " << std::endl;
      return;
    }

    PresetFactory* factory;

#ifndef DISABLE_MILKDROP_PRESETS
    factory = new MilkdropPresetFactory(_gx, _gy);
    registerFactory(factory->supportedExtensions(), factory);
#endif

#ifndef DISABLE_NATIVE_PRESETS
    factory = new NativePresetFactory();
    registerFactory(factory->supportedExtensions(), factory);
#endif
  }

  bool extensionHandled(const std::string& extension) const
  {
    return _factoryMap.count(extension);
  }

  PresetFactory& factory(const std::string& extension)
  {

    if (!extensionHandled(extension)) {
      std::ostringstream os;
      os << "No preset factory associated with \"" << extension << "\"." << std::endl;
      throw PresetFactoryException(os.str());
    }
    return *_factoryMap[extension];
  }

  std::unique_ptr<Preset> allocate(const std::string& url, const std::string& name)
  {
    try {
      const std::string extension = "." + parseExtension(url);

      return factory(extension).allocate(url, name);
    }
    catch (const PresetFactoryException& e) {
      throw e;
    }
    catch (const std::exception& e) {
      throw PresetFactoryException(e.what());
    }
    catch (...) {
      throw PresetFactoryException("uncaught preset factory exception");
    }
    return std::unique_ptr<Preset>();
  }

  std::vector<std::string> extensionsHandled() const
  {
    std::vector<std::string> retval;
    for (auto const& element : _factoryMap) {
      retval.push_back(element.first);
    }
    return retval;
  }
};

typedef std::size_t PresetIndex;
struct PresetLoader
{
  std::string _dirname;
  std::vector<int> _ratingsSums;
  std::vector<std::string> _entries;
  std::vector<std::string> _presetNames;
  mutable PresetFactoryManager _presetFactoryManager;
  std::vector<RatingList> _ratings;
  FileScanner fileScanner;

  PresetLoader(int gx, int gy, std::string dirname = std::string()) :_dirname(dirname)
  {
    _presetFactoryManager.initialize(gx, gy);

    std::vector<std::string> dirs{ _dirname };
    std::vector<std::string> extensions = _presetFactoryManager.extensionsHandled();
    fileScanner = FileScanner(dirs, extensions);

    // Do one scan
    if (_dirname != std::string())
      rescan();
    else
      clear();
  }

  void clearRatingsSum() {
    _ratingsSums = std::vector<int>(TOTAL_RATING_TYPES, 0);
  }

  void clear()
  {
    _entries.clear(); _presetNames.clear();
    _ratings = std::vector<RatingList>(TOTAL_RATING_TYPES, RatingList());
    clearRatingsSum();
  }

  void addScannedPresetFile(const std::string& path, const std::string& name)
  {
    auto ext = parseExtension(path);
    if (ext.empty())
      return;

    ext = "." + ext;

    // Verify extension is projectm or milkdrop
    if (!_presetFactoryManager.extensionHandled(ext))
      return;

    //    std::cout << "Loading preset file " << path << std::endl;

    _entries.push_back(path);
    _presetNames.push_back(name + ext);
  }

  void rescan()
  {
    // std::cerr << "Rescanning..." << std::endl;

      // Clear the directory entry collection
    clear();

    // scan for presets
    using namespace std::placeholders;
    fileScanner.scan(std::bind(&PresetLoader::addScannedPresetFile, this, _1, _2));

    // Give all presets equal rating of 3 - why 3? I don't know
    _ratings = std::vector<RatingList>(TOTAL_RATING_TYPES, RatingList(_presetNames.size(), 3));
    _ratingsSums = std::vector<int>(TOTAL_RATING_TYPES, 3 * _presetNames.size());

    assert(_entries.size() == _presetNames.size());
  }

  std::unique_ptr<Preset> loadPreset(PresetIndex index) const
  {
    // Check that index isn't insane
    assert(index < _entries.size());
    return _presetFactoryManager.allocate
    (_entries[index], _presetNames[index]);
  }

  std::unique_ptr<Preset> loadPreset(const std::string& url)  const
  {
    //    std::cout << "Loading preset " << url << std::endl;
    try {
      /// @bug probably should not use url for preset name
      return _presetFactoryManager.allocate
      (url, url);
    }
    catch (const std::exception& e) {
      throw PresetFactoryException(e.what());
    }
    catch (...) {
      throw PresetFactoryException("preset factory exception of unknown cause");
    }
    return std::unique_ptr<Preset>();
  }

  std::size_t size() const
  {
    return _entries.size();
  }

  const std::vector<RatingList>& getPresetRatings() const
  {
    return _ratings;
  }

  const std::vector<int>& getPresetRatingsSums() const
  {
    return _ratingsSums;
  }
};

struct PresetChooser;
struct PresetIterator
{
  const PresetChooser* _presetChooser;
  std::size_t _currentIndex;

  PresetIterator() {}

  PresetIterator(std::size_t start) : _currentIndex(start)
  {}

  void setChooser(const PresetChooser& chooser)
  {
    _presetChooser = &chooser;
  }

  std::size_t lastIndex() const
  {
    return _currentIndex;
  }

  std::unique_ptr<Preset> allocate();
};

struct PresetChooser
{
  typedef PresetIterator iterator;

  const PresetLoader* _presetLoader;
  bool _softCutRatingsEnabled;

  PresetChooser(const PresetLoader& presetLoader, bool softCutRatingsEnabled) : _presetLoader(&presetLoader), _softCutRatingsEnabled(softCutRatingsEnabled)
  {}

  bool empty() const
  {
    return _presetLoader->size() == 0;
  }

  PresetIterator begin(unsigned int index) const
  {
    PresetIterator pos(index);
    pos.setChooser(*this);
    return pos;
  }

  PresetIterator end() const
  {
    PresetIterator pos(_presetLoader->size());
    pos.setChooser(*this);
    return pos;
  }

  std::unique_ptr<Preset> directoryIndex(std::size_t index) const
  {
    return _presetLoader->loadPreset(index);
  }

  PresetChooser::iterator weightedRandom(bool hardCut) const
  {
    // TODO make a sophisticated function object interface to determine why a certain rating
    // category is chosen, or weighted distribution thereover.
    const PresetRatingType ratingType = hardCut || (!_softCutRatingsEnabled) ?
      HARD_CUT_RATING_TYPE : SOFT_CUT_RATING_TYPE;

    const std::size_t ratingsTypeIndex = static_cast<std::size_t>(ratingType);

    const std::vector<int>& weights = _presetLoader->getPresetRatings()[ratingsTypeIndex];

    const std::size_t index = RandomNumberGenerators::weightedRandom
    (weights,
      _presetLoader->getPresetRatingsSums()[ratingsTypeIndex]);

    return begin(index);
  }
};

std::unique_ptr<Preset> PresetIterator::allocate()
{
  return _presetChooser->directoryIndex(_currentIndex);
}

struct TypeIdPair
{
  TypeIdPair(const std::type_info& info1, const std::type_info& info2) : id1(info1.name()), id2(info2.name()) {}
  TypeIdPair(const std::string& _id1, const std::string& _id2) : id1(_id1), id2(_id2) {}
  std::string id1;
  std::string id2;
  inline bool operator<(const TypeIdPair& rhs) const {
    return this->id1 < rhs.id1 || (this->id1 == rhs.id1 && this->id2 < rhs.id2);
  }

  inline bool operator>(const TypeIdPair& rhs) const {
    return !operator<(rhs) && !operator==(rhs);
  }

  inline bool operator==(const TypeIdPair& rhs) const {
    return this->id1 == rhs.id1 && this->id2 == rhs.id2;
  }
};

struct RenderItemDistanceMetric
{
  virtual TypeIdPair typeIdPair() const = 0;
};

struct MasterRenderItemDistance
{
  typedef std::map<TypeIdPair, RenderItemDistanceMetric*> DistanceMetricMap;

  DistanceMetricMap _distanceMetricMap;

  void addMetric(RenderItemDistanceMetric* fun)
  {
    _distanceMetricMap[fun->typeIdPair()] = fun;
  }
};

struct RenderItemMatcher
{
  struct MatchResults
  {
  };


  MasterRenderItemDistance _distanceFunction;
  mutable MatchResults _results;

  MasterRenderItemDistance& distanceFunction() { return _distanceFunction; }

  MatchResults& matchResults() { return _results; }
};



struct RenderItemMergeFunction
{
  virtual TypeIdPair typeIdPair() const = 0;
};

struct PipelineMerger
{
  template <class T> inline static T lerp(T a, T b, float ratio)
  {
    return a * ratio + b * (1 - ratio);
  }

  static void mergePipelines(const Pipeline& a, const Pipeline& b, Pipeline& out, RenderItemMatcher::MatchResults& results, RenderItemMergeFunction& mergeFunction, float ratio)
  {
    const double invratio = 1.0 - ratio;

    out.textureWrap = (ratio < 0.5) ? a.textureWrap : b.textureWrap;

    out.screenDecay = lerp(b.screenDecay, a.screenDecay, ratio);

    out.drawables.clear();
    out.compositeDrawables.clear();

    for (std::vector<RenderItem*>::const_iterator pos = a.drawables.begin();
      pos != a.drawables.end(); ++pos)
    {
      (*pos)->masterAlpha = invratio;
      out.drawables.push_back(*pos);
    }

    for (std::vector<RenderItem*>::const_iterator pos = b.drawables.begin();
      pos != b.drawables.end(); ++pos)
    {
      (*pos)->masterAlpha = ratio;
      out.drawables.push_back(*pos);
    }

    if (ratio < 0.5)
    {
      const float local_ratio = (invratio - 0.5f) * 2.0f;

      for (std::vector<RenderItem*>::const_iterator pos = a.compositeDrawables.begin();
        pos != a.compositeDrawables.end(); ++pos)
      {
        (*pos)->masterAlpha = local_ratio;
        out.compositeDrawables.push_back(*pos);
      }
    }
    else
    {
      const float local_ratio = (ratio - 0.5f) * 2.0f;

      for (std::vector<RenderItem*>::const_iterator pos = b.compositeDrawables.begin();
        pos != b.compositeDrawables.end(); ++pos)
      {
        (*pos)->masterAlpha = local_ratio;
        out.compositeDrawables.push_back(*pos);
      }
    }

    /*
    for (RenderItemMatchList::iterator pos = results.matches.begin(); pos != results.matches.end(); ++pos) {

      RenderItem * itemA = pos->first;
      RenderItem * itemB = pos->second;

      RenderItem * itemC = mergeFunction(itemA, itemB, ratio);

      if (itemC == 0) {
        itemA->masterAlpha = ratio;
              out.drawables.push_back(itemA);
        itemB->masterAlpha = invratio;
              out.drawables.push_back(itemB);
      } else
        out.drawables.push_back(itemC);

    }


    for (std::vector<RenderItem*>::const_iterator pos = results.unmatchedLeft.begin();
      pos != results.unmatchedLeft.end(); ++pos)
        {
           (*pos)->masterAlpha = invratio;
           out.drawables.push_back(*pos);
        }

    for (std::vector<RenderItem*>::const_iterator pos = results.unmatchedRight.begin();
      pos != results.unmatchedRight.end(); ++pos)
      {
        (*pos)->masterAlpha = ratio;
         out.drawables.push_back(*pos);
      }
    */

    if (a.staticPerPixel && b.staticPerPixel)
    {
      out.staticPerPixel = true;
      for (int x = 0; x < a.gx; x++)
      {
        for (int y = 0; y < a.gy; y++)
        {
          out.x_mesh[x][y] = a.x_mesh[x][y] * invratio + b.x_mesh[x][y] * ratio;
        }
      }
      for (int x = 0; x < a.gx; x++)
      {
        for (int y = 0; y < a.gy; y++)
        {
          out.y_mesh[x][y] = a.y_mesh[x][y] * invratio + b.y_mesh[x][y] * ratio;
        }
      }
    }

    if (ratio < 0.5)
    {
      out.compositeShader = a.compositeShader;
      out.warpShader = a.warpShader;
      out.warpShaderFilename = a.warpShaderFilename;
      out.compositeShaderFilename = a.compositeShaderFilename;
    }
    else
    {
      out.compositeShader = b.compositeShader;
      out.warpShader = b.warpShader;
      out.warpShaderFilename = b.warpShaderFilename;
      out.compositeShaderFilename = b.compositeShaderFilename;
    }
  }
};



template <class R1, class R2 = R1, class R3 = R2>
struct RenderItemMerge : public RenderItemMergeFunction
{
  TypeIdPair typeIdPair() const {
    return TypeIdPair(typeid(const R1*).name(), typeid(const R2*).name());
  }
};

struct MasterRenderItemMerge : public RenderItemMerge<RenderItem>
{
  typedef std::map<TypeIdPair, RenderItemMergeFunction*> MergeFunctionMap;

  MergeFunctionMap _mergeFunctionMap;

  inline void add(RenderItemMergeFunction* fun) {
    _mergeFunctionMap[fun->typeIdPair()] = fun;
  }
};

struct ShapeMerge : public RenderItemMerge<Shape>
{

};

struct BorderMerge : public RenderItemMerge<Border>
{

};

template <class R1, class R2>
struct RenderItemDistance : public RenderItemDistanceMetric
{
  TypeIdPair typeIdPair() const override
  {
    return TypeIdPair(typeid(const R1*).name(), typeid(const R2*).name());
  }
};

struct ShapeXYDistance : public RenderItemDistance<Shape, Shape>
{
};

static const int FLAG_NONE = 0;
static const int FLAG_DISABLE_PLAYLIST_LOAD = 1 << 0;

struct projectM
{
  struct Settings
  {
    int meshX = 32;
    int meshY = 24;
    int fps = 35;
    int textureSize = 512;
    int windowWidth = 512;
    int windowHeight = 512;
    std::string presetURL;
    std::string titleFontURL;
    std::string menuFontURL;
    std::string datadir;
    int smoothPresetDuration = 10;
    int presetDuration = 15;
    bool hardcutEnabled = false;
    int hardcutDuration = 60;
    float hardcutSensitivity = 2.0f;
    float beatSensitivity = 1.0f;
    bool aspectCorrection = true;
    float easterEgg = 0.0f;
    bool shuffleEnabled = true;
    bool softCutRatingsEnabled = false;
  };

  Renderer_* renderer;
  PCM* _pcm;
  BeatDetect* beatDetect;
  PipelineContext* _pipelineContext;
  PipelineContext* _pipelineContext2;
  Settings _settings;
  int mspf;
  int timed;
  int timestart;
  int count;
  float fpsstart;
  PresetIterator* m_presetPos;
  PresetLoader* m_presetLoader;
  PresetChooser* m_presetChooser;
  std::unique_ptr<Preset> m_activePreset;
  std::unique_ptr<Preset> m_activePreset2;
  TimeKeeper* timeKeeper;
  int m_flags;
  RenderItemMatcher* _matcher;
  std::vector<int> presetHistory;
  std::vector<int> presetFuture;
  MasterRenderItemMerge* _merger;
  bool errorLoadingCurrentPreset;



  projectM(Settings settings, int flags = FLAG_NONE) :
    renderer(0), _pcm(0), beatDetect(0), _pipelineContext(new PipelineContext()), _pipelineContext2(new PipelineContext()), m_presetPos(0),
    timeKeeper(nullptr), m_flags(flags), _matcher(nullptr), _merger(nullptr)
  {
    readSettings(settings);
    projectM_reset();
    projectM_resetGL(_settings.windowWidth, _settings.windowHeight);
  }

  void projectM_resetengine()
  {

    if (beatDetect != nullptr)
    {
      beatDetect->reset();
      beatDetect->beatSensitivity = _settings.beatSensitivity;
    }

  }

  void projectM_reset()
  {
    this->mspf = 0;
    this->timed = 0;
    this->timestart = 0;
    this->count = 0;

    this->fpsstart = 0;

    projectM_resetengine();
  }

  int initPresetTools(int gx, int gy)
  {

    /* Set the seed to the current time in seconds */
    srand(time(nullptr));

    std::string url = (m_flags & FLAG_DISABLE_PLAYLIST_LOAD) ? std::string() : settings().presetURL;

    if ((m_presetLoader = new PresetLoader(gx, gy, url)) == 0)
    {
      m_presetLoader = 0;
      std::cerr << "[projectM] error allocating preset loader" << std::endl;
      return PROJECTM_FAILURE;
    }

    if ((m_presetChooser = new PresetChooser(*m_presetLoader, settings().softCutRatingsEnabled)) == 0)
    {
      delete (m_presetLoader);

      m_presetChooser = 0;
      m_presetLoader = 0;

      std::cerr << "[projectM] error allocating preset chooser" << std::endl;
      return PROJECTM_FAILURE;
    }

    // Start the iterator
    if (!m_presetPos)
      m_presetPos = new PresetIterator();

    // Initialize a preset queue position as well
    //	m_presetQueuePos = new PresetIterator();

    // Start at end ptr- this allows next/previous to easily be done from this position.
    *m_presetPos = m_presetChooser->end();

    // Load idle preset
//        std::cerr << "[projectM] Allocating idle preset..." << std::endl;
    m_activePreset = m_presetLoader->loadPreset
    ("idle://Geiss & Sperl - Feedback (projectM idle HDR mix).milk");
    renderer->setPresetName("Geiss & Sperl - Feedback (projectM idle HDR mix)");

    //populatePresetMenu();

    renderer->SetPipeline(m_activePreset->pipeline());

    // Case where no valid presets exist in directory. Could also mean
    // playlist initialization was deferred
    if (m_presetChooser->empty())
    {
      //std::cerr << "[projectM] warning: no valid files found in preset directory \""
      ///< m_presetLoader->directoryName() << "\"" << std::endl;
    }

    _matcher = new RenderItemMatcher();
    _merger = new MasterRenderItemMerge();
    //_merger->add(new WaveFormMergeFunction());
    _merger->add(new ShapeMerge());
    _merger->add(new BorderMerge());
    //_merger->add(new BorderMergeFunction());

    /// @bug These should be requested by the preset factories.
    _matcher->distanceFunction().addMetric(new ShapeXYDistance());

    //std::cerr << "[projectM] Idle preset allocated." << std::endl;

    projectM_resetengine();

    //std::cerr << "[projectM] engine has been reset." << std::endl;
    return PROJECTM_SUCCESS;
  }

  void projectM_init(int gx, int gy, int fps, int texsize, int width, int height)
  {
    /** Initialise start time */
    timeKeeper = new TimeKeeper(_settings.presetDuration, _settings.smoothPresetDuration, _settings.hardcutDuration, _settings.easterEgg);

    /** Nullify frame stash */

    /** Initialise per-pixel matrix calculations */
    /** We need to initialise this before the builtin param db otherwise bass/mid etc won't bind correctly */
    assert(!beatDetect);

    if (!_pcm)
      _pcm = new PCM();
    assert(pcm());
    beatDetect = new BeatDetect(_pcm);

    if (_settings.fps > 0)
      mspf = (int)(1000.0 / (float)_settings.fps);
    else mspf = 0;

    this->renderer = new Renderer_(width, height, gx, gy, beatDetect, settings().presetURL, settings().titleFontURL, settings().menuFontURL, settings().datadir);

    initPresetTools(gx, gy);


#ifdef USE_THREADS

#ifdef SYNC_PRESET_SWITCHES
    pthread_mutex_init(&preset_mutex, nullptr);
#endif

    worker_sync.reset();
    if (pthread_create(&thread, nullptr, thread_callback, this) != 0)
    {

      std::cerr << "[projectM] failed to allocate a thread! try building with option USE_THREADS turned off" << std::endl;
      exit(EXIT_FAILURE);
    }
#endif

    /// @bug order of operatoins here is busted
    //renderer->setPresetName ( m_activePreset->name() );
    timeKeeper->StartPreset();
    assert(pcm());

    pipelineContext().fps = fps;
    pipelineContext2().fps = fps;

  }

  void readSettings(const Settings& settings)
  {
    _settings.meshX = settings.meshX;
    _settings.meshY = settings.meshY;
    _settings.textureSize = settings.textureSize;
    _settings.fps = settings.fps;
    _settings.windowWidth = settings.windowWidth;
    _settings.windowHeight = settings.windowHeight;
    _settings.smoothPresetDuration = settings.smoothPresetDuration;
    _settings.presetDuration = settings.presetDuration;
    _settings.softCutRatingsEnabled = settings.softCutRatingsEnabled;

    _settings.presetURL = settings.presetURL;
    _settings.titleFontURL = settings.titleFontURL;
    _settings.menuFontURL = settings.menuFontURL;
    _settings.shuffleEnabled = settings.shuffleEnabled;
    _settings.datadir = settings.datadir;

    _settings.easterEgg = settings.easterEgg;

    _settings.hardcutEnabled = settings.hardcutEnabled;
    _settings.hardcutDuration = settings.hardcutDuration;
    _settings.hardcutSensitivity = settings.hardcutSensitivity;

    _settings.beatSensitivity = settings.beatSensitivity;

    projectM_init(_settings.meshX, _settings.meshY, _settings.fps,
      _settings.textureSize, _settings.windowWidth, _settings.windowHeight);


    _settings.aspectCorrection = settings.aspectCorrection;
  }

  PCM* pcm() {
    return _pcm;
  }

  void changePresetDuration(int seconds) {
    timeKeeper->ChangePresetDuration(seconds);
  }

  void projectM_resetGL(int w, int h)
  {
    /** sanity check **/
    assert(w > 0);
    assert(h > 0);

    /** Stash the new dimensions */
    _settings.windowWidth = w;
    _settings.windowHeight = h;
    renderer->reset(w, h);
  }

  const Settings& settings() const {
    return _settings;
  }

  PipelineContext& pipelineContext()
  {
    return *_pipelineContext;
  }

  PipelineContext& pipelineContext2()
  {
    return *_pipelineContext2;
  }

  std::unique_ptr<Preset> switchToCurrentPreset()
  {
    std::unique_ptr<Preset> new_preset;
#ifdef SYNC_PRESET_SWITCHES
    pthread_mutex_lock(&preset_mutex);
#endif
    try {
      new_preset = m_presetPos->allocate();
    }
    catch (const PresetFactoryException& e) {
      std::cerr << "problem allocating target preset: " << e.message()
        << std::endl;
    }

    if (new_preset == nullptr) {
#ifdef SYNC_PRESET_SWITCHES
      pthread_mutex_unlock(&preset_mutex);
#endif
      std::cerr << "Could not switch to current preset" << std::endl;
      return nullptr;
    }

    // Set preset name here- event is not done because at the moment this function
    // is oblivious to smooth/hard switches
    renderer->setPresetName(new_preset->name());
    std::string result = renderer->SetPipeline(new_preset->pipeline());
    if (!result.empty()) {
      std::cerr << "problem setting pipeline: " << result << std::endl;
    }

#ifdef SYNC_PRESET_SWITCHES
    pthread_mutex_unlock(&preset_mutex);
#endif
    return new_preset;
  }

  bool startPresetTransition(bool hard_cut)
  {
    std::unique_ptr<Preset> new_preset = switchToCurrentPreset();
    if (new_preset == nullptr) {
      //presetSwitchFailedEvent(hard_cut, **m_presetPos, "fake error");
      errorLoadingCurrentPreset = true;
      //populatePresetMenu();
      return false;
    }

    if (hard_cut) {
      m_activePreset = std::move(new_preset);
      timeKeeper->StartPreset();
    }
    else {
      m_activePreset2 = std::move(new_preset);
      timeKeeper->StartPreset();
      timeKeeper->StartSmoothing();
    }

    //presetSwitchedEvent(hard_cut, **m_presetPos);
    errorLoadingCurrentPreset = false;

    //populatePresetMenu();

    return true;
  }

  void selectRandom(const bool hardCut)
  {
    //if (m_presetChooser->empty())
    //  return;
    //presetHistory.push_back(m_presetPos->lastIndex());

    //for (int i = 0; i < kMaxSwitchRetries; ++i) {
    //  *m_presetPos = m_presetChooser->weightedRandom(hardCut);
    //  if (startPresetTransition(hardCut)) {
    //    break;
    //  }
    //}
    //// If presetHistory is tracking more than 10, then delete the oldest entry so we cap to a history of 10.
    //if (presetHistory.size() >= 10)
    //  presetHistory.erase(presetHistory.begin());
    //presetFuture.clear();
  }

  void evaluateSecondPreset()
  {
    pipelineContext2().time = timeKeeper->GetRunningTime();
    pipelineContext2().presetStartTime = timeKeeper->PresetTimeB();
    pipelineContext2().frame = timeKeeper->PresetFrameB();
    pipelineContext2().progress = timeKeeper->PresetProgressB();

    m_activePreset2->Render(*beatDetect, pipelineContext2());
  }

  Pipeline* renderFrameOnlyPass1(Pipeline* pPipeline) /*pPipeline is a pointer to a Pipeline for use in pass 2. returns the pointer if it was used, else returns nullptr */
  {
#ifdef SYNC_PRESET_SWITCHES
    pthread_mutex_lock(&preset_mutex);
#endif

    timeKeeper->UpdateTimers();
    /*
        if (timeKeeper->IsSmoothing())
        {
            printf("Smoothing A:%f, B:%f, S:%f\n", timeKeeper->PresetProgressA(), timeKeeper->PresetProgressB(), timeKeeper->SmoothRatio());
        }
        else
        {
            printf("          A:%f\n", timeKeeper->PresetProgressA());
        }*/

    mspf = (int)(1000.0 / (float)settings().fps); //milliseconds per frame

    /// @bug who is responsible for updating this now?"
    pipelineContext().time = timeKeeper->GetRunningTime();
    pipelineContext().presetStartTime = timeKeeper->PresetTimeA();
    pipelineContext().frame = timeKeeper->PresetFrameA();
    pipelineContext().progress = timeKeeper->PresetProgressA();

    beatDetect->detectFromSamples();

    //m_activePreset->evaluateFrame();

    //if the preset isn't locked and there are more presets
    if (renderer->noSwitch == false && !m_presetChooser->empty())
    {
      //if preset is done and we're not already switching
      if (timeKeeper->PresetProgressA() >= 1.0 && !timeKeeper->IsSmoothing())
      {
        selectRandom(false);
      }
      else if (settings().hardcutEnabled && (beatDetect->vol - beatDetect->vol_old > settings().hardcutSensitivity) && timeKeeper->CanHardCut())
      {
        selectRandom(true);
      }
    }

    if (timeKeeper->IsSmoothing() && timeKeeper->SmoothRatio() <= 1.0 && !m_presetChooser->empty())
    {
      //	 printf("start thread\n");
      assert(m_activePreset2.get());

#ifdef USE_THREADS
      worker_sync.wake_up_bg();
#endif

      m_activePreset->Render(*beatDetect, pipelineContext());

#ifdef USE_THREADS
      worker_sync.wait_for_bg_to_finish();
#else
      evaluateSecondPreset();
#endif


      pPipeline->setStaticPerPixel(settings().meshX, settings().meshY);

      assert(_matcher);
      PipelineMerger::mergePipelines(m_activePreset->pipeline(),
        m_activePreset2->pipeline(), *pPipeline,
        _matcher->matchResults(),
        *_merger, timeKeeper->SmoothRatio());

      renderer->RenderFrameOnlyPass1(*pPipeline, pipelineContext());


      return pPipeline;

    }
    else
    {


      if (timeKeeper->IsSmoothing() && timeKeeper->SmoothRatio() > 1.0)
      {
        //printf("End Smooth\n");
        m_activePreset = std::move(m_activePreset2);
        timeKeeper->EndSmoothing();
      }
      //printf("Normal\n");

      m_activePreset->Render(*beatDetect, pipelineContext());
      renderer->RenderFrameOnlyPass1(m_activePreset->pipeline(), pipelineContext());
      return nullptr; // indicating no transition

    }

    //	std::cout<< m_activePreset->absoluteFilePath()<<std::endl;
    //	renderer->presetName = m_activePreset->absoluteFilePath();
  }

  void renderFrameOnlyPass2(Pipeline* pPipeline, int xoffset, int yoffset, int eye) /*pPipeline can be null if we re not in transition */
  {
    if (pPipeline)
      //    if ( timeKeeper->IsSmoothing() && timeKeeper->SmoothRatio() <= 1.0 && !m_presetChooser->empty() )
    {
      //	 printf("start thread\n");
      assert(m_activePreset2.get());


      /* was other stuff */

      renderer->RenderFrameOnlyPass2(*pPipeline, pipelineContext(), xoffset, yoffset, eye);

    }
    else
    {
      renderer->RenderFrameOnlyPass2(m_activePreset->pipeline(), pipelineContext(), xoffset, yoffset, eye);
    }
  }

  void selectPreset(unsigned int index, bool hardCut)
  {
    if (m_presetChooser->empty())
      return;

    *m_presetPos = m_presetChooser->begin(index);
    if (!startPresetTransition(hardCut)) {
      selectRandom(hardCut);
    }
  }

  void renderFrameEndOnSeparatePasses(Pipeline* pPipeline)
  {

    if (pPipeline) {
      // mergePipelines() sets masterAlpha for each RenderItem, reset it before we forget
      for (RenderItem* drawable : pPipeline->drawables) {
        drawable->masterAlpha = 1.0;
      }
      pPipeline->drawables.clear();
    }

    count++;
    //#ifndef WIN32
    //    /** Frame-rate limiter */
    //    /** Compute once per preset */
    //    if (this->count % 100 == 0)
    //    {
    //      this->renderer->realfps = 100.0 / ((getTicks(&timeKeeper->startTime) - this->fpsstart) / 1000);
    //      this->fpsstart = getTicks(&timeKeeper->startTime);
    //    }
    //
    //#ifndef UNLOCK_FPS
    //    int timediff = getTicks(&timeKeeper->startTime) - this->timestart;
    //
    //    if (timediff < this->mspf)
    //    {
    //      // printf("%s:",this->mspf-timediff);
    //      int sleepTime = (unsigned int)(this->mspf - timediff) * 1000;
    //      //		DWRITE ( "usleep: %d\n", sleepTime );
    //      if (sleepTime > 0 && sleepTime < 100000)
    //      {
    //        if (usleep(sleepTime) != 0) {}
    //      }
    //    }
    //    this->timestart = getTicks(&timeKeeper->startTime);
    //#endif
    //
    //#endif /** !WIN32 */
#ifdef SYNC_PRESET_SWITCHES
    pthread_mutex_unlock(&preset_mutex);
#endif
    return;
  }

  void renderFrame()
  {
    Pipeline pipeline;
    Pipeline* comboPipeline;

    comboPipeline = renderFrameOnlyPass1(&pipeline);

    renderFrameOnlyPass2(comboPipeline, 0, 0, 0);

    projectM::renderFrameEndOnSeparatePasses(comboPipeline);
  }
};

CustomShape::CustomShape(int _id) : Shape()
{

  Param* param;

  this->id = _id;
  this->per_frame_count = 0;

  /* Start: Load custom shape parameters */
  param = Param::new_param_float("r", P_FLAG_NONE, &this->r, NULL, 1.0, 0.0, 0.5);
  if (!ParamUtils::insert(param, &this->param_tree))
  {
    abort();
  }
  param = Param::new_param_float("g", P_FLAG_NONE, &this->g, NULL, 1.0, 0.0, .5);
  if (!ParamUtils::insert(param, &this->param_tree))
  {
    abort();
  }
  param = Param::new_param_float("b", P_FLAG_NONE, &this->b, NULL, 1.0, 0.0, .5);
  if (!ParamUtils::insert(param, &this->param_tree))
  {
    abort();
  }
  param = Param::new_param_float("a", P_FLAG_NONE, &this->a, NULL, 1.0, 0.0, .5);
  if (!ParamUtils::insert(param, &this->param_tree))
  {
    abort();
  }
  param = Param::new_param_float("border_r", P_FLAG_NONE, &this->border_r, NULL, 1.0, 0.0, .5);
  if (!ParamUtils::insert(param, &this->param_tree))
  {
    abort();
  }
  param = Param::new_param_float("border_g", P_FLAG_NONE, &this->border_g, NULL, 1.0, 0.0, .5);
  if (!ParamUtils::insert(param, &this->param_tree))
  {
    abort();
  }
  param = Param::new_param_float("border_b", P_FLAG_NONE, &this->border_b, NULL, 1.0, 0.0, .5);
  if (!ParamUtils::insert(param, &this->param_tree))
  {
    abort();
  }
  param = Param::new_param_float("border_a", P_FLAG_NONE, &this->border_a, NULL, 1.0, 0.0, .5);
  if (!ParamUtils::insert(param, &this->param_tree))
  {
    abort();
  }
  param = Param::new_param_float("r2", P_FLAG_NONE, &this->r2, NULL, 1.0, 0.0, .5);
  if (!ParamUtils::insert(param, &this->param_tree))
  {
    abort();
  }
  param = Param::new_param_float("g2", P_FLAG_NONE, &this->g2, NULL, 1.0, 0.0, .5);
  if (!ParamUtils::insert(param, &this->param_tree))
  {
    abort();
  }
  param = Param::new_param_float("b2", P_FLAG_NONE, &this->b2, NULL, 1.0, 0.0, .5);
  if (!ParamUtils::insert(param, &this->param_tree))
  {
    abort();
  }
  param = Param::new_param_float("a2", P_FLAG_NONE, &this->a2, NULL, 1.0, 0.0, .5);
  if (!ParamUtils::insert(param, &this->param_tree))
  {
    abort();
  }
  param = Param::new_param_float("x", P_FLAG_NONE, &this->x, NULL, 1.0, 0.0, .5);
  if (!ParamUtils::insert(param, &this->param_tree))
  {
    abort();
  }
  param = Param::new_param_float("y", P_FLAG_NONE, &this->y, NULL, 1.0, 0.0, .5);
  if (!ParamUtils::insert(param, &this->param_tree))
  {
    abort();
  }
  param = Param::new_param_bool("thickoutline", P_FLAG_NONE, &this->thickOutline, 1, 0, 0);
  if (!ParamUtils::insert(param, &this->param_tree))
  {
    abort();
  }
  param = Param::new_param_bool("enabled", P_FLAG_NONE, &this->enabled, 1, 0, 0);
  if (!ParamUtils::insert(param, &this->param_tree))
  {
    abort();
  }
  param = Param::new_param_int("sides", P_FLAG_NONE, &this->sides, 100, 3, 3);
  if (!ParamUtils::insert(param, &this->param_tree))
  {
    abort();
  }
  param = Param::new_param_bool("additive", P_FLAG_NONE, &this->additive, 1, 0, 0);
  if (!ParamUtils::insert(param, &this->param_tree))
  {
    abort();
  }
  param = Param::new_param_bool("textured", P_FLAG_NONE, &this->textured, 1, 0, 0);
  if (!ParamUtils::insert(param, &this->param_tree))
  {
    abort();
  }
  param = Param::new_param_float("rad", P_FLAG_NONE, &this->radius, NULL, MAX_DOUBLE_SIZE, 0, 0.0);
  if (!ParamUtils::insert(param, &this->param_tree))
  {
    abort();
  }
  param = Param::new_param_float("ang", P_FLAG_NONE, &this->ang, NULL, MAX_DOUBLE_SIZE, -MAX_DOUBLE_SIZE, 0.0);
  if (!ParamUtils::insert(param, &this->param_tree))
  {
    abort();
  }
  param = Param::new_param_float("tex_zoom", P_FLAG_NONE, &this->tex_zoom, NULL, MAX_DOUBLE_SIZE, .00000000001, 0.0);
  if (!ParamUtils::insert(param, &this->param_tree))
  {
    abort();
  }
  param = Param::new_param_float("tex_ang", P_FLAG_NONE, &this->tex_ang, NULL, MAX_DOUBLE_SIZE, -MAX_DOUBLE_SIZE, 0.0);
  if (!ParamUtils::insert(param, &this->param_tree))
  {
    abort();
  }

  param = Param::new_param_float("t1", P_FLAG_TVAR, &this->t1, NULL, MAX_DOUBLE_SIZE, -MAX_DOUBLE_SIZE, 0.0);
  if (!ParamUtils::insert(param, &this->param_tree))
  {
    abort();
  }
  param = Param::new_param_float("t2", P_FLAG_TVAR, &this->t2, NULL, MAX_DOUBLE_SIZE, -MAX_DOUBLE_SIZE, 0.0);
  if (!ParamUtils::insert(param, &this->param_tree))
  {
    abort();
  }
  param = Param::new_param_float("t3", P_FLAG_TVAR, &this->t3, NULL, MAX_DOUBLE_SIZE, -MAX_DOUBLE_SIZE, 0.0);
  if (!ParamUtils::insert(param, &this->param_tree))
  {
    abort();
  }
  param = Param::new_param_float("t4", P_FLAG_TVAR, &this->t4, NULL, MAX_DOUBLE_SIZE, -MAX_DOUBLE_SIZE, 0.0);
  if (!ParamUtils::insert(param, &this->param_tree))
  {
    abort();
  }
  param = Param::new_param_float("t5", P_FLAG_TVAR, &this->t5, NULL, MAX_DOUBLE_SIZE, -MAX_DOUBLE_SIZE, 0.0);
  if (!ParamUtils::insert(param, &this->param_tree))
  {
    abort();
  }
  param = Param::new_param_float("t6", P_FLAG_TVAR, &this->t6, NULL, MAX_DOUBLE_SIZE, -MAX_DOUBLE_SIZE, 0.0);
  if (!ParamUtils::insert(param, &this->param_tree))
  {
    abort();
  }
  param = Param::new_param_float("t7", P_FLAG_TVAR, &this->t7, NULL, MAX_DOUBLE_SIZE, -MAX_DOUBLE_SIZE, 0.0);
  if (!ParamUtils::insert(param, &this->param_tree))
  {
    abort();
  }
  param = Param::new_param_float("t8", P_FLAG_TVAR, &this->t8, NULL, MAX_DOUBLE_SIZE, -MAX_DOUBLE_SIZE, 0.0);
  if (!ParamUtils::insert(param, &this->param_tree))
  {
    abort();
  }

  for (unsigned int i = 0; i < NUM_Q_VARIABLES; i++) {
    std::ostringstream os;
    os << "q" << i + 1;
    param = Param::new_param_float(os.str().c_str(), P_FLAG_QVAR, &this->q[i], NULL, MAX_DOUBLE_SIZE,
      -MAX_DOUBLE_SIZE, 0.0);
    if (!ParamUtils::insert(param, &this->param_tree))
    {
      abort();
    }
  }

  param = Param::new_param_string("imageurl", P_FLAG_NONE, &this->imageUrl);
  if (!ParamUtils::insert(param, &this->text_properties_tree))
  {
    abort();
  }
}

CustomWave::CustomWave(int _id) : Waveform(512),
id(_id),
per_frame_count(0),
r(0),
g(0),
b(0),
a(0),
per_point_program(nullptr)
{

  Param* param;

  /// @bug deprecate the use of wipemalloc
  this->r_mesh = (float*)wipemalloc(MAX_SAMPLE_SIZE * sizeof(float));
  this->g_mesh = (float*)wipemalloc(MAX_SAMPLE_SIZE * sizeof(float));
  this->b_mesh = (float*)wipemalloc(MAX_SAMPLE_SIZE * sizeof(float));
  this->a_mesh = (float*)wipemalloc(MAX_SAMPLE_SIZE * sizeof(float));
  this->x_mesh = (float*)wipemalloc(MAX_SAMPLE_SIZE * sizeof(float));
  this->y_mesh = (float*)wipemalloc(MAX_SAMPLE_SIZE * sizeof(float));

  /* Start: Load custom wave parameters */

  if ((param = Param::new_param_float("r", P_FLAG_NONE | P_FLAG_PER_POINT, &this->r, this->r_mesh, 1.0, 0.0, .5)) == NULL)
  {
    ;
    /// @bug make exception
    abort();
  }

  if (!ParamUtils::insert(param, &param_tree))
  {
    /// @bug make exception
    abort();
  }

  if ((param = Param::new_param_float("g", P_FLAG_NONE | P_FLAG_PER_POINT, &this->g, this->g_mesh, 1.0, 0.0, .5)) == NULL)
  {
    ;
    /// @bug make exception
    abort();
  }

  if (!ParamUtils::insert(param, &param_tree))
  {
    ;
    /// @bug make exception
    abort();
  }

  if ((param = Param::new_param_float("b", P_FLAG_NONE | P_FLAG_PER_POINT, &this->b, this->b_mesh, 1.0, 0.0, .5)) == NULL)
  {
    ;
    /// @bug make exception
    abort();

  }

  if (!ParamUtils::insert(param, &this->param_tree))
  {
    ;
    /// @bug make exception
    abort();
  }

  if ((param = Param::new_param_float("a", P_FLAG_NONE | P_FLAG_PER_POINT, &this->a, this->a_mesh, 1.0, 0.0, .5)) == NULL)
  {
    ;
    /// @bug make exception
    abort();
  }

  if (!ParamUtils::insert(param, &this->param_tree))
  {
    ;
    /// @bug make exception
    abort();
  }

  if ((param = Param::new_param_float("x", P_FLAG_NONE | P_FLAG_PER_POINT, &this->x, this->x_mesh, 1.0, 0.0, .5)) == NULL)
  {
    ;
    /// @bug make exception
    abort();
  }

  if (!ParamUtils::insert(param, &this->param_tree))
  {
    ;
    /// @bug make exception
    abort();
  }

  if ((param = Param::new_param_float("y", P_FLAG_NONE | P_FLAG_PER_POINT, &this->y, this->y_mesh, 1.0, 0.0, .5)) == NULL)
  {
    ;
    /// @bug make exception
    abort();
  }

  if (!ParamUtils::insert(param, &this->param_tree))
  {
    ;

    /// @bug make exception
    abort();

  }

  if ((param = Param::new_param_bool("enabled", P_FLAG_NONE, &this->enabled, 1, 0, 0)) == NULL)
  {
    ;
    /// @bug make exception
    abort();


  }

  if (!ParamUtils::insert(param, &this->param_tree))
  {
    ;

    /// @bug make exception
    abort();

  }

  if ((param = Param::new_param_int("sep", P_FLAG_NONE, &this->sep, 100, -100, 0)) == NULL)
  {
    ;
    /// @bug make exception
    abort();


  }

  if (!ParamUtils::insert(param, &this->param_tree))
  {
    ;
    /// @bug make exception
    abort();


  }

  if ((param = Param::new_param_bool("bspectrum", P_FLAG_NONE, &this->spectrum, 1, 0, 0)) == NULL)
  {
    /// @bug make exception
    abort();


  }

  if (!ParamUtils::insert(param, &this->param_tree))
  {
    /// @bug make exception
    abort();

  }

  if ((param = Param::new_param_bool("bdrawthick", P_FLAG_NONE, &this->thick, 1, 0, 0)) == NULL)
  {
    /// @bug make exception
    abort();
  }

  if (!ParamUtils::insert(param, &this->param_tree))
  {
    /// @bug make exception
    abort();

  }

  if ((param = Param::new_param_bool("busedots", P_FLAG_NONE, &this->dots, 1, 0, 0)) == NULL)
  {

    /// @bug make exception
    abort();
  }

  if (!ParamUtils::insert(param, &this->param_tree))
  {
    ;
    abort();
  }

  if ((param = Param::new_param_bool("badditive", P_FLAG_NONE, &this->additive, 1, 0, 0)) == NULL)
  {
    ;
    abort();
  }

  if (!ParamUtils::insert(param, &this->param_tree))
  {
    ;
    abort();
  }

  if ((param = Param::new_param_int("samples", P_FLAG_NONE, &this->samples, 2048, 1, 512)) == NULL)
  {
    ;
    abort();
  }

  if (!ParamUtils::insert(param, &this->param_tree))
  {
    ;
    abort();
  }

  if ((param = Param::new_param_float("sample", P_FLAG_READONLY | P_FLAG_NONE,
    &this->sample, NULL, 1.0, 0.0, 0.0)) == NULL)
  {
    ;
    abort();
  }

  if (!ParamUtils::insert(param, &this->param_tree))
  {
    abort();
  }

  if ((param = Param::new_param_float("value1", P_FLAG_READONLY | P_FLAG_NONE, &this->v1, NULL, 1.0, -1.0, 0.0)) == NULL)
  {
    abort();
  }

  if (!ParamUtils::insert(param, &this->param_tree))
  {
    abort();
  }

  if ((param = Param::new_param_float("value2", P_FLAG_READONLY | P_FLAG_NONE, &this->v2, NULL, 1.0, -1.0, 0.0)) == NULL)
  {
    abort();
  }

  if (!ParamUtils::insert(param, &this->param_tree))
  {
    ;
    abort();
  }

  if ((param = Param::new_param_float("smoothing", P_FLAG_NONE, &this->smoothing, NULL, 1.0, 0.0, 0.0)) == NULL)
  {
    ;
    abort();
  }

  if (!ParamUtils::insert(param, &this->param_tree))
  {
    ;
    abort();
  }

  if ((param = Param::new_param_float("scaling", P_FLAG_NONE, &this->scaling, NULL, MAX_DOUBLE_SIZE, 0.0, 1.0)) == NULL)
  {
    ;
    abort();
  }

  if (!ParamUtils::insert(param, &this->param_tree))
  {
    ;
    abort();
  }

  if ((param = Param::new_param_float("t1", P_FLAG_TVAR, &this->t1, NULL, MAX_DOUBLE_SIZE, -MAX_DOUBLE_SIZE, 0.0)) == NULL)
  {
    ;
    abort();
  }

  if (!ParamUtils::insert(param, &this->param_tree))
  {
    ;
    abort();
  }

  if ((param = Param::new_param_float("t2", P_FLAG_TVAR, &this->t2, NULL, MAX_DOUBLE_SIZE, -MAX_DOUBLE_SIZE, 0.0)) == NULL)
  {
    ;
    abort();
  }

  if (!ParamUtils::insert(param, &this->param_tree))
  {
    abort();
  }

  if ((param = Param::new_param_float("t3", P_FLAG_TVAR, &this->t3, NULL, MAX_DOUBLE_SIZE, -MAX_DOUBLE_SIZE, 0.0)) == NULL)
  {
    abort();
  }

  if (!ParamUtils::insert(param, &this->param_tree))
  {

    abort();
  }
  if ((param = Param::new_param_float("t4", P_FLAG_TVAR, &this->t4, NULL, MAX_DOUBLE_SIZE, -MAX_DOUBLE_SIZE, 0.0)) == NULL)
  {

    abort();
  }

  if (!ParamUtils::insert(param, &this->param_tree))
  {
    abort();
  }
  if ((param = Param::new_param_float("t5", P_FLAG_TVAR, &this->t5, NULL, MAX_DOUBLE_SIZE, -MAX_DOUBLE_SIZE, 0.0)) == NULL)
  {
    abort();
  }

  if (!ParamUtils::insert(param, &this->param_tree))
  {
    abort();
  }

  if ((param = Param::new_param_float("t6", P_FLAG_TVAR, &this->t6, NULL, MAX_DOUBLE_SIZE, -MAX_DOUBLE_SIZE, 0.0)) == NULL)
  {
    abort();
  }

  if (!ParamUtils::insert(param, &this->param_tree))
  {

    abort();
  }
  if ((param = Param::new_param_float("t7", P_FLAG_TVAR, &this->t7, NULL, MAX_DOUBLE_SIZE, -MAX_DOUBLE_SIZE, 0.0)) == NULL)
  {

    abort();
  }

  if (!ParamUtils::insert(param, &this->param_tree))
  {
    ;
    abort();
  }

  if ((param = Param::new_param_float("t8", P_FLAG_TVAR, &this->t8, NULL, MAX_DOUBLE_SIZE, -MAX_DOUBLE_SIZE, 0.0)) == NULL)
  {
    ;
    abort();
  }

  if (!ParamUtils::insert(param, &this->param_tree))
  {
    ;
    abort();
  }

  for (unsigned int i = 0; i < NUM_Q_VARIABLES; i++) {
    std::ostringstream os;
    os << "q" << i + 1;
    param = Param::new_param_float(os.str().c_str(), P_FLAG_QVAR, &this->q[i], NULL, MAX_DOUBLE_SIZE,
      -MAX_DOUBLE_SIZE, 0.0);
    if (!ParamUtils::insert(param, &this->param_tree))
    {
      abort();
    }
  }

  /* End of parameter loading. Note that the read only parameters associated
  with custom waves (ie, sample) are variables stored in PresetFrameIO.hpp,
  and not specific to the custom wave datastructure. */

}

void InitCondUtils::LoadUnspecInitCond::operator() (Param* param)
{
  InitCond* init_cond = 0;
  CValue init_val;

  /* Don't count these parameters as initial conditions */
  if (param->flags & P_FLAG_READONLY)
    return;
  if (param->flags & P_FLAG_QVAR)
    return;
  //    if (param->flags & P_FLAG_TVAR)
   //       return;
  if (param->flags & P_FLAG_USERDEF)
    return;

  /* If initial condition was not defined by the preset file, force a default one
     with the following code */

  if (m_initCondTree.find(param->name) == m_initCondTree.end()) {

    /* Make sure initial condition does not exist in the set of per frame initial equations */
    if (m_perFrameInitEqnTree.find(param->name) != m_perFrameInitEqnTree.end())
      return;

    init_val = param->default_init_val;

    //printf("%s\n", param->name);
    /* Create new initial condition */
//std::cerr << "[InitCondUtils] creating an unspecified initial condition of name " << param->name << std::endl;
    if ((init_cond = new InitCond(param, init_val)) == nullptr) {
      abort();
    }

    /* Insert the initial condition into this presets tree */
    std::pair<std::map<std::string, InitCond*>::iterator, bool> inserteePair =
      m_initCondTree.insert(std::make_pair(init_cond->param->name, init_cond));
    assert(inserteePair.second);
    assert(inserteePair.first->second);
  }
  else
    assert(m_initCondTree.find(param->name)->second);
}

#ifdef SHR3D_WINDOW_SDL
static SDL_GLContext glMilkContext;
#else // SHR3D_WINDOW_SDL
static HGLRC glMilkContext;
#endif // SHR3D_WINDOW_SDL
static projectM* pm = nullptr;
//static std::mutex soundAddMutex;

static void init_milk()
{
  if (pm != nullptr)
    return;

  const std::vector<std::u8string> paths = String::split(Settings::pathMilk, u8'|');
  for (const std::u8string& path : paths)
  {
    if (!File::directoryExists(path.c_str()))
      continue;

    const std::vector<std::u8string> files = File::filesInDirectory(path.c_str());

    for (const std::u8string& file : files)
    {
      if (File::extension(file.c_str()) != std::u8string(u8".milk"))
        continue;

      const char8_t* filename = File::filename(file.c_str());

      Global::milkPresetNames.push_back(filename);
    }
  }

  const std::vector<std::u8string> activePresets = String::split(Settings::environmentMilkActivePresets.c_str(), u8'|');
  for (const std::u8string& preset : activePresets)
  {
    for (i32 i = 0; i < Global::milkPresetNames.size(); ++i)
    {
      if (preset == Global::milkPresetNames[i])
      {
        Global::milkActivePresets.push_back(i);
        break;
      }
    }
  }

  projectM::Settings settings;
  settings.meshX = Settings::environmentMilkMeshSize;
  settings.meshY = i32(settings.meshX * (f32(Global::resolutionHeight) / f32(Global::resolutionWidth)));
  settings.fps = 60; // does not do anything?
  settings.textureSize = 512;
  settings.windowWidth = Global::resolutionWidth;
  settings.windowHeight = Global::resolutionHeight;
  settings.smoothPresetDuration = 3; // seconds
  settings.presetDuration = 22; // seconds
  settings.hardcutEnabled = true;
  settings.hardcutDuration = 60;
  settings.hardcutSensitivity = 1.0;
  settings.beatSensitivity = 1.0;
  settings.aspectCorrection = true;
  settings.shuffleEnabled = true;
  settings.softCutRatingsEnabled = true;
  settings.presetURL = reinterpret_cast<const char*>(Settings::pathMilk.c_str());

#ifdef SHR3D_WINDOW_SDL
  glMilkContext = SDL_GL_CreateContext(Global::window);
#else // SHR3D_WINDOW_SDL
  glMilkContext = wglCreateContext(Global::hdc);
  wglMakeCurrent(Global::hdc, glMilkContext);
#endif // SHR3D_WINDOW_SDL

  pm = new projectM(settings);

#ifdef SHR3D_WINDOW_SDL
  SDL_GL_MakeCurrent(Global::window, Global::glContext);
#else // SHR3D_WINDOW_SDL
  wglMakeCurrent(Global::hdc, Global::glContext);
#endif // SHR3D_WINDOW_SDL

  if (Global::milkActivePresets.size() >= 1)
    Global::milkCurrentPresetIndex = Global::milkActivePresets[0];
}

void Milk::tick()
{
  if (pm == nullptr)
    init_milk();

  pm->renderer->noSwitch = !Settings::environmentMilkShuffle;
  pm->beatDetect->beatSensitivity = Settings::environmentMilkBeatSensitivity;

  {
    static i32 lastMilkCurrentPresetIndex = -1;
    if (Global::milkCurrentPresetIndex != lastMilkCurrentPresetIndex)
    {
      if (Global::milkCurrentPresetIndex >= 0)
      {

#ifdef SHR3D_WINDOW_SDL
        SDL_GL_MakeCurrent(Global::window, glMilkContext);
#else // SHR3D_WINDOW_SDL
        wglMakeCurrent(Global::hdc, glMilkContext);
#endif // SHR3D_WINDOW_SDL

        pm->selectPreset(u32(Global::milkCurrentPresetIndex), true);

#ifdef SHR3D_WINDOW_SDL
        SDL_GL_MakeCurrent(Global::window, Global::glContext);
#else // SHR3D_WINDOW_SDL
        wglMakeCurrent(Global::hdc, Global::glContext);
#endif // SHR3D_WINDOW_SDL



      }
      else
      {
        ASSERT(lastMilkCurrentPresetIndex >= 0);
      }

      lastMilkCurrentPresetIndex = Global::milkCurrentPresetIndex;
    }
  }

  if (Settings::environmentMilkDuration > 0.0f && Global::milkActivePresets.size() >= 1)
  {
    static f32 time;
    time += TimeNS_To_Seconds(Global::frameDelta);
    if (time > Settings::environmentMilkDuration)
    {
      const i32 randIndex = rand() % Global::milkActivePresets.size();
      Global::milkCurrentPresetIndex = Global::milkActivePresets[randIndex];
      time = 0.0f;
    }
  }

  {
    static i32 lastPresetDuration = Settings::environmentMilkDuration;
    if (lastPresetDuration != Settings::environmentMilkDuration)
    {

#ifdef SHR3D_WINDOW_SDL
      SDL_GL_MakeCurrent(Global::window, glMilkContext);
#else // SHR3D_WINDOW_SDL
      wglMakeCurrent(Global::hdc, glMilkContext);
#endif // SHR3D_WINDOW_SDL

      pm->changePresetDuration(Settings::environmentMilkDuration);

#ifdef SHR3D_WINDOW_SDL
      SDL_GL_MakeCurrent(Global::window, Global::glContext);
#else // SHR3D_WINDOW_SDL
      wglMakeCurrent(Global::hdc, Global::glContext);
#endif // SHR3D_WINDOW_SDL

      lastPresetDuration = Settings::environmentMilkDuration;
    }
  }

  {
    static i32 lastResolutionWidth = Global::resolutionWidth;
    static i32 lastResolutionHeight = Global::resolutionHeight;
    if (Global::resolutionWidth != 0 && Global::resolutionHeight != 0)
    {
      if (Global::resolutionWidth != lastResolutionWidth || Global::resolutionHeight != lastResolutionHeight)
      {
#ifdef SHR3D_WINDOW_SDL
        SDL_GL_MakeCurrent(Global::window, glMilkContext);
#else // SHR3D_WINDOW_SDL
        wglMakeCurrent(Global::hdc, glMilkContext);
#endif // SHR3D_WINDOW_SDL

        pm->projectM_resetGL(Global::resolutionWidth, Global::resolutionHeight);

#ifdef SHR3D_WINDOW_SDL
        SDL_GL_MakeCurrent(Global::window, Global::glContext);
#else // SHR3D_WINDOW_SDL
        wglMakeCurrent(Global::hdc, Global::glContext);
#endif // SHR3D_WINDOW_SDL

        lastResolutionWidth = Global::resolutionWidth;
        lastResolutionHeight = Global::resolutionHeight;
      }
    }
  }
}

void Milk::processAudioFrame(const f32* const inBlock[2], const i32 blockSize)
{
  if (!Settings::environmentMilk || pm == nullptr)
    return;

  {
    //std::unique_lock<std::mutex> lock(soundAddMutex);
    pm->pcm()->addPCMfloat(inBlock, blockSize);
  }
}

void Milk::render()
{
  if (!Settings::environmentMilk)
    return;

  ASSERT(pm != nullptr);

#ifdef SHR3D_WINDOW_SDL
  SDL_GL_MakeCurrent(Global::window, glMilkContext);
#else // SHR3D_WINDOW_SDL
  wglMakeCurrent(Global::hdc, glMilkContext);
#endif // SHR3D_WINDOW_SDL

  {
    //std::unique_lock<std::mutex> lock(soundAddMutex);
    pm->renderFrame();
  }

#ifdef SHR3D_WINDOW_SDL
  SDL_GL_MakeCurrent(Global::window, Global::glContext);
#else // SHR3D_WINDOW_SDL
  wglMakeCurrent(Global::hdc, Global::glContext);
#endif // SHR3D_WINDOW_SDL

  glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), nullptr); // vertex coords
  glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (void*)(3 * sizeof(GLfloat))); // uv coords
}

#endif // SHR3D_ENVIRONMENT_MILK
