[
  {
    "path": ".gitignore",
    "content": ".vs/\nComLightLib/x64/\nWhisper/x64/\nx64/\nTools/CompressShaders/bin/\nTools/CompressShaders/obj/\nWhisper/D3D/shaderData-Debug.inl\nWhisper/D3D/shaderData-Release.inl\nWhisperNet/bin/\nWhisperNet/obj/\nExamples/TranscribeCS/bin/\nExamples/TranscribeCS/obj/\n*.aps\n*.json\n*.user\nExamples/MicrophoneCS/obj/\nExamples/MicrophoneCS/bin/\nTools/PerfSummary/bin/\nTools/PerfSummary/obj/\npackages/\nWhisperPS/obj/\nWhisperPS/bin/\nTools/CompressTables/bin/\nTools/CompressTables/obj/"
  },
  {
    "path": "ComLightLib/ComLightLib.vcxproj",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project DefaultTargets=\"Build\" ToolsVersion=\"15.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <ItemGroup Label=\"ProjectConfigurations\">\n    <ProjectConfiguration Include=\"Debug|x64\">\n      <Configuration>Debug</Configuration>\n      <Platform>x64</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Release|x64\">\n      <Configuration>Release</Configuration>\n      <Platform>x64</Platform>\n    </ProjectConfiguration>\n  </ItemGroup>\n  <ItemGroup>\n    <ClInclude Include=\"comLightClient.h\" />\n    <ClInclude Include=\"client\\CComPtr.hpp\" />\n    <ClInclude Include=\"comLightServer.h\" />\n    <ClInclude Include=\"comLightCommon.h\" />\n    <ClInclude Include=\"server\\freeThreadedMarshaller.h\" />\n    <ClInclude Include=\"hresult.h\" />\n    <ClInclude Include=\"server\\ObjectRoot.hpp\" />\n    <ClInclude Include=\"pal\\guiddef.h\" />\n    <ClInclude Include=\"server\\Object.hpp\" />\n    <ClInclude Include=\"server\\interfaceMap.h\" />\n    <ClInclude Include=\"server\\RefCounter.hpp\" />\n    <ClInclude Include=\"Exception.hpp\" />\n    <ClInclude Include=\"streams.h\" />\n    <ClInclude Include=\"utils\\guid_parse.hpp\" />\n    <ClInclude Include=\"pal\\hresult.h\" />\n    <ClInclude Include=\"unknwn.h\" />\n    <ClInclude Include=\"utils\\typeTraits.hpp\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ClCompile Include=\"server\\freeThreadedMarshaller.cpp\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Text Include=\"Readme.txt\" />\n  </ItemGroup>\n  <PropertyGroup Label=\"Globals\">\n    <VCProjectVersion>15.0</VCProjectVersion>\n    <ProjectGuid>{52F486E7-830C-45D8-BE47-E76B5AAB2772}</ProjectGuid>\n    <Keyword>Win32Proj</Keyword>\n    <RootNamespace>ComLightLib</RootNamespace>\n    <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>\n  </PropertyGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.Default.props\" />\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\" Label=\"Configuration\">\n    <ConfigurationType>StaticLibrary</ConfigurationType>\n    <UseDebugLibraries>true</UseDebugLibraries>\n    <PlatformToolset>v143</PlatformToolset>\n    <CharacterSet>Unicode</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\" Label=\"Configuration\">\n    <ConfigurationType>StaticLibrary</ConfigurationType>\n    <UseDebugLibraries>false</UseDebugLibraries>\n    <PlatformToolset>v143</PlatformToolset>\n    <WholeProgramOptimization>true</WholeProgramOptimization>\n    <CharacterSet>Unicode</CharacterSet>\n  </PropertyGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.props\" />\n  <ImportGroup Label=\"ExtensionSettings\">\n  </ImportGroup>\n  <ImportGroup Label=\"Shared\">\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <PropertyGroup Label=\"UserMacros\" />\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\n    <LinkIncremental>true</LinkIncremental>\n    <OutDir>$(Platform)\\$(Configuration)\\</OutDir>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <LinkIncremental>false</LinkIncremental>\n    <OutDir>$(Platform)\\$(Configuration)\\</OutDir>\n  </PropertyGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\n    <ClCompile>\n      <PrecompiledHeader>NotUsing</PrecompiledHeader>\n      <WarningLevel>Level3</WarningLevel>\n      <Optimization>Disabled</Optimization>\n      <SDLCheck>true</SDLCheck>\n      <PreprocessorDefinitions>_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <ConformanceMode>true</ConformanceMode>\n      <LanguageStandard>stdcpp20</LanguageStandard>\n      <EnableEnhancedInstructionSet>AdvancedVectorExtensions</EnableEnhancedInstructionSet>\n    </ClCompile>\n    <Link>\n      <SubSystem>Windows</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <ClCompile>\n      <PrecompiledHeader>NotUsing</PrecompiledHeader>\n      <WarningLevel>Level3</WarningLevel>\n      <Optimization>MaxSpeed</Optimization>\n      <FunctionLevelLinking>true</FunctionLevelLinking>\n      <IntrinsicFunctions>true</IntrinsicFunctions>\n      <SDLCheck>true</SDLCheck>\n      <PreprocessorDefinitions>NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <ConformanceMode>true</ConformanceMode>\n      <LanguageStandard>stdcpp20</LanguageStandard>\n      <EnableEnhancedInstructionSet>AdvancedVectorExtensions</EnableEnhancedInstructionSet>\n    </ClCompile>\n    <Link>\n      <SubSystem>Windows</SubSystem>\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\n      <OptimizeReferences>true</OptimizeReferences>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n    </Link>\n  </ItemDefinitionGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.targets\" />\n  <ImportGroup Label=\"ExtensionTargets\">\n  </ImportGroup>\n</Project>"
  },
  {
    "path": "ComLightLib/ComLightLib.vcxproj.filters",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <ItemGroup>\n    <ClInclude Include=\"pal\\hresult.h\" />\n    <ClInclude Include=\"pal\\guiddef.h\" />\n    <ClInclude Include=\"utils\\guid_parse.hpp\" />\n    <ClInclude Include=\"unknwn.h\" />\n    <ClInclude Include=\"comLightClient.h\" />\n    <ClInclude Include=\"comLightServer.h\" />\n    <ClInclude Include=\"client\\CComPtr.hpp\" />\n    <ClInclude Include=\"comLightCommon.h\" />\n    <ClInclude Include=\"server\\RefCounter.hpp\" />\n    <ClInclude Include=\"server\\interfaceMap.h\" />\n    <ClInclude Include=\"server\\Object.hpp\" />\n    <ClInclude Include=\"utils\\typeTraits.hpp\" />\n    <ClInclude Include=\"server\\freeThreadedMarshaller.h\" />\n    <ClInclude Include=\"hresult.h\" />\n    <ClInclude Include=\"server\\ObjectRoot.hpp\" />\n    <ClInclude Include=\"Exception.hpp\" />\n    <ClInclude Include=\"streams.h\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ClCompile Include=\"server\\freeThreadedMarshaller.cpp\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Text Include=\"Readme.txt\" />\n  </ItemGroup>\n</Project>"
  },
  {
    "path": "ComLightLib/Exception.hpp",
    "content": "#pragma once\n\nnamespace ComLight\n{\n\tclass Exception : public std::runtime_error\n\t{\n\t\t// I don't like C++ exceptions too much, but for some cases they are useful.\n\t\t// You can throw ComLight::Exception from constructor, or from FinalConstruct() method, the library will catch & return the code from the class factory function.\n\t\t// Unfortunately, for interface methods this doesn't work, the C++ parts of the library can't catch them without very complex trickery like code generation.\n\t\t// You can still use this class in methods, but you'll need to catch them manually near the API boundary or the app will crash.\n\t\t// C++ doesn't have an ABI, the framework can't catch C++ exception across the modules.\n\t\tconst HRESULT m_code;\n\n\tpublic:\n\n\t\tException( HRESULT hr ) : runtime_error( \"ComLight HRESULT exception\" ), m_code( hr ) { }\n\n\t\tHRESULT code() const { return m_code; }\n\t};\n}"
  },
  {
    "path": "ComLightLib/Readme.txt",
    "content": "﻿Copy-pasted from there:\nhttps://github.com/Const-me/ComLightInterop/tree/master/ComLightLib\nWith only a few minor changes."
  },
  {
    "path": "ComLightLib/client/CComPtr.hpp",
    "content": "#pragma once\n\nnamespace ComLight\n{\n\t// COM smart pointer, very comparable to CComPtr from ATL\n\ttemplate <class I>\n\tclass CComPtr\n\t{\n\t\tI* p;\n\n\t\tvoid callAddRef() const\n\t\t{\n\t\t\tif( nullptr == p )\n\t\t\t\treturn;\n\t\t\tp->AddRef();\n\t\t}\n\n\tpublic:\n\n\t\t// Construct with nullptr\n\t\tCComPtr() : p( nullptr ) { }\n\n\t\t// Release the pointer\n\t\tvoid release()\n\t\t{\n\t\t\tif( nullptr == p )\n\t\t\t\treturn;\n\t\t\tp->Release();\n\t\t\tp = nullptr;\n\t\t}\n\n\t\t~CComPtr()\n\t\t{\n\t\t\trelease();\n\t\t}\n\n\t\t// Attach without AddRef()\n\t\tvoid attach( I* raw )\n\t\t{\n\t\t\trelease();\n\t\t\tp = raw;\n\t\t}\n\n\t\t// Detach without Release(), set this pointer to nullptr\n\t\tI* detach()\n\t\t{\n\t\t\tI* const result = p;\n\t\t\tp = nullptr;\n\t\t\treturn result;\n\t\t}\n\n\t\t// Detach without Release() and place to the specified address, set this pointer to nullptr\n\t\ttemplate<class Other>\n\t\tvoid detach( Other** pp )\n\t\t{\n\t\t\t// If the argument points to a non-empty object, release the old instance: would leak memory otherwise.\n\t\t\tif( nullptr != *pp )\n\t\t\t\t( *pp )->Release();\n\t\t\t( *pp ) = detach();\n\t\t}\n\n\t\t// Set and AddRef()\n\t\tvoid assign( I* raw )\n\t\t{\n\t\t\trelease();\n\t\t\tattach( raw );\n\t\t\tcallAddRef();\n\t\t}\n\n\t\tvoid swap( CComPtr<I>& that )\n\t\t{\n\t\t\tstd::swap( p, that.p );\n\t\t}\n\n\t\t// Set and AddRef()\n\t\tCComPtr( I* raw ) : p( raw )\n\t\t{\n\t\t\tcallAddRef();\n\t\t}\n\n\t\t// Set and AddRef()\n\t\tCComPtr( const CComPtr<I>& that ) : CComPtr( that.p ) { }\n\t\t// Move constructor\n\t\tCComPtr( CComPtr<I>&& that ) : p( that.p ) { that.p = nullptr; }\n\n\t\t// Set and AddRef()\n\t\tvoid operator=( I* raw )\n\t\t{\n\t\t\tassign( raw );\n\t\t}\n\n\t\t// Set and AddRef()\n\t\tvoid operator=( const CComPtr<I>& that )\n\t\t{\n\t\t\tassign( that.p );\n\t\t}\n\n\t\t// Move assignment operator, destroys the other one\n\t\tvoid operator=( CComPtr<I>&& that )\n\t\t{\n\t\t\tattach( that.detach() );\n\t\t}\n\n\t\toperator I*( ) const { return p; }\n\t\tI* operator -> () const { return p; }\n\t\tI** operator &() { return &p; }\n\n\t\toperator bool() const { return nullptr != p; }\n\t};\n}"
  },
  {
    "path": "ComLightLib/comLightClient.h",
    "content": "#pragma once\n#include \"comLightCommon.h\"\n#include \"client/CComPtr.hpp\"\n#include \"utils/typeTraits.hpp\"\n\nnamespace ComLight\n{\n\tnamespace details\n\t{\n\t\ttemplate<typename T>\n\t\tinline constexpr void** castDoublePointerToVoid( T** pp )\n\t\t{\n\t\t\tstatic_assert( pointersAssignable<IUnknown, T>(), \"IID_PPV_ARGS macro should be used with IUnknown interfaces\" );\n\t\t\treturn reinterpret_cast<void**>( pp );\n\t\t}\n\t}\n}\n\n#ifdef IID_PPV_ARGS\n#undef IID_PPV_ARGS\n#endif\n\n#define IID_PPV_ARGS( pp ) decltype( **pp )::iid, ::ComLight::details::castDoublePointerToVoid( pp )"
  },
  {
    "path": "ComLightLib/comLightCommon.h",
    "content": "#pragma once\n#include \"hresult.h\"\n\n#ifdef _MSC_VER\n#include <guiddef.h>\n#else\n#include \"pal/guiddef.h\"\nusing LPCTSTR = const char*;\n#endif\n\n#include \"unknwn.h\""
  },
  {
    "path": "ComLightLib/comLightServer.h",
    "content": "#pragma once\n#include \"comLightCommon.h\"\n#include \"client/CComPtr.hpp\"\n\n#include \"server/ObjectRoot.hpp\"\n#include \"server/interfaceMap.h\"\n#include \"server/Object.hpp\"\n#include \"server/freeThreadedMarshaller.h\"\n\n#ifdef _MSC_VER\n// On Windows, it's controlled by library.def module definition file. There's __declspec(dllexport), but it adds underscore, I don't like that.\n#define DLLEXPORT extern \"C\"\n#else\n#define DLLEXPORT extern \"C\" __attribute__((visibility(\"default\")))\n#endif"
  },
  {
    "path": "ComLightLib/hresult.h",
    "content": "#pragma once\n#include <stdint.h>\n#ifdef _MSC_VER\n#include <winerror.h>\n#include <OleCtl.h>\n#else\n#include \"pal/hresult.h\"\n#endif\n\n#define CHECK( hr ) { const HRESULT __hr = ( hr ); if( FAILED( __hr ) ) return __hr; }\n\n#ifndef _MSC_VER\ninline constexpr HRESULT HRESULT_FROM_WIN32( int c )\n{\n\treturn c < 0 ? c : ( ( 0xFFFF & c ) | 0x80070000 );\n}\n\nconstexpr HRESULT OLE_E_BLANK = _HRESULT_TYPEDEF_( 0x80040007 );\nconstexpr HRESULT E_BOUNDS = _HRESULT_TYPEDEF_( 0x8000000BL ); \n\nconstexpr int ERROR_HANDLE_EOF = 38;\nconstexpr int ERROR_ALREADY_INITIALIZED = 1247;\n#endif\n\nconstexpr HRESULT E_EOF = HRESULT_FROM_WIN32( ERROR_HANDLE_EOF );\nconstexpr HRESULT E_ALREADY_INITIALIZED = HRESULT_FROM_WIN32( ERROR_ALREADY_INITIALIZED );"
  },
  {
    "path": "ComLightLib/pal/guiddef.h",
    "content": "#pragma once\n#include <stdint.h>\n#include <array>\n#ifndef GUID_DEFINED\n#define GUID_DEFINED\n#endif\n\nstruct GUID\n{\n\tuint32_t Data1;\n\tuint16_t Data2;\n\tuint16_t Data3;\n\tstd::array<uint8_t, 8> Data4;\n\n\tconstexpr inline bool operator==( const GUID& that ) const\n\t{\n\t\treturn Data1 == that.Data1 && Data2 == that.Data2 && Data3 == that.Data3 && Data4 == that.Data4;\n\t}\n};\n\nusing REFIID = const GUID&;"
  },
  {
    "path": "ComLightLib/pal/hresult.h",
    "content": "#pragma once\n#include <stdint.h>\nusing HRESULT = int32_t;\n#define _HRESULT_TYPEDEF_(_sc) ((HRESULT)_sc)\n#define SEVERITY_ERROR        1\n#define FACILITY_CONTROL      10\n\ninline constexpr HRESULT MAKE_SCODE( uint32_t sev, uint32_t fac, uint32_t code )\n{\n\treturn (HRESULT)( ( (uint32_t)( sev ) << 31 ) | ( (unsigned long)( fac ) << 16 ) | ( (unsigned long)( code ) ) );\n};\n\n// ==== Copy-pasted from coreclr-master\\src\\pal\\inc\\rt\\palrt.h ====\n#define S_OK                             _HRESULT_TYPEDEF_(0x00000000L)\n#define S_FALSE                          _HRESULT_TYPEDEF_(0x00000001L)\n\n#define E_NOTIMPL                        _HRESULT_TYPEDEF_(0x80004001L)\n#define E_NOINTERFACE                    _HRESULT_TYPEDEF_(0x80004002L)\n#define E_UNEXPECTED                     _HRESULT_TYPEDEF_(0x8000FFFFL)\n#define E_OUTOFMEMORY                    _HRESULT_TYPEDEF_(0x8007000EL)\n#define E_INVALIDARG                     _HRESULT_TYPEDEF_(0x80070057L)\n#define E_POINTER                        _HRESULT_TYPEDEF_(0x80004003L)\n#define E_HANDLE                         _HRESULT_TYPEDEF_(0x80070006L)\n#define E_ABORT                          _HRESULT_TYPEDEF_(0x80004004L)\n#define E_FAIL                           _HRESULT_TYPEDEF_(0x80004005L)\n#define E_ACCESSDENIED                   _HRESULT_TYPEDEF_(0x80070005L)\n#define E_PENDING                        _HRESULT_TYPEDEF_(0x8000000AL)\n\n#define DISP_E_PARAMNOTFOUND             _HRESULT_TYPEDEF_(0x80020004L)\n#define DISP_E_TYPEMISMATCH              _HRESULT_TYPEDEF_(0x80020005L)\n#define DISP_E_BADVARTYPE                _HRESULT_TYPEDEF_(0x80020008L)\n#define DISP_E_OVERFLOW                  _HRESULT_TYPEDEF_(0x8002000AL)\n#define DISP_E_DIVBYZERO                 _HRESULT_TYPEDEF_(0x80020012L)\n\n#define CLASS_E_CLASSNOTAVAILABLE        _HRESULT_TYPEDEF_(0x80040111L)\n#define CLASS_E_NOAGGREGATION            _HRESULT_TYPEDEF_(0x80040110L)\n\n#define CO_E_CLASSSTRING                 _HRESULT_TYPEDEF_(0x800401F3L)\n\n#define MK_E_SYNTAX                      _HRESULT_TYPEDEF_(0x800401E4L)\n\n#define STG_E_INVALIDFUNCTION            _HRESULT_TYPEDEF_(0x80030001L)\n#define STG_E_FILENOTFOUND               _HRESULT_TYPEDEF_(0x80030002L)\n#define STG_E_PATHNOTFOUND               _HRESULT_TYPEDEF_(0x80030003L)\n#define STG_E_WRITEFAULT                 _HRESULT_TYPEDEF_(0x8003001DL)\n#define STG_E_FILEALREADYEXISTS          _HRESULT_TYPEDEF_(0x80030050L)\n#define STG_E_ABNORMALAPIEXIT            _HRESULT_TYPEDEF_(0x800300FAL)\n\n#define NTE_BAD_UID                      _HRESULT_TYPEDEF_(0x80090001L)\n#define NTE_BAD_HASH                     _HRESULT_TYPEDEF_(0x80090002L)\n#define NTE_BAD_KEY                      _HRESULT_TYPEDEF_(0x80090003L)\n#define NTE_BAD_LEN                      _HRESULT_TYPEDEF_(0x80090004L)\n#define NTE_BAD_DATA                     _HRESULT_TYPEDEF_(0x80090005L)\n#define NTE_BAD_SIGNATURE                _HRESULT_TYPEDEF_(0x80090006L)\n#define NTE_BAD_VER                      _HRESULT_TYPEDEF_(0x80090007L)\n#define NTE_BAD_ALGID                    _HRESULT_TYPEDEF_(0x80090008L)\n#define NTE_BAD_FLAGS                    _HRESULT_TYPEDEF_(0x80090009L)\n#define NTE_BAD_TYPE                     _HRESULT_TYPEDEF_(0x8009000AL)\n#define NTE_BAD_KEY_STATE                _HRESULT_TYPEDEF_(0x8009000BL)\n#define NTE_BAD_HASH_STATE               _HRESULT_TYPEDEF_(0x8009000CL)\n#define NTE_NO_KEY                       _HRESULT_TYPEDEF_(0x8009000DL)\n#define NTE_NO_MEMORY                    _HRESULT_TYPEDEF_(0x8009000EL)\n#define NTE_SIGNATURE_FILE_BAD           _HRESULT_TYPEDEF_(0x8009001CL)\n#define NTE_FAIL                         _HRESULT_TYPEDEF_(0x80090020L)\n\n#define CRYPT_E_HASH_VALUE               _HRESULT_TYPEDEF_(0x80091007L)\n\n#define TYPE_E_SIZETOOBIG                _HRESULT_TYPEDEF_(0x800288C5L)\n#define TYPE_E_DUPLICATEID               _HRESULT_TYPEDEF_(0x800288C6L)\n\n#define STD_CTL_SCODE(n) MAKE_SCODE(SEVERITY_ERROR, FACILITY_CONTROL, n)\n#define CTL_E_OVERFLOW                  STD_CTL_SCODE(6)\n#define CTL_E_OUTOFMEMORY               STD_CTL_SCODE(7)\n#define CTL_E_DIVISIONBYZERO            STD_CTL_SCODE(11)\n#define CTL_E_OUTOFSTACKSPACE           STD_CTL_SCODE(28)\n#define CTL_E_FILENOTFOUND              STD_CTL_SCODE(53)\n#define CTL_E_DEVICEIOERROR             STD_CTL_SCODE(57)\n#define CTL_E_PERMISSIONDENIED          STD_CTL_SCODE(70)\n#define CTL_E_PATHFILEACCESSERROR       STD_CTL_SCODE(75)\n#define CTL_E_PATHNOTFOUND              STD_CTL_SCODE(76)\n\n#define INET_E_CANNOT_CONNECT            _HRESULT_TYPEDEF_(0x800C0004L)\n#define INET_E_RESOURCE_NOT_FOUND        _HRESULT_TYPEDEF_(0x800C0005L)\n#define INET_E_OBJECT_NOT_FOUND          _HRESULT_TYPEDEF_(0x800C0006L)\n#define INET_E_DATA_NOT_AVAILABLE        _HRESULT_TYPEDEF_(0x800C0007L)\n#define INET_E_DOWNLOAD_FAILURE          _HRESULT_TYPEDEF_(0x800C0008L)\n#define INET_E_CONNECTION_TIMEOUT        _HRESULT_TYPEDEF_(0x800C000BL)\n#define INET_E_UNKNOWN_PROTOCOL          _HRESULT_TYPEDEF_(0x800C000DL)\n\n#define DBG_PRINTEXCEPTION_C             _HRESULT_TYPEDEF_(0x40010006L)\n// ==== Done pasting ====\n\ninline constexpr bool SUCCEEDED( HRESULT hr )\n{\n\treturn hr >= 0;\n}\n\ninline constexpr bool FAILED( HRESULT hr )\n{\n\treturn hr < 0;\n}"
  },
  {
    "path": "ComLightLib/server/Object.hpp",
    "content": "#pragma once\n#include <type_traits>\n#include \"../comLightClient.h\"\n#include \"../utils/typeTraits.hpp\"\n#include \"../Exception.hpp\"\n\nnamespace ComLight\n{\n\tnamespace details\n\t{\n\t\tGENERATE_HAS_MEMBER( implQueryInterface );\n\t\tGENERATE_HAS_MEMBER( implAddRef );\n\t\tGENERATE_HAS_MEMBER( implRelease );\n\t}\n\n\t// Outer class of objects, implements IUnknown methods, also the class factory. The type argument must be your class implementing your interfaces, inherited from ObjectRoot<I>\n\ttemplate<class T>\n\tclass Object : public T\n\t{\n\tpublic:\n\t\tObject() = default;\n\n\t\ttemplate<typename ... Args>\n\t\tObject( Args&& ... args ) : T{ std::forward<Args>( args )... } {};\n\n\t\tinline virtual ~Object() override { }\n\n\t\t// Implement IUnknown methods\n\t\tHRESULT COMLIGHTCALL QueryInterface( REFIID riid, void** ppvObject ) override\n\t\t{\n\t\t\tstatic_assert( details::has_member_implQueryInterface<T>::value, \"Your object class must inherit from ComLight::ObjectRoot\" );\n\n\t\t\tif( nullptr == ppvObject )\n\t\t\t\treturn E_POINTER;\n\n\t\t\tif( T::implQueryInterface( riid, ppvObject ) )\n\t\t\t\treturn S_OK;\n\t\t\tif( T::queryExtraInterfaces( riid, ppvObject ) )\n\t\t\t\treturn S_OK;\n\n\t\t\tif( riid == IUnknown::iid() )\n\t\t\t{\n\t\t\t\tComLight::IUnknown* unk = T::getUnknown();\n\t\t\t\tunk->AddRef();\n\t\t\t\t*ppvObject = unk;\n\t\t\t\treturn S_OK;\n\t\t\t}\n\n\t\t\treturn E_NOINTERFACE;\n\t\t}\n\n\t\tuint32_t COMLIGHTCALL AddRef() override\n\t\t{\n\t\t\tstatic_assert( details::has_member_implAddRef<T>::value, \"Your object class must inherit from ComLight::ObjectRoot\" );\n\t\t\treturn T::implAddRef();\n\t\t}\n\n\t\tuint32_t COMLIGHTCALL Release() override\n\t\t{\n\t\t\tstatic_assert( details::has_member_implRelease<T>::value, \"Your object class must inherit from ComLight::ObjectRoot\" );\n\t\t\tconst uint32_t ret = T::implRelease();\n\t\t\tif( 0 == ret )\n\t\t\t{\n\t\t\t\tT::FinalRelease();\n\t\t\t\tdelete this;\n\t\t\t}\n\t\t\treturn ret;\n\t\t}\n\n\t\t// Create a new object on the heap, store in smart pointer\n\t\tstatic inline HRESULT create( CComPtr<Object<T>>& result )\n\t\t{\n\t\t\tCComPtr<Object<T>> ptr;\n\t\t\ttry\n\t\t\t{\n\t\t\t\tptr = new Object<T>();\t// The RefCounter constructor creates it with ref.counter 0. But then CComPtr constructor calls AddRef so we have RC=1 after this line.\n\n\t\t\t\tHRESULT hr = ptr->internalFinalConstruct();\n\t\t\t\tif( FAILED( hr ) )\n\t\t\t\t\treturn hr;\n\n\t\t\t\thr = ptr->FinalConstruct();\n\t\t\t\tif( FAILED( hr ) )\n\t\t\t\t\treturn hr;\n\n\t\t\t\tptr.swap( result );\n\t\t\t\treturn S_OK;\n\t\t\t}\n\t\t\tcatch( const Exception& ex )\n\t\t\t{\n\t\t\t\treturn ex.code();\n\t\t\t}\n\t\t}\n\n\t\t// Create a new object on the heap, store in smart pointer\n\t\ttemplate<typename ... Args>\n\t\tstatic inline HRESULT create( CComPtr<Object<T>>& result, Args&& ... args )\n\t\t{\n\t\t\tCComPtr<Object<T>> ptr;\n\t\t\ttry\n\t\t\t{\n\t\t\t\tptr = new Object<T>( std::forward<Args>( args )... );\n\n\t\t\t\tHRESULT hr = ptr->internalFinalConstruct();\n\t\t\t\tif( FAILED( hr ) )\n\t\t\t\t\treturn hr;\n\n\t\t\t\thr = ptr->FinalConstruct();\n\t\t\t\tif( FAILED( hr ) )\n\t\t\t\t\treturn hr;\n\n\t\t\t\tptr.swap( result );\n\t\t\t\treturn S_OK;\n\t\t\t}\n\t\t\tcatch( const Exception& ex )\n\t\t\t{\n\t\t\t\treturn ex.code();\n\t\t\t}\n\t\t\tcatch( HRESULT hr )\n\t\t\t{\n\t\t\t\treturn hr;\n\t\t\t}\n\t\t}\n\n\t\t// Create a new object on the heap, return one of it's interfaces. The caller is assumed to take ownership of the new object.\n\t\ttemplate<class I>\n\t\tstatic inline HRESULT create( I** pp )\n\t\t{\n\t\t\tif( pp == nullptr )\n\t\t\t\treturn E_POINTER;\n\n\t\t\tstatic_assert( details::pointersAssignable<I, T>(), \"Object::create can't cast object to the requested interface\" );\n\t\t\tCComPtr<Object<T>> ptr;\n\t\t\tCHECK( create( ptr ) );\n\t\t\tptr.detach( pp );\n\t\t\treturn S_OK;\n\t\t}\n\t};\n}"
  },
  {
    "path": "ComLightLib/server/ObjectRoot.hpp",
    "content": "#pragma once\n#include \"RefCounter.hpp\"\n#include \"../comLightCommon.h\"\n#include \"../utils/typeTraits.hpp\"\n\nnamespace ComLight\n{\n\t// Base class of objects, implements reference counting, also a few lifetime methods.\n\t// The template argument is the interface you want clients to get when they ask for IID_IUnknown. By convention, that pointer defines object's identity.\n\ttemplate<class I>\n\tclass ObjectRoot : public RefCounter, public I\n\t{\n\tprotected:\n\n\t\tinline HRESULT internalFinalConstruct()\n\t\t{\n\t\t\treturn S_FALSE;\n\t\t}\n\n\t\tinline HRESULT FinalConstruct()\n\t\t{\n\t\t\treturn S_FALSE;\n\t\t}\n\n\t\tinline void FinalRelease() { }\n\n\t\tIUnknown* getUnknown()\n\t\t{\n\t\t\tstatic_assert( details::pointersAssignable<IUnknown, I>(), \"The interface doesn't derive from IUnknown\" );\n\t\t\treturn static_cast<I*>( this );\n\t\t}\n\n\t\tbool queryExtraInterfaces( REFIID riid, void **ppvObject ) const\n\t\t{\n\t\t\treturn false;\n\t\t}\n\n\t\t// Implement query interface with 2 entries, IUnknown and I.\n\t\tbool implQueryInterface( REFIID riid, void** ppvObject )\n\t\t{\n\t\t\tif( riid == I::iid() || riid == IUnknown::iid() )\n\t\t\t{\n\t\t\t\tI* const result = this;\n\t\t\t\tresult->AddRef();\n\t\t\t\t*ppvObject = result;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t};\n}"
  },
  {
    "path": "ComLightLib/server/RefCounter.hpp",
    "content": "#pragma once\n#include <atomic>\n#include <assert.h>\n#include <limits.h>\n\nnamespace ComLight\n{\n\t// Very base class of objects, implements reference counting.\n\tclass RefCounter\n\t{\n\t\tstd::atomic_uint referenceCounter;\n\n\tpublic:\n\n\t\tRefCounter() : referenceCounter( 0 ) { }\n\n\t\tinline virtual ~RefCounter() { }\n\n\t\tRefCounter( const RefCounter &that ) = delete;\n\t\tRefCounter( RefCounter &&that ) = delete;\n\n\tprotected:\n\n\t\tuint32_t implAddRef()\n\t\t{\n\t\t\treturn ++referenceCounter;\n\t\t}\n\n\t\tuint32_t implRelease()\n\t\t{\n\t\t\t// Might be a good idea to use locks, at least in debug builds. They're much slower than atomics, but with locks it's possible to detect when 2 threads call release at the same time, for object with counter = 1.\n\t\t\t// It's a memory management bug, but it would be nice if debug builds would handle that case gracefully.\n\t\t\tconst uint32_t rc = --referenceCounter;\n\t\t\tassert( rc != UINT_MAX );\n\t\t\treturn rc;\n\t\t}\n\t};\n}"
  },
  {
    "path": "ComLightLib/server/freeThreadedMarshaller.cpp",
    "content": "#include \"freeThreadedMarshaller.h\"\n#ifdef _MSC_VER\n#include <combaseapi.h>\n\nHRESULT ComLight::details::createFreeThreadedMarshaller( IUnknown* pUnkOuter, IUnknown** ppUnkMarshal )\n{\n\treturn ::CoCreateFreeThreadedMarshaler( (LPUNKNOWN)pUnkOuter, (LPUNKNOWN *)ppUnkMarshal );\n}\n\nbool ComLight::details::queryMarshallerInterface( REFIID riid, void **ppvObject, IUnknown* marshaller )\n{\n\tif( riid != IID_IMarshal || nullptr == marshaller )\n\t\treturn false;\n\tconst HRESULT hr = marshaller->QueryInterface( IID_IMarshal, ppvObject );\n\treturn SUCCEEDED( hr ) ? true : false;\n}\n#endif"
  },
  {
    "path": "ComLightLib/server/freeThreadedMarshaller.h",
    "content": "#pragma once\n#ifdef _MSC_VER\n#include \"../comLightCommon.h\"\n\nnamespace ComLight\n{\n\tnamespace details\n\t{\n\t\tHRESULT createFreeThreadedMarshaller( IUnknown* pUnkOuter, IUnknown** ppUnkMarshal );\n\t\tbool queryMarshallerInterface( REFIID riid, void **ppvObject, IUnknown* marshaller );\n\t}\n}\n\n#define DECLARE_FREE_THREADED_MARSHALLER()                                                              \\\nprivate:                                                                                                \\\nComLight::CComPtr<ComLight::IUnknown> m_freeThreadedMarshaller;                                         \\\nprotected:                                                                                              \\\nHRESULT internalFinalConstruct()                                                                        \\\n{                                                                                                       \\\n\treturn ComLight::details::createFreeThreadedMarshaller( getUnknown(), &m_freeThreadedMarshaller );  \\\n}                                                                                                       \\\nbool queryExtraInterfaces( REFIID riid, void **ppvObject ) const                                        \\\n{                                                                                                       \\\n\treturn ComLight::details::queryMarshallerInterface( riid, ppvObject, m_freeThreadedMarshaller );    \\\n}\n\n#else\n#define DECLARE_FREE_THREADED_MARSHALLER()\n#endif"
  },
  {
    "path": "ComLightLib/server/interfaceMap.h",
    "content": "#pragma once\n#include \"../utils/typeTraits.hpp\"\n\n// Unlike ATL, the interface map is optional for ComLight.\n// If you won't declare a map, the object will support 2 interfaces: IUnknown, and whatever template argument was passed to ObjectRoot class.\n#define BEGIN_COM_MAP()                                      \\\nprotected:                                                   \\\nbool implQueryInterface( REFIID iid, void** ppvObject ) {\n\n#define END_COM_MAP() return false; }\n\nnamespace ComLight\n{\n\tnamespace details\n\t{\n\t\ttemplate<typename I, typename C>\n\t\tinline bool tryReturnInterface( REFIID iid, C* pThis, void** ppvResult )\n\t\t{\n\t\t\tstatic_assert( pointersAssignable<IUnknown, I>(), \"Trying to implement an interface that doesn't derive from IUnknown\" );\n\t\t\tstatic_assert( pointersAssignable<I, C>(), \"Declared support for an interface, but the class doesn't implement it\" );\n\t\t\tif( I::iid() != iid )\n\t\t\t\treturn false;\n\t\t\tI* const result = pThis;\n\t\t\tresult->AddRef();\n\t\t\t*ppvResult = result;\n\t\t\treturn true;\n\t\t}\n\t}\n}\n\n#define COM_INTERFACE_ENTRY( I ) if( ComLight::details::tryReturnInterface<I>( iid, this, ppvObject ) ) return true;"
  },
  {
    "path": "ComLightLib/streams.h",
    "content": "#pragma once\n#include <vector>\n#include \"comLightCommon.h\"\n\n// COM interfaces to marshal streams across the interop.\nnamespace ComLight\n{\n\tenum struct eSeekOrigin : uint8_t\n\t{\n\t\tBegin = 0,\n\t\tCurrent = 1,\n\t\tEnd = 2\n\t};\n\n\tnamespace details\n\t{\n\t\ttemplate<class E>\n\t\tinline size_t sizeofVector( const std::vector<E>& vec )\n\t\t{\n\t\t\treturn sizeof( E ) * vec.size();\n\t\t}\n\t}\n\n\t// COM interface for readonly stream. You'll get these interfaces what you use [ReadStream] attribute in C#.\n\tstruct DECLSPEC_NOVTABLE iReadStream : public IUnknown\n\t{\n\t\tDEFINE_INTERFACE_ID( \"006af6db-734e-4595-8c94-19304b2389ac\" );\n\n\t\tvirtual HRESULT COMLIGHTCALL read( void* lpBuffer, int nNumberOfBytesToRead, int &lpNumberOfBytesRead ) = 0;\n\t\tvirtual HRESULT COMLIGHTCALL seek( int64_t offset, eSeekOrigin origin ) = 0;\n\t\tvirtual HRESULT COMLIGHTCALL getPosition( int64_t& position ) = 0;\n\t\tvirtual HRESULT COMLIGHTCALL getLength( int64_t& length ) = 0;\n\n\t\ttemplate<class E>\n\t\tinline HRESULT read( std::vector<E>& vec )\n\t\t{\n\t\t\tconst int cb = (int)details::sizeofVector( vec );\n\t\t\tint cbRead = 0;\n\t\t\tCHECK( read( vec.data(), cb, cbRead ) );\n\t\t\tif( cbRead >= cb )\n\t\t\t\treturn S_OK;\n\t\t\treturn E_EOF;\n\t\t}\n\t};\n\n\t// COM interface for readonly stream. You'll get these interfaces what you use [WriteStream] attribute in C#.\n\tstruct DECLSPEC_NOVTABLE iWriteStream : public IUnknown\n\t{\n\t\tDEFINE_INTERFACE_ID( \"d7c3eb39-9170-43b9-ba98-2ea1f2fed8a8\" );\n\n\t\tvirtual HRESULT COMLIGHTCALL write( const void* lpBuffer, int nNumberOfBytesToWrite ) = 0;\n\t\tvirtual HRESULT COMLIGHTCALL flush() = 0;\n\n\t\ttemplate<class E>\n\t\tinline HRESULT write( const std::vector<E>& vec )\n\t\t{\n\t\t\tconst int cb = (int)details::sizeofVector( vec );\n\t\t\treturn write( vec.data(), cb );\n\t\t}\n\t};\n}"
  },
  {
    "path": "ComLightLib/unknwn.h",
    "content": "#pragma once\n#include <type_traits>\n\n// Calling conventions\n#ifdef _MSC_VER\n#define COMLIGHTCALL __stdcall\n#define DECLSPEC_NOVTABLE   __declspec(novtable)\n#elif defined(__GNUC__) || defined(__clang__)\n#if defined(__i386__)\n#define COMLIGHTCALL __attribute__((stdcall))\n#else\n#define COMLIGHTCALL\n#endif\n#define DECLSPEC_NOVTABLE\n#else\n#error Unsupported C++ compiler\n#endif\n\n#include \"utils/guid_parse.hpp\"\n\n#define DEFINE_INTERFACE_ID( guidString ) static constexpr GUID iid() { return ::ComLight::make_guid( guidString ); }\n\nnamespace ComLight\n{\n\t// This thing is binary compatible with IUnknown from Windows SDK. See DesktopClient demo project, it uses normal COM interop in .NET framework 4.7 to call my implementation.\n\tstruct DECLSPEC_NOVTABLE IUnknown\n\t{\n\t\tDEFINE_INTERFACE_ID( \"00000000-0000-0000-c000-000000000046\" );\n\n\t\tvirtual HRESULT COMLIGHTCALL QueryInterface( REFIID riid, void **ppvObject ) = 0;\n\n\t\tvirtual uint32_t COMLIGHTCALL AddRef() = 0;\n\n\t\tvirtual uint32_t COMLIGHTCALL Release() = 0;\n\t};\n}"
  },
  {
    "path": "ComLightLib/utils/guid_parse.hpp",
    "content": "// https://github.com/tobias-loew/constexpr-GUID-cpp-11\n\n//-------------------------------------------------------------------------------------------------------\n// constexpr GUID parsing\n// Written by Alexander Bessonov\n// Written by Tobias Loew\n//\n// Licensed under the MIT license.\n//-------------------------------------------------------------------------------------------------------\n\n#pragma once\n#include <stdexcept>\n#include <string>\n#include <cassert>\n#include <cstdint>\n\n#if !defined(GUID_DEFINED)\n#define GUID_DEFINED\nstruct GUID {\n\tuint32_t Data1;\n\tuint16_t Data2;\n\tuint16_t Data3;\n\tuint8_t Data4[ 8 ];\n};\n#endif\n\nnamespace ComLight\n{\n\tnamespace details\n\t{\n\t\tconstexpr const size_t short_guid_form_length = 36;\t// XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX\n\t\tconstexpr const size_t long_guid_form_length = 38;\t// {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}\n\n\t\tconstexpr uint8_t parse_hex_digit( const char c )\n\t\t{\n\t\t\tusing namespace std::string_literals;\n\t\t\treturn\n\t\t\t\t( '0' <= c && c <= '9' )\n\t\t\t\t? c - '0'\n\t\t\t\t: ( 'a' <= c && c <= 'f' )\n\t\t\t\t? 10 + c - 'a'\n\t\t\t\t: ( 'A' <= c && c <= 'F' )\n\t\t\t\t? 10 + c - 'A'\n\t\t\t\t:\n\t\t\t\tthrow std::domain_error{ \"invalid character in GUID\"s };\n\t\t}\n\n\t\tconstexpr uint8_t parse_hex_uint8_t( const char *ptr )\n\t\t{\n\t\t\treturn ( parse_hex_digit( ptr[ 0 ] ) << 4 ) + parse_hex_digit( ptr[ 1 ] );\n\t\t}\n\n\t\tconstexpr uint16_t parse_hex_uint16_t( const char *ptr )\n\t\t{\n\t\t\treturn ( parse_hex_uint8_t( ptr ) << 8 ) + parse_hex_uint8_t( ptr + 2 );\n\t\t}\n\n\t\tconstexpr uint32_t parse_hex_uint32_t( const char *ptr )\n\t\t{\n\t\t\treturn ( parse_hex_uint16_t( ptr ) << 16 ) + parse_hex_uint16_t( ptr + 4 );\n\t\t}\n\n\t\tconstexpr GUID parse_guid( const char *begin )\n\t\t{\n\t\t\treturn GUID{\n\t\t\t\tparse_hex_uint32_t( begin ),\n\t\t\t\tparse_hex_uint16_t( begin + 8 + 1 ),\n\t\t\t\tparse_hex_uint16_t( begin + 8 + 1 + 4 + 1 ),\n\t\t\t\t{\n\t\t\t\t\tparse_hex_uint8_t( begin + 8 + 1 + 4 + 1 + 4 + 1 ),\n\t\t\t\t\tparse_hex_uint8_t( begin + 8 + 1 + 4 + 1 + 4 + 1 + 2 ),\n\t\t\t\t\tparse_hex_uint8_t( begin + 8 + 1 + 4 + 1 + 4 + 1 + 2 + 2 + 1 ),\n\t\t\t\t\tparse_hex_uint8_t( begin + 8 + 1 + 4 + 1 + 4 + 1 + 2 + 2 + 1 + 2 ),\n\t\t\t\t\tparse_hex_uint8_t( begin + 8 + 1 + 4 + 1 + 4 + 1 + 2 + 2 + 1 + 2 + 2 ),\n\t\t\t\t\tparse_hex_uint8_t( begin + 8 + 1 + 4 + 1 + 4 + 1 + 2 + 2 + 1 + 2 + 2 + 2 ),\n\t\t\t\t\tparse_hex_uint8_t( begin + 8 + 1 + 4 + 1 + 4 + 1 + 2 + 2 + 1 + 2 + 2 + 2 + 2 ),\n\t\t\t\t\tparse_hex_uint8_t( begin + 8 + 1 + 4 + 1 + 4 + 1 + 2 + 2 + 1 + 2 + 2 + 2 + 2 + 2 )\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\n\t\tconstexpr GUID make_guid_helper( const char *str, size_t N )\n\t\t{\n\t\t\tusing namespace std::string_literals;\n\t\t\tusing namespace details;\n\n\t\t\treturn ( !( N == long_guid_form_length || N == short_guid_form_length ) )\n\t\t\t\t? throw std::domain_error{ \"String GUID of the form {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX} or XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX is expected\"s }\n\t\t\t\t: ( N == long_guid_form_length && ( str[ 0 ] != '{' || str[ long_guid_form_length - 1 ] != '}' ) )\n\t\t\t\t? throw std::domain_error{ \"Missing opening or closing brace\"s }\n\n\t\t\t: parse_guid( str + ( N == long_guid_form_length ? 1 : 0 ) );\n\t\t}\n\n\n\t\ttemplate<size_t N>\n\t\tconstexpr GUID make_guid( const char( &str )[ N ] )\n\t\t{\n\t\t\treturn make_guid_helper( str, N - 1 );\n\t\t}\n\t}\n\tusing details::make_guid;\n}"
  },
  {
    "path": "ComLightLib/utils/typeTraits.hpp",
    "content": "#pragma once\n#include <type_traits>\n\nnamespace ComLight\n{\n\tnamespace details\n\t{\n\t\ttemplate<class TResult, class TValue>\n\t\tconstexpr bool pointersAssignable()\n\t\t{\n\t\t\t// See this for why `&` is required: https://stackoverflow.com/a/52429468/126995\n\t\t\treturn std::is_assignable<TResult*&, TValue*>::value;\n\t\t}\n\t}\n}\n\n// https://en.wikibooks.org/wiki/More_C++_Idioms/Member_Detector\n#define GENERATE_HAS_MEMBER(member)                                               \\\n                                                                                  \\\ntemplate < class T >                                                              \\\nclass HasMember_##member                                                          \\\n{                                                                                 \\\nprivate:                                                                          \\\n    using Yes = char[2];                                                          \\\n    using  No = char[1];                                                          \\\n                                                                                  \\\n    struct Fallback { int member; };                                              \\\n    struct Derived : T, Fallback { };                                             \\\n                                                                                  \\\n    template < class U >                                                          \\\n    static No& test ( decltype(U::member)* );                                     \\\n    template < typename U >                                                       \\\n    static Yes& test ( U* );                                                      \\\n                                                                                  \\\npublic:                                                                           \\\n    static constexpr bool RESULT = sizeof(test<Derived>(nullptr)) == sizeof(Yes); \\\n};                                                                                \\\n                                                                                  \\\ntemplate < class T >                                                              \\\nstruct has_member_##member                                                        \\\n: public std::integral_constant<bool, HasMember_##member<T>::RESULT>              \\\n{                                                                                 \\\n};\n"
  },
  {
    "path": "ComputeShaders/ComputeShaders.cpp",
    "content": "void fnComputeShaders()\n{\n}"
  },
  {
    "path": "ComputeShaders/ComputeShaders.vcxproj",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project DefaultTargets=\"Build\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <ItemGroup Label=\"ProjectConfigurations\">\n    <ProjectConfiguration Include=\"Release|x64\">\n      <Configuration>Release</Configuration>\n      <Platform>x64</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Debug|x64\">\n      <Configuration>Debug</Configuration>\n      <Platform>x64</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Release|x64\">\n      <Configuration>Release</Configuration>\n      <Platform>x64</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Debug|x64\">\n      <Configuration>Debug</Configuration>\n      <Platform>x64</Platform>\n    </ProjectConfiguration>\n  </ItemGroup>\n  <PropertyGroup Label=\"Globals\">\n    <VCProjectVersion>16.0</VCProjectVersion>\n    <Keyword>Win32Proj</Keyword>\n    <ProjectGuid>{1c39d386-96d0-47a1-bbfa-68bbdb24439c}</ProjectGuid>\n    <RootNamespace>ComputeShaders</RootNamespace>\n    <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>\n  </PropertyGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.Default.props\" />\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\" Label=\"Configuration\">\n    <ConfigurationType>StaticLibrary</ConfigurationType>\n    <UseDebugLibraries>false</UseDebugLibraries>\n    <PlatformToolset>v143</PlatformToolset>\n    <WholeProgramOptimization>true</WholeProgramOptimization>\n    <CharacterSet>Unicode</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\" Label=\"Configuration\">\n    <ConfigurationType>StaticLibrary</ConfigurationType>\n    <UseDebugLibraries>false</UseDebugLibraries>\n    <PlatformToolset>v143</PlatformToolset>\n    <WholeProgramOptimization>true</WholeProgramOptimization>\n    <CharacterSet>Unicode</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\" Label=\"Configuration\">\n    <ConfigurationType>StaticLibrary</ConfigurationType>\n    <UseDebugLibraries>false</UseDebugLibraries>\n    <PlatformToolset>v143</PlatformToolset>\n    <WholeProgramOptimization>true</WholeProgramOptimization>\n    <CharacterSet>Unicode</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\" Label=\"Configuration\">\n    <ConfigurationType>StaticLibrary</ConfigurationType>\n    <UseDebugLibraries>false</UseDebugLibraries>\n    <PlatformToolset>v143</PlatformToolset>\n    <WholeProgramOptimization>true</WholeProgramOptimization>\n    <CharacterSet>Unicode</CharacterSet>\n  </PropertyGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.props\" />\n  <ImportGroup Label=\"ExtensionSettings\">\n  </ImportGroup>\n  <ImportGroup Label=\"Shared\">\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <PropertyGroup Label=\"UserMacros\" />\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <MultiProcFXC>true</MultiProcFXC>\n    <OutDir>$(Platform)\\$(Configuration)\\</OutDir>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\n    <OutDir>$(Platform)\\$(Configuration)\\</OutDir>\n    <MultiProcFXC>true</MultiProcFXC>\n  </PropertyGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <FunctionLevelLinking>true</FunctionLevelLinking>\n      <IntrinsicFunctions>true</IntrinsicFunctions>\n      <SDLCheck>true</SDLCheck>\n      <PreprocessorDefinitions>NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <ConformanceMode>true</ConformanceMode>\n    </ClCompile>\n    <Link>\n      <SubSystem>\n      </SubSystem>\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\n      <OptimizeReferences>true</OptimizeReferences>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <FunctionLevelLinking>true</FunctionLevelLinking>\n      <IntrinsicFunctions>true</IntrinsicFunctions>\n      <SDLCheck>true</SDLCheck>\n      <PreprocessorDefinitions>NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <ConformanceMode>true</ConformanceMode>\n    </ClCompile>\n    <Link>\n      <SubSystem>\n      </SubSystem>\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\n      <OptimizeReferences>true</OptimizeReferences>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <FunctionLevelLinking>true</FunctionLevelLinking>\n      <IntrinsicFunctions>true</IntrinsicFunctions>\n      <SDLCheck>true</SDLCheck>\n      <PreprocessorDefinitions>NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <ConformanceMode>true</ConformanceMode>\n    </ClCompile>\n    <Link>\n      <SubSystem>\n      </SubSystem>\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\n      <OptimizeReferences>true</OptimizeReferences>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n    </Link>\n    <FxCompile>\n      <ShaderModel>5.0</ShaderModel>\n      <ShaderType>Compute</ShaderType>\n    </FxCompile>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <FunctionLevelLinking>true</FunctionLevelLinking>\n      <IntrinsicFunctions>true</IntrinsicFunctions>\n      <SDLCheck>true</SDLCheck>\n      <PreprocessorDefinitions>NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <ConformanceMode>true</ConformanceMode>\n    </ClCompile>\n    <Link>\n      <SubSystem>\n      </SubSystem>\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\n      <OptimizeReferences>true</OptimizeReferences>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n    </Link>\n    <FxCompile>\n      <ShaderModel>5.0</ShaderModel>\n      <ShaderType>Compute</ShaderType>\n      <DisableOptimizations>true</DisableOptimizations>\n      <EnableDebuggingInformation>true</EnableDebuggingInformation>\n    </FxCompile>\n  </ItemDefinitionGroup>\n  <ItemGroup>\n    <ClCompile Include=\"ComputeShaders.cpp\" />\n  </ItemGroup>\n  <ItemGroup>\n    <FxCompile Include=\"add.hlsl\" />\n    <FxCompile Include=\"addInPlace.hlsl\" />\n    <FxCompile Include=\"addRepeat.hlsl\" />\n    <FxCompile Include=\"addRepeat64.hlsl\" />\n    <FxCompile Include=\"addRepeatEx.hlsl\" />\n    <FxCompile Include=\"addRepeatGelu.hlsl\" />\n    <FxCompile Include=\"addRepeatGelu64.hlsl\" />\n    <FxCompile Include=\"addRepeatScale.hlsl\" />\n    <FxCompile Include=\"addRows.hlsl\" />\n    <FxCompile Include=\"convolutionMain.hlsl\" />\n    <FxCompile Include=\"convolutionMain2.hlsl\" />\n    <FxCompile Include=\"convolutionMain2Fixed.hlsl\" />\n    <FxCompile Include=\"convolutionPrep1.hlsl\" />\n    <FxCompile Include=\"convolutionPrep2.hlsl\" />\n    <FxCompile Include=\"copyConvert.hlsl\" />\n    <FxCompile Include=\"copyTranspose.hlsl\" />\n    <FxCompile Include=\"dbgFindNaN.hlsl\" />\n    <FxCompile Include=\"diagMaskInf.hlsl\" />\n    <FxCompile Include=\"flashAttention.hlsl\" />\n    <FxCompile Include=\"flashAttentionCompat1.hlsl\" />\n    <FxCompile Include=\"flashAttentionCompat2.hlsl\" />\n    <FxCompile Include=\"flashAttentionCompat3.hlsl\" />\n    <FxCompile Include=\"fmaRepeat1.hlsl\" />\n    <FxCompile Include=\"fmaRepeat164.hlsl\" />\n    <FxCompile Include=\"fmaRepeat2.hlsl\" />\n    <FxCompile Include=\"matReshapePanels.hlsl\" />\n    <FxCompile Include=\"mulMatByRow.hlsl\" />\n    <FxCompile Include=\"mulMatByRow64.hlsl\" />\n    <FxCompile Include=\"mulMatByRowTiled.hlsl\" />\n    <FxCompile Include=\"mulMatByRowTiledEx.hlsl\" />\n    <FxCompile Include=\"mulMatByScalar.hlsl\" />\n    <FxCompile Include=\"mulMatDotMain.hlsl\" />\n    <FxCompile Include=\"mulMatDotReshape.hlsl\" />\n    <FxCompile Include=\"mulMatMadMain.hlsl\" />\n    <FxCompile Include=\"mulMatTiled.hlsl\" />\n    <FxCompile Include=\"mulMatTiledEx.hlsl\" />\n    <FxCompile Include=\"norm.hlsl\" />\n    <FxCompile Include=\"normCompat.hlsl\" />\n    <FxCompile Include=\"normFixed.hlsl\" />\n    <FxCompile Include=\"normFixed64.hlsl\" />\n    <FxCompile Include=\"scaleInPlace.hlsl\" />\n    <FxCompile Include=\"softMax.hlsl\" />\n    <FxCompile Include=\"softMax64.hlsl\" />\n    <FxCompile Include=\"softMaxCompat.hlsl\" />\n    <FxCompile Include=\"softMaxFixed.hlsl\" />\n    <FxCompile Include=\"softMaxLong.hlsl\" />\n    <FxCompile Include=\"zeroMemory.hlsl\" />\n  </ItemGroup>\n  <ItemGroup>\n    <None Include=\"componentwiseBinaryOp.hlsli\" />\n    <None Include=\"flashAttentionCommon.hlsli\" />\n    <None Include=\"fp64Utils.hlsli\" />\n    <None Include=\"groupReduce.hlsli\" />\n    <None Include=\"groupReduce64.hlsli\" />\n    <None Include=\"miscUtils.hlsli\" />\n    <None Include=\"repeatUtils.hlsli\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Text Include=\"Readme.txt\" />\n  </ItemGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.targets\" />\n  <ImportGroup Label=\"ExtensionTargets\">\n  </ImportGroup>\n</Project>"
  },
  {
    "path": "ComputeShaders/ComputeShaders.vcxproj.filters",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <ItemGroup>\n    <ClCompile Include=\"ComputeShaders.cpp\" />\n  </ItemGroup>\n  <ItemGroup>\n    <FxCompile Include=\"mulMatDotMain.hlsl\" />\n    <FxCompile Include=\"mulMatDotReshape.hlsl\" />\n    <FxCompile Include=\"convolutionMain.hlsl\" />\n    <FxCompile Include=\"convolutionPrep1.hlsl\" />\n    <FxCompile Include=\"convolutionPrep2.hlsl\" />\n    <FxCompile Include=\"add.hlsl\" />\n    <FxCompile Include=\"flashAttention.hlsl\" />\n    <FxCompile Include=\"convolutionMain2.hlsl\" />\n    <FxCompile Include=\"norm.hlsl\" />\n    <FxCompile Include=\"copyConvert.hlsl\" />\n    <FxCompile Include=\"copyTranspose.hlsl\" />\n    <FxCompile Include=\"normCompat.hlsl\" />\n    <FxCompile Include=\"flashAttentionCompat1.hlsl\" />\n    <FxCompile Include=\"flashAttentionCompat3.hlsl\" />\n    <FxCompile Include=\"flashAttentionCompat2.hlsl\" />\n    <FxCompile Include=\"scaleInPlace.hlsl\" />\n    <FxCompile Include=\"diagMaskInf.hlsl\" />\n    <FxCompile Include=\"softMaxCompat.hlsl\" />\n    <FxCompile Include=\"mulMatMadMain.hlsl\" />\n    <FxCompile Include=\"addRepeat.hlsl\" />\n    <FxCompile Include=\"fmaRepeat1.hlsl\" />\n    <FxCompile Include=\"fmaRepeat2.hlsl\" />\n    <FxCompile Include=\"addInPlace.hlsl\" />\n    <FxCompile Include=\"softMax.hlsl\" />\n    <FxCompile Include=\"addRepeatScale.hlsl\" />\n    <FxCompile Include=\"mulMatByRow.hlsl\" />\n    <FxCompile Include=\"mulMatByScalar.hlsl\" />\n    <FxCompile Include=\"mulMatTiled.hlsl\" />\n    <FxCompile Include=\"mulMatByRow64.hlsl\" />\n    <FxCompile Include=\"softMax64.hlsl\" />\n    <FxCompile Include=\"softMaxFixed.hlsl\" />\n    <FxCompile Include=\"addRepeat64.hlsl\" />\n    <FxCompile Include=\"fmaRepeat164.hlsl\" />\n    <FxCompile Include=\"addRepeatGelu.hlsl\" />\n    <FxCompile Include=\"addRepeatGelu64.hlsl\" />\n    <FxCompile Include=\"normFixed.hlsl\" />\n    <FxCompile Include=\"normFixed64.hlsl\" />\n    <FxCompile Include=\"mulMatByRowTiled.hlsl\" />\n    <FxCompile Include=\"convolutionMain2Fixed.hlsl\" />\n    <FxCompile Include=\"addRows.hlsl\" />\n    <FxCompile Include=\"zeroMemory.hlsl\" />\n    <FxCompile Include=\"mulMatTiledEx.hlsl\" />\n    <FxCompile Include=\"matReshapePanels.hlsl\" />\n    <FxCompile Include=\"mulMatByRowTiledEx.hlsl\" />\n    <FxCompile Include=\"addRepeatEx.hlsl\" />\n    <FxCompile Include=\"softMaxLong.hlsl\" />\n    <FxCompile Include=\"dbgFindNaN.hlsl\" />\n  </ItemGroup>\n  <ItemGroup>\n    <None Include=\"componentwiseBinaryOp.hlsli\" />\n    <None Include=\"miscUtils.hlsli\" />\n    <None Include=\"groupReduce.hlsli\" />\n    <None Include=\"fp64Utils.hlsli\" />\n    <None Include=\"flashAttentionCommon.hlsli\" />\n    <None Include=\"repeatUtils.hlsli\" />\n    <None Include=\"groupReduce64.hlsli\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Text Include=\"Readme.txt\" />\n  </ItemGroup>\n</Project>"
  },
  {
    "path": "ComputeShaders/Readme.txt",
    "content": "﻿This project compiles all the compute shaders which implement the model.\n\nMany shaders come in 2 versions, something.hlsl and something64.hlsl\n\nThe version with the `64` suffix is used on AMD GPUs, the version without suffix is used on nVidia and Intel GPUs.\n\nNot all of these shaders are actually used for anything.\nSome of them are implementing binary compatibility for the reference CPU version, and not used unless messing with the `constexpr` flags in MlContext C++ class.\nSuch shaders often require FP64 support, which is an optional feature in D3D11.\nCompressShaders tool detects such shaders by looking at the SFI0 chunk in the binary, and outputs a bitmap of the FP64 shaders.\nThis way, missing FP64 hardware support shouldn’t break the library."
  },
  {
    "path": "ComputeShaders/add.hlsl",
    "content": "inline float compute( float a, float b )\n{\n\treturn a + b;\n}\n\n#include \"componentwiseBinaryOp.hlsli\""
  },
  {
    "path": "ComputeShaders/addInPlace.hlsl",
    "content": "#ifndef THREADS\n#define THREADS 512\n#endif\n\nBuffer<float> arg0: register( t0 );\nRWBuffer<float> result: register( u0 );\n\ncbuffer Constants: register( b0 )\n{\n\tuint4 size: packoffset( c0 );\n\tuint4 strides: packoffset( c1 );\n\tuint4 argStrides: packoffset( c3 );\n}\n\ninline uint rowOffset( uint3 idx, uint4 strides )\n{\n\treturn idx[ 0 ] * strides[ 1 ] + idx[ 1 ] * strides[ 2 ] + idx[ 2 ] * strides[ 3 ];\n}\n\n[ numthreads( THREADS, 1, 1 ) ]\nvoid main( uint3 group: SV_GroupID, uint thread : SV_GroupIndex )\n{\n\tuint rdi = rowOffset( group, strides );\n\tuint rsi = rowOffset( group, argStrides );\n\n\tconst uint rdiEnd = rdi + size[ 0 ] * strides[ 0 ];\n\trdi += thread * strides[ 0 ];\n\trsi += thread * argStrides[ 0 ];\n\n\tconst uint rdiInc = THREADS * strides[ 0 ];\n\tconst uint rsiInc = THREADS * argStrides[ 0 ];\n\n\tfor( ; rdi < rdiEnd; rdi += rdiInc, rsi += rsiInc )\n\t{\n\t\tfloat f = result[ rdi ];\n\t\tf += arg0[ rsi ];\n\t\tresult[ rdi ] = f;\n\t}\n}"
  },
  {
    "path": "ComputeShaders/addRepeat.hlsl",
    "content": "// Compute tensor = tensor + repeat( pattern, tensor ) in 1 shot, without VRAM allocations\n// Dispatch [ nb[ 1 ], nb[ 2 ], nb[ 3 ] ] thread groups of this shader, where nb is size of the destination tensor\nRWBuffer<float> tensor: register( u0 );\nBuffer<float> pattern: register( t0 );\n\ncbuffer Constants: register( b0 )\n{\n\tuint4 tensorSize: packoffset( c0 );\n\tuint4 tensorStrides: packoffset( c1 );\n\tuint4 patternSize: packoffset( c2 );\n\tuint4 patternStrides: packoffset( c3 );\n}\n\n#ifndef THREADS\n#define THREADS 256\n#endif\n\n#include \"repeatUtils.hlsli\"\n\ninline void computeSimple( uint idx, float add )\n{\n\tfloat f = tensor[ idx ];\n\tf += add;\n\ttensor[ idx ] = f;\n}\n\n[ numthreads( THREADS, 1, 1 ) ]\nvoid main( uint3 group: SV_GroupID, uint thread : SV_GroupIndex )\n{\n\tuint3 it = tensorIteratorState( group, thread, tensorSize, tensorStrides );\n\tuint rsi = rowOffset( group % patternSize.yzw, patternStrides );\n\n\tif( patternSize[ 0 ] == 1 )\n\t{\n\t\t// The pattern only has 1 column - broadcasting over the row\n\t\tconst float p = pattern[ rsi ];\n\t\tROW_LOOP( it )\n\t\t\tcomputeSimple( it.x, p );\n\t}\n\telse if( patternSize[ 0 ] <= THREADS )\n\t{\n\t\t// pattern size doesn't exceed thread group size: load pattern value outside of the loop\n\t\tconst uint threadsPerGroup = THREADS - ( THREADS % patternSize[ 0 ] );\n\t\tif( thread >= threadsPerGroup )\n\t\t\treturn;\n\n\t\tconst float p = pattern[ rsi + ( thread % patternSize[ 0 ] ) * patternStrides[ 0 ] ];\n\t\tROW_LOOP_EX( it, threadsPerGroup, tensorStrides )\n\t\t\tcomputeSimple( it.x, p );\n\t}\n\telse\n\t{\n\t\t// Pattern rows are larger than the thread group, need to stream from both buffers\n\t\tconst uint rsiInc = THREADS * patternStrides[ 0 ];\n\t\tconst uint rsiDec = patternSize[ 0 ] * patternStrides[ 0 ];\n\t\tconst uint rsiEnd = rsi + rsiDec;\n\t\trsi += thread * patternStrides[ 0 ];\n\n\t\tROW_LOOP( it )\n\t\t{\n\t\t\tfloat f = tensor[ it.x ];\n\t\t\tfloat p = pattern[ rsi ];\n\t\t\trsi += rsiInc;\n\t\t\tif( rsi >= rsiEnd )\n\t\t\t\trsi -= rsiDec;\n\t\t\tf += p;\n\t\t\ttensor[ it.x ] = f;\n\t\t}\n\t}\n}"
  },
  {
    "path": "ComputeShaders/addRepeat64.hlsl",
    "content": "#define THREADS 64\n#include \"addRepeat.hlsl\""
  },
  {
    "path": "ComputeShaders/addRepeatEx.hlsl",
    "content": "// An equivalent of \"addRepeat.hlsl\" followed by \"addInPlace.hlsl\".\n// Merging into a single shader saves some global memory bandwidth and reduces CPU overhead wasted binding resources and dispatching shaders\nRWBuffer<float> tensor: register( u0 );\nBuffer<float> pattern: register( t0 );\nBuffer<float> finalAdd: register( t1 );\n\ncbuffer Constants: register( b0 )\n{\n\tuint4 tensorSize: packoffset( c0 );\n\tuint4 tensorStrides: packoffset( c1 );\n\tuint4 patternSize: packoffset( c2 );\n\tuint4 patternStrides: packoffset( c3 );\n\t// uint4 finalSize: packoffset( c4 );\n\tuint4 finalStrides: packoffset( c5 );\n}\n\n#ifndef THREADS\n#define THREADS 256\n#endif\n\n#include \"repeatUtils.hlsli\"\n\n// The micro-kernel of the shader, computes tensor[ rsi.x ] += pattern + finalAdd[ rsi.y ]\ninline void add2( uint2 rsi, float pattern )\n{\n\tfloat f = tensor[ rsi.x ];\n\tf += pattern;\n\tf += finalAdd[ rsi.y ];\n\ttensor[ rsi.x ] = f;\n}\n\n[ numthreads( THREADS, 1, 1 ) ]\nvoid main( uint3 group: SV_GroupID, uint thread : SV_GroupIndex )\n{\n\tconst uint2 stridesX = uint2( tensorStrides.x, finalStrides.x );\n\tuint2 rsi;\n\trsi.x = rowOffset( group, tensorStrides );\n\trsi.y = rowOffset( group, finalStrides );\n\tconst uint rsiEnd = rsi.x + tensorSize.x * stridesX.x;\n\trsi += stridesX * thread;\n\n\tuint pat = rowOffset( group % patternSize.yzw, patternStrides );\n\n\tif( patternSize.x == 1 )\n\t{\n\t\t// The pattern only has 1 column, broadcasting over the row\n\t\tconst uint2 rsiInc = stridesX * THREADS;\n\t\tconst float p = pattern[ pat ];\n\t\tfor( ; rsi.x < rsiEnd; rsi += rsiInc )\n\t\t\tadd2( rsi, p );\n\t}\n\telse if( patternSize.x <= THREADS )\n\t{\n\t\t// pattern size doesn't exceed thread group size, load outside of the loop\n\t\tconst uint threadsPerGroup = THREADS - ( THREADS % patternSize.x );\n\t\tif( thread >= threadsPerGroup )\n\t\t\treturn;\n\n\t\tconst uint2 rsiInc = stridesX * threadsPerGroup;\n\t\tpat += ( thread % patternSize.x ) * patternStrides.x;\n\t\tconst float p = pattern[ pat ];\n\t\tfor( ; rsi.x < rsiEnd; rsi += rsiInc )\n\t\t\tadd2( rsi, p );\n\t}\n\telse\n\t{\n\t\t// Pattern rows are longer than the thread group, need to stream from both buffers\n\t\tuint3 rsi3;\n\t\trsi3.xy = rsi;\n\t\trsi3.z = pat + thread * patternStrides.x;\n\n\t\tconst uint3 rsiInc = uint3( stridesX, patternStrides.x ) * THREADS;\n\t\twhile( rsi3.x < rsiEnd )\n\t\t{\n\t\t\tadd2( rsi3.xy, pattern[ rsi3.z ] );\n\n\t\t\trsi3 += rsiInc;\n\t\t\tif( rsi3.z >= patternSize.x )\n\t\t\t\trsi3.z -= patternSize.x;\n\t\t}\n\t}\n}"
  },
  {
    "path": "ComputeShaders/addRepeatGelu.hlsl",
    "content": "// Compute tensor = GELU( tensor + repeat( pattern, tensor ) ) in 1 shot, without VRAM allocations\n// Dispatch [ nb[ 1 ], nb[ 2 ], nb[ 3 ] ] thread groups of this shader, where nb is size of the destination tensor\nRWBuffer<float> tensor: register( u0 );\nBuffer<float> pattern: register( t0 );\nBuffer<uint> lookupTable: register( t1 );\n\ncbuffer Constants: register( b0 )\n{\n\tuint4 tensorSize: packoffset( c0 );\n\tuint4 tensorStrides: packoffset( c1 );\n\tuint4 patternSize: packoffset( c2 );\n\tuint4 patternStrides: packoffset( c3 );\n}\n\n#ifndef THREADS\n#define THREADS 1024\n#endif\n\n#include \"repeatUtils.hlsli\"\n#include \"miscUtils.hlsli\"\n\ninline float gelu( float x )\n{\n#if 1\n\tconst uint index = fp16Rounded( x );\n\tconst uint res16 = lookupTable[ index ];\n\treturn f16tof32( res16 );\n#else\n\t// This version is much slower, at least on AMD, despite saving these VRAM loads.\n\tconst float GELU_COEF_A = 0.044715;\n\tconst float SQRT_2_OVER_PI = 0.79788456080286535587989211986876;\n\treturn 0.5 * x * ( 1.0 + tanh( SQRT_2_OVER_PI * x * ( 1.0 + GELU_COEF_A * x * x ) ) );\n#endif\n}\n\ninline void computeSimple( uint idx, float add )\n{\n\tfloat f = tensor[ idx ];\n\tf += add;\n\tf = gelu( f );\n\ttensor[ idx ] = f;\n}\n\n[ numthreads( THREADS, 1, 1 ) ]\nvoid main( uint3 group: SV_GroupID, uint thread : SV_GroupIndex )\n{\n\tuint3 it = tensorIteratorState( group, thread, tensorSize, tensorStrides );\n\tuint rsi = rowOffset( group % patternSize.yzw, patternStrides );\n\n\tif( patternSize[ 0 ] == 1 )\n\t{\n\t\t// The pattern only has 1 column - broadcasting over the row\n\t\tconst float p = pattern[ rsi ];\n\t\tROW_LOOP( it )\n\t\t\tcomputeSimple( it.x, p );\n\t}\n\telse if( patternSize[ 0 ] <= THREADS )\n\t{\n\t\t// pattern size doesn't exceed thread group size: load pattern value outside of the loop\n\t\tconst uint threadsPerGroup = THREADS - ( THREADS % patternSize[ 0 ] );\n\t\tif( thread >= threadsPerGroup )\n\t\t\treturn;\n\n\t\tconst float p = pattern[ rsi + ( thread % patternSize[ 0 ] ) * patternStrides[ 0 ] ];\n\t\tROW_LOOP_EX( it, threadsPerGroup, tensorStrides )\n\t\t\tcomputeSimple( it.x, p );\n\t}\n\telse\n\t{\n\t\t// Pattern rows are larger than the thread group, need to stream from both buffers\n\t\tconst uint rsiInc = THREADS * patternStrides[ 0 ];\n\t\tconst uint rsiDec = patternSize[ 0 ] * patternStrides[ 0 ];\n\t\tconst uint rsiEnd = rsi + rsiDec;\n\t\trsi += thread * patternStrides[ 0 ];\n\n\t\tROW_LOOP( it )\n\t\t{\n\t\t\tfloat f = tensor[ it.x ];\n\t\t\tfloat p = pattern[ rsi ];\n\t\t\trsi += rsiInc;\n\t\t\tif( rsi >= rsiEnd )\n\t\t\t\trsi -= rsiDec;\n\t\t\tf += p;\n\t\t\tf = gelu( f );\n\t\t\ttensor[ it.x ] = f;\n\t\t}\n\t}\n}"
  },
  {
    "path": "ComputeShaders/addRepeatGelu64.hlsl",
    "content": "#define THREADS 64\n#include \"addRepeatGelu.hlsl\""
  },
  {
    "path": "ComputeShaders/addRepeatScale.hlsl",
    "content": "// Compute tensor = ( tensor + repeat( pattern, tensor ) ) * scale in 1 shot, without VRAM allocations\n// Dispatch [ nb[ 1 ], nb[ 2 ], nb[ 3 ] ] thread groups of this shader, where nb is size of the destination tensor\nRWBuffer<float> tensor: register( u0 );\nBuffer<float> pattern: register( t0 );\n\ncbuffer Constants: register( b0 )\n{\n\tuint4 tensorSize: packoffset( c0 );\n\tuint4 tensorStrides: packoffset( c1 );\n\tuint4 patternSize: packoffset( c2 );\n\tuint4 patternStrides: packoffset( c3 );\n\tfloat scalingMul : packoffset( c4.x );\n}\n\n#ifndef THREADS\n#define THREADS 512\n#endif\n\n#include \"repeatUtils.hlsli\"\n\ninline void computeSimple( uint idx, float add )\n{\n\tfloat f = tensor[ idx ];\n\tf += add;\n\tf *= scalingMul;\n\ttensor[ idx ] = f;\n}\n\n[ numthreads( THREADS, 1, 1 ) ]\nvoid main( uint3 group: SV_GroupID, uint thread : SV_GroupIndex )\n{\n\tuint3 it = tensorIteratorState( group, thread, tensorSize, tensorStrides );\n\tuint rsi = rowOffset( group % patternSize.yzw, patternStrides );\n\n\tif( patternSize[ 0 ] == 1 )\n\t{\n\t\t// The pattern only has 1 column - broadcasting over the row\n\t\tconst float p = pattern[ rsi ];\n\t\tROW_LOOP( it )\n\t\t\tcomputeSimple( it.x, p );\n\t}\n\telse if( patternSize[ 0 ] <= THREADS )\n\t{\n\t\t// pattern size doesn't exceed thread group size: load pattern value outside of the loop\n\t\tconst uint threadsPerGroup = THREADS - ( THREADS % patternSize[ 0 ] );\n\t\tif( thread >= threadsPerGroup )\n\t\t\treturn;\n\n\t\tconst float p = pattern[ rsi + ( thread % patternSize[ 0 ] ) * patternStrides[ 0 ] ];\n\t\tROW_LOOP_EX( it, threadsPerGroup, tensorStrides )\n\t\t\tcomputeSimple( it.x, p );\n\t}\n\telse\n\t{\n\t\t// Pattern rows are larger than the thread group, need to stream from both buffers\n\t\tconst uint rsiInc = THREADS * patternStrides[ 0 ];\n\t\tconst uint rsiDec = patternSize[ 0 ] * patternStrides[ 0 ];\n\t\tconst uint rsiEnd = rsi + rsiDec;\n\t\trsi += thread * patternStrides[ 0 ];\n\n\t\tROW_LOOP( it )\n\t\t{\n\t\t\tfloat f = tensor[ it.x ];\n\t\t\tfloat p = pattern[ rsi ];\n\t\t\trsi += rsiInc;\n\t\t\tif( rsi >= rsiEnd )\n\t\t\t\trsi -= rsiDec;\n\t\t\tf += p;\n\t\t\tf *= scalingMul;\n\t\t\ttensor[ it.x ] = f;\n\t\t}\n\t}\n}"
  },
  {
    "path": "ComputeShaders/addRows.hlsl",
    "content": "#ifndef THREADS\n#define THREADS 256\n#endif\n\n// dec.tokenEmbedding tensor\nBuffer<float> tokenEmbedding: register( t0 );\n// dec.positionalEmbedding tensor\nBuffer<float> positionalEmbedding: register( t1 );\n// R32_UINT buffer with the input tokens\nBuffer<uint> embd: register( t2 );\n// Output tensor\nRWBuffer<float> result: register( u0 );\n\ncbuffer Constants: register( b0 )\n{\n\tuint rowLength: packoffset( c0.x );\n\tuint pastTokensCount: packoffset( c0.y );\n\tuint outputRowStride: packoffset( c0.z );\n\tuint2 embStrides: packoffset( c1.x );\n\tuint2 posStrides: packoffset( c1.z );\n}\n\n[ numthreads( THREADS, 1, 1 ) ]\nvoid main( uint3 group: SV_GroupID, uint thread : SV_GroupIndex )\n{\n\tconst uint row = group.x;\n\tconst uint rowTok = embd[ row ];\n\tconst uint rowPos = row + pastTokensCount;\n\n\tuint rdi = row * outputRowStride;\n\tconst uint rdiEnd = rdi + rowLength;\n\trdi += thread;\n\n\tuint rsiTok = rowTok * embStrides.y;\n\trsiTok += thread * embStrides.x;\n\n\tuint rsiPos = rowPos * posStrides.y;\n\trsiPos += thread * posStrides.x;\n\n\tfor( ; rdi < rdiEnd; rdi += THREADS, rsiTok += THREADS * embStrides.x, rsiPos += THREADS * posStrides.x )\n\t{\n\t\tfloat a = tokenEmbedding[ rsiTok ];\n\t\tfloat b = positionalEmbedding[ rsiPos ];\n\t\tresult[ rdi ] = a + b;\n\t}\n}"
  },
  {
    "path": "ComputeShaders/componentwiseBinaryOp.hlsli",
    "content": "Buffer<float> arg0: register( t0 );\nBuffer<float> arg1: register( t1 );\nRWBuffer<float> result: register( u0 );\n\ncbuffer Constants: register( b0 )\n{\n\tuint4 src0_elements: packoffset( c0 );\n\tuint4 src0_strides: packoffset( c1 );\n\tuint4 src1_elements: packoffset( c2 );\n\tuint4 src1_strides: packoffset( c3 );\n\tuint4 result_elements: packoffset( c4 );\n\tuint4 result_strides: packoffset( c5 );\n}\n\n[ numthreads( 32, 1, 1 ) ]\nvoid main( uint3 group: SV_GroupID, uint thread : SV_GroupIndex )\n{\n\tconst uint j = group.x;\n\tconst uint nb1 = result_strides[ 1 ];\n\tconst uint nb01 = src0_strides[ 1 ];\n\n\tconst uint nb10 = src1_strides[ 0 ];\n\tconst uint nb11 = src1_strides[ 1 ];\n\tconst uint nc = src0_elements[ 0 ];\n\n\tuint rsi0 = j * nb01;\n\tuint rsi1 = j * nb11;\n\tuint rdi = j * nb1;\n\tconst uint rsi0End = rsi0 + nc;\n\n\trsi0 += thread;\n\trsi1 += thread * nb10;\n\trdi += thread;\n\n\tconst uint rsi1Inc = 32 * nb10;\n\tfor( ; rsi0 < rsi0End; rsi0 += 32, rsi1 += rsi1Inc, rdi += 32 )\n\t{\n\t\tconst float a = arg0[ rsi0 ];\n\t\tconst float b = arg1[ rsi1 ];\n\t\tconst float res = compute( a, b );\n\t\tresult[ rdi ] = res;\n\t}\n}"
  },
  {
    "path": "ComputeShaders/convolutionMain.hlsl",
    "content": "// ggml_compute_forward_conv_1d_1s_f16_f32, GGML_TASK_COMPUTE implementation\n// Dispatch [ ne10, ne02, 1 ] thread groups\nBuffer<float> arg0: register( t0 );\nBuffer<float> arg1: register( t1 );\nRWBuffer<float> result: register( u0 );\n\ncbuffer Constants: register( b0 )\n{\n\tuint4 src0_elements: packoffset( c0 );\n\tuint4 src0_strides: packoffset( c1 );\n\tuint4 src1_elements: packoffset( c2 );\n\tuint4 result_elements: packoffset( c4 );\n\tuint4 result_strides: packoffset( c5 );\n}\n\n#include \"groupReduce.hlsli\"\n\ninline void computeDotProduct( uint s0, uint s1, uint len, uint thread, inout float acc )\n{\n\tfloat curr = 0;\n\tconst uint completeVectors = len / 32;\n\tuint i;\n\tfor( i = 0; i < completeVectors; i++, s0 += 32, s1 += 32 )\n\t\tcurr = mad( arg0[ s0 + thread ], arg1[ s1 + thread ], curr );\n\n\thorizontalSumCompatNew( thread, curr );\n\n\tif( 0 == thread )\n\t{\n\t\tconst uint rem = len % 32;\n\t\tif( 0 != rem )\n\t\t{\n\t\t\tdouble f64 = curr;\n\t\t\tfor( i = 0; i < rem; i++ )\n\t\t\t{\n\t\t\t\tprecise float a = arg0[ s0 + i ];\n\t\t\t\tprecise float b = arg1[ s1 + i ];\n\t\t\t\tprecise float prod = a * b;\n\t\t\t\tf64 += prod;\n\t\t\t}\n\t\t\tcurr = (float)f64;\n\t\t}\n\t\tacc += curr;\n\t}\n}\n\n#include \"miscUtils.hlsli\"\n\n[ numthreads( 32, 1, 1 ) ]\nvoid main( uint3 group: SV_GroupID, uint thread : SV_GroupIndex )\n{\n\tconst uint i1 = group.y;\n\tconst uint i0 = group.x;\n\n\tconst uint ne00 = src0_elements[ 0 ];\n\tconst uint nk = ne00;\n\tconst int nh = (int)( nk / 2 );\n\n\tconst uint ne01 = src0_elements[ 1 ];\n\tconst int ew0 = roundUp32( ne01 );\n\n\tfloat res = 0;\n\tfor( int k = -nh; k <= nh; k++ )\n\t{\n\t\tconst uint source0 = i1 * ew0 * ne00 + uint( nh + k ) * ew0;\n\t\tconst uint source1 = uint( i0 + nh + k ) * ew0;\n\t\tcomputeDotProduct( source0, source1, ew0, thread, res );\n\t}\n\n\tif( 0 != thread )\n\t\treturn;\n\n\tconst uint nb1 = result_strides[ 1 ];\n\tconst uint rdi = i1 * nb1 + i0;\n\tresult[ rdi ] = res;\n}"
  },
  {
    "path": "ComputeShaders/convolutionMain2.hlsl",
    "content": "// ggml_compute_forward_conv_1d_2s_f16_f32, GGML_TASK_COMPUTE implementation\n// Dispatch [ ne10 / 2, ne02, 1 ] thread groups\nBuffer<float> arg0: register( t0 );\nBuffer<float> arg1: register( t1 );\nRWBuffer<float> result: register( u0 );\n\ncbuffer Constants: register( b0 )\n{\n\tuint4 src0_elements: packoffset( c0 );\n\tuint4 src0_strides: packoffset( c1 );\n\tuint4 src1_elements: packoffset( c2 );\n\tuint4 result_elements: packoffset( c4 );\n\tuint4 result_strides: packoffset( c5 );\n}\n\n#include \"groupReduce.hlsli\"\n\ninline void computeDotProduct( uint s0, uint s1, uint len, uint thread, inout float acc )\n{\n\tfloat curr = 0;\n\tconst uint s0End = s0 + len;\n\ts0 += thread;\n\ts1 += thread;\n\tfor( ; s0 < s0End; s0 += 32, s1 += 32 )\n\t\tcurr = mad( arg0[ s0 ], arg1[ s1 ], curr );\n\n\thorizontalSumCompatNew( thread, curr );\n\tif( 0 == thread )\n\t\tacc += curr;\n}\n\n#include \"miscUtils.hlsli\"\n\n[ numthreads( 32, 1, 1 ) ]\nvoid main( uint3 group: SV_GroupID, uint thread : SV_GroupIndex )\n{\n\tconst uint ne00 = src0_elements[ 0 ];\n\tconst uint ne01 = src0_elements[ 1 ];\n\tconst int ew0 = roundUp32( ne01 );\n\n\tfloat res = 0;\n\tuint s0 = group.y * ew0 * ne00;\n\tuint s1 = group.x * 2 * ew0;\n\t// The original implementation did following:\n\t// int nh = (int)( nk / 2 );\n\t// for( int k = -nh; k <= nh; k++ )\n\t// What we doing instead:\n\t// for( uint len = ( nk / 2 ) * 2 + 1, i = 0; i < len; i++ )\n\t// len = ( nk / 2 ) * 2 + 1 is equal to ( nk | 1 )\n\tconst uint s0End = s0 + ( ne00 | 1u ) * ew0;\n\tfor( ; s0 < s0End; s0 += ew0, s1 += ew0 )\n\t\tcomputeDotProduct( s0, s1, ew0, thread, res );\n\n\tif( 0 != thread )\n\t\treturn;\n\n\tconst uint nb1 = result_strides[ 1 ];\n\tconst uint rdi = group.y * nb1 + group.x;\n\tresult[ rdi ] = res;\n}"
  },
  {
    "path": "ComputeShaders/convolutionMain2Fixed.hlsl",
    "content": "// Optimized version of convolutionMain2.hlsl for kernel size = 3\n// Dispatch [ ( ( ne10 / 2 ) + TILE_Y - 1 ) / TILE_Y, ne02, 1 ] thread groups of this shader\n#ifndef TILE_Y\nstatic const uint TILE_Y = 8;\n#endif\n#ifndef THREADS\nstatic const uint THREADS = 64;\n#endif\n\nBuffer<float> arg0: register( t0 );\nBuffer<float> arg1: register( t1 );\nRWBuffer<float> result: register( u0 );\n\ncbuffer Constants: register( b0 )\n{\n\tuint4 src0_elements: packoffset( c0 );\n\tuint4 src0_strides: packoffset( c1 );\n\tuint4 src1_elements: packoffset( c2 );\n\tuint4 result_elements: packoffset( c4 );\n\tuint4 result_strides: packoffset( c5 );\n}\n\n// The accumulators we're after\ngroupshared float resTemp[ TILE_Y ][ THREADS ];\n\n// Multiply + accumulate the specified row\ninline void accumulate( float a0, float a1, const uint resultRow, const uint thread )\n{\n\tfloat acc = resTemp[ resultRow ][ thread ];\n\tacc = mad( a0, a1, acc );\n\tresTemp[ resultRow ][ thread ] = acc;\n}\n\ninline void convolutionTile( const uint s0, uint s1, const uint thread, const uint stride, const uint height )\n{\n\t// Load 3 rows from arg0\n\tconst float3 a0 = float3( arg0[ s0 ], arg0[ s0 + stride ], arg0[ s0 + stride * 2 ] );\n\n\t// Row 0\n\tfloat a1 = arg1[ s1 ];\n\taccumulate( a0[ 0 ], a1, 0, thread );\n\ts1 += stride;\n\n\tfor( uint i = 1; i < height; i++ )\n\t{\n\t\t// Row i*2-1\n\t\t// Even-indexed rows only contribute to a single output rows, after muiltiplied by kernel row #1\n\t\ta1 = arg1[ s1 ];\n\t\taccumulate( a0[ 1 ], a1, i - 1, thread );\n\t\ts1 += stride;\n\n\t\t// Row i*2, contributes to 2 output rows corresponding to kernel rows #0 and #2\n\t\ta1 = arg1[ s1 ];\n\t\taccumulate( a0[ 2 ], a1, i - 1, thread );\n\t\taccumulate( a0[ 0 ], a1, i, thread );\n\t\ts1 += stride;\n\t}\n\n\t// Row height*2 - 1\n\ta1 = arg1[ s1 ];\n\taccumulate( a0[ 1 ], a1, height - 1, thread );\n\ts1 += stride;\n\n\t// Row height*2\n\ta1 = arg1[ s1 ];\n\taccumulate( a0[ 2 ], a1, height - 1, thread );\n}\n\n#include \"miscUtils.hlsli\"\n\n[ numthreads( THREADS, 1, 1 ) ]\nvoid main( uint3 group: SV_GroupID, uint thread : SV_GroupIndex )\n{\n\tuint i;\n\t// Zero out the accumulators\n\tfor( i = 0; i < TILE_Y; i++ )\n\t\tresTemp[ i ][ thread ] = 0.0;\n\tGroupMemoryBarrierWithGroupSync();\n\n\tconst uint i1 = group.y;\n\tconst uint i0 = group.x * TILE_Y * 2;\n\tconst uint height = min( TILE_Y, ( src1_elements.x / 2 ) - group.x * TILE_Y );\n\n\tconst uint ne00 = src0_elements[ 0 ];\n\tconst uint ne01 = src0_elements[ 1 ];\n\tconst int ew0 = roundUp32( ne01 );\n\n\tuint s0 = i1 * ew0 * ne00;\n\tconst uint s0End = s0 + ew0;\n\tuint s1 = i0 * ew0;\n\ts0 += thread;\n\ts1 += thread;\n\tfor( ; s0 < s0End; s0 += THREADS, s1 += THREADS )\n\t\tconvolutionTile( s0, s1, thread, ew0, height );\n\n\tGroupMemoryBarrierWithGroupSync();\n\n\t// Now we need horizontal sums of these shared accumulators, i.e. reduce [height][THREADS] shared array into [height][1] column\n\tfor( i = THREADS / 2; i > 0; i /= 2 )\n\t{\n\t\tif( thread < i )\n\t\t{\n\t\t\tfor( uint j = 0; j < height; j++ )\n\t\t\t{\n\t\t\t\tfloat sum = resTemp[ j ][ thread ];\n\t\t\t\tsum += resTemp[ j ][ thread + i ];\n\t\t\t\tresTemp[ j ][ thread ] = sum;\n\t\t\t}\n\t\t}\n\t\tGroupMemoryBarrierWithGroupSync();\n\t}\n\n\t// And finally, store that column to global memory\n\tif( thread >= height )\n\t\treturn;\n\tconst uint nb1 = result_strides[ 1 ];\n\tconst uint rdi = i1 * nb1 + group.x * TILE_Y + thread;\n\tresult[ rdi ] = resTemp[ thread ][ 0 ];\n}"
  },
  {
    "path": "ComputeShaders/convolutionPrep1.hlsl",
    "content": "// ggml_compute_forward_conv_1d_1s_f16_f32, prepare kernel data (src0)\n// Dispatch [ ne01, ne02, 1 ] thread groups\nBuffer<float> arg0: register( t0 );\nRWBuffer<float> result: register( u0 );\n\ncbuffer Constants: register( b0 )\n{\n\tuint4 src0_elements: packoffset( c0 );\n\tuint4 src0_strides: packoffset( c1 );\n}\n\ninline uint roundUp32( uint x )\n{\n\treturn ( x + 31 ) & ( ~31u );\n}\n\n[ numthreads( 32, 1, 1 ) ]\nvoid main( uint3 group: SV_GroupID, uint thread : SV_GroupIndex )\n{\n\tconst uint nb01 = src0_strides[ 1 ];\n\tconst uint nb02 = src0_strides[ 2 ];\n\n\tconst uint ne00 = src0_elements[ 0 ];\n\tconst uint ne01 = src0_elements[ 1 ];\n\tconst uint ew0 = roundUp32( ne01 );\n\n\tconst uint i02 = group.y;\n\tconst uint i01 = group.x;\n\n\tuint rsi = i02 * nb02 + i01 * nb01;\n\tconst uint rsiEnd = rsi + ne00;\n\tuint rdi = i02 * ew0 * ne00 + i01;\n\trsi += thread;\n\trdi += thread * ew0;\n\tconst uint rdiInc = 32 * ew0;\n\n\tfor( ; rsi < rsiEnd; rsi += 32, rdi += rdiInc )\n\t\tresult[ rdi ] = arg0[ rsi ];\n}"
  },
  {
    "path": "ComputeShaders/convolutionPrep2.hlsl",
    "content": "// ggml_compute_forward_conv_1d_1s_f16_f32, prepare source data (src1)\n// Dispatch [ ne11, 1, 1 ] thread groups\nBuffer<float> arg1: register( t0 );\nRWBuffer<float> result: register( u0 );\n\ncbuffer Constants: register( b0 )\n{\n\tuint4 src0_elements: packoffset( c0 );\n\tuint4 src1_elements: packoffset( c2 );\n\tuint4 src1_strides: packoffset( c3 );\n}\n\n#include \"miscUtils.hlsli\"\n\n[ numthreads( 32, 1, 1 ) ]\nvoid main( uint3 group: SV_GroupID, uint thread : SV_GroupIndex )\n{\n\tconst uint i11 = group.x;\n\n\tconst uint ne00 = src0_elements[ 0 ];\n\tconst uint ne01 = src0_elements[ 1 ];\n\tconst uint ne10 = src1_elements[ 0 ];\n\tconst uint nb11 = src1_strides[ 1 ];\n\n\tconst uint nk = ne00;\n\tconst uint nh = nk / 2;\n\tconst int ew0 = roundUp32( ne01 );\n\n\tuint rsi = i11 * nb11;\n\tuint rdi = nh * ew0 + i11;\n\tconst uint rdiInc = ew0 * 32;\n\tconst uint rsiEnd = rsi + ne10;\n\n\trsi += thread;\n\trdi += thread * ew0;\n\n\tfor( ; rsi < rsiEnd; rsi += 32, rdi += rdiInc )\n\t{\n\t\tfloat f = arg1[ rsi ];\n\t\tf = adjustFp16( f );\n\t\tresult[ rdi ] = f;\n\t}\n}"
  },
  {
    "path": "ComputeShaders/copyConvert.hlsl",
    "content": "// ggml_compute_forward_dup_f32 when we only need to convert types, but not reshape the tensor\n// Dispatch [ ne01, ne02, ne03 ] thread groups of this shader\nBuffer<float> arg0: register( t0 );\nRWBuffer<float> result: register( u0 );\n\ncbuffer Constants: register( b0 )\n{\n\tuint4 src0_elements: packoffset( c0 );\n\tuint4 src0_strides: packoffset( c1 );\n\tbool downcastFp32 : packoffset( c2.x );\n}\n\n#include \"miscUtils.hlsli\"\n\n[ numthreads( 32, 1, 1 ) ]\nvoid main( uint3 group: SV_GroupID, uint thread : SV_GroupIndex )\n{\n\tconst uint nb00 = src0_strides[ 0 ];\n\tconst uint nb01 = src0_strides[ 1 ];\n\tconst uint nb02 = src0_strides[ 2 ];\n\tconst uint nb03 = src0_strides[ 3 ];\n\n\tconst uint ne00 = src0_elements[ 0 ];\n\tconst uint ne01 = src0_elements[ 1 ];\n\tconst uint ne02 = src0_elements[ 2 ];\n\tconst uint ne03 = src0_elements[ 3 ];\n\n\tconst uint i01 = group.x;\n\tconst uint i02 = group.y;\n\tconst uint i03 = group.z;\n\n\tconst uint rs = ne00 * nb00;\n\t//const uint id = i01 + i02 * ne02 + i03 * ne01 * ne02;\n\tconst uint id = ( i03 * ne01 + i02 ) * ne02 + i01;\n\n\tuint rsi = i01 * nb01 + i02 * nb02 + i03 * nb03;\n\tuint rdi = id * rs;\n\n\tconst uint rsiEnd = rsi + rs;\n\trsi += thread;\n\trdi += thread;\n\tfor( ; rsi < rsiEnd; rsi += 32, rdi += 32 )\n\t{\n\t\tfloat f = arg0[ rsi ];\n\t\t[branch]\n\t\tif( downcastFp32 )\n\t\t\tf = adjustFp16( f );\n\t\tresult[ rdi ] = f;\n\t}\n}"
  },
  {
    "path": "ComputeShaders/copyTranspose.hlsl",
    "content": "// ggml_compute_forward_dup_f32 when we actually need to reshape the tensor\n// Dispatch [ ne01, ne02, ne03 ] thread groups of this shader\nBuffer<float> arg0: register( t0 );\nRWBuffer<float> result: register( u0 );\n\ncbuffer Constants: register( b0 )\n{\n\tuint4 src0_elements: packoffset( c0 );\n\tuint4 src0_strides: packoffset( c1 );\n\tbool downcastFp32 : packoffset( c2.x );\n}\n\n#include \"miscUtils.hlsli\"\n\n[ numthreads( 32, 1, 1 ) ]\nvoid main( uint3 group: SV_GroupID, uint thread : SV_GroupIndex )\n{\n\tconst uint nb00 = src0_strides[ 0 ];\n\tconst uint nb01 = src0_strides[ 1 ];\n\tconst uint nb02 = src0_strides[ 2 ];\n\tconst uint nb03 = src0_strides[ 3 ];\n\n\tconst uint ne00 = src0_elements[ 0 ];\n\tconst uint ne01 = src0_elements[ 1 ];\n\tconst uint ne02 = src0_elements[ 2 ];\n\tconst uint ne03 = src0_elements[ 3 ];\n\n\tconst uint i01 = group.x;\n\tconst uint i02 = group.y;\n\tconst uint i03 = group.z;\n\n\t// We need following integer: i01*ne00 + i02*ne00*ne01 + i03*ne00*ne01*ne02\n\t// We want to minimize count of integer multiplications\n\t// Also, DXBC assembly features `imad` instruction which computes a*b+c for integers, the actual hardware hopefully has an equivalent\n\t// i03*ne00*ne01*ne02 + i02*ne00*ne01 + i01*ne00\n\t// ( i03*ne01*ne02 + i02*ne01 + i01 ) * ne00\n\t// ( ( i03*ne02 + i02) * ne01 + i01 ) * ne00\n\tuint rdi = ( ( i03 * ne02 + i02 ) * ne01 + i01 ) * ne00;\n\n\tconst uint rdiEnd = rdi + ne00;\n\n\tuint rsi = i01 * nb01 + i02 * nb02 + i03 * nb03;\n\tconst uint rsiInc = 32 * nb00;\n\n\trdi += thread;\n\trsi += thread * nb00;\n\n\tfor( ; rdi < rdiEnd; rdi += 32, rsi += rsiInc )\n\t{\n\t\tfloat f = arg0[ rsi ];\n\t\t[branch]\n\t\tif( downcastFp32 )\n\t\t\tf = adjustFp16( f );\n\t\tresult[ rdi ] = f;\n\t}\n}"
  },
  {
    "path": "ComputeShaders/dbgFindNaN.hlsl",
    "content": "// When reset = TRUE, write zero to the output buffer\n// When reset = FALSE, test input tensor for NaN, when found at least 1 NaN element, write 1 to the output buffer\n\n// FP32 or FP16 tensor to test for NAN\nBuffer<float> tensor: register( t0 );\n// A buffer with a single element for the output boolean. Zero means there were no NAN values in the tensor.\nRWBuffer<uint> result: register( u0 );\n\ncbuffer Constants: register( b0 )\n{\n\tuint elements: packoffset( c0.x );\n\tbool reset : packoffset( c0.y );\n}\n\n// Thread group index is 16 bits per coordinate:\n// https://learn.microsoft.com/en-us/windows/win32/api/d3d11/nf-d3d11-id3d11devicecontext-dispatch\n// We want this shader to support buffers up to 2 GB.\n#ifndef THREADS\nstatic const uint THREADS = 512;\n#endif\n#ifndef ITERATIONS\nstatic const uint ITERATIONS = 128;\n#endif\n\nstatic const uint itemsPerGroup = THREADS * ITERATIONS;\n\ninline bool isNaN( float x )\n{\n\t// https://sakibsaikia.github.io/graphics/2022/01/04/Nan-Checks-In-HLSL.html\n\treturn ( asuint( x ) & 0x7fffffff ) > 0x7f800000;\n}\n\ngroupshared uint reductionBuffer;\n\n[numthreads( THREADS, 1, 1 )]\nvoid main( uint3 group: SV_GroupID, uint thread : SV_GroupIndex )\n{\n\tif( reset )\n\t{\n\t\tif( 0 == thread )\n\t\t\tresult[ 0 ] = 0;\n\t\treturn;\n\t}\n\n\tuint rsi = group.x * itemsPerGroup;\n\tconst uint rsiEnd = min( rsi + itemsPerGroup, elements );\n\n\t// The main loop updates a local variable. There're THREADS instances of that variable for the group of threads.\n\tbool foundNan = false;\n\tfor( rsi += thread; rsi < rsiEnd; rsi += THREADS )\n\t{\n\t\tconst float val = tensor[ rsi ];\n\t\tif( !isNaN( val ) )\n\t\t\tcontinue;\n\t\tfoundNan = true;\n\t\tbreak;\n\t}\n\n\t// Reduce THREADS booleans to a single one, using group shared memory atomics\n\tif( 0 == thread )\n\t\treductionBuffer = 0;\n\tGroupMemoryBarrierWithGroupSync();\n\n\tif( foundNan )\n\t\tInterlockedOr( reductionBuffer, 1u );\n\n\tGroupMemoryBarrierWithGroupSync();\n\n\t// When found, update output value with global memory atomic\n\tif( 0 != thread )\n\t\treturn;\n\tif( 0 == reductionBuffer )\n\t\treturn;\n\n\tInterlockedOr( result[ 0 ], 1u );\n}"
  },
  {
    "path": "ComputeShaders/diagMaskInf.hlsl",
    "content": "// ggml_compute_forward_diag_mask_inf_f32\nRWBuffer<float> result: register( u0 );\n\ncbuffer Constants: register( b0 )\n{\n\tuint4 elements: packoffset( c0 );\n\tuint4 strides: packoffset( c1 );\n\tuint n_past : packoffset( c2.x );\n}\n\nstatic const float negativeInfinity = asfloat( 0xff800000 );\n\n[numthreads( 32, 1, 1 )]\nvoid main( uint3 group: SV_GroupID, uint thread : SV_GroupIndex )\n{\n\tconst uint k = group.y;\n\tconst uint j = group.x;\n\n\t// Start of the row\n\tuint rdi = k * strides[ 2 ] + j * strides[ 1 ];\n\t// End of the row\n\tconst uint rdiEnd = rdi + elements[ 0 ] * strides[ 0 ];\n\t// First index to write in this thread\n\trdi += ( n_past + j + thread + 1 ) * strides[ 0 ];\n\t// Index increment\n\tconst uint rdiInc = 32 * strides[ 0 ];\n\n\tfor( ; rdi < rdiEnd; rdi += rdiInc )\n\t\tresult[ rdi ] = negativeInfinity;\n}"
  },
  {
    "path": "ComputeShaders/flashAttention.hlsl",
    "content": "// Ported from ggml_compute_forward_flash_attn_f16\n// Dispatch with [ neq1*neq2*neq3, 1, 1 ] thread groups\n\n#include \"flashAttentionCommon.hlsli\"\nBuffer<uint> lookupTable: register( t3 );\n#include \"groupReduce.hlsli\"\n\ninline void computeDotProduct( Buffer<float> buff0, Buffer<float> buff1, uint s0, uint s1, const uint len, const uint thread, inout float acc )\n{\n\tacc = 0;\n\tconst uint s0End = s0 + len;\n\ts0 += thread;\n\ts1 += thread;\n\tfor( ; s0 < s0End; s0 += 32, s1 += 32 )\n\t\tacc = mad( buff0[ s0 ], buff1[ s1 ], acc );\n\n\thorizontalSum( thread, acc );\n}\n\ninline void computeDotProduct( Buffer<float> buff0, RWBuffer<float> buff1, uint s0, uint s1, const uint len, const uint thread, inout float acc )\n{\n\tacc = 0;\n\tconst uint s0End = s0 + len;\n\ts0 += thread;\n\ts1 += thread;\n\tfor( ; s0 < s0End; s0 += 32, s1 += 32 )\n\t\tacc = mad( buff0[ s0 ], buff1[ s1 ], acc );\n\n\thorizontalSum( thread, acc );\n}\n\nvoid scaleTempVector( uint i, const uint length, const uint thread, const float multiplier, bool round )\n{\n\tconst uint end = i + length;\n\tfor( i += thread; i < end; i += 32 )\n\t{\n\t\tfloat f = temp[ i ];\n\t\tf *= multiplier;\n\t\tif( round )\n\t\t\tf = roundToFp16( f );\n\t\ttemp[ i ] = f;\n\t}\n}\n\n#include \"miscUtils.hlsli\"\n\n// Transform temp[ i ] = exp( temp[ i ] - tempMax ), and return the sum of these values\ninline float applySoftMax( uint i, const uint length, const uint thread, const float tempMax )\n{\n\t// Transform the values, and compute per-thread sum\n\tconst uint end = i + length;\n\tfloat sum = 0;\n\tfor( i += thread; i < end; i += 32 )\n\t{\n\t\tfloat f = temp[ i ];\n\t\t[branch]\n\t\tif( f != negativeInfinity )\n\t\t{\n\t\t\tf -= tempMax;\n\t\t\tconst uint index = fp16Rounded( f );\n\t\t\tconst uint res16 = lookupTable[ index ];\n\t\t\tf = f16tof32( res16 );\n\t\t}\n\t\telse\n\t\t\tf = 0;\n\n\t\ttemp[ i ] = f;\n\t\tsum += f;\n\t}\n\n\t// Reduce per-thread sum to the global one, over all threads of the group\n\thorizontalSumBroadcast( thread, sum );\n\treturn sum;\n}\n\n[ numthreads( 32, 1, 1 ) ]\nvoid main( uint3 group: SV_GroupID, uint thread : SV_GroupIndex )\n{\n\tconst uint neq0 = q_elements[ 0 ];\n\tconst uint neq1 = q_elements[ 1 ];\n\tconst uint neq2 = q_elements[ 2 ];\n\tconst uint neq3 = q_elements[ 3 ];\n\n\tconst uint nek0 = k_elements[ 0 ];\n\tconst uint nek1 = k_elements[ 1 ];\n\n\tconst uint nev1 = v_elements[ 1 ];\n\n\tconst uint ne0 = res_elements[ 0 ];\n\tconst uint ne1 = res_elements[ 1 ];\n\n\tconst uint nbk0 = k_strides[ 0 ];\n\tconst uint nbk1 = k_strides[ 1 ];\n\tconst uint nbk2 = k_strides[ 2 ];\n\tconst uint nbk3 = k_strides[ 3 ];\n\n\tconst uint nbq0 = q_strides[ 0 ];\n\tconst uint nbq1 = q_strides[ 1 ];\n\tconst uint nbq2 = q_strides[ 2 ];\n\tconst uint nbq3 = q_strides[ 3 ];\n\n\tconst uint nbv0 = v_strides[ 0 ];\n\tconst uint nbv1 = v_strides[ 1 ];\n\tconst uint nbv2 = v_strides[ 2 ];\n\tconst uint nbv3 = v_strides[ 3 ];\n\n\tconst uint nb0 = res_strides[ 0 ];\n\tconst uint nb1 = res_strides[ 1 ];\n\tconst uint nb2 = res_strides[ 2 ];\n\tconst uint nb3 = res_strides[ 3 ];\n\n\tconst uint D = neq0;\n\tconst uint N = neq1;\n\tconst uint P = nek1 - N;\n\tconst uint M = nek1;\n\n\tconst uint ir = group.x;\n\tconst uint iq3 = ir / ( neq2 * neq1 );\n\tconst uint iq2 = ( ir - iq3 * neq2 * neq1 ) / neq1;\n\tconst uint iq1 = ( ir - iq3 * neq2 * neq1 - iq2 * neq1 );\n\n\tconst uint tempIndex = ir * tempBufferStride;\n\n\tuint ic;\n\tfloat tvm = negativeInfinity;\n\tconst uint s1 = iq1 * nbq1 + iq2 * nbq2 + iq3 * nbq3;\n\tuint s0 = iq2 * nbk2 + iq3 * nbk3;\n\tfor( ic = 0; ic < nek1; ic++, s0 += nbk1 )\n\t{\n\t\tif( masked )\n\t\t{\n\t\t\tif( ic > P + iq1 )\n\t\t\t{\n\t\t\t\tif( 0 == thread )\n\t\t\t\t\ttemp[ tempIndex + ic ] = negativeInfinity;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\tfloat dp;\n\t\tcomputeDotProduct( k, q, s0, s1, neq0, thread, dp );\n\t\tif( 0 == thread )\n\t\t{\n\t\t\tdp *= scale;\n\t\t\ttemp[ tempIndex + ic ] = dp;\n\t\t\ttvm = max( tvm, dp );\n\t\t}\n\t}\n\n\tif( 0 == thread )\n\t\tsharedAccumulators[ 0 ] = tvm;\n\tGroupMemoryBarrierWithGroupSync();\n\ttvm = sharedAccumulators[ 0 ];\n\n\t// Softmax\n\t{\n\t\tfloat sum = applySoftMax( tempIndex, M, thread, tvm );\n\t\tscaleTempVector( tempIndex, M, thread, 1.0 / sum, true );\n\t}\n\n\ts0 = iq2 * nbv2 + iq3 * nbv3;\n\tuint rdi = iq1 * nb1 + iq2 * nb2 + iq3 * nb3;\n\tfor( ic = 0; ic < nev1; ic++, s0 += nbv1, rdi += nb0 )\n\t{\n\t\tfloat dp;\n\t\tcomputeDotProduct( v, temp, s0, tempIndex, nek1, thread, dp );\n\t\tif( 0 == thread )\n\t\t\tresult[ rdi ] = dp;\n\t}\n}"
  },
  {
    "path": "ComputeShaders/flashAttentionCommon.hlsli",
    "content": "// Ported from ggml_compute_forward_flash_attn_f16\n// Dispatch with [ neq1*neq2*neq3, 1, 1 ] thread groups\nBuffer<float> q: register( t0 );\nBuffer<float> k: register( t1 );\nBuffer<float> v: register( t2 );\n\nRWBuffer<float> result: register( u0 );\n// This temporary buffer should fit tempBufferStride * neq1 * neq2 * neq3 elements, FP32 precision\nRWBuffer<float> temp: register( u1 );\n\ncbuffer Constants: register( b0 )\n{\n\tuint4 q_elements: packoffset( c0 );\n\tuint4 q_strides: packoffset( c1 );\n\tuint4 k_elements: packoffset( c2 );\n\tuint4 k_strides: packoffset( c3 );\n\tuint4 v_elements: packoffset( c4 );\n\tuint4 v_strides: packoffset( c5 );\n\tuint4 res_elements: packoffset( c6 );\n\tuint4 res_strides: packoffset( c7 );\n\n\tbool masked : packoffset( c8.x );\n\t// 1.0 / sqrt( (double) D )\n\tfloat scale : packoffset( c8.y );\n\t// This number is required to be >= nek1, and ideally rounded up to either 32 (L2 line) or 128 (L1 line) bytes\n\tuint tempBufferStride: packoffset( c8.z );\n}\n\nstatic const float negativeInfinity = asfloat( 0xff800000 );\n\n// Convert FP32 number to FP16 using rounding to nearest, then upcast back to FP32\ninline float roundToFp16( const float src )\n{\n\tconst uint trunc16 = f32tof16( src );\n\tconst float trunc32 = f16tof32( trunc16 );\n\n\tconst uint truncExp = ( trunc16 >> 10 ) & 0x1F;\n\tif( truncExp != 0x1F )\n\t{\n\t\tconst uint next16 = trunc16 + 1;\n\t\tconst float next32 = f16tof32( next16 );\n\n\t\tconst float errTrunc = abs( src - trunc32 );\n\t\tconst float errNext = abs( src - next32 );\n\n\t\tif( errTrunc < errNext )\n\t\t{\n\t\t\t// Truncated was closer to the source\n\t\t\treturn trunc32;\n\t\t}\n\t\telse if( errTrunc > errNext )\n\t\t{\n\t\t\t// Truncated + 1 was closer to the source\n\t\t\treturn next32;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// Exactly half, doing banker's rounding to nearest even\n\t\t\treturn ( 0 == ( trunc16 & 1 ) ) ? trunc32 : next32;\n\t\t}\n\t}\n\telse\n\t{\n\t\t// INF or NAN\n\t\treturn trunc32;\n\t}\n}"
  },
  {
    "path": "ComputeShaders/flashAttentionCompat1.hlsl",
    "content": "// Dispatch with [ neq1*neq2*neq3, 1, 1 ] thread groups\n#include \"flashAttentionCommon.hlsli\"\n#include \"groupReduce.hlsli\"\n\ninline void computeDotProduct( Buffer<float> buff0, Buffer<float> buff1, uint s0, uint s1, const uint len, const uint thread, inout float acc )\n{\n\tacc = 0;\n\t/*\n\tconst uint s0End = s0 + len;\n\ts0 += thread;\n\ts1 += thread;\n\tfor( ; s0 < s0End; s0 += 32, s1 += 32 )\n\t\tacc = mad( buff0[ s0 ], buff1[ s1 ], acc );\n\thorizontalSumCompatNew( thread, acc );\n\t*/\n\n\tconst uint completeVectors = len / 32;\n\tuint i;\n\tfor( i = 0; i < completeVectors; i++, s0 += 32, s1 += 32 )\n\t\tacc = mad( buff0[ s0 + thread ], buff1[ s1 + thread ], acc );\n\n\thorizontalSumCompatNew( thread, acc );\n\n\tif( 0 == thread )\n\t{\n\t\tconst uint rem = len % 32;\n\t\tfor( i = 0; i < rem; i++ )\n\t\t{\n\t\t\tprecise float a = buff0[ s0 + i ];\n\t\t\tprecise float b = buff1[ s1 + i ];\n\t\t\tprecise float prod = a * b;\n\t\t\tacc += prod;\n\t\t}\n\t}\n}\n\nvoid scaleTempVector( uint i, const uint length, const uint thread, const float multiplier )\n{\n\tconst uint end = i + length;\n\tfor( i += thread; i < end; i += 32 )\n\t{\n\t\tfloat f = temp[ i ];\n\t\tf *= multiplier;\n\t\ttemp[ i ] = f;\n\t}\n}\n\n[ numthreads( 32, 1, 1 ) ]\nvoid main( uint3 group: SV_GroupID, uint thread : SV_GroupIndex )\n{\n\tconst uint neq0 = q_elements[ 0 ];\n\tconst uint neq1 = q_elements[ 1 ];\n\tconst uint neq2 = q_elements[ 2 ];\n\tconst uint neq3 = q_elements[ 3 ];\n\n\tconst uint nek0 = k_elements[ 0 ];\n\tconst uint nek1 = k_elements[ 1 ];\n\n\tconst uint nev1 = v_elements[ 1 ];\n\n\tconst uint ne0 = res_elements[ 0 ];\n\tconst uint ne1 = res_elements[ 1 ];\n\n\tconst uint nbk0 = k_strides[ 0 ];\n\tconst uint nbk1 = k_strides[ 1 ];\n\tconst uint nbk2 = k_strides[ 2 ];\n\tconst uint nbk3 = k_strides[ 3 ];\n\n\tconst uint nbq0 = q_strides[ 0 ];\n\tconst uint nbq1 = q_strides[ 1 ];\n\tconst uint nbq2 = q_strides[ 2 ];\n\tconst uint nbq3 = q_strides[ 3 ];\n\n\tconst uint nbv0 = v_strides[ 0 ];\n\tconst uint nbv1 = v_strides[ 1 ];\n\tconst uint nbv2 = v_strides[ 2 ];\n\tconst uint nbv3 = v_strides[ 3 ];\n\n\tconst uint nb0 = res_strides[ 0 ];\n\tconst uint nb1 = res_strides[ 1 ];\n\tconst uint nb2 = res_strides[ 2 ];\n\tconst uint nb3 = res_strides[ 3 ];\n\n\tconst uint D = neq0;\n\tconst uint N = neq1;\n\tconst uint P = nek1 - N;\n\t// const uint M = P + N;\n\tconst uint M = nek1;\n\n\tconst uint ir = group.x;\n\tconst uint iq3 = ir / ( neq2 * neq1 );\n\tconst uint iq2 = ( ir - iq3 * neq2 * neq1 ) / neq1;\n\tconst uint iq1 = ( ir - iq3 * neq2 * neq1 - iq2 * neq1 );\n\n\tconst uint tempIndex = ir * tempBufferStride;\n\n\tuint ic;\n\tfor( ic = 0; ic < nek1; ic++ )\n\t{\n\t\t// k indices\n\t\tconst uint ik3 = iq3;\n\t\tconst uint ik2 = iq2;\n\t\tconst uint ik1 = ic;\n\n\t\t// S indices\n\t\tconst uint i1 = ik1;\n\n\t\tif( masked )\n\t\t{\n\t\t\tif( ic > P + iq1 )\n\t\t\t{\n\t\t\t\tif( 0 == thread )\n\t\t\t\t\ttemp[ tempIndex + ic ] = negativeInfinity;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\tconst uint s0 = ik1 * nbk1 + ik2 * nbk2 + ik3 * nbk3;\n\t\tconst uint s1 = iq1 * nbq1 + iq2 * nbq2 + iq3 * nbq3;\n\t\tfloat dp;\n\t\tcomputeDotProduct( k, q, s0, s1, neq0, thread, dp );\n\t\tif( 0 == thread )\n\t\t\ttemp[ tempIndex + ic ] = dp * scale;\n\t}\n}"
  },
  {
    "path": "ComputeShaders/flashAttentionCompat2.hlsl",
    "content": "// Dispatch with [ ( neq1*neq2*neq3 + 31 ) / 32, 1, 1 ] thread groups\n#include \"flashAttentionCommon.hlsli\"\nBuffer<uint> lookupTable: register( t3 );\n\nvoid scaleTempVector( uint i, const uint length, const float multiplier )\n{\n\tconst uint end = i + length;\n\tfor( ; i < end; i++ )\n\t{\n\t\tfloat f = temp[ i ];\n\t\tf *= multiplier;\n\t\t// Rounding in this shader causes numerical errors on my GeForce 1080 Ti GPU, driver 527.56\n\t\t// f = roundToFp16( f );\n\t\ttemp[ i ] = f;\n\t}\n}\n\ninline float computeTempVectorMax( uint i, const uint length )\n{\n\t// Compute per-thread maximum\n\tconst uint end = i + length;\n\tfloat ax = negativeInfinity;\n\tfor( ; i < end; i++ )\n\t\tax = max( ax, temp[ i ] );\n\treturn ax;\n}\n\n#include \"miscUtils.hlsli\"\n#include \"fp64Utils.hlsli\"\n\n// Transform temp[ i ] = exp( temp[ i ] - tempMax ), and return the sum of these values\ninline double applySoftMax( uint i, const uint length, const float tempMax )\n{\n\t// Transform the values, and compute per-thread sum\n\tconst uint end = i + length;\n\tdouble sum = 0;\n\tfor( ; i < end; i++ )\n\t{\n\t\tfloat f = temp[ i ];\n\t\t[branch]\n\t\tif( f != negativeInfinity )\n\t\t{\n\t\t\tf -= tempMax;\n\t\t\tconst uint index = fp16Rounded( f );\n\t\t\tconst uint res16 = lookupTable[ index ];\n\t\t\tf = f16tof32( res16 );\n\t\t\tsum += f;\n\t\t}\n\t\telse\n\t\t\tf = 0;\n\n\t\ttemp[ i ] = f;\n\t}\n\treturn sum;\n}\n\n[ numthreads( 32, 1, 1 ) ]\nvoid main( uint3 dtid: SV_DispatchThreadID )\n{\n\tconst uint neq0 = q_elements[ 0 ];\n\tconst uint neq1 = q_elements[ 1 ];\n\tconst uint neq2 = q_elements[ 2 ];\n\tconst uint neq3 = q_elements[ 3 ];\n\n\tconst uint nek0 = k_elements[ 0 ];\n\tconst uint nek1 = k_elements[ 1 ];\n\n\tconst uint nev1 = v_elements[ 1 ];\n\n\tconst uint ne0 = res_elements[ 0 ];\n\tconst uint ne1 = res_elements[ 1 ];\n\n\tconst uint nbk0 = k_strides[ 0 ];\n\tconst uint nbk1 = k_strides[ 1 ];\n\tconst uint nbk2 = k_strides[ 2 ];\n\tconst uint nbk3 = k_strides[ 3 ];\n\n\tconst uint nbq0 = q_strides[ 0 ];\n\tconst uint nbq1 = q_strides[ 1 ];\n\tconst uint nbq2 = q_strides[ 2 ];\n\tconst uint nbq3 = q_strides[ 3 ];\n\n\tconst uint nbv0 = v_strides[ 0 ];\n\tconst uint nbv1 = v_strides[ 1 ];\n\tconst uint nbv2 = v_strides[ 2 ];\n\tconst uint nbv3 = v_strides[ 3 ];\n\n\tconst uint nb0 = res_strides[ 0 ];\n\tconst uint nb1 = res_strides[ 1 ];\n\tconst uint nb2 = res_strides[ 2 ];\n\tconst uint nb3 = res_strides[ 3 ];\n\n\tconst uint D = neq0;\n\tconst uint N = neq1;\n\tconst uint P = nek1 - N;\n\t// const uint M = P + N;\n\tconst uint M = nek1;\n\n\tconst uint ir = dtid.x;\n\tif( ir >= neq1 * neq2 * neq3 )\n\t\treturn;\n\n\tconst uint iq3 = ir / ( neq2 * neq1 );\n\tconst uint iq2 = ( ir - iq3 * neq2 * neq1 ) / neq1;\n\tconst uint iq1 = ( ir - iq3 * neq2 * neq1 - iq2 * neq1 );\n\n\tconst uint tempIndex = ir * tempBufferStride;\n\n\t// Softmax\n\tfloat tvm = computeTempVectorMax( tempIndex, M );\n\tdouble sum = applySoftMax( tempIndex, M, tvm );\n\n\tscaleTempVector( tempIndex, M, (float)( 1.0 / sum ) );\n}"
  },
  {
    "path": "ComputeShaders/flashAttentionCompat3.hlsl",
    "content": "// Dispatch with [ neq1*neq2*neq3, 1, 1 ] thread groups\n#include \"flashAttentionCommon.hlsli\"\n#include \"groupReduce.hlsli\"\n#include \"miscUtils.hlsli\"\n\ninline void roundTempVector( uint i, const uint len, const uint thread )\n{\n\tconst uint iEnd = i + len;\n\tfor( i += thread; i < iEnd; i += 32 )\n\t{\n\t\tfloat f = temp[ i ];\n\t\tf = roundToFp16( f );\n\t\ttemp[ i ] = f;\n\t}\n}\n\ninline void computeDotProduct( Buffer<float> buff0, RWBuffer<float> buff1, uint s0, uint s1, const uint len, const uint thread, inout float acc )\n{\n\tacc = 0;\n/*\tconst uint s0End = s0 + len;\n\ts0 += thread;\n\ts1 += thread;\n\tfor( ; s0 < s0End; s0 += 32, s1 += 32 )\n\t\tacc = mad( buff0[ s0 ], buff1[ s1 ], acc );\n\n\thorizontalSumCompatNew( thread, acc ); */\n\tconst uint completeVectors = len / 32;\n\tuint i;\n\tfor( i = 0; i < completeVectors; i++, s0 += 32, s1 += 32 )\n\t\tacc = mad( buff0[ s0 + thread ], buff1[ s1 + thread ], acc );\n\n\thorizontalSumCompatNew( thread, acc );\n\n\tif( 0 == thread )\n\t{\n\t\tconst uint rem = len % 32;\n\t\tif( 0 != rem )\n\t\t{\n\t\t\tdouble f64 = acc;\n\t\t\tfor( i = 0; i < rem; i++ )\n\t\t\t{\n\t\t\t\tprecise float a = buff0[ s0 + i ];\n\t\t\t\tprecise float b = buff1[ s1 + i ];\n\t\t\t\tprecise float prod = a * b;\n\t\t\t\tf64 += prod;\n\t\t\t}\n\t\t\tacc = (float)f64;\n\t\t}\n\t}\n}\n\n[ numthreads( 32, 1, 1 ) ]\nvoid main( uint3 group: SV_GroupID, uint thread : SV_GroupIndex )\n{\n\tconst uint neq0 = q_elements[ 0 ];\n\tconst uint neq1 = q_elements[ 1 ];\n\tconst uint neq2 = q_elements[ 2 ];\n\tconst uint neq3 = q_elements[ 3 ];\n\n\tconst uint nek0 = k_elements[ 0 ];\n\tconst uint nek1 = k_elements[ 1 ];\n\n\tconst uint nev1 = v_elements[ 1 ];\n\n\tconst uint ne0 = res_elements[ 0 ];\n\tconst uint ne1 = res_elements[ 1 ];\n\n\tconst uint nbk0 = k_strides[ 0 ];\n\tconst uint nbk1 = k_strides[ 1 ];\n\tconst uint nbk2 = k_strides[ 2 ];\n\tconst uint nbk3 = k_strides[ 3 ];\n\n\tconst uint nbq0 = q_strides[ 0 ];\n\tconst uint nbq1 = q_strides[ 1 ];\n\tconst uint nbq2 = q_strides[ 2 ];\n\tconst uint nbq3 = q_strides[ 3 ];\n\n\tconst uint nbv0 = v_strides[ 0 ];\n\tconst uint nbv1 = v_strides[ 1 ];\n\tconst uint nbv2 = v_strides[ 2 ];\n\tconst uint nbv3 = v_strides[ 3 ];\n\n\tconst uint nb0 = res_strides[ 0 ];\n\tconst uint nb1 = res_strides[ 1 ];\n\tconst uint nb2 = res_strides[ 2 ];\n\tconst uint nb3 = res_strides[ 3 ];\n\n\tconst uint D = neq0;\n\tconst uint N = neq1;\n\tconst uint P = nek1 - N;\n\t// const uint M = P + N;\n\tconst uint M = nek1;\n\n\tconst uint ir = group.x;\n\tconst uint iq3 = ir / ( neq2 * neq1 );\n\tconst uint iq2 = ( ir - iq3 * neq2 * neq1 ) / neq1;\n\tconst uint iq1 = ( ir - iq3 * neq2 * neq1 - iq2 * neq1 );\n\n\tconst uint tempIndex = ir * tempBufferStride;\n\n\troundTempVector( tempIndex, nek1, thread );\n\tAllMemoryBarrierWithGroupSync();\n\n\tuint rdi = iq1 * nb1 + iq2 * nb2 + iq3 * nb3;\n\tfor( uint ic = 0; ic < nev1; ic++, rdi += nb0 )\n\t{\n\t\t// dst indices\n\t\tconst uint i1 = iq1;\n\t\tconst uint i2 = iq2;\n\t\tconst uint i3 = iq3;\n\n\t\tconst uint s0 = ic * nbv1 + i2 * nbv2 + i3 * nbv3;\n\t\tfloat dp;\n\t\tcomputeDotProduct( v, temp, s0, tempIndex, nek1, thread, dp );\n\t\tif( 0 == thread )\n\t\t\tresult[ rdi ] = dp;\n\t}\n}"
  },
  {
    "path": "ComputeShaders/fmaRepeat1.hlsl",
    "content": "// Implementation of fmaRepeat() when both source arguments have same size and strides\n// Dispatch [ nb[ 1 ], nb[ 2 ], nb[ 3 ] ] thread groups of this shader, where nb is size of the destination tensor\nRWBuffer<float> tensor: register( u0 );\nBuffer<float> patternMul: register( t0 );\nBuffer<float> patternAdd: register( t1 );\n\ncbuffer Constants: register( b0 )\n{\n\tuint4 tensorSize: packoffset( c0 );\n\tuint4 tensorStrides: packoffset( c1 );\n\tuint4 patternSize: packoffset( c2 );\n\tuint4 patternStrides: packoffset( c3 );\n}\n\n#ifndef THREADS\n#define THREADS 512\n#endif\n\n#include \"repeatUtils.hlsli\"\n\ninline void computeSimple( uint idx, float mul, float add )\n{\n\tprecise float f = tensor[ idx ];\n\tf *= mul;\n\tf += add;\n\ttensor[ idx ] = f;\n}\n\n[ numthreads( THREADS, 1, 1 ) ]\nvoid main( uint3 group: SV_GroupID, uint thread : SV_GroupIndex )\n{\n\tuint3 it = tensorIteratorState( group, thread, tensorSize, tensorStrides );\n\tuint rsi = rowOffset( group % patternSize.yzw, patternStrides );\n\n\tif( patternSize[ 0 ] == 1 )\n\t{\n\t\t// The pattern only has 1 column - broadcasting over the row\n\t\tconst float pMul = patternMul[ rsi ];\n\t\tconst float pAdd = patternAdd[ rsi ];\n\t\tROW_LOOP( it )\n\t\t\tcomputeSimple( it.x, pMul, pAdd );\n\t}\n\telse if( patternSize[ 0 ] <= THREADS )\n\t{\n\t\t// pattern size doesn't exceed thread group size: load pattern value outside of the loop\n\t\tconst uint threadsPerGroup = THREADS - ( THREADS % patternSize[ 0 ] );\n\t\tif( thread >= threadsPerGroup )\n\t\t\treturn;\n\n\t\trsi += ( thread % patternSize[ 0 ] ) * patternStrides[ 0 ];\n\t\tconst float pMul = patternMul[ rsi ];\n\t\tconst float pAdd = patternAdd[ rsi ];\n\t\tROW_LOOP_EX( it, threadsPerGroup, tensorStrides )\n\t\t\tcomputeSimple( it.x, pMul, pAdd );\n\t}\n\telse\n\t{\n\t\t// Pattern rows are larger than the thread group, need to stream from both buffers\n\t\tconst uint rsiInc = THREADS * patternStrides[ 0 ];\n\t\tconst uint rsiDec = patternSize[ 0 ] * patternStrides[ 0 ];\n\t\tconst uint rsiEnd = rsi + rsiDec;\n\t\trsi += thread * patternStrides[ 0 ];\n\n\t\tROW_LOOP( it )\n\t\t{\n\t\t\tprecise float f = tensor[ it.x ];\n\t\t\tfloat mul = patternMul[ rsi ];\n\t\t\tfloat add = patternAdd[ rsi ];\n\t\t\trsi += rsiInc;\n\t\t\tif( rsi >= rsiEnd )\n\t\t\t\trsi -= rsiDec;\n\t\t\tf *= mul;\n\t\t\tf += add;\n\t\t\ttensor[ it.x ] = f;\n\t\t}\n\t}\n}"
  },
  {
    "path": "ComputeShaders/fmaRepeat164.hlsl",
    "content": "#define THREADS 64\n#include \"fmaRepeat1.hlsl\""
  },
  {
    "path": "ComputeShaders/fmaRepeat2.hlsl",
    "content": "// Implementation of fmaRepeat() when source arguments have different shape or VRAM layout\n// Dispatch [ nb[ 1 ], nb[ 2 ], nb[ 3 ] ] thread groups of this shader, where nb is size of the destination tensor\nRWBuffer<float> tensor: register( u0 );\nBuffer<float> patternMul: register( t0 );\nBuffer<float> patternAdd: register( t1 );\n\ncbuffer Constants: register( b0 )\n{\n\tuint4 tensorSize: packoffset( c0 );\n\tuint4 tensorStrides: packoffset( c1 );\n\tuint4 patternSizeMul: packoffset( c2 );\n\tuint4 patternStridesMul: packoffset( c3 );\n\tuint4 patternSizeAdd: packoffset( c4 );\n\tuint4 patternStridesAdd: packoffset( c5 );\n}\n\n#ifndef THREADS\n#define THREADS 32\n#endif\n\n#include \"repeatUtils.hlsli\"\n\ninline float loadPattern( Buffer<float> buffer, uint rowStart, uint i, uint4 size, uint4 stride )\n{\n\ti %= size.x;\n\treturn buffer[ i * stride.x + rowStart ];\n}\n\n[ numthreads( THREADS, 1, 1 ) ]\nvoid main( uint3 group: SV_GroupID, uint thread : SV_GroupIndex )\n{\n\tuint3 it = tensorIteratorState( group, thread, tensorSize, tensorStrides );\n\tconst uint rsiMul = rowOffset( group % patternSizeMul.yzw, patternStridesMul );\n\tconst uint rsiAdd = rowOffset( group % patternSizeAdd.yzw, patternStridesAdd );\n\n\tfor( uint i = thread; it.x < it.z; it.x += it.y, i++ )\n\t{\n\t\tprecise float f = tensor[ it.x ];\n\t\tfloat mul = loadPattern( patternMul, rsiMul, i, patternSizeMul, patternStridesMul );\n\t\tfloat add = loadPattern( patternAdd, rsiAdd, i, patternSizeAdd, patternStridesAdd );\n\t\tf *= mul;\n\t\tf += add;\n\t\ttensor[ it.x ] = f;\n\t}\n}"
  },
  {
    "path": "ComputeShaders/fp64Utils.hlsli",
    "content": "// TODO: compile another version of these shader, and use it on GPUs with ExtendedDoublesShaderInstructions flag, will become slightly faster\n// https://learn.microsoft.com/en-us/windows/win32/api/d3d11/ns-d3d11-d3d11_feature_data_d3d11_options\n#ifndef ExtendedDoublesShaderInstructions\n#define ExtendedDoublesShaderInstructions 0\n#endif\n\n// Compute num/den in FP64 precision\ninline double div64( double num, double den )\n{\n#if ExtendedDoublesShaderInstructions\n\treturn num / den;\n#else\n\t// https://en.wikipedia.org/wiki/Division_algorithm#Newton%E2%80%93Raphson_division\n\tdouble x = 1.0f / (float)den;\n\tx += x * ( 1.0 - den * x );\n\tx += x * ( 1.0 - den * x );\n\treturn num * x;\n#endif\n}\n\n// Compute sqrt(x) in FP64 precision\ninline double sqrt64( double x )\n{\n\tdouble root = sqrt( (float)x );\n\troot = 0.5 * ( root + div64( x, root ) );\n\troot = 0.5 * ( root + div64( x, root ) );\n\treturn root;\n}"
  },
  {
    "path": "ComputeShaders/groupReduce.hlsli",
    "content": "groupshared float sharedAccumulators[ 32 ];\n\n// Compute horisontal sum of the numbers. The result is only correct on the thread #0 of the group.\nvoid horizontalSum( const uint thread, inout float sum )\n{\n\tsharedAccumulators[ thread ] = sum;\n\tfor( uint i = 16; i > 1; i /= 2 )\n\t{\n\t\tGroupMemoryBarrierWithGroupSync();\n\t\tif( thread < i )\n\t\t{\n\t\t\tsum += sharedAccumulators[ thread + i ];\n\t\t\tsharedAccumulators[ thread ] = sum;\n\t\t}\n\t}\n\tGroupMemoryBarrierWithGroupSync();\n\tif( 0 == thread )\n\t\tsum += sharedAccumulators[ 1 ];\n}\n\n// Compute horisontal sum of the numbers, and broadcast to all threads of the group.\nvoid horizontalSumBroadcast( const uint thread, inout float sum )\n{\n\thorizontalSum( thread, sum );\n\tif( 0 == thread )\n\t\tsharedAccumulators[ 0 ] = sum;\n\tGroupMemoryBarrierWithGroupSync();\n\tsum = sharedAccumulators[ 0 ];\n}\n\n// Compute horisontal sum of the numbers, in the order equal to the CPU-running dot product implementation.\n// The result is only correct on the thread #0 of the group.\nvoid horizontalSumCompat( const uint thread, inout float sum )\n{\n\tsharedAccumulators[ thread ] = sum;\n\tGroupMemoryBarrierWithGroupSync();\n\n\tif( 0 == ( thread & 8 ) )\n\t{\n\t\t// This runs on threads [ 0 .. 7 ] and [ 16 .. 23 ]\n\t\t// sum01 = _mm256_add_ps( sum0, sum1 );\n\t\t// sum23 = _mm256_add_ps( sum2, sum3 );\n\t\tsum += sharedAccumulators[ thread + 8 ];\n\t\tsharedAccumulators[ thread ] = sum;\n\t}\n\n\tGroupMemoryBarrierWithGroupSync();\n\tif( thread < 8 )\n\t{\n\t\t// This runs on threads [ 0 .. 7 ]\n\t\t// sum0123 = _mm256_add_ps( sum01, sum23 );\n\t\tsum += sharedAccumulators[ thread + 16 ];\n\t\tsharedAccumulators[ thread ] = sum;\n\t}\n\n\tGroupMemoryBarrierWithGroupSync();\n\tif( thread < 4 )\n\t{\n\t\t// const __m128 r4 = _mm_add_ps( _mm256_castps256_ps128( sum0123 ), _mm256_extractf128_ps( sum0123, 1 ) );\n\t\tsum += sharedAccumulators[ thread + 4 ];\n\t\tsharedAccumulators[ thread ] = sum;\n\t}\n\n\tGroupMemoryBarrierWithGroupSync();\n\tif( thread < 2 )\n\t{\n\t\t// const __m128 r2 = _mm_add_ps( r4, _mm_movehl_ps( r4, r4 ) );\n\t\tsum += sharedAccumulators[ thread + 2 ];\n\t\tsharedAccumulators[ thread ] = sum;\n\t}\n\n\tGroupMemoryBarrierWithGroupSync();\n\tif( 0 == thread )\n\t{\n\t\t// const __m128 r1 = _mm_add_ss( r2, _mm_movehdup_ps( r2 ) );\n\t\tsum += sharedAccumulators[ 1 ];\n\t}\n}\n\n// Compute horisontal sum of the numbers, in yet another creative summation order recently implemented in the upstream\nvoid horizontalSumCompatNew( const uint thread, inout float sum )\n{\n\t// GGML_F32x8_REDUCE\n\tsharedAccumulators[ thread ] = sum;\n\tGroupMemoryBarrierWithGroupSync();\n\n\tif( 0 == ( thread & 8 ) )\n\t{\n\t\t// Runs on threads [ 0 .. 7 ] and [ 16 .. 23 ]\n\t\tsum += sharedAccumulators[ thread | 8 ];\n\t\tsharedAccumulators[ thread ] = sum;\n\t}\n\tGroupMemoryBarrierWithGroupSync();\n\n\tif( thread < 8 )\n\t{\n\t\t// Runs on threads [ 0 .. 7 ]\n\t\tsum += sharedAccumulators[ thread | 0x10 ];\n\t\tsharedAccumulators[ thread ] = sum;\n\t}\n\tGroupMemoryBarrierWithGroupSync();\n\n\tif( thread < 4 )\n\t{\n\t\t// Runs on threads [ 0 .. 3 ]\n\t\tsum += sharedAccumulators[ thread | 4 ];\n\t\tsharedAccumulators[ thread ] = sum;\n\t}\n\tGroupMemoryBarrierWithGroupSync();\n\n\tif( thread < 4 && 0 == ( thread & 1 ) )\n\t{\n\t\t// Runs on threads [ 0, 2 ]\n\t\tsum += sharedAccumulators[ thread | 1 ];\n\t\tsharedAccumulators[ thread ] = sum;\n\t}\n\tGroupMemoryBarrierWithGroupSync();\n\n\tif( 0 == thread )\n\t\tsum += sharedAccumulators[ 2 ];\n}\n\n\n// Compute horizontal maximum of the numbers, and broadcast to all threads of the group.\nvoid horizontalMaxBroadcast( const uint thread, inout float ax )\n{\n\tsharedAccumulators[ thread ] = ax;\n\tfor( uint i = 16; i > 0; i /= 2 )\n\t{\n\t\tGroupMemoryBarrierWithGroupSync();\n\t\tif( thread < i )\n\t\t{\n\t\t\tax = max( ax, sharedAccumulators[ thread + i ] );\n\t\t\tsharedAccumulators[ thread ] = ax;\n\t\t}\n\t}\n\tGroupMemoryBarrierWithGroupSync();\n\tax = sharedAccumulators[ 0 ];\n}"
  },
  {
    "path": "ComputeShaders/groupReduce64.hlsli",
    "content": "groupshared float sharedAccumulators[ 64 ];\n\n// Compute horisontal sum of the numbers. The result is only correct on the thread #0 of the group.\nvoid horizontalSum( const uint thread, inout float sum )\n{\n\tsharedAccumulators[ thread ] = sum;\n\tfor( uint i = 32; i > 1; i /= 2 )\n\t{\n\t\tGroupMemoryBarrierWithGroupSync();\n\t\tif( thread < i )\n\t\t{\n\t\t\tsum += sharedAccumulators[ thread + i ];\n\t\t\tsharedAccumulators[ thread ] = sum;\n\t\t}\n\t}\n\tGroupMemoryBarrierWithGroupSync();\n\tif( 0 == thread )\n\t\tsum += sharedAccumulators[ 1 ];\n}\n\n// Compute horisontal sum of the numbers, and broadcast to all threads of the group.\nvoid horizontalSumBroadcast( const uint thread, inout float sum )\n{\n\thorizontalSum( thread, sum );\n\tif( 0 == thread )\n\t\tsharedAccumulators[ 0 ] = sum;\n\tGroupMemoryBarrierWithGroupSync();\n\tsum = sharedAccumulators[ 0 ];\n}\n\n// Compute horizontal maximum of the numbers, and broadcast to all threads of the group.\nvoid horizontalMaxBroadcast( const uint thread, inout float ax )\n{\n\tsharedAccumulators[ thread ] = ax;\n\tfor( uint i = 32; i > 0; i /= 2 )\n\t{\n\t\tGroupMemoryBarrierWithGroupSync();\n\t\tif( thread < i )\n\t\t{\n\t\t\tax = max( ax, sharedAccumulators[ thread + i ] );\n\t\t\tsharedAccumulators[ thread ] = ax;\n\t\t}\n\t}\n\tGroupMemoryBarrierWithGroupSync();\n\tax = sharedAccumulators[ 0 ];\n}"
  },
  {
    "path": "ComputeShaders/matReshapePanels.hlsl",
    "content": "// This shader reshapes a matrix into the shape expected by mulMatTiledEx.hlsl and mulMatByRowTiledEx.hlsl compute shaders\n// It's called in runtime, also while loading models from disk.\n// So far, it's only used when running on AMD GPUs.\n#ifndef TILE_SIZE\nstatic const uint TILE_SIZE = 32;\n#endif\n\n// Input tensor\nBuffer<float> source: register( t0 );\n// Output tensor\nRWBuffer<float> result: register( u0 );\n\ncbuffer Constants: register( b0 )\n{\n\tuint4 arg0Size: packoffset( c0 );\n\tuint4 arg0Strides: packoffset( c1 );\n\t// Count of elements per panel\n\tuint panelSize : packoffset( c2.y );\n\t// Layer strides of the output matrix\n\tuint2 layerStrides: packoffset( c2.z );\n}\n\ninline uint hadd( uint2 v2 ) { return v2.x + v2.y; }\n\ngroupshared float tileBuffer[ TILE_SIZE ][ TILE_SIZE ];\n\n[ numthreads( TILE_SIZE, 1, 1 ) ]\nvoid main( const uint3 group: SV_GroupID, const uint thread : SV_GroupIndex )\n{\n\tuint rdi = hadd( group.yz * layerStrides );\n\trdi += group.x * panelSize;\n\trdi += thread;\n\n\tuint rsi = hadd( group.yz * arg0Strides.zw );\n\tconst uint baseY = group.x * TILE_SIZE;\n\tconst uint dispatchThread = baseY + thread;\n\t// Reshaping into a column major horizontal panel, height = TILE_SIZE, width = width of the source matrix\n\tuint width = arg0Size.x;\n\t// Usually TILE_SIZE; can be less for the last panel on the matrix when we need to generate zeros instead of loading these numbers\n\tconst uint height = min( TILE_SIZE, arg0Size.y - baseY );\n\n\tif( arg0Strides.x == 1 )\n\t{\n\t\t// The input matrix is row major, can improve performance with coalesced loads and group shared buffer.\n\t\trsi += baseY * arg0Strides.y;\n\n\t\tconst uint widthCompleteTiles = width / TILE_SIZE;\n\n\t\tif( height < TILE_SIZE )\n\t\t{\n\t\t\t// This thread group was dispatched for the last panel of the matrix, it doesn't have enough rows\n\t\t\t// Write zeros to the corresponding elements of the groupshared buffer\n\t\t\tfor( uint j = height; j < TILE_SIZE; j++ )\n\t\t\t\ttileBuffer[ thread ][ j ] = 0.0;\n\t\t}\n\n\t\tfor( uint i = 0; i < widthCompleteTiles; i++, rsi += TILE_SIZE )\n\t\t{\n\t\t\t// Load [ TILE_SIZE ] * [ TILE_SIZE ] block with fully coalesced loads, store to group shared buffer in transposed order\n\t\t\tuint rsiTile = rsi + thread;\n\t\t\tuint j;\n\t\t\tfor( j = 0; j < height; j++, rsiTile += arg0Strides.y )\n\t\t\t{\n\t\t\t\t// Each iteration of the loop loads a row of [ TILE_SIZE ] elements from the corresponding row of the source tensor\n\t\t\t\t// Fully coalesced load\n\t\t\t\tfloat f = source[ rsiTile ];\n\t\t\t\t// Random store but the local memory's fast, this works rather well in practice\n\t\t\t\ttileBuffer[ thread ][ j ] = f;\n\t\t\t}\n\n\t\t\tGroupMemoryBarrierWithGroupSync();\n\n\t\t\t// Copy from group shared buffer to output tensor\n\t\t\tfor( j = 0; j < TILE_SIZE; j++, rdi += TILE_SIZE )\n\t\t\t{\n\t\t\t\t// Fully coalesced loads and stores\n\t\t\t\tfloat f = tileBuffer[ j ][ thread ];\n\t\t\t\tresult[ rdi ] = f;\n\t\t\t}\n\n\t\t\tGroupMemoryBarrierWithGroupSync();\n\t\t}\n\n\t\twidth %= TILE_SIZE;\n\t\tif( 0 == width )\n\t\t\treturn;\n\t\trsi += thread * arg0Strides.y;\n\t}\n\telse\n\t\trsi += dispatchThread * arg0Strides.y;\n\n\tfor( uint i = 0; i < width; i++ )\n\t{\n\t\tfloat f;\n\t\t[branch]\n\t\tif( thread < height )\n\t\t\tf = source[ rsi ];\n\t\telse\n\t\t\tf = 0.0;\n\t\trsi += arg0Strides.x;\n\n\t\tresult[ rdi ] = f;\n\t\trdi += TILE_SIZE;\n\t}\n}"
  },
  {
    "path": "ComputeShaders/miscUtils.hlsli",
    "content": "// When GPUs are converting FP32 to FP16, they always truncate towards 0, documented there:\n// https://learn.microsoft.com/en-us/windows/win32/direct3d10/d3d10-graphics-programming-guide-resources-data-conversion#conververting-from-a-higher-range-representation-to-a-lower-range-representation\n// Whisper code uses _mm_cvtps_ph( x, 0 ), the 0 stands for \"Round to nearest even\": https://www.felixcloutier.com/x86/vcvtps2ph\n// This function adjusts FP32 value making it so that truncation towards 0 results in the value equal to what CPU is doing\ninline float adjustFp16( const float src )\n{\n\tconst uint trunc16 = f32tof16( src );\n\tconst float trunc32 = f16tof32( trunc16 );\n\n\tconst uint truncExp = ( trunc16 >> 10 ) & 0x1F;\n\tif( truncExp != 0x1F )\n\t{\n\t\tconst uint next16 = trunc16 + 1;\n\t\tconst float next32 = f16tof32( next16 );\n\n\t\tconst float errTrunc = abs( src - trunc32 );\n\t\tconst float errNext = abs( src - next32 );\n\n\t\tif( errTrunc < errNext )\n\t\t{\n\t\t\t// Truncated was closer to the source\n\t\t\treturn src;\n\t\t}\n\t\telse if( errTrunc > errNext )\n\t\t{\n\t\t\t// Truncated + 1 was closer to the source\n\t\t\treturn next32;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// Exactly half, doing banker's rounding to nearest even\n\t\t\treturn ( 0 == ( trunc16 & 1 ) ) ? src : next32;\n\t\t}\n\t}\n\telse\n\t{\n\t\t// INF or NAN\n\t\treturn src;\n\t}\n}\n\n// Convert FP32 number to FP16, using rounding to nearest\ninline uint fp16Rounded( const float src )\n{\n\tconst uint trunc16 = f32tof16( src );\n\tconst float trunc32 = f16tof32( trunc16 );\n\n\tconst uint truncExp = ( trunc16 >> 10 ) & 0x1F;\n\tif( truncExp != 0x1F )\n\t{\n\t\tconst uint next16 = trunc16 + 1;\n\t\tconst float next32 = f16tof32( next16 );\n\n\t\tconst float errTrunc = abs( src - trunc32 );\n\t\tconst float errNext = abs( src - next32 );\n\n\t\tif( errTrunc < errNext )\n\t\t{\n\t\t\t// Truncated was closer to the source\n\t\t\treturn trunc16;\n\t\t}\n\t\telse if( errTrunc > errNext )\n\t\t{\n\t\t\t// Truncated + 1 was closer to the source\n\t\t\treturn next16;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// Exactly half, doing banker's rounding to nearest even\n\t\t\treturn ( 0 == ( trunc16 & 1 ) ) ? trunc16 : next16;\n\t\t}\n\t}\n\telse\n\t{\n\t\t// INF or NAN\n\t\treturn trunc16;\n\t}\n}\n\n// Round up the number to be a multiple of 32\ninline uint roundUp32( uint x )\n{\n\treturn ( x + 31 ) & ( ~31u );\n}"
  },
  {
    "path": "ComputeShaders/mulMatByRow.hlsl",
    "content": "// Matrix * row product, like [ E0, E1, E2, E3 ] * [ E0, 1, E2, E3 ] = [ E1, 1, E2, E3 ]\n// Dispatch [ E1, E2, E3 ] groups of this shader\nBuffer<float> arg0: register( t0 );\nBuffer<float> arg1: register( t1 );\nRWBuffer<float> result: register( u0 );\n\ncbuffer Constants: register( b0 )\n{\n\tuint4 arg0Size: packoffset( c0 );\n\tuint4 arg0Strides: packoffset( c1 );\n\tuint4 arg1Size: packoffset( c2 );\n\tuint4 arg1Strides: packoffset( c3 );\n\tuint4 resultSize: packoffset( c4 );\n\tuint4 resultStrides: packoffset( c5 );\n}\n\n#include \"groupReduce.hlsli\"\n\ninline uint hadd( uint3 vec )\n{\n\treturn vec.x + vec.y + vec.z;\n}\ninline uint hadd( uint2 vec )\n{\n\treturn vec.x + vec.y;\n}\n\n[ numthreads( 32, 1, 1 ) ]\nvoid main( uint3 group: SV_GroupID, uint thread : SV_GroupIndex )\n{\n\tuint s0 = hadd( group * arg0Strides.yzw );\n\tuint s1 = hadd( group.yz * arg1Strides.zw );\n\tconst uint s0End = s0 + arg0Size.x * arg0Strides.x;\n\tconst uint s0Inc = 32 * arg0Strides.x;\n\tconst uint s1Inc = 32 * arg1Strides.x;\n\n\ts0 += thread * arg0Strides.x;\n\ts1 += thread * arg1Strides.x;\n\tfloat dp = 0;\n\tfor( ; s0 < s0End; s0 += s0Inc, s1 += s1Inc )\n\t\tdp = mad( arg0[ s0 ], arg1[ s1 ], dp );\n\n\thorizontalSum( thread, dp );\n\tif( 0 != thread )\n\t\treturn;\n\n\tconst uint rdi = group.x + hadd( group.yz * resultStrides.zw );\n\tresult[ rdi ] = dp;\n}"
  },
  {
    "path": "ComputeShaders/mulMatByRow64.hlsl",
    "content": "// Matrix * row product, like [ E0, E1, E2, E3 ] * [ E0, 1, E2, E3 ] = [ E1, 1, E2, E3 ]\n// Dispatch [ E1, E2, E3 ] groups of this shader\nBuffer<float> arg0: register( t0 );\nBuffer<float> arg1: register( t1 );\nRWBuffer<float> result: register( u0 );\n\ncbuffer Constants: register( b0 )\n{\n\tuint4 arg0Size: packoffset( c0 );\n\tuint4 arg0Strides: packoffset( c1 );\n\tuint4 arg1Size: packoffset( c2 );\n\tuint4 arg1Strides: packoffset( c3 );\n\tuint4 resultSize: packoffset( c4 );\n\tuint4 resultStrides: packoffset( c5 );\n}\n\ninline uint hadd( uint3 vec )\n{\n\treturn vec.x + vec.y + vec.z;\n}\ninline uint hadd( uint2 vec )\n{\n\treturn vec.x + vec.y;\n}\n\n// No idea why, but that particular configuration appears to be the fastest one on Ryzen 7 5700G iGPU\n// Not by much, though: when trying a few numbers I saw 1.30 - 1.42 seconds for this compute shader\nstatic const uint THREADS = 64;\nstatic const uint REDUCTION_BUFFER = 32;\ngroupshared float sharedAccumulators[ REDUCTION_BUFFER ];\n\n// Compute horisontal sum of the numbers. The result is only correct on the thread #0 of the group.\nvoid horizontalSum( const uint thread, inout float sum )\n{\n\tif( THREADS > REDUCTION_BUFFER )\n\t{\n\t\tfor( uint t = REDUCTION_BUFFER; t < THREADS; t += REDUCTION_BUFFER )\n\t\t{\n\t\t\t// Threads [ t .. t + REDUCTION_BUFFER ] store into the buffer\n\t\t\tif( thread >= t && thread < t + REDUCTION_BUFFER )\n\t\t\t\tsharedAccumulators[ thread - t ] = sum;\n\n\t\t\tGroupMemoryBarrierWithGroupSync();\n\n\t\t\t// Threads [ 0 .. REDUCTION_BUFFER ] increment their local sum with the value loaded from the buffer\n\t\t\tif( thread < REDUCTION_BUFFER )\n\t\t\t\tsum += sharedAccumulators[ thread ];\n\t\t}\n\t}\n\n\tif( thread < REDUCTION_BUFFER )\n\t\tsharedAccumulators[ thread ] = sum;\n\n\tfor( uint i = REDUCTION_BUFFER / 2; i > 1; i /= 2 )\n\t{\n\t\tGroupMemoryBarrierWithGroupSync();\n\t\tif( thread < i )\n\t\t{\n\t\t\tsum += sharedAccumulators[ thread + i ];\n\t\t\tsharedAccumulators[ thread ] = sum;\n\t\t}\n\t}\n\n\tGroupMemoryBarrierWithGroupSync();\n\tif( 0 == thread )\n\t\tsum += sharedAccumulators[ 1 ];\n}\n\n[ numthreads( THREADS, 1, 1 ) ]\nvoid main( uint3 group: SV_GroupID, uint thread : SV_GroupIndex )\n{\n\tuint s0 = hadd( group * arg0Strides.yzw );\n\tuint s1 = hadd( group.yz * arg1Strides.zw );\n\tconst uint s0End = s0 + arg0Size.x * arg0Strides.x;\n\tconst uint s0Inc = THREADS * arg0Strides.x;\n\tconst uint s1Inc = THREADS * arg1Strides.x;\n\n\ts0 += thread * arg0Strides.x;\n\ts1 += thread * arg1Strides.x;\n\tfloat dp = 0;\n\tfor( ; s0 < s0End; s0 += s0Inc, s1 += s1Inc )\n\t\tdp = mad( arg0[ s0 ], arg1[ s1 ], dp );\n\n\thorizontalSum( thread, dp );\n\tif( 0 != thread )\n\t\treturn;\n\n\tconst uint rdi = group.x + hadd( group.yz * resultStrides.zw );\n\tresult[ rdi ] = dp;\n}"
  },
  {
    "path": "ComputeShaders/mulMatByRowTiled.hlsl",
    "content": "// Matrix * row product, like [ E0, E1, E2, E3 ] * [ E0, 1, E2, E3 ] = [ E1, 1, E2, E3 ]\n// Dispatch [ ( E1 + TILE_Y - 1 ) / TILE_Y, E2, E3 ] thread groups of this shader\n// This one here is the second most expensive shader in the model, after matrix*matrix product.\n// Optimized heavily, as a result the readability ain't great.\n\n#ifndef TILE_Y\nstatic const uint TILE_Y = 64;\n#endif\n#ifndef THREADS_X\nstatic const uint THREADS_X = 32;\n#endif\n#ifndef THREADS_Y\nstatic const uint THREADS_Y = 16;\n#endif\n\nBuffer<float> arg0: register( t0 );\nBuffer<float> arg1: register( t1 );\nRWBuffer<float> result: register( u0 );\n\ncbuffer Constants: register( b0 )\n{\n\tuint4 arg0Size: packoffset( c0 );\n\tuint4 arg0Strides: packoffset( c1 );\n\tuint4 arg1Size: packoffset( c2 );\n\tuint4 arg1Strides: packoffset( c3 );\n\tuint4 resultSize: packoffset( c4 );\n\tuint4 resultStrides: packoffset( c5 );\n}\n\ninline uint hadd( uint2 vec )\n{\n\treturn vec.x + vec.y;\n}\n\n// Count of FP32 accumulators we need in every thread of the shader\nstatic const uint heightScalars = TILE_Y / THREADS_Y;\n// The local accumulators are float4 vectors, compute count of these vectors\nstatic const uint heightVectors = ( heightScalars + 3 ) / 4;\n\ngroupshared float4 reductionBuffer[ heightVectors ][ THREADS_Y ][ THREADS_X ];\n\n[numthreads( THREADS_X, THREADS_Y, 1 )]\nvoid main( uint3 group: SV_GroupID, uint3 thread : SV_GroupThreadID )\n{\n\tuint i;\n\t// Despite inside GPU cores, the shared memory is still much slower than registers\n\t// For this reason, this shader accumulates numbers in local variables. Only uses groupshared buffer for the final reduction.\n\tfloat4 acc[ heightVectors ];\n\t// Zero out the accumulators\n\t[unroll]\n\tfor( i = 0; i < heightVectors; i++ )\n\t\tacc[ i ] = 0.0;\n\n\t// Count of rows to compute in this thread group\n\tconst uint height = min( TILE_Y, arg0Size.y - group.x * TILE_Y );\n\n\tuint s0 = hadd( group.yz * arg0Strides.zw );   //< arg0 layer for the thread group\n\ts0 += group.x * TILE_Y * arg0Strides.y;        //< arg0 first row for the thread group\n\ts0 += hadd( arg0Strides.xy * thread.xy );      //< arg0 load index for the thread\n\n\tuint s1 = hadd( group.yz * arg1Strides.zw );   //< arg1 layer for the thread group\n\ts1 += thread.x * arg1Strides.x;                //< arg1 load index for the thread\n\n\tconst uint completeTiles = arg0Size.x / THREADS_X;\n\t// Each iteration of that loop loads THREADS_X elements from arg1,\n\t// a block of [ THREADS_X, height ] elements from arg0,\n\t// and accumulates these dot products in the local variables\n\tfor( uint t = 0; t < completeTiles; t++, s0 += THREADS_X * arg0Strides.x, s1 += THREADS_X * arg1Strides.x )\n\t{\n\t\t// Load THREADS_X elements from arg1\n\t\tconst float v1 = arg1[ s1 ];\n\n\t\tuint rsi = s0;\n\t\t[unroll]\n\t\tfor( i = 0; i < heightVectors; i++ )\n\t\t{\n\t\t\tfloat4 v0 = 0.0;\n\t\t\t// Load up to 4*THREADS_X elements from arg0\n\t\t\t[unroll]\n\t\t\tfor( uint j = 0; j < 4; j++, rsi += arg0Strides.y * THREADS_Y )\n\t\t\t{\n\t\t\t\tconst uint y = ( i * 4 + j ) * THREADS_Y + thread.y;\n\t\t\t\t[branch]\n\t\t\t\tif( y < height )\n\t\t\t\t\tv0[ j ] = arg0[ rsi ];\n\t\t\t}\n\t\t\t// Multiply + accumulate\n\t\t\tacc[ i ] = mad( v0, v1, acc[ i ] );\n\t\t}\n\t}\n\n\tconst uint rem = arg0Size.x % THREADS_X;\n\tif( thread.x < rem )\n\t{\n\t\t// E0 ain't a multiple of THREADS_X, we have a remainder\n\n\t\t// Load `rem` elements from arg1\n\t\tconst float v1 = arg1[ s1 ];\n\n\t\t[unroll]\n\t\tfor( i = 0; i < heightVectors; i++ )\n\t\t{\n\t\t\tfloat4 v0 = 0.0;\n\t\t\t// Load up to 4*rem elements from arg0\n\t\t\t[unroll]\n\t\t\tfor( uint j = 0; j < 4; j++, s0 += arg0Strides.y * THREADS_Y )\n\t\t\t{\n\t\t\t\tconst uint y = ( i * 4 + j ) * THREADS_Y + thread.y;\n\t\t\t\t[branch]\n\t\t\t\tif( y < height )\n\t\t\t\t\tv0[ j ] = arg0[ s0 ];\n\t\t\t}\n\t\t\t// Multiply + accumulate\n\t\t\tacc[ i ] = mad( v0, v1, acc[ i ] );\n\t\t}\n\t}\n\n\t// Now we need horizontal sum of these accumulators, reducing [height][THREADS_X] of them into [height][1] column\n\t// First, store local variables into the shared memory.\n\t[ unroll ]\n\tfor( i = 0; i < heightVectors; i++ )\n\t\treductionBuffer[ i ][ thread.y ][ thread.x ] = acc[ i ];\n\tGroupMemoryBarrierWithGroupSync();\n\n\t// Run reduction using that shared memory buffer\n\tfor( i = THREADS_X / 2; i > 1; i /= 2 )\n\t{\n\t\tif( thread.x < i )\n\t\t{\n\t\t\t[unroll]\n\t\t\tfor( uint iv = 0; iv < heightVectors; iv++ )\n\t\t\t{\n\t\t\t\tfloat4 that = reductionBuffer[ iv ][ thread.y ][ thread.x + i ];\n\t\t\t\tfloat4 tmp = acc[ iv ];\n\t\t\t\ttmp += that;\n\t\t\t\treductionBuffer[ iv ][ thread.y ][ thread.x ] = tmp;\n\t\t\t\tacc[ iv ] = tmp;\n\t\t\t}\n\t\t}\n\t\tGroupMemoryBarrierWithGroupSync();\n\t}\n\n\t// And finally, store that column to global memory.\n\t// Only running that code on the threads of the group with thread.x = 0, to save a few loads from the groupshared buffer\n\t// This allows to use registers instead, faster to access\n\tif( thread.x != 0 )\n\t\treturn;\n\n\tuint rdi = hadd( group.yz * resultStrides.zw );\n\trdi += ( group.x * TILE_Y + thread.y ) * resultStrides.x;\n\tconst uint rdiInc = THREADS_Y * resultStrides.x;\n\n\t[unroll]\n\tfor( i = 0; i < heightVectors; i++ )\n\t{\n\t\t// The previous loop had \"i > 1\" continue condition, it didn't complete the last step of the reduction\n\t\t// The following line is doing that last reduction step\n\t\tconst float4 resultVec = acc[ i ] + reductionBuffer[ i ][ thread.y ][ 1 ];\n\n\t\t// Conditionally store these 4 floats to the output tensor\n\t\t[unroll]\n\t\tfor( uint j = 0; j < 4; j++, rdi += rdiInc )\n\t\t{\n\t\t\tconst uint y = ( i * 4 + j ) * THREADS_Y + thread.y;\n\t\t\t[branch]\n\t\t\tif( y < height )\n\t\t\t\tresult[ rdi ] = resultVec[ j ];\n\t\t}\n\t}\n}"
  },
  {
    "path": "ComputeShaders/mulMatByRowTiledEx.hlsl",
    "content": "// matrix*row vector product, needs first argument reshaped into a sequence of horizontal column major panels\n#ifndef TILE_SIZE\nstatic const uint TILE_SIZE = 32;\n#endif\n#ifndef THREADS_Y\nstatic const uint THREADS_Y = 8;\n#endif\n\n// First tensor, reshaped into dense column major horizontal panels of size [ width, TILE_SIZE ]\nBuffer<float> arg0: register( t0 );\n// Second tensor, reshaped into dense column major horizontal panels of size [ width, TILE_SIZE ]\nBuffer<float> arg1: register( t1 );\n// FP32 output tensor, row major and continuous\nRWBuffer<float> result: register( u0 );\n\ncbuffer Constants: register( b0 )\n{\n\tuint4 arg0Size: packoffset( c0 );\n\tuint arg0panel: packoffset( c1.y );\n\tuint2 arg0LayerStrides: packoffset( c1.z );\n\t// uint4 arg1Size: packoffset( c2 );\n\tuint4 arg1Strides: packoffset( c3 );\n\tuint4 resultSize: packoffset( c4 );\n\tuint4 resultStrides: packoffset( c5 );\n}\n\ninline uint hadd4( const uint4 v )\n{\n\tconst uint2 v2 = v.xy + v.zw;\n\treturn v2.x + v2.y;\n}\n\ninline float hadd4( const float4 v )\n{\n\tconst float2 v2 = v.xy + v.zw;\n\treturn v2.x + v2.y;\n}\n\ngroupshared float reductionBuffer[ THREADS_Y ][ TILE_SIZE ];\n\n[numthreads( TILE_SIZE, THREADS_Y, 1 )]\nvoid main( const uint3 group: SV_GroupID, const uint3 thread : SV_GroupThreadID )\n{\n\tconst uint2 layer = group.yz;\n\t// Source offsets for the complete thread group\n\tuint2 rsi;\n\trsi.x = group.x * arg0panel + layer.x * arg0LayerStrides.x + layer.y * arg0LayerStrides.y;\n\trsi.y = layer.x * arg1Strides.z + layer.y * arg1Strides.w;\n\t// Apply source offsets for this particular thread\n\trsi.x += thread.y * TILE_SIZE + thread.x;\n\trsi.y += thread.y * arg1Strides.x;\n\n\tconst uint2 rsiInc = uint2( THREADS_Y * TILE_SIZE, THREADS_Y * arg1Strides.x );\n\n\tconst uint completeTiles = arg0Size.x / ( THREADS_Y * 4 );\n\tuint i;\n\tfloat4 acc = 0.0;\n\tfor( i = 0; i < completeTiles; i++ )\n\t{\n\t\t// Each iteration of this loop consumes THREADS_Y*4 columns from the arg0 panel, and THREADS_Y*4 values from arg1\n\t\tfloat4 v0, v1;\n\t\t[unroll]\n\t\tfor( uint j = 0; j < 4; j++, rsi += rsiInc )\n\t\t{\n\t\t\t// Load [ TILE_SIZE, THREADS_Y ] block from the first source tensor\n\t\t\tv0[ j ] = arg0[ rsi.x ];\n\t\t\t// Broadcast [ THREADS_Y ] row from the second source tensor\n\t\t\tv1[ j ] = arg1[ rsi.y ];\n\t\t}\n\n\t\t// Now we have [ TILE_SIZE, THREADS_Y * 4 ] block from the first source tensor in the v0 vector,\n\t\t// and [ THREADS_Y * 4 ] row from the second one in the v1 vector\n\t\t// Multiply and accumulate.\n\t\tacc = mad( v0, v1, acc );\n\t}\n\n\t// Handle the remainder columns, if any.\n\t// When present, their count is in [ 1 .. THREADS_Y * 4 - 1 ] interval\n\tconst uint rem = arg0Size.x % ( THREADS_Y * 4 );\n\tif( rem != 0 )\n\t{\n\t\tfloat4 v0 = 0.0, v1 = 0.0;\n\t\t[unroll]\n\t\tfor( uint j = 0; j < 4; j++, rsi += rsiInc )\n\t\t{\n\t\t\tconst uint x = ( j * THREADS_Y ) + thread.y;\n\t\t\tif( x < rem )\n\t\t\t{\n\t\t\t\tv0[ j ] = arg0[ rsi.x ];\n\t\t\t\tv1[ j ] = arg1[ rsi.y ];\n\t\t\t}\n\t\t}\n\t\tacc = mad( v0, v1, acc );\n\t}\n\n\t// We now have [ TILE_SIZE, THREADS_Y * 4 ] block in the local variables of this thread group\n\t// The group however only outputs [ TILE_SIZE ] elements max, need a reduction\n\tfloat acc1 = hadd4( acc );\n\treductionBuffer[ thread.y ][ thread.x ] = acc1;\n\tGroupMemoryBarrierWithGroupSync();\n\n\tfor( i = THREADS_Y / 2; i > 1; i /= 2 )\n\t{\n\t\tif( thread.y < i )\n\t\t{\n\t\t\tacc1 += reductionBuffer[ thread.y + i ][ thread.x ];\n\t\t\treductionBuffer[ thread.y ][ thread.x ] = acc1;\n\t\t}\n\t\tGroupMemoryBarrierWithGroupSync();\n\t}\n\n\tif( thread.y != 0 )\n\t\treturn;\n\n\tconst uint resultPos = group.x * TILE_SIZE;\n\tconst uint outputSize = min( TILE_SIZE, resultSize.x - resultPos );\n\tif( thread.x >= outputSize )\n\t\treturn;\n\n\tconst uint4 resultPos4 = uint4( resultPos + thread.x, 0, layer );\n\tconst uint rdi = hadd4( resultPos4 * resultStrides );\n\tresult[ rdi ] = acc1 + reductionBuffer[ 1 ][ thread.x ];\n}"
  },
  {
    "path": "ComputeShaders/mulMatByScalar.hlsl",
    "content": "// Matrix * scalar product, like [ 1, E1, E2, E3 ] * [ 1, 1, E2, E3 ] = [ E1, 1, E2, E3 ]\n// Dispatch [ E2, E3, 1 ] thread groups of this shader\nBuffer<float> arg0: register( t0 );\nBuffer<float> arg1: register( t1 );\nRWBuffer<float> result: register( u0 );\n\ncbuffer Constants: register( b0 )\n{\n\tuint4 arg0Size: packoffset( c0 );\n\tuint4 arg0Strides: packoffset( c1 );\n\tuint4 arg1Size: packoffset( c2 );\n\tuint4 arg1Strides: packoffset( c3 );\n\tuint4 resultSize: packoffset( c4 );\n\tuint4 resultStrides: packoffset( c5 );\n}\n\ninline uint hadd( uint2 vec )\n{\n\treturn vec.x + vec.y;\n}\n\n[ numthreads( 32, 1, 1 ) ]\nvoid main( uint3 group: SV_GroupID, uint thread : SV_GroupIndex )\n{\n\tconst float scalarValue = arg1[ hadd( group.xy * arg1Strides.zw ) ];\n\n\tuint s0 = hadd( group.xy * arg0Strides.zw );\n\tconst uint s0Inc = 32 * arg0Strides.y;\n\ts0 += thread * arg0Strides.y;\n\n\tuint rdi = hadd( group.xy * resultStrides.zw );\n\tconst uint rdiEnd = rdi + arg0Size.y;\n\trdi += thread;\n\n\tfor( ; rdi < rdiEnd; rdi += 32, s0 += s0Inc )\n\t{\n\t\tfloat f = arg0[ s0 ];\n\t\tf *= scalarValue;\n\t\tresult[ rdi ] = f;\n\t}\n}"
  },
  {
    "path": "ComputeShaders/mulMatDotMain.hlsl",
    "content": "// GGML_TASK_COMPUTE step for matrix*matrix product, where nb01 >= nb00;\n// Dispatch with [ ne11, ne01*ne02*ne03 ] thread groups\n// Each thread group computes a single dot product\nBuffer<float> arg0: register( t0 );\nBuffer<float> arg1: register( t1 );\nRWBuffer<float> result: register( u0 );\n\ncbuffer Constants: register( b0 )\n{\n\tuint4 src0_elements: packoffset( c0 );\n\tuint4 src0_strides: packoffset( c1 );\n\tuint4 src1_elements: packoffset( c2 );\n\tuint4 result_elements: packoffset( c4 );\n\tuint4 result_strides: packoffset( c5 );\n}\n\ninline uint product( uint3 vec )\n{\n\treturn vec.x * vec.y * vec.z;\n}\n\ninline uint product( uint4 vec )\n{\n\tuint2 tmp = vec.xy * vec.zw;\n\treturn tmp.x * tmp.y;\n}\n\ninline float dotProductInner( uint i0, uint i1, uint length, uint thread )\n{\n\tfloat res = 0;\n\tfor( uint i = thread; i < length; i += 32 )\n\t\tres = mad( arg0[ i0 + i ], arg1[ i1 + i ], res );\n\treturn res;\n}\n\n#include \"groupReduce.hlsli\"\n\n[numthreads( 32, 1, 1 )]\nvoid main( uint3 group: SV_GroupID, uint thread : SV_GroupIndex )\n{\n\tconst uint ne00 = src0_elements.x;\n\tconst uint ne01 = src0_elements.y;\n\tconst uint ne02 = src0_elements.z;\n\tconst uint ne03 = src0_elements.w;\n\n\tconst uint ne10 = src1_elements.x;\n\tconst uint ne11 = src1_elements.y;\n\tconst uint ne12 = src1_elements.z;\n\tconst uint ne13 = src1_elements.w;\n\n\tconst int nb00 = src0_strides.x;\n\tconst int nb01 = src0_strides.y;\n\tconst int nb02 = src0_strides.z;\n\tconst int nb03 = src0_strides.w;\n\n\t// total rows in src0\n\t// const int nr = ne01*ne02*ne03;\n\tconst uint nr = product( src0_elements.yzw );\n\n\tconst uint ir = group.y;\n\n\t// src0 indices\n\tconst uint i03 = ir / ( ne02 * ne01 );\n\tconst uint i02 = ( ir - i03 * ne02 * ne01 ) / ne01;\n\tconst uint i01 = ( ir - i03 * ne02 * ne01 - i02 * ne01 );\n\n\tconst uint i13 = i03;\n\tconst uint i12 = i02;\n\n\tconst uint i0 = i01;\n\tconst uint i2 = i02;\n\tconst uint i3 = i03;\n\n\t// src0_row = (ggml_fp16_t *) ((char *) src0->data + (i01*nb01 + i02*nb02 + i03*nb03));\n\t// src1_col = wdata + ( i13 * ne12 * ne11 + i12 * ne11 + 0 ) * ne00;\n\tconst uint src0_row = i01 * nb01 + i02 * nb02 + i03 * nb03;\n\tconst uint src1_col = ( i13 * ne12 * ne11 + i12 * ne11 ) * ne00;\n\n\tconst uint ic = group.x;\n\tfloat curr = dotProductInner( src0_row, src1_col + ic * ne00, ne00, thread );\n\thorizontalSumCompatNew( thread, curr );\n\n\tif( 0 != thread )\n\t\treturn;\n\n\tconst uint nb0 = result_strides.x;\n\tconst uint nb1 = result_strides.y;\n\tconst uint nb2 = result_strides.z;\n\tconst uint nb3 = result_strides.w;\n\n\tconst uint ne0 = result_elements.x;\n\t// float * dst_col = (float *) ((char *) dst->data + (i0*nb0 + 0*nb1 + i2*nb2 + i3*nb3));\n\tconst uint dst_col = i0 * nb0 + i2 * nb2 + i3 * nb3;\n\tresult[ dst_col + ic * ne0 ] = curr;\n}"
  },
  {
    "path": "ComputeShaders/mulMatDotReshape.hlsl",
    "content": "// GGML_TASK_INIT step for matrix*matrix product, where nb01 >= nb00;\n// Dispatch with [ ne11, ne12 ] groups\nBuffer<float> arg0: register( t0 );\nRWBuffer<float> result: register( u0 );\n\ncbuffer Constants: register( b0 )\n{\n\tuint4 src0_elements: packoffset( c0 );\n\tuint4 src0_strides: packoffset( c1 );\n}\n\n#include \"miscUtils.hlsli\"\n\n// Each thread group of this shader copies a single rows of the matrix\n[ numthreads( 32, 1, 1 ) ]\nvoid main( uint3 group: SV_GroupID, uint thread : SV_GroupIndex )\n{\n\tconst uint i12 = group.y;\n\tconst uint i11 = group.x;\n\tconst uint ne10 = src0_elements.x;\n\tconst uint ne11 = src0_elements.y;\n\tconst uint nb12 = src0_strides.z;\n\tconst uint nb11 = src0_strides.y;\n\n\tuint rdi = i11 * ne10 + i12 * ne10 * ne11;\n\tconst uint rdiEnd = rdi + ne10;\n\tuint rsi = i12 * nb12 + i11 * nb11;\n\trdi += thread;\n\trsi += thread;\n\n\tfor( ; rdi < rdiEnd; rdi += 32, rsi += 32 )\n\t\tresult[ rdi ] = adjustFp16( arg0[ rsi ] );\n}"
  },
  {
    "path": "ComputeShaders/mulMatMadMain.hlsl",
    "content": "// GGML_TASK_COMPUTE step for matrix*matrix product, where nb01 < nb00\nBuffer<float> arg0: register( t0 );\nBuffer<float> arg1: register( t1 );\nRWBuffer<float> resultTensor: register( u0 );\nRWBuffer<float> tempBuffer: register( u1 );\n\ncbuffer Constants: register( b0 )\n{\n\tuint4 aSize: packoffset( c0 );\n\tuint4 aStride: packoffset( c1 );\n\tuint4 bSize: packoffset( c2 );\n\tuint4 bStride: packoffset( c3 );\n\tuint4 resSize: packoffset( c4 );\n\tbool resultFp16 : packoffset( c5.x );\n\tuint ne: packoffset( c5.y );\n}\n\n#include \"miscUtils.hlsli\"\n\n// tempBuffer[ rdi .. ] = 0.0\ninline void writeTempZeros( uint rdi, const uint len, const uint thread )\n{\n\tconst uint rdiEnd = rdi + len;\n\tfor( rdi += thread; rdi < rdiEnd; rdi += 32 )\n\t\ttempBuffer[ rdi ] = 0.0;\n}\n\n// tempBuffer[ rdi .. ] += mul * arg0[ rsi .. ]\ninline void vectorMad( uint rsi, uint rdi, const uint len, const float mul, const uint thread )\n{\n\tconst uint rsiEnd = rsi + len;\n\trsi += thread;\n\trdi += thread;\n\tfor( ; rsi < rsiEnd; rsi += 32, rdi += 32 )\n\t{\n\t\tfloat f = tempBuffer[ rdi ];\n\t\tf = mad( mul, arg0[ rsi ], f );\n\t\t[branch]\n\t\tif( resultFp16 )\n\t\t\tf = adjustFp16( f );\n\t\ttempBuffer[ rdi ] = f;\n\t}\n}\n\n// resultTensor[ rdi .. ] = tempBuffer[ rsi .. ]\ninline void copyRow( uint rsi, uint rdi, const uint len, const uint thread )\n{\n\tconst uint rsiEnd = rsi + len;\n\trsi += thread;\n\trdi += thread;\n\tfor( ; rsi < rsiEnd; rsi += 32, rdi += 32 )\n\t{\n\t\tfloat f = tempBuffer[ rsi ];\n\t\tresultTensor[ rdi ] = f;\n\t}\n}\n\n// resultTensor[ rdi .. ] += tempBuffer[ rsi .. ]\ninline void addRow( uint rsi, uint rdi, const uint len, const uint thread )\n{\n\tconst uint rsiEnd = rsi + len;\n\trsi += thread;\n\trdi += thread;\n\tfor( ; rsi < rsiEnd; rsi += 32, rdi += 32 )\n\t{\n\t\tfloat f = resultTensor[ rdi ];\n\t\tf += tempBuffer[ rsi ];\n\t\tresultTensor[ rdi ] = f;\n\t}\n}\n\n[numthreads( 32, 1, 1 )]\nvoid main( const uint3 group: SV_GroupID, const uint thread : SV_GroupIndex )\n{\n\tconst uint i1 = group[ 0 ];\n\tconst uint i2 = group[ 1 ];\n\tconst uint i3 = group[ 2 ];\n\n\tconst uint ne00 = aSize[ 0 ];\n\tconst uint ne01 = aSize[ 1 ];\n\tconst uint ne02 = aSize[ 2 ];\n\tconst uint ne03 = aSize[ 3 ];\n\n\tconst uint ne10 = bSize[ 0 ];\n\tconst uint ne11 = bSize[ 1 ];\n\tconst uint ne12 = bSize[ 2 ];\n\tconst uint ne13 = bSize[ 3 ];\n\n\tconst uint ne0 = resSize[ 0 ];\n\tconst uint ne1 = resSize[ 1 ];\n\tconst uint ne2 = resSize[ 2 ];\n\tconst uint ne3 = resSize[ 3 ];\n\n\tconst uint nb00 = aStride[ 0 ];\n\tconst uint nb01 = aStride[ 1 ];\n\tconst uint nb02 = aStride[ 2 ];\n\tconst uint nb03 = aStride[ 3 ];\n\n\tconst uint nb10 = bStride[ 0 ];\n\tconst uint nb11 = bStride[ 1 ];\n\tconst uint nb12 = bStride[ 2 ];\n\tconst uint nb13 = bStride[ 3 ];\n\n\t// dst_row = wdata + wo + i3*ne2*ne1*ne0 + i2*ne1*ne0 + i1*ne0;\n\tconst uint tempRowThread0 = i3 * ne2 * ne1 * ne0 + i2 * ne1 * ne0 + i1 * ne0;\n\n\t// Faking 4 CPU threads trying to achieve bitwise compatibility with the CPU version\n\tconst uint nth = 4;\n\n\t// GGML_TASK_COMPUTE\n\t{\n\t\t// src0_col = src0->data + ( i00 * nb00 + i02 * nb02 + i03 * nb03 );\n\t\tconst uint aBase = i2 * nb02 + i3 * nb03;\n\t\t// src1_val = *      (float *) ((char *) src1->data + (i10*nb10 + i11*nb11 + i12*nb12 + i13*nb13));\n\t\tconst uint bBase = i1 * nb11 + i2 * nb12 + i3 * nb13;\n\n\t\t// total columns in src1\n\t\tconst uint nc = ne10;\n\t\t// columns per thread\n\t\tconst uint dc = ( nc + nth - 1 ) / nth;\n\n\t\tuint tempRow = tempRowThread0;\n\t\tfor( uint ith = 0; ith < nth; ith++, tempRow += ne )\n\t\t{\n\t\t\twriteTempZeros( tempRow, ne01, thread );\n\n\t\t\t// column range for this thread\n\t\t\tconst uint ic0 = dc * ith;\n\t\t\tconst uint ic1 = min( ic0 + dc, nc );\n\n\t\t\tfor( uint ic = ic0; ic < ic1; ic++ )\n\t\t\t{\n\t\t\t\tconst uint idxA = aBase + ic * aStride[ 0 ];\n\t\t\t\tconst uint idxB = bBase + ic * bStride[ 0 ];\n\t\t\t\tconst float bValue = arg1[ idxB ];\n\t\t\t\tvectorMad( idxA, tempRow, ne01, bValue, thread );\n\t\t\t}\n\t\t}\n\t}\n\n\t// GGML_TASK_FINALIZE\n\t{\n\t\tconst uint rdi = tempRowThread0;\n\t\t// const uint rdi = i1 * resSize[ 0 ] + i2 * resSize[ 0 ] * resSize[ 1 ] + i3 * resSize[ 0 ] * resSize[ 1 ] * resSize[ 2 ];\n\t\t// const uint rdi = ( ( i3 * resSize[ 2 ] + i2 ) * resSize[ 1 ] + i1 ) * resSize[ 0 ];\n\n\t\tuint tempRow = tempRowThread0;\n\t\tcopyRow( tempRow, rdi, ne01, thread );\n\n\t\ttempRow += ne;\n\t\tfor( uint ith = 1; ith < nth; ith++, tempRow += ne )\n\t\t\taddRow( tempRow, rdi, ne01, thread );\n\t}\n}"
  },
  {
    "path": "ComputeShaders/mulMatTiled.hlsl",
    "content": "// This compute shader implements matrix*matrix product, using tiling and many other tricks to improve the performance\n// This one here is _the_ most expensive shader in the model. Optimized heavily, as a result the readability ain't great.\n\n#ifndef TILE_SIZE\nstatic const uint TILE_SIZE = 32;\n#endif\n#ifndef THREADS_Y\nstatic const uint THREADS_Y = 8;\n#endif\n// The above values have a following constraint: TILE_SIZE = THREADS_Y * N * 4 where N is an integer\n\n#ifndef STREAM_SECOND_MATRIX\n// Funfact: enabling this on 1080Ti ruins the performance, by a factor of 3.5\n#define STREAM_SECOND_MATRIX 0\n#endif\n\n#ifndef LOAD_ORDER\n\n// Load with coalesced loads from global memory whenever possible, store into groupshared buffer with random stores\n// #define LOAD_ORDER bool2( ( 1 == arg0Strides[ 0 ] ) || ( 1 != arg0Strides[ 1 ] ), ( 1 == arg1Strides[ 0 ] ) || ( 1 != arg1Strides[ 1 ] ) )\n\n// Load with random loads from global memory, store into groupshared buffer with coalesced stores\n// On my AMD iGPU inside Ryzen 7 5700G, there's whopping 15% performance win with that tactics, from 6.67 to 5.66 seconds for this shader.\n// My nVidia GPU does about the same\n#define LOAD_ORDER bool2( false, true )\n\n#endif\n\nBuffer<float> arg0: register( t0 );\nBuffer<float> arg1: register( t1 );\nRWBuffer<float> result: register( u0 );\n\ncbuffer Constants: register( b0 )\n{\n\tuint4 arg0Size: packoffset( c0 );\n\tuint4 arg0Strides: packoffset( c1 );\n\tuint4 arg1Strides: packoffset( c3 );\n\tuint4 resultSize: packoffset( c4 );\n\tuint4 resultStrides: packoffset( c5 );\n}\n\ngroupshared float tile0[ TILE_SIZE ][ TILE_SIZE ];\n#if !STREAM_SECOND_MATRIX\ngroupshared float tile1[ TILE_SIZE ][ TILE_SIZE ];\n#endif\n\n// Count of FP32 accumulators we need in every thread of the shader\nstatic const uint heightScalars = TILE_SIZE / THREADS_Y;\n// The local accumulators are float4 vectors, compute count of these vectors\nstatic const uint heightVectors = ( heightScalars + 3 ) / 4;\n\n#if STREAM_SECOND_MATRIX\nvoid multiplyTiles( uint rsi, const uint3 thread, const uint w, const uint h, inout float4 acc[ heightVectors ] )\n{\n\tuint4 rsi4 = ( THREADS_Y * arg1Strides.y ) * uint4( 0, 1, 2, 3 ) + rsi;\n\t[unroll]\n\tfor( uint iv = 0; iv < heightVectors; iv++, rsi4 += THREADS_Y * 4 * arg1Strides.y )\n\t{\n\t\tfloat4 r = 0;\n\t\tuint4 rsiRow = rsi4;\n\t\tfor( uint j = 0; j < w; j++, rsiRow += arg1Strides.x )\n\t\t{\n\t\t\t// One TILE_SIZE * 4 bytes coalesced load, broadcasted into THREADS_Y copies\n\t\t\tconst float s0 = tile0[ j ][ thread.x ];\n\t\t\tfloat4 s1 = 0.0;\n\t\t\t[unroll]\n\t\t\tfor( uint k = 0; k < 4; k++ )\n\t\t\t{\n\t\t\t\tconst uint i = ( iv * 4 + k ) * THREADS_Y + thread.y;\n\t\t\t\tif( i < h )\n\t\t\t\t\ts1[ k ] = arg1[ rsiRow[ k ] ];\n\t\t\t}\n\t\t\t// Multiply and accumulate\n\t\t\tr = mad( s0, s1, r );\n\t\t}\n\t\t// Accumulate into the output tile\n\t\tacc[ iv ] += r;\n\t}\n}\n#else\n// Compute resTemp += tile0 * tile1, for TILE_SIZE^2 square matrices\n// The group size is TILE_SIZE*THREADS_Y threads in this shader\nvoid multiplyTiles( const uint3 thread, inout float4 acc[ heightVectors ] )\n{\n\t[unroll]\n\tfor( uint iv = 0; iv < heightVectors; iv++ )\n\t{\n\t\tfloat4 r = 0;\n\t\tfor( uint j = 0; j < TILE_SIZE; j++ )\n\t\t{\n\t\t\t// One TILE_SIZE * 4 bytes coalesced load, broadcasted into THREADS_Y copies\n\t\t\tconst float s0 = tile0[ j ][ thread.x ];\n\t\t\tfloat4 s1;\n\t\t\t[unroll]\n\t\t\tfor( uint k = 0; k < 4; k++ )\n\t\t\t{\n\t\t\t\tconst uint i = ( iv * 4 + k ) * THREADS_Y + thread.y;\n\t\t\t\t// THREADS_Y broadcasts, each one is 4 bytes broadcasted into TILE_SIZE copies\n\t\t\t\ts1[ k ] = tile1[ i ][ j ];\n\t\t\t}\n\t\t\t// Multiply and accumulate\n\t\t\tr = mad( s0, s1, r );\n\t\t}\n\t\t// Accumulate into the output tile\n\t\tacc[ iv ] += r;\n\t}\n}\n#endif\n\n// Note we transposed these tiles while loading\nvoid loadTile0( uint rsi, const uint3 thread, const uint w, const uint h, const bool rowMajor )\n{\n\tuint i;\n\tif( rowMajor )\n\t{\n\t\trsi += arg0Strides.y * thread.y;\n\t\tfor( i = thread.y; i < h; i += THREADS_Y, rsi += arg0Strides.y * THREADS_Y )\n\t\t{\n\t\t\tif( thread.x < w )\n\t\t\t\ttile0[ thread.x ][ i ] = arg0[ rsi + thread.x * arg0Strides.x ];\n\t\t\telse\n\t\t\t\ttile0[ thread.x ][ i ] = 0.0;\n\t\t}\n\t}\n\telse\n\t{\n\t\t// Unlike width which is smaller for the last tile, the height is always the same, and all these tiles are zero-initialized\n\t\tif( thread.x >= h )\n\t\t\treturn;\n\n\t\trsi += arg0Strides.x * thread.y;\n\t\tfor( i = thread.y; i < w; i += THREADS_Y, rsi += arg0Strides.x * THREADS_Y )\n\t\t\ttile0[ i ][ thread.x ] = arg0[ rsi + thread.x * arg0Strides.y ];\n\n\t\tif( i >= TILE_SIZE )\n\t\t\treturn;\n\t\tfor( ; i < TILE_SIZE; i += THREADS_Y )\n\t\t\ttile0[ i ][ thread.x ] = 0.0;\n\t}\n}\n\n#if !STREAM_SECOND_MATRIX\nvoid loadTile1( uint rsi, const uint3 thread, const uint w, const uint h, const bool rowMajor )\n{\n\tuint i;\n\tif( rowMajor )\n\t{\n\t\trsi += thread.y * arg1Strides.y;\n\n\t\tfor( i = thread.y; i < h; i += THREADS_Y, rsi += arg1Strides.y * THREADS_Y )\n\t\t{\n\t\t\tif( thread.x < w )\n\t\t\t\ttile1[ i ][ thread.x ] = arg1[ rsi + thread.x * arg1Strides.x ];\n\t\t\telse\n\t\t\t\ttile1[ i ][ thread.x ] = 0.0;\n\t\t}\n\t}\n\telse\n\t{\n\t\t// Unlike width which is smaller for the last tile, the height is always the same, and all these tiles are zero-initialized\n\t\tif( thread.x >= h )\n\t\t\treturn;\n\n\t\trsi += thread.y * arg1Strides.x;\n\t\tfor( i = thread.y; i < w; i += THREADS_Y, rsi += arg1Strides.x * THREADS_Y )\n\t\t\ttile1[ thread.x ][ i ] = arg1[ rsi + thread.x * arg0Strides.y ];\n\t\tif( i >= TILE_SIZE )\n\t\t\treturn;\n\t\tfor( ; i < TILE_SIZE; i += THREADS_Y )\n\t\t\ttile1[ thread.x ][ i ] = 0.0;\n\t}\n}\n#endif\n\nvoid storeTile( const uint3 thread, const uint4 pos, const uint2 size, in float4 acc[ heightVectors ] )\n{\n\tif( thread.x >= size.x )\n\t\treturn;\n\n\tconst uint4 prod4 = pos * resultStrides;\n\tconst uint2 prod2 = prod4.xy + prod4.zw;\n\tuint rdi = prod2.x + prod2.y;\n\trdi += resultStrides.y * thread.y;\n\trdi += resultStrides.x * thread.x;\n\n\tconst uint4 offsets = THREADS_Y * uint4( 0, 1, 2, 3 );\t//< a compile-time constant vector\n\tuint4 rdi4 = resultStrides.y * offsets + rdi;\n\n\t[unroll]\n\tfor( uint iv = 0; iv < heightVectors; iv++, rdi4 += resultStrides.y * THREADS_Y * 4 )\n\t{\n\t\tconst float4 source = acc[ iv ];\n\t\t[unroll]\n\t\tfor( uint k = 0; k < 4; k++ )\n\t\t{\n\t\t\tconst uint i = ( iv * 4 + k ) * THREADS_Y + thread.y;\n\t\t\tif( i < size.y )\n\t\t\t\tresult[ rdi4[ k ] ] = source[ k ];\n\t\t}\n\t}\n}\n\n[ numthreads( TILE_SIZE, THREADS_Y, 1 ) ]\nvoid main( uint3 group: SV_GroupID, uint3 thread : SV_GroupThreadID )\n{\n\t// Zero out these shared buffers\n\tfor( uint i = 0; i < TILE_SIZE; i += THREADS_Y )\n\t{\n\t\ttile0[ i + thread.y ][ thread.x ] = 0.0;\n#if !STREAM_SECOND_MATRIX\n\t\ttile1[ i + thread.y ][ thread.x ] = 0.0;\n#endif\n\t}\n\t// Despite inside GPU cores, the shared memory is still much slower than registers\n\t// For this reason, this shader accumulates numbers in local variables. Only uses groupshared memory for tiles of the argument matrices.\n\tfloat4 acc[ heightVectors ];\n\t// Zero out the accumulators\n\t[unroll]\n\tfor( i = 0; i < heightVectors; i++ )\n\t\tacc[ i ] = 0.0;\n\n\tconst uint2 resultPos = group.xy * TILE_SIZE;\n\tconst uint2 layer = uint2( group.z % resultSize.z, group.z / resultSize.z );\n\tuint rsi0 = resultPos.x * arg0Strides.y + layer.x * arg0Strides.z + layer.y * arg0Strides.w;\n\tuint rsi1 = resultPos.y * arg1Strides.y + layer.x * arg1Strides.z + layer.y * arg1Strides.w;\n\n\tconst uint rsi0Inc = TILE_SIZE * arg0Strides.x;\n\tconst uint rsi1Inc = TILE_SIZE * arg1Strides.x;\n\n\tconst uint completeTiles = arg0Size.x / TILE_SIZE;\n\tconst uint rsi0AndAligned = rsi0 + rsi0Inc * completeTiles;\n\t// Output tile size\n\t// Normally TILE_SIZE^2, less than that for the tiles at the right and bottom edges of the output matrix\n\tconst uint2 outputSize = min( TILE_SIZE, resultSize.xy - resultPos );\n\n\tconst bool2 loadOrder = LOAD_ORDER;\n\n#if STREAM_SECOND_MATRIX\n\trsi1 += thread.y * arg1Strides.y;\n#endif\n\tfor( ; rsi0 < rsi0AndAligned; rsi0 += rsi0Inc, rsi1 += rsi1Inc )\n\t{\n\t\tloadTile0( rsi0, thread, TILE_SIZE, outputSize.x, loadOrder.x );\n#if STREAM_SECOND_MATRIX\n\t\tGroupMemoryBarrierWithGroupSync();\n\t\tmultiplyTiles( rsi1, thread, TILE_SIZE, outputSize.y, acc );\n#else\n\t\tloadTile1( rsi1, thread, TILE_SIZE, outputSize.y, loadOrder.y );\n\t\tGroupMemoryBarrierWithGroupSync();\n\t\tmultiplyTiles( thread, acc );\n#endif\n\t\t// Need one moar barrier here.\n\t\t// Otherwise, some threads of the group are loading the next tile into tile0/tile1 groupshared buffers on the next iteration of the loop,\n\t\t// while other threads of the same group are still computing the matrix product, and getting incorrect values from that groupshared buffer.\n\t\t// The missing barrier only caused a bug on AMD, and only with \"ggml-large.bin\" model; no idea why that is.\n\t\tGroupMemoryBarrierWithGroupSync();\n\t}\n\n\tconst uint rem = arg0Size.x % TILE_SIZE;\n\tif( 0 != rem )\n\t{\n\t\tloadTile0( rsi0, thread, rem, outputSize.x, loadOrder.x );\n#if STREAM_SECOND_MATRIX\n\t\tGroupMemoryBarrierWithGroupSync();\n\t\tmultiplyTiles( rsi1, thread, rem, outputSize.y, acc );\n#else\n\t\tloadTile1( rsi1, thread, rem, outputSize.y, loadOrder.y );\n\t\tGroupMemoryBarrierWithGroupSync();\n\t\tmultiplyTiles( thread, acc );\n#endif\n\t}\n\n\tstoreTile( thread, uint4( resultPos, layer ), outputSize, acc );\n}"
  },
  {
    "path": "ComputeShaders/mulMatTiledEx.hlsl",
    "content": "// This compute shader implements yet another version of matrix*matrix product\n// For optimal VRAM access pattern, it requires both arguments to be reshaped into a sequence of horizontal column major panels.\n// The panel height is TILE_SIZE, and the last panel of the matrix needs to be padded with zeros; see matReshapePanels.hlsl shader for the reshaping.\n// So far, it's only used when running on AMD GPUs.\n#ifndef TILE_SIZE\nstatic const uint TILE_SIZE = 32;\n#endif\n#ifndef TILE_HEIGHT\nstatic const uint TILE_HEIGHT = 64;\n#endif\n#ifndef THREADS_Y\nstatic const uint THREADS_Y = 8;\n#endif\n// The above values have a following constraint: TILE_SIZE = THREADS_Y * N * 4 where N is an integer\n\n#ifndef STREAM_SECOND_MATRIX\n#define STREAM_SECOND_MATRIX 1\n#endif\n\n// First tensor, reshaped into dense column major horizontal panels of size [ width, TILE_SIZE ]\nBuffer<float> arg0: register( t0 );\n// Second tensor, reshaped into dense column major horizontal panels of size [ width, TILE_SIZE ]\nBuffer<float> arg1: register( t1 );\n// FP32 output tensor, row major and continuous\nRWBuffer<float> result: register( u0 );\n\ncbuffer Constants: register( b0 )\n{\n\tuint4 arg0Size: packoffset( c0 );\n\tuint arg0panel: packoffset( c1.y );\n\tuint2 arg0LayerStrides: packoffset( c1.z );\n\n\t// uint4 arg1Size: packoffset( c2 );\n\tuint arg1panel: packoffset( c3.y );\n\tuint2 arg1LayerStrides: packoffset( c3.z );\n\n\tuint4 resultSize: packoffset( c4 );\n\tuint4 resultStrides: packoffset( c5 );\n}\n\n// A smaller tile loaded from the first source matrix\ngroupshared float tile0[ TILE_HEIGHT ][ TILE_SIZE ];\n#if !STREAM_SECOND_MATRIX\n// A smaller tile loaded from the second source matrix\ngroupshared float tile1[ TILE_HEIGHT ][ TILE_SIZE ];\n#endif\n\n// Count of FP32 accumulators we need in every thread of the shader\nstatic const uint heightScalars = TILE_SIZE / THREADS_Y;\n// The local accumulators are float4 vectors, compute count of these vectors\nstatic const uint heightVectors = ( heightScalars + 3 ) / 4;\n\n#if STREAM_SECOND_MATRIX\nvoid multiplyTiles( const uint3 thread, uint rsi, const uint h, inout float4 acc[ heightVectors ] )\n{\n\tuint4 rsi4 = rsi + uint4( 0, THREADS_Y, THREADS_Y * 2, THREADS_Y * 3 );\n\t[unroll]\n\tfor( uint iv = 0; iv < heightVectors; iv++, rsi4 += THREADS_Y * 4 )\n\t{\n\t\tfloat4 r = 0.0;\n\t\tuint4 rsiRow = rsi4;\n\t\tfor( uint j = 0; j < h; j++, rsiRow += TILE_SIZE )\n\t\t{\n\t\t\tconst float a = tile0[ j ][ thread.x ];\n\t\t\tfloat4 b = 0.0;\n\t\t\t[unroll]\n\t\t\tfor( uint k = 0; k < 4; k++ )\n\t\t\t{\n\t\t\t\tb[ k ] = arg1[ rsiRow[ k ] ];\n\t\t\t}\n\t\t\tr = mad( a, b, r );\n\t\t}\n\t\tacc[ iv ] += r;\n\t}\n}\n#else\nvoid multiplyTiles( const uint3 thread, inout float4 acc[ heightVectors ] )\n{\n\t[unroll]\n\tfor( uint i = 0; i < heightVectors; i++ )\n\t{\n\t\tfloat4 r = 0.0;\n\t\tfor( uint j = 0; j < TILE_HEIGHT; j++ )\n\t\t{\n\t\t\tconst float a = tile0[ j ][ thread.x ];\n\t\t\tfloat4 b;\n\t\t\t[unroll]\n\t\t\tfor( uint k = 0; k < 4; k++ )\n\t\t\t{\n\t\t\t\tconst uint row = ( i * 4 + k ) * THREADS_Y + thread.y;\n\t\t\t\tb[ k ] = tile1[ j ][ row ];\n\t\t\t}\n\t\t\tr = mad( a, b, r );\n\t\t}\n\t\tacc[ i ] += r;\n\t}\n}\n#endif\n\nvoid storeTile( const uint3 thread, const uint4 pos, const uint2 size, in float4 acc[ heightVectors ] )\n{\n\tif( thread.x >= size.x )\n\t\treturn;\n\n\tconst uint4 prod4 = pos * resultStrides;\n\tconst uint2 prod2 = prod4.xy + prod4.zw;\n\tuint rdi = prod2.x + prod2.y;\n\trdi += resultStrides.y * thread.y;\n\trdi += resultStrides.x * thread.x;\n\n\tconst uint4 offsets = THREADS_Y * uint4( 0, 1, 2, 3 );\t//< a compile-time constant vector\n\tuint4 rdi4 = resultStrides.y * offsets + rdi;\n\n\t[unroll]\n\tfor( uint iv = 0; iv < heightVectors; iv++, rdi4 += resultStrides.y * THREADS_Y * 4 )\n\t{\n\t\tconst float4 source = acc[ iv ];\n\t\t[unroll]\n\t\tfor( uint k = 0; k < 4; k++ )\n\t\t{\n\t\t\tconst uint i = ( iv * 4 + k ) * THREADS_Y + thread.y;\n\t\t\tif( i < size.y )\n\t\t\t\tresult[ rdi4[ k ] ] = source[ k ];\n\t\t}\n\t}\n}\n\n[numthreads( TILE_SIZE, THREADS_Y, 1 )]\nvoid main( const uint3 group: SV_GroupID, const uint3 thread : SV_GroupThreadID )\n{\n\tuint i;\n\t// Zero all shared buffers\n\tfor( i = thread.y; i < TILE_HEIGHT; i += THREADS_Y )\n\t{\n\t\ttile0[ i ][ thread.x ] = 0.0;\n#if !STREAM_SECOND_MATRIX\n\t\ttile1[ i ][ thread.x ] = 0.0;\n#endif\n\t}\n\t// Despite inside GPU cores, the shared memory is still much slower than registers\n\t// For this reason, this shader accumulates numbers in local variables. Only uses groupshared memory for tiles of the argument matrices.\n\tfloat4 acc[ heightVectors ];\n\t// Zero out the accumulators\n\t[unroll]\n\tfor( i = 0; i < heightVectors; i++ )\n\t\tacc[ i ] = 0.0;\n\n\tconst uint2 layer = uint2( group.z % resultSize.z, group.z / resultSize.z );\n\n\tuint rsi0 = group.x * arg0panel + layer.x * arg0LayerStrides.x + layer.y * arg0LayerStrides.y;\n\tuint rsi1 = group.y * arg1panel + layer.x * arg1LayerStrides.x + layer.y * arg1LayerStrides.y;\n\n\tconst uint threadOffset = thread.y * TILE_SIZE + thread.x;\n\trsi0 += threadOffset;\n#if STREAM_SECOND_MATRIX\n\trsi1 += thread.y;\n#else\n\trsi1 += threadOffset;\n#endif\n\n\tconst uint completeTiles = arg0Size.x / TILE_HEIGHT;\n\tfor( i = 0; i < completeTiles; i++ )\n\t{\n\t\t// Load [ TILE_SIZE, TILE_HEIGHT ] block from both source tensors into these groupshared buffers\n\t\tfor( uint j = thread.y; j < TILE_HEIGHT; j += THREADS_Y )\n\t\t{\n\t\t\ttile0[ j ][ thread.x ] = arg0[ rsi0 ];\n\t\t\trsi0 += THREADS_Y * TILE_SIZE;\n#if !STREAM_SECOND_MATRIX\n\t\t\ttile1[ j ][ thread.x ] = arg1[ rsi1 ];\n\t\t\trsi1 += THREADS_Y * TILE_SIZE;\n#endif\n\t\t}\n\n\t\t// Wait for all threads in the group to complete these loads\n\t\tGroupMemoryBarrierWithGroupSync();\n\n#if STREAM_SECOND_MATRIX\n\t\tmultiplyTiles( thread, rsi1, TILE_HEIGHT, acc );\n\t\trsi1 += TILE_HEIGHT * TILE_SIZE;\n#else\n\t\t// Multiply + accumulate the elements collected in the groupshared buffers\n\t\tmultiplyTiles( thread, acc );\n#endif\n\t\tGroupMemoryBarrierWithGroupSync();\n\t}\n\n\tconst uint rem = arg0Size.x % TILE_HEIGHT;\n\tif( rem != 0 )\n\t{\n\t\t// Load [ TILE_SIZE, rem ] block from both source tensors, and zero out the padding elements\n\t\tfor( uint j = thread.y; j < TILE_HEIGHT; j += THREADS_Y )\n\t\t{\n\t\t\t[branch]\n\t\t\tif( j < rem )\n\t\t\t{\n\t\t\t\ttile0[ j ][ thread.x ] = arg0[ rsi0 ];\n\t\t\t\trsi0 += THREADS_Y * TILE_SIZE;\n#if !STREAM_SECOND_MATRIX\n\t\t\t\ttile1[ j ][ thread.x ] = arg1[ rsi1 ];\n\t\t\t\trsi1 += THREADS_Y * TILE_SIZE;\n#endif\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\ttile0[ j ][ thread.x ] = 0.0;\n#if !STREAM_SECOND_MATRIX\n\t\t\t\ttile1[ j ][ thread.x ] = 0.0;\n#endif\n\t\t\t}\n\t\t}\n\n\t\t// Wait for all threads in the group to complete these loads\n\t\tGroupMemoryBarrierWithGroupSync();\n\n\t\t// Multiply + accumulate the elements collected in the groupshared buffers\n#if STREAM_SECOND_MATRIX\n\t\tmultiplyTiles( thread, rsi1, rem, acc );\n#else\n\t\tmultiplyTiles( thread, acc );\n#endif\n\t\tGroupMemoryBarrierWithGroupSync();\n\t}\n\n\tconst uint2 resultPos = group.xy * TILE_SIZE;\n\tconst uint2 outputSize = min( TILE_SIZE, resultSize.xy - resultPos );\n\tstoreTile( thread, uint4( resultPos, layer ), outputSize, acc );\n}"
  },
  {
    "path": "ComputeShaders/norm.hlsl",
    "content": "// Ported from ggml_compute_forward_norm_f32\n// Dispatch [ ne01, ne02, ne03 ] thread groups of this shader\nBuffer<float> arg0: register( t0 );\nRWBuffer<float> result: register( u0 );\n\ncbuffer Constants: register( b0 )\n{\n\tuint4 src0_elements: packoffset( c0 );\n\tuint4 src0_strides: packoffset( c1 );\n\tuint4 result_strides: packoffset( c3 );\n}\n\nstatic const float eps = 1e-5f; // TODO: make this a parameter\n\n#include \"groupReduce.hlsli\"\n\nfloat computeVectorSum( uint i, const uint length, const uint thread )\n{\n\tfloat res = 0.0;\n\n\tconst uint iEnd = i + length;\n\ti += thread;\n\tfor( ; i < iEnd; i += 32 )\n\t\tres += arg0[ i ];\n\n\thorizontalSumBroadcast( thread, res );\n\treturn res;\n}\n\nfloat offsetAndComputeSumSquares( uint rsi, uint rdi, const float mean, const uint length, const uint thread )\n{\n\tfloat sum2 = 0.0;\n\n\tconst uint rsiEnd = rsi + length;\n\trsi += thread;\n\trdi += thread;\n\tfor( ; rsi < rsiEnd; rsi += 32, rdi += 32 )\n\t{\n\t\tfloat v = arg0[ rsi ] - mean;\n\t\tresult[ rdi ] = v;\n\t\tsum2 = mad( v, v, sum2 );\n\t}\n\n\thorizontalSumBroadcast( thread, sum2 );\n\treturn sum2;\n}\n\nvoid scaleVector( uint rdi, const float scale, const uint length, const uint thread )\n{\n\tconst uint rdiEnd = rdi + length;\n\tfor( rdi += thread; rdi < rdiEnd; rdi += 32 )\n\t{\n\t\tfloat f = result[ rdi ];\n\t\tf *= scale;\n\t\tresult[ rdi ] = f;\n\t}\n}\n\n[ numthreads( 32, 1, 1 ) ]\nvoid main( uint3 group: SV_GroupID, uint thread : SV_GroupIndex )\n{\n\tconst uint i03 = group.z;\n\tconst uint i02 = group.y;\n\tconst uint i01 = group.x;\n\n\tconst uint nb01 = src0_strides[ 1 ];\n\tconst uint nb02 = src0_strides[ 2 ];\n\tconst uint nb03 = src0_strides[ 3 ];\n\n\tconst uint p = i01 * nb01 + i02 * nb02 + i03 * nb03;\n\n\tconst uint ne00 = src0_elements[ 0 ];\n\n\tfloat mean = computeVectorSum( p, ne00, thread );\n\tmean /= (float)(int)ne00;\n\n\tconst uint nb1 = result_strides[ 1 ];\n\tconst uint nb2 = result_strides[ 2 ];\n\tconst uint nb3 = result_strides[ 3 ];\n\tconst uint y = i01 * nb1 + i02 * nb2 + i03 * nb3;\n\n\tfloat sum2 = offsetAndComputeSumSquares( p, y, mean, ne00, thread );\n\tconst float scale = 1.0 / sqrt( sum2 / (float)(int)ne00 + eps );\n\n\tscaleVector( y, scale, ne00, thread );\n}"
  },
  {
    "path": "ComputeShaders/normCompat.hlsl",
    "content": "// Ported from ggml_compute_forward_norm_f32\n// Dispatch [ ( ne01 + 31 ) / 32, ne02, ne03 ] thread groups of this shader\nBuffer<float> arg0: register( t0 );\nRWBuffer<float> result: register( u0 );\n\ncbuffer Constants: register( b0 )\n{\n\tuint4 src0_elements: packoffset( c0 );\n\tuint4 src0_strides: packoffset( c1 );\n\tuint4 result_strides: packoffset( c3 );\n}\n\nstatic const double eps = 1e-5; // TODO: make this a parameter\n\n#include \"groupReduce.hlsli\"\n\ndouble computeVectorSum( uint i, const uint length )\n{\n\tdouble res = 0.0;\n\tconst uint iEnd = i + length;\n\tfor( ; i < iEnd; i++ )\n\t\tres += arg0[ i ];\n\treturn res;\n}\n\ndouble offsetAndComputeSumSquares( uint rsi, uint rdi, const double mean, const uint length )\n{\n\tprecise double sum2 = 0.0;\n\tconst uint rsiEnd = rsi + length;\n\tfor( ; rsi < rsiEnd; rsi++, rdi++ )\n\t{\n\t\tdouble v = arg0[ rsi ];\n\t\tv -= mean;\n\t\tresult[ rdi ] = (float)v;\n\t\tdouble prod = v * v;\n\t\tsum2 += prod;\n\t}\n\treturn sum2;\n}\n\nvoid scaleVector( uint rdi, const float scale, const uint length )\n{\n\tconst uint rdiEnd = rdi + length;\n\tfor( ; rdi < rdiEnd; rdi++ )\n\t{\n\t\tfloat f = result[ rdi ];\n\t\tf *= scale;\n\t\tresult[ rdi ] = f;\n\t}\n}\n\n#include \"fp64Utils.hlsli\"\n\n[ numthreads( 32, 1, 1 ) ]\nvoid main( uint3 dtid: SV_DispatchThreadID )\n{\n\tconst uint i03 = dtid.z;\n\tconst uint i02 = dtid.y;\n\tconst uint i01 = dtid.x;\n\tif( i01 >= src0_elements[ 1 ] )\n\t\treturn;\n\n\tconst uint nb01 = src0_strides[ 1 ];\n\tconst uint nb02 = src0_strides[ 2 ];\n\tconst uint nb03 = src0_strides[ 3 ];\n\n\tconst uint p = i01 * nb01 + i02 * nb02 + i03 * nb03;\n\tconst uint ne00 = src0_elements[ 0 ];\n\n\tdouble mean = computeVectorSum( p, ne00 );\n\tmean = div64( mean, (double)(int)ne00 );\n\n\tconst uint nb1 = result_strides[ 1 ];\n\tconst uint nb2 = result_strides[ 2 ];\n\tconst uint nb3 = result_strides[ 3 ];\n\tconst uint y = i01 * nb1 + i02 * nb2 + i03 * nb3;\n\n\tconst double sum2 = offsetAndComputeSumSquares( p, y, mean, ne00 );\n\tconst float scale = (float)div64( 1.0, sqrt64( sum2 / (float)(int)ne00 + eps ) );\n\n\tscaleVector( y, scale, ne00 );\n}"
  },
  {
    "path": "ComputeShaders/normFixed.hlsl",
    "content": "// Ported from ggml_compute_forward_norm_f32\n// Dispatch [ ne01, ne02, ne03 ] thread groups of this shader\nBuffer<float> arg0: register( t0 );\nRWBuffer<float> result: register( u0 );\n\ncbuffer Constants: register( b0 )\n{\n\tuint4 src0_elements: packoffset( c0 );\n\tuint4 src0_strides: packoffset( c1 );\n\tuint4 result_strides: packoffset( c3 );\n}\n\nstatic const float eps = 1e-5f; // TODO: make this a parameter\n\n// #include \"groupReduce.hlsli\"\n\n#ifndef THREADS\nstatic const uint THREADS = 32;\n#endif\nstatic const uint ROW_LENGTH = 1024;\ngroupshared float rowBuffer[ ROW_LENGTH ];\n\nstatic const uint REDUCTION_BUFFER = 32;\ngroupshared float sharedAccumulators[ REDUCTION_BUFFER ];\n\n// Compute horisontal sum of the numbers. The result is only correct on the thread #0 of the group.\nvoid horizontalSum( const uint thread, inout float sum )\n{\n\tif( THREADS > REDUCTION_BUFFER )\n\t{\n\t\tfor( uint t = REDUCTION_BUFFER; t < THREADS; t += REDUCTION_BUFFER )\n\t\t{\n\t\t\t// Threads [ t .. t + REDUCTION_BUFFER ] store into the buffer\n\t\t\tif( thread >= t && thread < t + REDUCTION_BUFFER )\n\t\t\t\tsharedAccumulators[ thread - t ] = sum;\n\n\t\t\tGroupMemoryBarrierWithGroupSync();\n\n\t\t\t// Threads [ 0 .. REDUCTION_BUFFER ] increment their local sum with the value loaded from the buffer\n\t\t\tif( thread < REDUCTION_BUFFER )\n\t\t\t\tsum += sharedAccumulators[ thread ];\n\t\t}\n\t}\n\n\tif( thread < REDUCTION_BUFFER )\n\t\tsharedAccumulators[ thread ] = sum;\n\n\tfor( uint i = REDUCTION_BUFFER / 2; i > 1; i /= 2 )\n\t{\n\t\tGroupMemoryBarrierWithGroupSync();\n\t\tif( thread < i )\n\t\t{\n\t\t\tsum += sharedAccumulators[ thread + i ];\n\t\t\tsharedAccumulators[ thread ] = sum;\n\t\t}\n\t}\n\n\tGroupMemoryBarrierWithGroupSync();\n\tif( 0 == thread )\n\t\tsum += sharedAccumulators[ 1 ];\n}\n\n[ numthreads( THREADS, 1, 1 ) ]\nvoid main( uint3 group: SV_GroupID, uint thread : SV_GroupIndex )\n{\n\tconst uint i03 = group.z;\n\tconst uint i02 = group.y;\n\tconst uint i01 = group.x;\n\tconst uint ne00 = ROW_LENGTH;\n\n\t// First pass: copy the data to local buffer, and compute sum\n\t{\n\t\tconst uint nb01 = src0_strides[ 1 ];\n\t\tconst uint nb02 = src0_strides[ 2 ];\n\t\tconst uint nb03 = src0_strides[ 3 ];\n\t\tconst uint p = i01 * nb01 + i02 * nb02 + i03 * nb03;\n\n\t\tfloat sum = 0;\n\t\tfor( uint i = thread; i < ne00; i += THREADS )\n\t\t{\n\t\t\tfloat f = arg0[ p + i ];\n\t\t\trowBuffer[ i ] = f;\n\t\t\tsum += f;\n\t\t}\n\t\thorizontalSum( thread, sum );\n\t\tif( 0 == thread )\n\t\t\tsharedAccumulators[ 0 ] = sum / (float)(int)ne00;\n\t\tGroupMemoryBarrierWithGroupSync();\n\t}\n\n\t// Second pass: offset and compute sum of squares\n\t{\n\t\tconst float mean = sharedAccumulators[ 0 ];\n\t\tfloat sum2 = 0;\n\t\tfor( uint i = thread; i < ne00; i += THREADS )\n\t\t{\n\t\t\tfloat v = rowBuffer[ i ];\n\t\t\tv -= mean;\n\t\t\trowBuffer[ i ] = v;\n\t\t\tsum2 = mad( v, v, sum2 );\n\t\t}\n\t\thorizontalSum( thread, sum2 );\n\t\tif( 0 == thread )\n\t\t\tsharedAccumulators[ 0 ] = 1.0 / sqrt( sum2 / (float)(int)ne00 + eps );\n\t\tGroupMemoryBarrierWithGroupSync();\n\t}\n\n\t// Final pass: apply the scale, and copy from group shared buffer to the destination\n\t{\n\t\tconst float scale = sharedAccumulators[ 0 ];\n\n\t\tconst uint nb1 = result_strides[ 1 ];\n\t\tconst uint nb2 = result_strides[ 2 ];\n\t\tconst uint nb3 = result_strides[ 3 ];\n\t\tconst uint y = i01 * nb1 + i02 * nb2 + i03 * nb3;\n\n\t\tfor( uint i = thread; i < ne00; i += THREADS )\n\t\t{\n\t\t\tfloat v = rowBuffer[ i ];\n\t\t\tv *= scale;\n\t\t\tresult[ y + i ] = v;\n\t\t}\n\t}\n}"
  },
  {
    "path": "ComputeShaders/normFixed64.hlsl",
    "content": "#define THREADS 64\n#include \"normFixed.hlsl\""
  },
  {
    "path": "ComputeShaders/repeatUtils.hlsli",
    "content": "inline uint rowOffset( uint3 idx, uint4 strides )\n{\n\treturn idx[ 0 ] * strides[ 1 ] + idx[ 1 ] * strides[ 2 ] + idx[ 2 ] * strides[ 3 ];\n}\n\n// Initial iterator state for a row of the output tensor\n// x = current index, y = index increment, z = end of the index\ninline uint3 tensorIteratorState( uint3 group, uint thread, uint4 size, uint4 stride )\n{\n\tuint3 res;\n\tres.x = rowOffset( group, stride );\n\tres.y = THREADS * stride[ 0 ];\n\tres.z = res.x + size[ 0 ] * stride[ 0 ];\n\tres.x += thread * stride[ 0 ];\n\treturn res;\n}\n\n// Handle a complete row of output tensor, using the iterator made by tensorIteratorState() function\n#define ROW_LOOP( ts ) for( ; ts.x < ts.z; ts.x += ts.y )\n// Same as above, using different row length\n#define ROW_LOOP_EX( ts, len, stride ) for( ; ts.x < ts.z; ts.x += len * stride[ 0 ] )"
  },
  {
    "path": "ComputeShaders/scaleInPlace.hlsl",
    "content": "RWBuffer<float> buffer: register( u0 );\n\ncbuffer Constants: register( b0 )\n{\n\tuint4 src0_elements: packoffset( c0 );\n\tuint4 src0_strides: packoffset( c1 );\n\tfloat multiplier: packoffset( c2.x );\n}\n\n[ numthreads( 32, 1, 1 ) ]\nvoid main( uint3 group: SV_GroupID, uint thread : SV_GroupIndex )\n{\n\tconst uint nc0 = src0_elements[ 0 ];\n\tuint i = group.x * src0_strides[ 1 ];\n\tconst uint iEnd = i + nc0;\n\tconst float mul = multiplier;\n\tfor( i += thread; i < iEnd; i += 32 )\n\t{\n\t\tfloat f = buffer[ i ];\n\t\tf *= mul;\n\t\tbuffer[ i ] = f;\n\t}\n}"
  },
  {
    "path": "ComputeShaders/softMax.hlsl",
    "content": "// Dispatch [ nr, 1, 1 ] thread groups of this shader\nRWBuffer<float> result: register( u0 );\n\ncbuffer Constants: register( b0 )\n{\n\tuint4 elements: packoffset( c0 );\n\tuint4 strides: packoffset( c1 );\n\tuint nr: packoffset( c2.x );\n\tfloat inputScale: packoffset( c2.y );\n}\n\n#ifndef THREADS\nstatic const uint THREADS = 32;\n#endif\n\ngroupshared float sharedAccumulators[ THREADS ];\n\n// Compute horizontal maximum of the numbers, and broadcast to all threads of the group.\nvoid horizontalMaxBroadcast( const uint thread, inout float ax )\n{\n\tsharedAccumulators[ thread ] = ax;\n\tfor( uint i = THREADS / 2; i > 0; i /= 2 )\n\t{\n\t\tGroupMemoryBarrierWithGroupSync();\n\t\tif( thread < i )\n\t\t{\n\t\t\tax = max( ax, sharedAccumulators[ thread + i ] );\n\t\t\tsharedAccumulators[ thread ] = ax;\n\t\t}\n\t}\n\tGroupMemoryBarrierWithGroupSync();\n\tax = sharedAccumulators[ 0 ];\n}\n\n// Compute horisontal sum of the numbers. The result is only correct on the thread #0 of the group.\nvoid horizontalSum( const uint thread, inout float sum )\n{\n\tsharedAccumulators[ thread ] = sum;\n\tfor( uint i = THREADS / 2; i > 1; i /= 2 )\n\t{\n\t\tGroupMemoryBarrierWithGroupSync();\n\t\tif( thread < i )\n\t\t{\n\t\t\tsum += sharedAccumulators[ thread + i ];\n\t\t\tsharedAccumulators[ thread ] = sum;\n\t\t}\n\t}\n\tGroupMemoryBarrierWithGroupSync();\n\tif( 0 == thread )\n\t\tsum += sharedAccumulators[ 1 ];\n}\n\nstatic const float negativeInfinity = asfloat( 0xff800000 );\n\n[numthreads( THREADS, 1, 1 )]\nvoid main( uint3 group: SV_GroupID, uint thread : SV_GroupIndex )\n{\n\tconst uint p = group.x * strides[ 1 ];\n\tconst uint nc = elements[ 0 ];\n\tconst uint pEnd = p + nc;\n\tuint i;\n\n\tfloat m = negativeInfinity;\n\tfor( i = p + thread; i < pEnd; i += THREADS )\n\t\tm = max( m, result[ i ] );\n\thorizontalMaxBroadcast( thread, m );\n\n\tfloat sum = 0;\n\tfor( i = p + thread; i < pEnd; i += THREADS )\n\t{\n\t\tfloat f = result[ i ];\n\n\t\t[branch]\n\t\tif( f != negativeInfinity )\n\t\t{\n\t\t\tf = ( f - m ) * inputScale;\n\t\t\t// On both Radeon Graphics and nVidia 1080Ti, computing the exponent is slightly faster than loading from the lookup table\n\t\t\tf = exp( f );\n\t\t\tsum += f;\n\t\t}\n\t\telse\n\t\t\tf = 0;\n\n\t\tresult[ i ] = f;\n\t}\n\n\thorizontalSum( thread, sum );\n\tif( 0 == thread )\n\t\tsharedAccumulators[ 0 ] = 1.0 / sum;\n\tGroupMemoryBarrierWithGroupSync();\n\tconst float scale = sharedAccumulators[ 0 ];\n\n\t// ggml_vec_scale_f32\n\tfor( i = p + thread; i < pEnd; i += THREADS )\n\t{\n\t\tfloat f = result[ i ];\n\t\tf *= scale;\n\t\tresult[ i ] = f;\n\t}\n}"
  },
  {
    "path": "ComputeShaders/softMax64.hlsl",
    "content": "#define THREADS 64\n#include \"softMax.hlsl\""
  },
  {
    "path": "ComputeShaders/softMaxCompat.hlsl",
    "content": "// ggml_compute_forward_soft_max_f32\n// Dispatch [ ( nr + 31 ) / 32, 1, 1 ] thread groups of this shader\nRWBuffer<float> result: register( u0 );\n\n// table_exp_f16\nBuffer<uint> lookupTable: register( t0 );\n\ncbuffer Constants: register( b0 )\n{\n\tuint4 elements: packoffset( c0 );\n\tuint4 strides: packoffset( c1 );\n\tuint nr: packoffset( c2.x );\n}\n\n#include \"miscUtils.hlsli\"\n#include \"fp64Utils.hlsli\"\n\nstatic const float negativeInfinity = asfloat( 0xff800000 );\n\n[ numthreads( 32, 1, 1 ) ]\nvoid main( uint3 dtid: SV_DispatchThreadID )\n{\n\tif( dtid.x >= nr )\n\t\treturn;\n\n\tconst uint p = dtid.x * strides[ 1 ];\n\tconst uint nc = elements[ 0 ];\n\tconst uint pEnd = p + nc;\n\tuint i;\n\n\tfloat m = negativeInfinity;\n\tfor( i = p; i < pEnd; i++ )\n\t\tm = max( m, result[ i ] );\n\n\tdouble sum = 0;\n\tfor( i = p; i < pEnd; i++ )\n\t{\n\t\tfloat f = result[ i ];\n\n\t\t[branch]\n\t\tif( f != negativeInfinity )\n\t\t{\n\t\t\tuint s = fp16Rounded( f - m );\n\t\t\ts = lookupTable[ s ];\n\t\t\tf = f16tof32( s );\n\t\t\tsum += f;\n\t\t}\n\t\telse\n\t\t\tf = 0;\n\n\t\tresult[ i ] = f;\n\t}\n\n\tconst float scale = (float)div64( 1.0, sum );\n\t// ggml_vec_scale_f32\n\tfor( i = p; i < pEnd; i++ )\n\t{\n\t\tfloat f = result[ i ];\n\t\tf *= scale;\n\t\tresult[ i ] = f;\n\t}\n}"
  },
  {
    "path": "ComputeShaders/softMaxFixed.hlsl",
    "content": "// Special softMax shader for matrices with rows of 1500 elements.\n// Uses group shared buffer of that length to save global memory bandwidth, more than 2x faster than the original.\n// Dispatch [ nr, 1, 1 ] thread groups of this shader\nRWBuffer<float> result: register( u0 );\n\ncbuffer Constants: register( b0 )\n{\n\tuint4 elements: packoffset( c0 );\n\tuint4 strides: packoffset( c1 );\n\tuint nr: packoffset( c2.x );\n\tfloat inputScale: packoffset( c2.y );\n}\n\n#include \"miscUtils.hlsli\"\n#include \"groupReduce64.hlsli\"\n\nstatic const uint THREADS = 64;\nstatic const uint ROW_LENGTH = 1500;\ngroupshared float rowBuffer[ ROW_LENGTH ];\n\nstatic const float negativeInfinity = asfloat( 0xff800000 );\n\n[ numthreads( THREADS, 1, 1 ) ]\nvoid main( uint3 group: SV_GroupID, uint thread : SV_GroupIndex )\n{\n\tconst uint p = group.x * strides[ 1 ];\n\tconst uint nc = ROW_LENGTH;\n\tuint i;\n\n\tfloat m = negativeInfinity;\n\t// First pass: compute maximum, and copy the row into the group shared buffer\n\tfor( i = thread; i < nc; i += THREADS )\n\t{\n\t\tfloat f = result[ p + i ];\n\t\tm = max( m, f );\n\t\trowBuffer[ i ] = f;\n\t}\n\thorizontalMaxBroadcast( thread, m );\n\n\t// Second pass: apply initial scale, compute the exponent, and compute total sum over the row\n\tfloat sum = 0;\n\tfor( i = thread; i < nc; i += THREADS )\n\t{\n\t\tfloat f = rowBuffer[ i ];\n\n\t\t[branch]\n\t\tif( f != negativeInfinity )\n\t\t{\n\t\t\tf = ( f - m ) * inputScale;\n#if 1\n\t\t\t// At least on Radeon Graphics GPU inside Ryzen 7 5700G, computing exponent instead of loading from the buffer improves the performance\n\t\t\tf = exp( f );\n#else\n\t\t\tuint s = fp16Rounded( f );\n\t\t\ts = lookupTable[ s ];\n\t\t\tf = f16tof32( s );\n#endif\n\t\t\tsum += f;\n\t\t}\n\t\telse\n\t\t\tf = 0;\n\n\t\trowBuffer[ i ] = f;\n\t}\n\n\thorizontalSum( thread, sum );\n\tif( 0 == thread )\n\t\tsharedAccumulators[ 0 ] = 1.0 / sum;\n\tGroupMemoryBarrierWithGroupSync();\n\tconst float scale = sharedAccumulators[ 0 ];\n\n\t// Final pass: apply the final scale, and copy the row from the group shared buffer back into the global memory\n\tfor( i = thread; i < nc; i += THREADS )\n\t{\n\t\tfloat f = rowBuffer[ i ];\n\t\tf *= scale;\n\t\tresult[ p + i ] = f;\n\t}\n}"
  },
  {
    "path": "ComputeShaders/softMaxLong.hlsl",
    "content": "// This version is for the \"dec.probs\" shader tag\n// The input tensor has a size [ 51865, 3 ], a very long tensor with just 3 rows.\n// Despite the shader only runs on 3 GPU cores, large count of threads helps substantially, this shader is about 50% faster.\n#define THREADS 1024\n\n#include \"softMax.hlsl\""
  },
  {
    "path": "ComputeShaders/zeroMemory.hlsl",
    "content": "RWBuffer<float> result: register( u0 );\n\ncbuffer Constants: register( b0 )\n{\n\tuint elements: packoffset( c0.x );\n\tbool writeNan: packoffset( c0.y );\n}\n\n// Thread group index is 16 bits per coordinate:\n// https://learn.microsoft.com/en-us/windows/win32/api/d3d11/nf-d3d11-id3d11devicecontext-dispatch\n// We want this shader to support buffers up to 2 GB.\n#ifndef THREADS\nstatic const uint THREADS = 512;\n#endif\n#ifndef ITERATIONS\nstatic const uint ITERATIONS = 128;\n#endif\n\nstatic const uint itemsPerGroup = THREADS * ITERATIONS;\n\n[numthreads( THREADS, 1, 1 )]\nvoid main( uint3 group: SV_GroupID, uint thread : SV_GroupIndex )\n{\n\tuint rdi = group.x * itemsPerGroup;\n\tconst uint rdiEnd = min( rdi + itemsPerGroup, elements );\n\t// https://www.h-schmidt.net/FloatConverter/IEEE754.html\n\tconst float pattern = writeNan ? asfloat( 0x7FFFFFFFu ) : 0.0;\n\tfor( rdi += thread; rdi < rdiEnd; rdi += THREADS )\n\t\tresult[ rdi ] = pattern;\n}"
  },
  {
    "path": "Examples/MicrophoneCS/CaptureThread.cs",
    "content": "﻿using System.Runtime.ExceptionServices;\nusing Whisper;\n\nnamespace MicrophoneCS\n{\n\tsealed class CaptureThread: CaptureCallbacks\n\t{\n\t\tpublic CaptureThread( CommandLineArgs args, Context context, iAudioCapture source )\n\t\t{\n\t\t\tcallbacks = new TranscribeCallbacks( args );\n\t\t\tthis.context = context;\n\t\t\tthis.source = source;\n\n\t\t\tthread = new Thread( threadMain ) { Name = \"Capture Thread\" };\n\t\t\tConsole.WriteLine( \"Press any key to quit\" );\n\t\t\tthread.Start();\n\t\t}\n\n\t\tstatic void readKeyCallback( object? state )\n\t\t{\n\t\t\tCaptureThread ct = ( state as CaptureThread ) ?? throw new ApplicationException();\n\t\t\tConsole.ReadKey();\n\t\t\tct.shouldQuit = true;\n\t\t}\n\n\t\tpublic void join()\n\t\t{\n\t\t\tThreadPool.QueueUserWorkItem( readKeyCallback, this );\n\t\t\tthread.Join();\n\t\t\tedi?.Throw();\n\t\t}\n\n\t\tvolatile bool shouldQuit = false;\n\n\t\tprotected override bool shouldCancel( Context sender ) =>\n\t\t\tshouldQuit;\n\n\t\tprotected override void captureStatusChanged( Context sender, eCaptureStatus status )\n\t\t{\n\t\t\tConsole.WriteLine( $\"CaptureStatusChanged: {status}\" );\n\t\t}\n\n\t\treadonly TranscribeCallbacks callbacks;\n\t\treadonly Thread thread;\n\t\treadonly Context context;\n\t\treadonly iAudioCapture source;\n\t\tExceptionDispatchInfo? edi = null;\n\n\t\tvoid threadMain()\n\t\t{\n\t\t\ttry\n\t\t\t{\n\t\t\t\tcontext.runCapture( source, callbacks, this );\n\t\t\t}\n\t\t\tcatch( Exception ex )\n\t\t\t{\n\t\t\t\tedi = ExceptionDispatchInfo.Capture( ex );\n\t\t\t}\n\t\t}\n\t}\n}"
  },
  {
    "path": "Examples/MicrophoneCS/CommandLineArgs.cs",
    "content": "﻿using System.Globalization;\nusing System.Reflection;\nusing Whisper;\n\nnamespace MicrophoneCS\n{\n\tsealed record class CommandLineArgs\n\t{\n\t\tpublic int n_threads = Environment.ProcessorCount;\n\t\tpublic int offset_t_ms = 0;\n\t\tpublic int offset_n = 0;\n\t\tpublic int duration_ms = 0;\n\t\tpublic int max_context = -1;\n\t\tpublic int max_len = 0;\n\n\t\tpublic float word_thold = 0.01f;\n\n\t\tpublic bool speed_up = false;\n\t\tpublic bool translate = false;\n\t\tpublic bool diarize = false;\n\t\tpublic bool output_txt = false;\n\t\tpublic bool print_special = false;\n\t\tpublic bool print_progress = false;\n\t\tpublic bool print_colors = true;\n\t\tpublic bool no_timestamps = false;\n\t\tpublic int[]? prompt = null;\n\t\tpublic int captureDeviceIndex = 0;\n\n\t\tpublic eLanguage language = eLanguage.English;\n\t\tpublic string model = string.Empty;\n\n\t\tconst bool output_wts = false;\n\t\tpublic bool listDevices = false;\n\n\t\tpublic void apply( ref Parameters p )\n\t\t{\n\t\t\tp.setFlag( eFullParamsFlags.PrintRealtime, false );\n\t\t\tp.setFlag( eFullParamsFlags.PrintProgress, print_progress );\n\t\t\tp.setFlag( eFullParamsFlags.PrintTimestamps, !no_timestamps );\n\t\t\tp.setFlag( eFullParamsFlags.PrintSpecial, print_special );\n\t\t\tp.setFlag( eFullParamsFlags.Translate, translate );\n\t\t\tp.language = language;\n\t\t\tp.cpuThreads = n_threads;\n\t\t\tif( max_context >= 0 )\n\t\t\t\tp.n_max_text_ctx = max_context;\n\t\t\tp.offset_ms = offset_t_ms;\n\t\t\tp.duration_ms = duration_ms;\n\t\t\tp.setFlag( eFullParamsFlags.TokenTimestamps, output_wts || max_len > 0 );\n\t\t\tp.thold_pt = word_thold;\n\t\t\tp.max_len = output_wts && max_len == 0 ? 60 : max_len;\n\t\t\tp.setFlag( eFullParamsFlags.SpeedupAudio, speed_up );\n\t\t}\n\n\t\tpublic eResultFlags resultFlags()\n\t\t{\n\t\t\teResultFlags flags = eResultFlags.None;\n\t\t\tbool wts = output_wts || max_len > 0;\n\t\t\tif( !no_timestamps || wts )\n\t\t\t\tflags |= eResultFlags.Timestamps;\n\t\t\tif( wts || print_colors )\n\t\t\t\tflags |= eResultFlags.Tokens;\n\t\t\treturn flags;\n\t\t}\n\n\t\tstatic eLanguage parseLanguage( string lang ) =>\n\t\t\tLibrary.languageFromCode( lang ) ?? throw new ArgumentException( $\"Unknown language code \\\"{lang}\\\"\" );\n\n\t\tpublic CommandLineArgs( string[] argv )\n\t\t{\n\t\t\tfor( int i = 0; i < argv.Length; i++ )\n\t\t\t{\n\t\t\t\tstring arg = argv[ i ];\n\t\t\t\tif( arg == \"-h\" || arg == \"--help\" )\n\t\t\t\t{\n\t\t\t\t\tprintUsage();\n\t\t\t\t\tthrow new OperationCanceledException();\n\t\t\t\t}\n\t\t\t\telse if( arg == \"-c\" || arg == \"--capture\" ) captureDeviceIndex = int.Parse( argv[ ++i ] );\n\t\t\t\telse if( arg == \"-ld\" || arg == \"--list-devices\" ) listDevices = true;\n\t\t\t\telse if( arg == \"-t\" || arg == \"--threads\" ) n_threads = int.Parse( argv[ ++i ] );\n\t\t\t\telse if( arg == \"-ot\" || arg == \"--offset-t\" ) offset_t_ms = int.Parse( argv[ ++i ] );\n\t\t\t\telse if( arg == \"-on\" || arg == \"--offset-n\" ) offset_n = int.Parse( argv[ ++i ] );\n\t\t\t\telse if( arg == \"-d\" || arg == \"--duration\" ) duration_ms = int.Parse( argv[ ++i ] );\n\t\t\t\telse if( arg == \"-mc\" || arg == \"--max-context\" ) max_context = int.Parse( argv[ ++i ] );\n\t\t\t\telse if( arg == \"-ml\" || arg == \"--max-len\" ) max_len = int.Parse( argv[ ++i ] );\n\t\t\t\telse if( arg == \"-wt\" || arg == \"--word-thold\" ) word_thold = float.Parse( argv[ ++i ], CultureInfo.InvariantCulture );\n\t\t\t\telse if( arg == \"-su\" || arg == \"--speed-up\" ) speed_up = true;\n\t\t\t\telse if( arg == \"-tr\" || arg == \"--translate\" ) translate = true;\n\t\t\t\telse if( arg == \"-di\" || arg == \"--diarize\" ) diarize = true;\n\t\t\t\telse if( arg == \"-otxt\" || arg == \"--output-txt\" ) output_txt = true;\n\t\t\t\telse if( arg == \"-ps\" || arg == \"--print-special\" ) print_special = true;\n\t\t\t\telse if( arg == \"-nc\" || arg == \"--no-colors\" ) print_colors = false;\n\t\t\t\telse if( arg == \"-pp\" || arg == \"--print-progress\" ) print_progress = true;\n\t\t\t\telse if( arg == \"-nt\" || arg == \"--no-timestamps\" ) no_timestamps = true;\n\t\t\t\telse if( arg == \"-l\" || arg == \"--language\" ) language = parseLanguage( argv[ ++i ] );\n\t\t\t\telse if( arg == \"--prompt\" ) prompt = parsePrompt( argv[ ++i ] );\n\t\t\t\telse if( arg == \"-m\" || arg == \"--model\" ) model = argv[ ++i ];\n\t\t\t\telse\n\t\t\t\t\tthrow new ArgumentException( $\"Unknown argument: \\\"{arg}\\\"\" );\n\t\t\t}\n\t\t\tif( listDevices )\n\t\t\t\treturn;\n\t\t\tif( string.IsNullOrWhiteSpace( model ) )\n\t\t\t\tthrow new ArgumentException( \"The model file is not provided in the arguments\" );\n\t\t\tif( !File.Exists( model ) )\n\t\t\t\tthrow new FileNotFoundException( \"Model not found\", model );\n\t\t}\n\n\t\tstatic string cstr( bool b ) => b.ToString();\n\n\t\tstatic int[]? parsePrompt( string str )\n\t\t{\n\t\t\tif( string.IsNullOrWhiteSpace( str ) )\n\t\t\t\treturn null;\n\t\t\t// TODO: expose whisper_tokenize function, as a method of iModel COM interface\n\t\t\tthrow new NotImplementedException();\n\t\t}\n\n\t\tvoid printUsage()\n\t\t{\n\t\t\tConsole.WriteLine();\n\n\t\t\tConsole.WriteLine( \"usage: {0} [options] file0.mp3 file1.wma ...\", Path.GetFileName( Assembly.GetExecutingAssembly().Location ) );\n\t\t\tConsole.WriteLine();\n\t\t\tConsole.WriteLine( \"options:\" );\n\t\t\tConsole.WriteLine( \"  -h,       --help          [default] show this help message and exit\" );\n\t\t\tConsole.WriteLine( \"  -t N,     --threads N     [{0,-7:D}] number of threads to use during computation\", n_threads );\n\t\t\tConsole.WriteLine( \"  -ot N,    --offset-t N    [{0,-7:D}] time offset in milliseconds\", offset_t_ms );\n\t\t\tConsole.WriteLine( \"  -on N,    --offset-n N    [{0,-7:D}] segment index offset\", offset_n );\n\t\t\tConsole.WriteLine( \"  -d  N,    --duration N    [{0,-7:D}] duration of audio to process in milliseconds\", duration_ms );\n\t\t\tConsole.WriteLine( \"  -mc N,    --max-context N [{0,-7:D}] maximum number of text context tokens to store\", max_context );\n\t\t\tConsole.WriteLine( \"  -ml N,    --max-len N     [{0,-7:D}] maximum segment length in characters\", max_len );\n\t\t\tConsole.WriteLine( \"  -wt N,    --word-thold N  [{0,-7:F2}] word timestamp probability threshold\", word_thold );\n\t\t\tConsole.WriteLine( \"  -su,      --speed-up      [{0,-7}] speed up audio by x2 (reduced accuracy)\", cstr( speed_up ) );\n\t\t\tConsole.WriteLine( \"  -tr,      --translate     [{0,-7}] translate from source language to english\", cstr( translate ) );\n\t\t\tConsole.WriteLine( \"  -di,      --diarize       [{0,-7}] stereo audio diarization\", cstr( diarize ) );\n\t\t\tConsole.WriteLine( \"  -otxt,    --output-txt    [{0,-7}] output result in a text file\", cstr( output_txt ) );\n\t\t\tConsole.WriteLine( \"  -ps,      --print-special [{0,-7}] print special tokens\", cstr( print_special ) );\n\t\t\tConsole.WriteLine( \"  -nc,      --no-colors     [{0,-7}] do not print colors\", cstr( !print_colors ) );\n\t\t\tConsole.WriteLine( \"  -nt,      --no-timestamps [{0,-7}] do not print timestamps\", cstr( no_timestamps ) );\n\t\t\tConsole.WriteLine( \"  -l LANG,  --language LANG [{0,-7}] spoken language\", language.getCode() );\n\t\t\tConsole.WriteLine( \"            --prompt PROMPT [       ] initial prompt\" );\n\t\t\tConsole.WriteLine( \"  -m FNAME, --model FNAME   [{0,-7}] model path\", model );\n\t\t\tConsole.WriteLine( \"  -f FNAME, --file FNAME    [{0,-7}] path of the input audio file\", \"\" );\n\t\t}\n\t}\n}"
  },
  {
    "path": "Examples/MicrophoneCS/MicrophoneCS.cs",
    "content": "﻿using Whisper;\n\nnamespace MicrophoneCS\n{\n\tstatic class Program\n\t{\n\t\tstatic int Main( string[] args )\n\t\t{\n\t\t\ttry\n\t\t\t{\n\t\t\t\tCommandLineArgs cla;\n\t\t\t\ttry\n\t\t\t\t{\n\t\t\t\t\tcla = new CommandLineArgs( args );\n\t\t\t\t}\n\t\t\t\tcatch( OperationCanceledException )\n\t\t\t\t{\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t\tconst eLoggerFlags loggerFlags = eLoggerFlags.UseStandardError | eLoggerFlags.SkipFormatMessage;\n\t\t\t\tLibrary.setLogSink( eLogLevel.Debug, loggerFlags );\n\n\t\t\t\tusing iMediaFoundation mf = Library.initMediaFoundation();\n\t\t\t\tCaptureDeviceId[] devices = mf.listCaptureDevices() ??\n\t\t\t\t\tthrow new ApplicationException( \"This computer has no audio capture devices\" );\n\n\t\t\t\tif( cla.listDevices )\n\t\t\t\t{\n\t\t\t\t\tfor( int i = 0; i < devices.Length; i++ )\n\t\t\t\t\t\tConsole.WriteLine( \"#{0}: {1}\", i, devices[ i ].displayName );\n\t\t\t\t\treturn 0;\n\t\t\t\t}\n\t\t\t\tif( cla.captureDeviceIndex < 0 || cla.captureDeviceIndex >= devices.Length )\n\t\t\t\t\tthrow new ApplicationException( $\"Capture device index is out of range; the valid range is [ 0 .. {devices.Length - 1} ]\" );\n\n\t\t\t\tsCaptureParams cp = new sCaptureParams( true );\n\t\t\t\tif( cla.diarize )\n\t\t\t\t\tcp.flags |= eCaptureFlags.Stereo;\n\t\t\t\tusing iAudioCapture captureDev = mf.openCaptureDevice( devices[ cla.captureDeviceIndex ], cp );\n\n\t\t\t\tusing iModel model = Library.loadModel( cla.model );\n\t\t\t\tusing Context context = model.createContext();\n\t\t\t\tcla.apply( ref context.parameters );\n\n\t\t\t\tCaptureThread thread = new CaptureThread( cla, context, captureDev );\n\t\t\t\tthread.join();\n\n\t\t\t\tcontext.timingsPrint();\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\tcatch( Exception ex )\n\t\t\t{\n\t\t\t\t// Console.WriteLine( ex.Message );\n\t\t\t\tConsole.WriteLine( ex.ToString() );\n\t\t\t\treturn ex.HResult;\n\t\t\t}\n\t\t}\n\t}\n}"
  },
  {
    "path": "Examples/MicrophoneCS/MicrophoneCS.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n\t<PropertyGroup>\n\t\t<OutputType>Exe</OutputType>\n\t\t<TargetFramework>net6.0-windows</TargetFramework>\n\t\t<ImplicitUsings>enable</ImplicitUsings>\n\t\t<Nullable>enable</Nullable>\n\t\t<CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>\n\t\t<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>\n\t\t<Platforms>x64</Platforms>\n\t</PropertyGroup>\n\n\t<ItemGroup>\n\t  <Compile Include=\"..\\TranscribeCS\\AnsiCodes.cs\" Link=\"AnsiCodes.cs\" />\n\t</ItemGroup>\n\n\t<ItemGroup>\n\t\t<Content Include=\"..\\..\\x64\\$(Configuration)\\Whisper.dll\" Link=\"Whisper.dll\">\n\t\t\t<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n\t\t</Content>\n\t</ItemGroup>\n\n\t<ItemGroup>\n\t\t<ProjectReference Include=\"..\\..\\WhisperNet\\WhisperNet.csproj\" />\n\t</ItemGroup>\n\n</Project>"
  },
  {
    "path": "Examples/MicrophoneCS/Readme.txt",
    "content": "﻿This example builds .NET 6 console application which shows how to use audio capture API of the .NET wrapper."
  },
  {
    "path": "Examples/MicrophoneCS/TranscribeCallbacks.cs",
    "content": "﻿using System.Globalization;\nusing Whisper;\n\nnamespace MicrophoneCS\n{\n\t/// <summary>Implementation of Callbacks abstract class, to print these segments as soon as they’re produced by the library.</summary>\n\tsealed class TranscribeCallbacks: Callbacks\n\t{\n\t\treadonly CommandLineArgs args;\n\t\treadonly eResultFlags resultFlags;\n\n\t\tpublic TranscribeCallbacks( CommandLineArgs args )\n\t\t{\n\t\t\tthis.args = args;\n\t\t\tresultFlags = args.resultFlags();\n\t\t\tConsole.OutputEncoding = System.Text.Encoding.UTF8;\n\t\t}\n\n\t\t// Terminal color map. 10 colors grouped in ranges [0.0, 0.1, ..., 0.9]\n\t\t// Lowest is red, middle is yellow, highest is green.\n\t\treadonly string[] k_colors = new string[]\n\t\t{\n\t\t\t\"\\x1B[38;5;196m\", \"\\x1B[38;5;202m\", \"\\x1B[38;5;208m\", \"\\x1B[38;5;214m\", \"\\x1B[38;5;220m\",\n\t\t\t\"\\x1B[38;5;226m\", \"\\x1B[38;5;190m\", \"\\x1B[38;5;154m\", \"\\x1B[38;5;118m\", \"\\x1B[38;5;82m\"\n\t\t};\n\n\t\tint colorIndex( in sToken tok )\n\t\t{\n\t\t\tfloat p = tok.probability;\n\t\t\tfloat p3 = p * p * p;\n\t\t\tint col = (int)( p3 * k_colors.Length );\n\t\t\tcol = Math.Clamp( col, 0, k_colors.Length - 1 );\n\t\t\treturn col;\n\t\t}\n\n\t\tpublic static string printTime( TimeSpan ts ) =>\n\t\t\tts.ToString( \"hh':'mm':'ss'.'fff\", CultureInfo.InvariantCulture );\n\t\tpublic static string printTimeWithComma( TimeSpan ts ) =>\n\t\t\tts.ToString( \"hh':'mm':'ss','fff\", CultureInfo.InvariantCulture );\n\n\t\tprotected override void onNewSegment( Context sender, int countNew )\n\t\t{\n\t\t\tTranscribeResult res = sender.results( resultFlags );\n\t\t\tReadOnlySpan<sToken> tokens = res.tokens;\n\n\t\t\tint s0 = res.segments.Length - countNew;\n\t\t\tif( s0 == 0 )\n\t\t\t\tConsole.WriteLine();\n\n\t\t\tfor( int i = s0; i < res.segments.Length; i++ )\n\t\t\t{\n\t\t\t\tsSegment seg = res.segments[ i ];\n\n\t\t\t\tif( args.no_timestamps )\n\t\t\t\t{\n\t\t\t\t\tif( args.print_colors && AnsiCodes.enabled )\n\t\t\t\t\t{\n\t\t\t\t\t\tforeach( sToken tok in res.getTokens( seg ) )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif( !args.print_special && tok.hasFlag( eTokenFlags.Special ) )\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\tConsole.Write( \"{0}{1}{2}\", k_colors[ colorIndex( tok ) ], tok.text, \"\\x1B[0m\" );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tConsole.Write( seg.text );\n\t\t\t\t\tConsole.Out.Flush();\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tstring speaker = \"\";\n\t\t\t\tif( args.diarize )\n\t\t\t\t{\n\t\t\t\t\tspeaker = sender.detectSpeaker( seg.time ) switch\n\t\t\t\t\t{\n\t\t\t\t\t\teSpeakerChannel.Unsure => \"(speaker ?)\",\n\t\t\t\t\t\teSpeakerChannel.Left => \"(speaker 0)\",\n\t\t\t\t\t\teSpeakerChannel.Right => \"(speaker 1)\",\n\t\t\t\t\t\t_ => \"\"\n\t\t\t\t\t};\n\t\t\t\t}\n\n\t\t\t\tif( args.print_colors && AnsiCodes.enabled )\n\t\t\t\t{\n\t\t\t\t\tConsole.Write( \"[{0} --> {1}] {2} \", printTime( seg.time.begin ), printTime( seg.time.end ), speaker );\n\t\t\t\t\tforeach( sToken tok in res.getTokens( seg ) )\n\t\t\t\t\t{\n\t\t\t\t\t\tif( !args.print_special && tok.hasFlag( eTokenFlags.Special ) )\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\tConsole.Write( \"{0}{1}{2}\", k_colors[ colorIndex( tok ) ], tok.text, \"\\x1B[0m\" );\n\t\t\t\t\t}\n\t\t\t\t\tConsole.WriteLine();\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tConsole.WriteLine( \"[{0} --> {1}] {2} {3}\", printTime( seg.time.begin ), printTime( seg.time.end ), speaker, seg.text );\n\t\t\t}\n\t\t}\n\t}\n}"
  },
  {
    "path": "Examples/OldMain/OldMain.vcxproj",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project DefaultTargets=\"Build\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <ItemGroup Label=\"ProjectConfigurations\">\n    <ProjectConfiguration Include=\"Debug|x64\">\n      <Configuration>Debug</Configuration>\n      <Platform>x64</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Release|x64\">\n      <Configuration>Release</Configuration>\n      <Platform>x64</Platform>\n    </ProjectConfiguration>\n  </ItemGroup>\n  <PropertyGroup Label=\"Globals\">\n    <VCProjectVersion>16.0</VCProjectVersion>\n    <Keyword>Win32Proj</Keyword>\n    <ProjectGuid>{596f9770-9aeb-49d3-86ca-4200197df12b}</ProjectGuid>\n    <RootNamespace>OldMain</RootNamespace>\n    <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>\n  </PropertyGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.Default.props\" />\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\" Label=\"Configuration\">\n    <ConfigurationType>Application</ConfigurationType>\n    <UseDebugLibraries>true</UseDebugLibraries>\n    <PlatformToolset>v143</PlatformToolset>\n    <CharacterSet>Unicode</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\" Label=\"Configuration\">\n    <ConfigurationType>Application</ConfigurationType>\n    <UseDebugLibraries>false</UseDebugLibraries>\n    <PlatformToolset>v143</PlatformToolset>\n    <WholeProgramOptimization>true</WholeProgramOptimization>\n    <CharacterSet>Unicode</CharacterSet>\n  </PropertyGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.props\" />\n  <ImportGroup Label=\"ExtensionSettings\">\n  </ImportGroup>\n  <ImportGroup Label=\"Shared\">\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <PropertyGroup Label=\"UserMacros\" />\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\n    <IncludePath>$(ProjectDir);$(SolutionDir)Whisper\\Source\\;$(IncludePath)</IncludePath>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <IncludePath>$(ProjectDir);$(SolutionDir)Whisper\\Source\\;$(IncludePath)</IncludePath>\n  </PropertyGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <SDLCheck>true</SDLCheck>\n      <PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <ConformanceMode>true</ConformanceMode>\n      <EnableEnhancedInstructionSet>AdvancedVectorExtensions2</EnableEnhancedInstructionSet>\n      <LanguageStandard>stdcpp20</LanguageStandard>\n    </ClCompile>\n    <Link>\n      <SubSystem>Console</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <FunctionLevelLinking>true</FunctionLevelLinking>\n      <IntrinsicFunctions>true</IntrinsicFunctions>\n      <SDLCheck>true</SDLCheck>\n      <PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <ConformanceMode>true</ConformanceMode>\n      <EnableEnhancedInstructionSet>AdvancedVectorExtensions2</EnableEnhancedInstructionSet>\n      <LanguageStandard>stdcpp20</LanguageStandard>\n    </ClCompile>\n    <Link>\n      <SubSystem>Console</SubSystem>\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\n      <OptimizeReferences>true</OptimizeReferences>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n      <LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemGroup>\n    <ClCompile Include=\"..\\..\\Whisper\\source.compat\\ggmlMsvc.c\" />\n    <ClCompile Include=\"..\\..\\Whisper\\source\\ggml.c\">\n      <ExcludedFromBuild>true</ExcludedFromBuild>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\Whisper\\source\\whisper.cpp\" />\n    <ClCompile Include=\"main.cpp\" />\n    <ClCompile Include=\"Utils\\Logger.cpp\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ClInclude Include=\"..\\..\\Whisper\\source\\ggml.h\" />\n    <ClInclude Include=\"..\\..\\Whisper\\source\\whisper.h\" />\n    <ClInclude Include=\"dr_wav.h\" />\n    <ClInclude Include=\"Utils\\Logger.h\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Text Include=\"Readme.txt\" />\n  </ItemGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.targets\" />\n  <ImportGroup Label=\"ExtensionTargets\">\n  </ImportGroup>\n</Project>"
  },
  {
    "path": "Examples/OldMain/OldMain.vcxproj.filters",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <ItemGroup>\n    <ClCompile Include=\"..\\..\\Whisper\\source\\whisper.cpp\" />\n    <ClCompile Include=\"main.cpp\" />\n    <ClCompile Include=\"..\\..\\Whisper\\source\\ggml.c\" />\n    <ClCompile Include=\"..\\..\\Whisper\\source.compat\\ggmlMsvc.c\" />\n    <ClCompile Include=\"Utils\\Logger.cpp\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ClInclude Include=\"..\\..\\Whisper\\source\\ggml.h\" />\n    <ClInclude Include=\"..\\..\\Whisper\\source\\whisper.h\" />\n    <ClInclude Include=\"dr_wav.h\" />\n    <ClInclude Include=\"Utils\\Logger.h\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Text Include=\"Readme.txt\" />\n  </ItemGroup>\n</Project>"
  },
  {
    "path": "Examples/OldMain/Readme.txt",
    "content": "﻿This project builds the original whisper.cpp command-line sample"
  },
  {
    "path": "Examples/OldMain/Utils/Logger.cpp",
    "content": "#include <stdint.h>\n#include <vector>\n#include <cstdarg>\n#include \"Logger.h\"\n\nnamespace\n{\n\tvoid logMessage( const char* lvl, const char8_t* pszFormat, std::va_list va )\n\t{\n\t\tfprintf( stderr, \"%s: \", lvl );\n\t\tvfprintf( stderr, (const char*)pszFormat, va );\n\t\tfprintf( stderr, \"\\n\" );\n\t}\n}\n\n#define LOG_MESSAGE_IMPL( lvl )                \\\n\tstd::va_list args;                         \\\n\tva_start( args, pszFormat );               \\\n\tlogMessage( lvl, pszFormat, args );        \\\n\tva_end( args );\n\nvoid logError( const char8_t* pszFormat, ... )\n{\n\tLOG_MESSAGE_IMPL( \"Error\" );\n}\n\nvoid logWarning( const char8_t* pszFormat, ... )\n{\n\tLOG_MESSAGE_IMPL( \"Warning\" );\n}\n\nvoid logInfo( const char8_t* pszFormat, ... )\n{\n\tLOG_MESSAGE_IMPL( \"Info\" );\n}\n\nvoid logDebug( const char8_t* pszFormat, ... )\n{\n\tLOG_MESSAGE_IMPL( \"Debug\" );\n}"
  },
  {
    "path": "Examples/OldMain/Utils/Logger.h",
    "content": "#pragma once\n\n#ifdef  __cplusplus\nextern \"C\" {\n#endif\n\nstruct ggml_tensor;\n\nvoid logError( const char8_t* pszFormat, ... );\nvoid logWarning( const char8_t* pszFormat, ... );\nvoid logInfo( const char8_t* pszFormat, ... );\nvoid logDebug( const char8_t* pszFormat, ... );\n\n#ifdef  __cplusplus\n}\n\nnamespace Tracing\n{\n\tstruct ItemName\n\t{\n\t\tItemName( const char* str ) { }\n\t\tItemName( const char* str, uint32_t a0 ) { }\n\t\tItemName( const char* str, int a0 ) { }\n\t};\n\n\tinline void tensor( const ItemName& name, const ggml_tensor* tensor ) { }\n\tinline void delayTensor( const ItemName& name, const ggml_tensor* tensor ) { }\n\tinline void vector( const ItemName& name, const std::vector<float>& vec ) { }\n\tinline void writeDelayedTensors() { }\n}\n#endif"
  },
  {
    "path": "Examples/OldMain/dr_wav.h",
    "content": "/*\nWAV audio loader and writer. Choice of public domain or MIT-0. See license statements at the end of this file.\ndr_wav - v0.12.16 - 2020-12-02\n\nDavid Reid - mackron@gmail.com\n\nGitHub: https://github.com/mackron/dr_libs\n*/\n\n/*\nRELEASE NOTES - VERSION 0.12\n============================\nVersion 0.12 includes breaking changes to custom chunk handling.\n\n\nChanges to Chunk Callback\n-------------------------\ndr_wav supports the ability to fire a callback when a chunk is encounted (except for WAVE and FMT chunks). The callback has been updated to include both the\ncontainer (RIFF or Wave64) and the FMT chunk which contains information about the format of the data in the wave file.\n\nPreviously, there was no direct way to determine the container, and therefore no way to discriminate against the different IDs in the chunk header (RIFF and\nWave64 containers encode chunk ID's differently). The `container` parameter can be used to know which ID to use.\n\nSometimes it can be useful to know the data format at the time the chunk callback is fired. A pointer to a `drwav_fmt` object is now passed into the chunk\ncallback which will give you information about the data format. To determine the sample format, use `drwav_fmt_get_format()`. This will return one of the\n`DR_WAVE_FORMAT_*` tokens.\n*/\n\n/*\nIntroduction\n============\nThis is a single file library. To use it, do something like the following in one .c file.\n    \n    ```c\n    #define DR_WAV_IMPLEMENTATION\n    #include \"dr_wav.h\"\n    ```\n\nYou can then #include this file in other parts of the program as you would with any other header file. Do something like the following to read audio data:\n\n    ```c\n    drwav wav;\n    if (!drwav_init_file(&wav, \"my_song.wav\", NULL)) {\n        // Error opening WAV file.\n    }\n\n    drwav_int32* pDecodedInterleavedPCMFrames = malloc(wav.totalPCMFrameCount * wav.channels * sizeof(drwav_int32));\n    size_t numberOfSamplesActuallyDecoded = drwav_read_pcm_frames_s32(&wav, wav.totalPCMFrameCount, pDecodedInterleavedPCMFrames);\n\n    ...\n\n    drwav_uninit(&wav);\n    ```\n\nIf you just want to quickly open and read the audio data in a single operation you can do something like this:\n\n    ```c\n    unsigned int channels;\n    unsigned int sampleRate;\n    drwav_uint64 totalPCMFrameCount;\n    float* pSampleData = drwav_open_file_and_read_pcm_frames_f32(\"my_song.wav\", &channels, &sampleRate, &totalPCMFrameCount, NULL);\n    if (pSampleData == NULL) {\n        // Error opening and reading WAV file.\n    }\n\n    ...\n\n    drwav_free(pSampleData);\n    ```\n\nThe examples above use versions of the API that convert the audio data to a consistent format (32-bit signed PCM, in this case), but you can still output the\naudio data in its internal format (see notes below for supported formats):\n\n    ```c\n    size_t framesRead = drwav_read_pcm_frames(&wav, wav.totalPCMFrameCount, pDecodedInterleavedPCMFrames);\n    ```\n\nYou can also read the raw bytes of audio data, which could be useful if dr_wav does not have native support for a particular data format:\n\n    ```c\n    size_t bytesRead = drwav_read_raw(&wav, bytesToRead, pRawDataBuffer);\n    ```\n\ndr_wav can also be used to output WAV files. This does not currently support compressed formats. To use this, look at `drwav_init_write()`,\n`drwav_init_file_write()`, etc. Use `drwav_write_pcm_frames()` to write samples, or `drwav_write_raw()` to write raw data in the \"data\" chunk.\n\n    ```c\n    drwav_data_format format;\n    format.container = drwav_container_riff;     // <-- drwav_container_riff = normal WAV files, drwav_container_w64 = Sony Wave64.\n    format.format = DR_WAVE_FORMAT_PCM;          // <-- Any of the DR_WAVE_FORMAT_* codes.\n    format.channels = 2;\n    format.sampleRate = 44100;\n    format.bitsPerSample = 16;\n    drwav_init_file_write(&wav, \"data/recording.wav\", &format, NULL);\n\n    ...\n\n    drwav_uint64 framesWritten = drwav_write_pcm_frames(pWav, frameCount, pSamples);\n    ```\n\ndr_wav has seamless support the Sony Wave64 format. The decoder will automatically detect it and it should Just Work without any manual intervention.\n\n\nBuild Options\n=============\n#define these options before including this file.\n\n#define DR_WAV_NO_CONVERSION_API\n  Disables conversion APIs such as `drwav_read_pcm_frames_f32()` and `drwav_s16_to_f32()`.\n\n#define DR_WAV_NO_STDIO\n  Disables APIs that initialize a decoder from a file such as `drwav_init_file()`, `drwav_init_file_write()`, etc.\n\n\n\nNotes\n=====\n- Samples are always interleaved.\n- The default read function does not do any data conversion. Use `drwav_read_pcm_frames_f32()`, `drwav_read_pcm_frames_s32()` and `drwav_read_pcm_frames_s16()`\n  to read and convert audio data to 32-bit floating point, signed 32-bit integer and signed 16-bit integer samples respectively. Tested and supported internal\n  formats include the following:\n  - Unsigned 8-bit PCM\n  - Signed 12-bit PCM\n  - Signed 16-bit PCM\n  - Signed 24-bit PCM\n  - Signed 32-bit PCM\n  - IEEE 32-bit floating point\n  - IEEE 64-bit floating point\n  - A-law and u-law\n  - Microsoft ADPCM\n  - IMA ADPCM (DVI, format code 0x11)\n- dr_wav will try to read the WAV file as best it can, even if it's not strictly conformant to the WAV format.\n*/\n\n#ifndef dr_wav_h\n#define dr_wav_h\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define DRWAV_STRINGIFY(x)      #x\n#define DRWAV_XSTRINGIFY(x)     DRWAV_STRINGIFY(x)\n\n#define DRWAV_VERSION_MAJOR     0\n#define DRWAV_VERSION_MINOR     12\n#define DRWAV_VERSION_REVISION  16\n#define DRWAV_VERSION_STRING    DRWAV_XSTRINGIFY(DRWAV_VERSION_MAJOR) \".\" DRWAV_XSTRINGIFY(DRWAV_VERSION_MINOR) \".\" DRWAV_XSTRINGIFY(DRWAV_VERSION_REVISION)\n\n#include <stddef.h> /* For size_t. */\n\n/* Sized types. */\ntypedef   signed char           drwav_int8;\ntypedef unsigned char           drwav_uint8;\ntypedef   signed short          drwav_int16;\ntypedef unsigned short          drwav_uint16;\ntypedef   signed int            drwav_int32;\ntypedef unsigned int            drwav_uint32;\n#if defined(_MSC_VER)\n    typedef   signed __int64    drwav_int64;\n    typedef unsigned __int64    drwav_uint64;\n#else\n    #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))\n        #pragma GCC diagnostic push\n        #pragma GCC diagnostic ignored \"-Wlong-long\"\n        #if defined(__clang__)\n            #pragma GCC diagnostic ignored \"-Wc++11-long-long\"\n        #endif\n    #endif\n    typedef   signed long long  drwav_int64;\n    typedef unsigned long long  drwav_uint64;\n    #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))\n        #pragma GCC diagnostic pop\n    #endif\n#endif\n#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__)\n    typedef drwav_uint64        drwav_uintptr;\n#else\n    typedef drwav_uint32        drwav_uintptr;\n#endif\ntypedef drwav_uint8             drwav_bool8;\ntypedef drwav_uint32            drwav_bool32;\n#define DRWAV_TRUE              1\n#define DRWAV_FALSE             0\n\n#if !defined(DRWAV_API)\n    #if defined(DRWAV_DLL)\n        #if defined(_WIN32)\n            #define DRWAV_DLL_IMPORT  __declspec(dllimport)\n            #define DRWAV_DLL_EXPORT  __declspec(dllexport)\n            #define DRWAV_DLL_PRIVATE static\n        #else\n            #if defined(__GNUC__) && __GNUC__ >= 4\n                #define DRWAV_DLL_IMPORT  __attribute__((visibility(\"default\")))\n                #define DRWAV_DLL_EXPORT  __attribute__((visibility(\"default\")))\n                #define DRWAV_DLL_PRIVATE __attribute__((visibility(\"hidden\")))\n            #else\n                #define DRWAV_DLL_IMPORT\n                #define DRWAV_DLL_EXPORT\n                #define DRWAV_DLL_PRIVATE static\n            #endif\n        #endif\n\n        #if defined(DR_WAV_IMPLEMENTATION) || defined(DRWAV_IMPLEMENTATION)\n            #define DRWAV_API  DRWAV_DLL_EXPORT\n        #else\n            #define DRWAV_API  DRWAV_DLL_IMPORT\n        #endif\n        #define DRWAV_PRIVATE DRWAV_DLL_PRIVATE\n    #else\n        #define DRWAV_API extern\n        #define DRWAV_PRIVATE static\n    #endif\n#endif\n\ntypedef drwav_int32 drwav_result;\n#define DRWAV_SUCCESS                        0\n#define DRWAV_ERROR                         -1   /* A generic error. */\n#define DRWAV_INVALID_ARGS                  -2\n#define DRWAV_INVALID_OPERATION             -3\n#define DRWAV_OUT_OF_MEMORY                 -4\n#define DRWAV_OUT_OF_RANGE                  -5\n#define DRWAV_ACCESS_DENIED                 -6\n#define DRWAV_DOES_NOT_EXIST                -7\n#define DRWAV_ALREADY_EXISTS                -8\n#define DRWAV_TOO_MANY_OPEN_FILES           -9\n#define DRWAV_INVALID_FILE                  -10\n#define DRWAV_TOO_BIG                       -11\n#define DRWAV_PATH_TOO_LONG                 -12\n#define DRWAV_NAME_TOO_LONG                 -13\n#define DRWAV_NOT_DIRECTORY                 -14\n#define DRWAV_IS_DIRECTORY                  -15\n#define DRWAV_DIRECTORY_NOT_EMPTY           -16\n#define DRWAV_END_OF_FILE                   -17\n#define DRWAV_NO_SPACE                      -18\n#define DRWAV_BUSY                          -19\n#define DRWAV_IO_ERROR                      -20\n#define DRWAV_INTERRUPT                     -21\n#define DRWAV_UNAVAILABLE                   -22\n#define DRWAV_ALREADY_IN_USE                -23\n#define DRWAV_BAD_ADDRESS                   -24\n#define DRWAV_BAD_SEEK                      -25\n#define DRWAV_BAD_PIPE                      -26\n#define DRWAV_DEADLOCK                      -27\n#define DRWAV_TOO_MANY_LINKS                -28\n#define DRWAV_NOT_IMPLEMENTED               -29\n#define DRWAV_NO_MESSAGE                    -30\n#define DRWAV_BAD_MESSAGE                   -31\n#define DRWAV_NO_DATA_AVAILABLE             -32\n#define DRWAV_INVALID_DATA                  -33\n#define DRWAV_TIMEOUT                       -34\n#define DRWAV_NO_NETWORK                    -35\n#define DRWAV_NOT_UNIQUE                    -36\n#define DRWAV_NOT_SOCKET                    -37\n#define DRWAV_NO_ADDRESS                    -38\n#define DRWAV_BAD_PROTOCOL                  -39\n#define DRWAV_PROTOCOL_UNAVAILABLE          -40\n#define DRWAV_PROTOCOL_NOT_SUPPORTED        -41\n#define DRWAV_PROTOCOL_FAMILY_NOT_SUPPORTED -42\n#define DRWAV_ADDRESS_FAMILY_NOT_SUPPORTED  -43\n#define DRWAV_SOCKET_NOT_SUPPORTED          -44\n#define DRWAV_CONNECTION_RESET              -45\n#define DRWAV_ALREADY_CONNECTED             -46\n#define DRWAV_NOT_CONNECTED                 -47\n#define DRWAV_CONNECTION_REFUSED            -48\n#define DRWAV_NO_HOST                       -49\n#define DRWAV_IN_PROGRESS                   -50\n#define DRWAV_CANCELLED                     -51\n#define DRWAV_MEMORY_ALREADY_MAPPED         -52\n#define DRWAV_AT_END                        -53\n\n/* Common data formats. */\n#define DR_WAVE_FORMAT_PCM          0x1\n#define DR_WAVE_FORMAT_ADPCM        0x2\n#define DR_WAVE_FORMAT_IEEE_FLOAT   0x3\n#define DR_WAVE_FORMAT_ALAW         0x6\n#define DR_WAVE_FORMAT_MULAW        0x7\n#define DR_WAVE_FORMAT_DVI_ADPCM    0x11\n#define DR_WAVE_FORMAT_EXTENSIBLE   0xFFFE\n\n/* Constants. */\n#ifndef DRWAV_MAX_SMPL_LOOPS\n#define DRWAV_MAX_SMPL_LOOPS        1\n#endif\n\n/* Flags to pass into drwav_init_ex(), etc. */\n#define DRWAV_SEQUENTIAL            0x00000001\n\nDRWAV_API void drwav_version(drwav_uint32* pMajor, drwav_uint32* pMinor, drwav_uint32* pRevision);\nDRWAV_API const char* drwav_version_string(void);\n\ntypedef enum\n{\n    drwav_seek_origin_start,\n    drwav_seek_origin_current\n} drwav_seek_origin;\n\ntypedef enum\n{\n    drwav_container_riff,\n    drwav_container_w64,\n    drwav_container_rf64\n} drwav_container;\n\ntypedef struct\n{\n    union\n    {\n        drwav_uint8 fourcc[4];\n        drwav_uint8 guid[16];\n    } id;\n\n    /* The size in bytes of the chunk. */\n    drwav_uint64 sizeInBytes;\n\n    /*\n    RIFF = 2 byte alignment.\n    W64  = 8 byte alignment.\n    */\n    unsigned int paddingSize;\n} drwav_chunk_header;\n\ntypedef struct\n{\n    /*\n    The format tag exactly as specified in the wave file's \"fmt\" chunk. This can be used by applications\n    that require support for data formats not natively supported by dr_wav.\n    */\n    drwav_uint16 formatTag;\n\n    /* The number of channels making up the audio data. When this is set to 1 it is mono, 2 is stereo, etc. */\n    drwav_uint16 channels;\n\n    /* The sample rate. Usually set to something like 44100. */\n    drwav_uint32 sampleRate;\n\n    /* Average bytes per second. You probably don't need this, but it's left here for informational purposes. */\n    drwav_uint32 avgBytesPerSec;\n\n    /* Block align. This is equal to the number of channels * bytes per sample. */\n    drwav_uint16 blockAlign;\n\n    /* Bits per sample. */\n    drwav_uint16 bitsPerSample;\n\n    /* The size of the extended data. Only used internally for validation, but left here for informational purposes. */\n    drwav_uint16 extendedSize;\n\n    /*\n    The number of valid bits per sample. When <formatTag> is equal to WAVE_FORMAT_EXTENSIBLE, <bitsPerSample>\n    is always rounded up to the nearest multiple of 8. This variable contains information about exactly how\n    many bits are valid per sample. Mainly used for informational purposes.\n    */\n    drwav_uint16 validBitsPerSample;\n\n    /* The channel mask. Not used at the moment. */\n    drwav_uint32 channelMask;\n\n    /* The sub-format, exactly as specified by the wave file. */\n    drwav_uint8 subFormat[16];\n} drwav_fmt;\n\nDRWAV_API drwav_uint16 drwav_fmt_get_format(const drwav_fmt* pFMT);\n\n\n/*\nCallback for when data is read. Return value is the number of bytes actually read.\n\npUserData   [in]  The user data that was passed to drwav_init() and family.\npBufferOut  [out] The output buffer.\nbytesToRead [in]  The number of bytes to read.\n\nReturns the number of bytes actually read.\n\nA return value of less than bytesToRead indicates the end of the stream. Do _not_ return from this callback until\neither the entire bytesToRead is filled or you have reached the end of the stream.\n*/\ntypedef size_t (* drwav_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead);\n\n/*\nCallback for when data is written. Returns value is the number of bytes actually written.\n\npUserData    [in]  The user data that was passed to drwav_init_write() and family.\npData        [out] A pointer to the data to write.\nbytesToWrite [in]  The number of bytes to write.\n\nReturns the number of bytes actually written.\n\nIf the return value differs from bytesToWrite, it indicates an error.\n*/\ntypedef size_t (* drwav_write_proc)(void* pUserData, const void* pData, size_t bytesToWrite);\n\n/*\nCallback for when data needs to be seeked.\n\npUserData [in] The user data that was passed to drwav_init() and family.\noffset    [in] The number of bytes to move, relative to the origin. Will never be negative.\norigin    [in] The origin of the seek - the current position or the start of the stream.\n\nReturns whether or not the seek was successful.\n\nWhether or not it is relative to the beginning or current position is determined by the \"origin\" parameter which will be either drwav_seek_origin_start or\ndrwav_seek_origin_current.\n*/\ntypedef drwav_bool32 (* drwav_seek_proc)(void* pUserData, int offset, drwav_seek_origin origin);\n\n/*\nCallback for when drwav_init_ex() finds a chunk.\n\npChunkUserData    [in] The user data that was passed to the pChunkUserData parameter of drwav_init_ex() and family.\nonRead            [in] A pointer to the function to call when reading.\nonSeek            [in] A pointer to the function to call when seeking.\npReadSeekUserData [in] The user data that was passed to the pReadSeekUserData parameter of drwav_init_ex() and family.\npChunkHeader      [in] A pointer to an object containing basic header information about the chunk. Use this to identify the chunk.\ncontainer         [in] Whether or not the WAV file is a RIFF or Wave64 container. If you're unsure of the difference, assume RIFF.\npFMT              [in] A pointer to the object containing the contents of the \"fmt\" chunk.\n\nReturns the number of bytes read + seeked.\n\nTo read data from the chunk, call onRead(), passing in pReadSeekUserData as the first parameter. Do the same for seeking with onSeek(). The return value must\nbe the total number of bytes you have read _plus_ seeked.\n\nUse the `container` argument to discriminate the fields in `pChunkHeader->id`. If the container is `drwav_container_riff` or `drwav_container_rf64` you should\nuse `id.fourcc`, otherwise you should use `id.guid`.\n\nThe `pFMT` parameter can be used to determine the data format of the wave file. Use `drwav_fmt_get_format()` to get the sample format, which will be one of the\n`DR_WAVE_FORMAT_*` identifiers. \n\nThe read pointer will be sitting on the first byte after the chunk's header. You must not attempt to read beyond the boundary of the chunk.\n*/\ntypedef drwav_uint64 (* drwav_chunk_proc)(void* pChunkUserData, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pReadSeekUserData, const drwav_chunk_header* pChunkHeader, drwav_container container, const drwav_fmt* pFMT);\n\ntypedef struct\n{\n    void* pUserData;\n    void* (* onMalloc)(size_t sz, void* pUserData);\n    void* (* onRealloc)(void* p, size_t sz, void* pUserData);\n    void  (* onFree)(void* p, void* pUserData);\n} drwav_allocation_callbacks;\n\n/* Structure for internal use. Only used for loaders opened with drwav_init_memory(). */\ntypedef struct\n{\n    const drwav_uint8* data;\n    size_t dataSize;\n    size_t currentReadPos;\n} drwav__memory_stream;\n\n/* Structure for internal use. Only used for writers opened with drwav_init_memory_write(). */\ntypedef struct\n{\n    void** ppData;\n    size_t* pDataSize;\n    size_t dataSize;\n    size_t dataCapacity;\n    size_t currentWritePos;\n} drwav__memory_stream_write;\n\ntypedef struct\n{\n    drwav_container container;  /* RIFF, W64. */\n    drwav_uint32 format;        /* DR_WAVE_FORMAT_* */\n    drwav_uint32 channels;\n    drwav_uint32 sampleRate;\n    drwav_uint32 bitsPerSample;\n} drwav_data_format;\n\n\n/* See the following for details on the 'smpl' chunk: https://sites.google.com/site/musicgapi/technical-documents/wav-file-format#smpl */\ntypedef struct\n{\n    drwav_uint32 cuePointId;\n    drwav_uint32 type;\n    drwav_uint32 start;\n    drwav_uint32 end;\n    drwav_uint32 fraction;\n    drwav_uint32 playCount;\n} drwav_smpl_loop;\n\n typedef struct\n{\n    drwav_uint32 manufacturer;\n    drwav_uint32 product;\n    drwav_uint32 samplePeriod;\n    drwav_uint32 midiUnityNotes;\n    drwav_uint32 midiPitchFraction;\n    drwav_uint32 smpteFormat;\n    drwav_uint32 smpteOffset;\n    drwav_uint32 numSampleLoops;\n    drwav_uint32 samplerData;\n    drwav_smpl_loop loops[DRWAV_MAX_SMPL_LOOPS];\n} drwav_smpl;\n\ntypedef struct\n{\n    /* A pointer to the function to call when more data is needed. */\n    drwav_read_proc onRead;\n\n    /* A pointer to the function to call when data needs to be written. Only used when the drwav object is opened in write mode. */\n    drwav_write_proc onWrite;\n\n    /* A pointer to the function to call when the wav file needs to be seeked. */\n    drwav_seek_proc onSeek;\n\n    /* The user data to pass to callbacks. */\n    void* pUserData;\n\n    /* Allocation callbacks. */\n    drwav_allocation_callbacks allocationCallbacks;\n\n\n    /* Whether or not the WAV file is formatted as a standard RIFF file or W64. */\n    drwav_container container;\n\n\n    /* Structure containing format information exactly as specified by the wav file. */\n    drwav_fmt fmt;\n\n    /* The sample rate. Will be set to something like 44100. */\n    drwav_uint32 sampleRate;\n\n    /* The number of channels. This will be set to 1 for monaural streams, 2 for stereo, etc. */\n    drwav_uint16 channels;\n\n    /* The bits per sample. Will be set to something like 16, 24, etc. */\n    drwav_uint16 bitsPerSample;\n\n    /* Equal to fmt.formatTag, or the value specified by fmt.subFormat if fmt.formatTag is equal to 65534 (WAVE_FORMAT_EXTENSIBLE). */\n    drwav_uint16 translatedFormatTag;\n\n    /* The total number of PCM frames making up the audio data. */\n    drwav_uint64 totalPCMFrameCount;\n\n\n    /* The size in bytes of the data chunk. */\n    drwav_uint64 dataChunkDataSize;\n    \n    /* The position in the stream of the first byte of the data chunk. This is used for seeking. */\n    drwav_uint64 dataChunkDataPos;\n\n    /* The number of bytes remaining in the data chunk. */\n    drwav_uint64 bytesRemaining;\n\n\n    /*\n    Only used in sequential write mode. Keeps track of the desired size of the \"data\" chunk at the point of initialization time. Always\n    set to 0 for non-sequential writes and when the drwav object is opened in read mode. Used for validation.\n    */\n    drwav_uint64 dataChunkDataSizeTargetWrite;\n\n    /* Keeps track of whether or not the wav writer was initialized in sequential mode. */\n    drwav_bool32 isSequentialWrite;\n\n\n    /* smpl chunk. */\n    drwav_smpl smpl;\n\n\n    /* A hack to avoid a DRWAV_MALLOC() when opening a decoder with drwav_init_memory(). */\n    drwav__memory_stream memoryStream;\n    drwav__memory_stream_write memoryStreamWrite;\n\n    /* Generic data for compressed formats. This data is shared across all block-compressed formats. */\n    struct\n    {\n        drwav_uint64 iCurrentPCMFrame;  /* The index of the next PCM frame that will be read by drwav_read_*(). This is used with \"totalPCMFrameCount\" to ensure we don't read excess samples at the end of the last block. */\n    } compressed;\n    \n    /* Microsoft ADPCM specific data. */\n    struct\n    {\n        drwav_uint32 bytesRemainingInBlock;\n        drwav_uint16 predictor[2];\n        drwav_int32  delta[2];\n        drwav_int32  cachedFrames[4];  /* Samples are stored in this cache during decoding. */\n        drwav_uint32 cachedFrameCount;\n        drwav_int32  prevFrames[2][2]; /* The previous 2 samples for each channel (2 channels at most). */\n    } msadpcm;\n\n    /* IMA ADPCM specific data. */\n    struct\n    {\n        drwav_uint32 bytesRemainingInBlock;\n        drwav_int32  predictor[2];\n        drwav_int32  stepIndex[2];\n        drwav_int32  cachedFrames[16]; /* Samples are stored in this cache during decoding. */\n        drwav_uint32 cachedFrameCount;\n    } ima;\n} drwav;\n\n\n/*\nInitializes a pre-allocated drwav object for reading.\n\npWav                         [out]          A pointer to the drwav object being initialized.\nonRead                       [in]           The function to call when data needs to be read from the client.\nonSeek                       [in]           The function to call when the read position of the client data needs to move.\nonChunk                      [in, optional] The function to call when a chunk is enumerated at initialized time.\npUserData, pReadSeekUserData [in, optional] A pointer to application defined data that will be passed to onRead and onSeek.\npChunkUserData               [in, optional] A pointer to application defined data that will be passed to onChunk.\nflags                        [in, optional] A set of flags for controlling how things are loaded.\n\nReturns true if successful; false otherwise.\n\nClose the loader with drwav_uninit().\n\nThis is the lowest level function for initializing a WAV file. You can also use drwav_init_file() and drwav_init_memory()\nto open the stream from a file or from a block of memory respectively.\n\nPossible values for flags:\n  DRWAV_SEQUENTIAL: Never perform a backwards seek while loading. This disables the chunk callback and will cause this function\n                    to return as soon as the data chunk is found. Any chunks after the data chunk will be ignored.\n\ndrwav_init() is equivalent to \"drwav_init_ex(pWav, onRead, onSeek, NULL, pUserData, NULL, 0);\".\n\nThe onChunk callback is not called for the WAVE or FMT chunks. The contents of the FMT chunk can be read from pWav->fmt\nafter the function returns.\n\nSee also: drwav_init_file(), drwav_init_memory(), drwav_uninit()\n*/\nDRWAV_API drwav_bool32 drwav_init(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks);\nDRWAV_API drwav_bool32 drwav_init_ex(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_chunk_proc onChunk, void* pReadSeekUserData, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks);\n\n/*\nInitializes a pre-allocated drwav object for writing.\n\nonWrite   [in]           The function to call when data needs to be written.\nonSeek    [in]           The function to call when the write position needs to move.\npUserData [in, optional] A pointer to application defined data that will be passed to onWrite and onSeek.\n\nReturns true if successful; false otherwise.\n\nClose the writer with drwav_uninit().\n\nThis is the lowest level function for initializing a WAV file. You can also use drwav_init_file_write() and drwav_init_memory_write()\nto open the stream from a file or from a block of memory respectively.\n\nIf the total sample count is known, you can use drwav_init_write_sequential(). This avoids the need for dr_wav to perform\na post-processing step for storing the total sample count and the size of the data chunk which requires a backwards seek.\n\nSee also: drwav_init_file_write(), drwav_init_memory_write(), drwav_uninit()\n*/\nDRWAV_API drwav_bool32 drwav_init_write(drwav* pWav, const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks);\nDRWAV_API drwav_bool32 drwav_init_write_sequential(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_write_proc onWrite, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks);\nDRWAV_API drwav_bool32 drwav_init_write_sequential_pcm_frames(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, drwav_write_proc onWrite, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks);\n\n/*\nUtility function to determine the target size of the entire data to be written (including all headers and chunks).\n\nReturns the target size in bytes.\n\nUseful if the application needs to know the size to allocate.\n\nOnly writing to the RIFF chunk and one data chunk is currently supported.\n\nSee also: drwav_init_write(), drwav_init_file_write(), drwav_init_memory_write()\n*/\nDRWAV_API drwav_uint64 drwav_target_write_size_bytes(const drwav_data_format* pFormat, drwav_uint64 totalSampleCount);\n\n/*\nUninitializes the given drwav object.\n\nUse this only for objects initialized with drwav_init*() functions (drwav_init(), drwav_init_ex(), drwav_init_write(), drwav_init_write_sequential()).\n*/\nDRWAV_API drwav_result drwav_uninit(drwav* pWav);\n\n\n/*\nReads raw audio data.\n\nThis is the lowest level function for reading audio data. It simply reads the given number of\nbytes of the raw internal sample data.\n\nConsider using drwav_read_pcm_frames_s16(), drwav_read_pcm_frames_s32() or drwav_read_pcm_frames_f32() for\nreading sample data in a consistent format.\n\npBufferOut can be NULL in which case a seek will be performed.\n\nReturns the number of bytes actually read.\n*/\nDRWAV_API size_t drwav_read_raw(drwav* pWav, size_t bytesToRead, void* pBufferOut);\n\n/*\nReads up to the specified number of PCM frames from the WAV file.\n\nThe output data will be in the file's internal format, converted to native-endian byte order. Use\ndrwav_read_pcm_frames_s16/f32/s32() to read data in a specific format.\n\nIf the return value is less than <framesToRead> it means the end of the file has been reached or\nyou have requested more PCM frames than can possibly fit in the output buffer.\n\nThis function will only work when sample data is of a fixed size and uncompressed. If you are\nusing a compressed format consider using drwav_read_raw() or drwav_read_pcm_frames_s16/s32/f32().\n\npBufferOut can be NULL in which case a seek will be performed.\n*/\nDRWAV_API drwav_uint64 drwav_read_pcm_frames(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut);\nDRWAV_API drwav_uint64 drwav_read_pcm_frames_le(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut);\nDRWAV_API drwav_uint64 drwav_read_pcm_frames_be(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut);\n\n/*\nSeeks to the given PCM frame.\n\nReturns true if successful; false otherwise.\n*/\nDRWAV_API drwav_bool32 drwav_seek_to_pcm_frame(drwav* pWav, drwav_uint64 targetFrameIndex);\n\n\n/*\nWrites raw audio data.\n\nReturns the number of bytes actually written. If this differs from bytesToWrite, it indicates an error.\n*/\nDRWAV_API size_t drwav_write_raw(drwav* pWav, size_t bytesToWrite, const void* pData);\n\n/*\nWrites PCM frames.\n\nReturns the number of PCM frames written.\n\nInput samples need to be in native-endian byte order. On big-endian architectures the input data will be converted to\nlittle-endian. Use drwav_write_raw() to write raw audio data without performing any conversion.\n*/\nDRWAV_API drwav_uint64 drwav_write_pcm_frames(drwav* pWav, drwav_uint64 framesToWrite, const void* pData);\nDRWAV_API drwav_uint64 drwav_write_pcm_frames_le(drwav* pWav, drwav_uint64 framesToWrite, const void* pData);\nDRWAV_API drwav_uint64 drwav_write_pcm_frames_be(drwav* pWav, drwav_uint64 framesToWrite, const void* pData);\n\n\n/* Conversion Utilities */\n#ifndef DR_WAV_NO_CONVERSION_API\n\n/*\nReads a chunk of audio data and converts it to signed 16-bit PCM samples.\n\npBufferOut can be NULL in which case a seek will be performed.\n\nReturns the number of PCM frames actually read.\n\nIf the return value is less than <framesToRead> it means the end of the file has been reached.\n*/\nDRWAV_API drwav_uint64 drwav_read_pcm_frames_s16(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut);\nDRWAV_API drwav_uint64 drwav_read_pcm_frames_s16le(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut);\nDRWAV_API drwav_uint64 drwav_read_pcm_frames_s16be(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut);\n\n/* Low-level function for converting unsigned 8-bit PCM samples to signed 16-bit PCM samples. */\nDRWAV_API void drwav_u8_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount);\n\n/* Low-level function for converting signed 24-bit PCM samples to signed 16-bit PCM samples. */\nDRWAV_API void drwav_s24_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount);\n\n/* Low-level function for converting signed 32-bit PCM samples to signed 16-bit PCM samples. */\nDRWAV_API void drwav_s32_to_s16(drwav_int16* pOut, const drwav_int32* pIn, size_t sampleCount);\n\n/* Low-level function for converting IEEE 32-bit floating point samples to signed 16-bit PCM samples. */\nDRWAV_API void drwav_f32_to_s16(drwav_int16* pOut, const float* pIn, size_t sampleCount);\n\n/* Low-level function for converting IEEE 64-bit floating point samples to signed 16-bit PCM samples. */\nDRWAV_API void drwav_f64_to_s16(drwav_int16* pOut, const double* pIn, size_t sampleCount);\n\n/* Low-level function for converting A-law samples to signed 16-bit PCM samples. */\nDRWAV_API void drwav_alaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount);\n\n/* Low-level function for converting u-law samples to signed 16-bit PCM samples. */\nDRWAV_API void drwav_mulaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount);\n\n\n/*\nReads a chunk of audio data and converts it to IEEE 32-bit floating point samples.\n\npBufferOut can be NULL in which case a seek will be performed.\n\nReturns the number of PCM frames actually read.\n\nIf the return value is less than <framesToRead> it means the end of the file has been reached.\n*/\nDRWAV_API drwav_uint64 drwav_read_pcm_frames_f32(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut);\nDRWAV_API drwav_uint64 drwav_read_pcm_frames_f32le(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut);\nDRWAV_API drwav_uint64 drwav_read_pcm_frames_f32be(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut);\n\n/* Low-level function for converting unsigned 8-bit PCM samples to IEEE 32-bit floating point samples. */\nDRWAV_API void drwav_u8_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount);\n\n/* Low-level function for converting signed 16-bit PCM samples to IEEE 32-bit floating point samples. */\nDRWAV_API void drwav_s16_to_f32(float* pOut, const drwav_int16* pIn, size_t sampleCount);\n\n/* Low-level function for converting signed 24-bit PCM samples to IEEE 32-bit floating point samples. */\nDRWAV_API void drwav_s24_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount);\n\n/* Low-level function for converting signed 32-bit PCM samples to IEEE 32-bit floating point samples. */\nDRWAV_API void drwav_s32_to_f32(float* pOut, const drwav_int32* pIn, size_t sampleCount);\n\n/* Low-level function for converting IEEE 64-bit floating point samples to IEEE 32-bit floating point samples. */\nDRWAV_API void drwav_f64_to_f32(float* pOut, const double* pIn, size_t sampleCount);\n\n/* Low-level function for converting A-law samples to IEEE 32-bit floating point samples. */\nDRWAV_API void drwav_alaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount);\n\n/* Low-level function for converting u-law samples to IEEE 32-bit floating point samples. */\nDRWAV_API void drwav_mulaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount);\n\n\n/*\nReads a chunk of audio data and converts it to signed 32-bit PCM samples.\n\npBufferOut can be NULL in which case a seek will be performed.\n\nReturns the number of PCM frames actually read.\n\nIf the return value is less than <framesToRead> it means the end of the file has been reached.\n*/\nDRWAV_API drwav_uint64 drwav_read_pcm_frames_s32(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut);\nDRWAV_API drwav_uint64 drwav_read_pcm_frames_s32le(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut);\nDRWAV_API drwav_uint64 drwav_read_pcm_frames_s32be(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut);\n\n/* Low-level function for converting unsigned 8-bit PCM samples to signed 32-bit PCM samples. */\nDRWAV_API void drwav_u8_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount);\n\n/* Low-level function for converting signed 16-bit PCM samples to signed 32-bit PCM samples. */\nDRWAV_API void drwav_s16_to_s32(drwav_int32* pOut, const drwav_int16* pIn, size_t sampleCount);\n\n/* Low-level function for converting signed 24-bit PCM samples to signed 32-bit PCM samples. */\nDRWAV_API void drwav_s24_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount);\n\n/* Low-level function for converting IEEE 32-bit floating point samples to signed 32-bit PCM samples. */\nDRWAV_API void drwav_f32_to_s32(drwav_int32* pOut, const float* pIn, size_t sampleCount);\n\n/* Low-level function for converting IEEE 64-bit floating point samples to signed 32-bit PCM samples. */\nDRWAV_API void drwav_f64_to_s32(drwav_int32* pOut, const double* pIn, size_t sampleCount);\n\n/* Low-level function for converting A-law samples to signed 32-bit PCM samples. */\nDRWAV_API void drwav_alaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount);\n\n/* Low-level function for converting u-law samples to signed 32-bit PCM samples. */\nDRWAV_API void drwav_mulaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount);\n\n#endif  /* DR_WAV_NO_CONVERSION_API */\n\n\n/* High-Level Convenience Helpers */\n\n#ifndef DR_WAV_NO_STDIO\n/*\nHelper for initializing a wave file for reading using stdio.\n\nThis holds the internal FILE object until drwav_uninit() is called. Keep this in mind if you're caching drwav\nobjects because the operating system may restrict the number of file handles an application can have open at\nany given time.\n*/\nDRWAV_API drwav_bool32 drwav_init_file(drwav* pWav, const char* filename, const drwav_allocation_callbacks* pAllocationCallbacks);\nDRWAV_API drwav_bool32 drwav_init_file_ex(drwav* pWav, const char* filename, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks);\nDRWAV_API drwav_bool32 drwav_init_file_w(drwav* pWav, const wchar_t* filename, const drwav_allocation_callbacks* pAllocationCallbacks);\nDRWAV_API drwav_bool32 drwav_init_file_ex_w(drwav* pWav, const wchar_t* filename, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks);\n\n/*\nHelper for initializing a wave file for writing using stdio.\n\nThis holds the internal FILE object until drwav_uninit() is called. Keep this in mind if you're caching drwav\nobjects because the operating system may restrict the number of file handles an application can have open at\nany given time.\n*/\nDRWAV_API drwav_bool32 drwav_init_file_write(drwav* pWav, const char* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks);\nDRWAV_API drwav_bool32 drwav_init_file_write_sequential(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks);\nDRWAV_API drwav_bool32 drwav_init_file_write_sequential_pcm_frames(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks);\nDRWAV_API drwav_bool32 drwav_init_file_write_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks);\nDRWAV_API drwav_bool32 drwav_init_file_write_sequential_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks);\nDRWAV_API drwav_bool32 drwav_init_file_write_sequential_pcm_frames_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks);\n#endif  /* DR_WAV_NO_STDIO */\n\n/*\nHelper for initializing a loader from a pre-allocated memory buffer.\n\nThis does not create a copy of the data. It is up to the application to ensure the buffer remains valid for\nthe lifetime of the drwav object.\n\nThe buffer should contain the contents of the entire wave file, not just the sample data.\n*/\nDRWAV_API drwav_bool32 drwav_init_memory(drwav* pWav, const void* data, size_t dataSize, const drwav_allocation_callbacks* pAllocationCallbacks);\nDRWAV_API drwav_bool32 drwav_init_memory_ex(drwav* pWav, const void* data, size_t dataSize, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks);\n\n/*\nHelper for initializing a writer which outputs data to a memory buffer.\n\ndr_wav will manage the memory allocations, however it is up to the caller to free the data with drwav_free().\n\nThe buffer will remain allocated even after drwav_uninit() is called. The buffer should not be considered valid\nuntil after drwav_uninit() has been called.\n*/\nDRWAV_API drwav_bool32 drwav_init_memory_write(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks);\nDRWAV_API drwav_bool32 drwav_init_memory_write_sequential(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks);\nDRWAV_API drwav_bool32 drwav_init_memory_write_sequential_pcm_frames(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks);\n\n\n#ifndef DR_WAV_NO_CONVERSION_API\n/*\nOpens and reads an entire wav file in a single operation.\n\nThe return value is a heap-allocated buffer containing the audio data. Use drwav_free() to free the buffer.\n*/\nDRWAV_API drwav_int16* drwav_open_and_read_pcm_frames_s16(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);\nDRWAV_API float* drwav_open_and_read_pcm_frames_f32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);\nDRWAV_API drwav_int32* drwav_open_and_read_pcm_frames_s32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);\n#ifndef DR_WAV_NO_STDIO\n/*\nOpens and decodes an entire wav file in a single operation.\n\nThe return value is a heap-allocated buffer containing the audio data. Use drwav_free() to free the buffer.\n*/\nDRWAV_API drwav_int16* drwav_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);\nDRWAV_API float* drwav_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);\nDRWAV_API drwav_int32* drwav_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);\nDRWAV_API drwav_int16* drwav_open_file_and_read_pcm_frames_s16_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);\nDRWAV_API float* drwav_open_file_and_read_pcm_frames_f32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);\nDRWAV_API drwav_int32* drwav_open_file_and_read_pcm_frames_s32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);\n#endif\n/*\nOpens and decodes an entire wav file from a block of memory in a single operation.\n\nThe return value is a heap-allocated buffer containing the audio data. Use drwav_free() to free the buffer.\n*/\nDRWAV_API drwav_int16* drwav_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);\nDRWAV_API float* drwav_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);\nDRWAV_API drwav_int32* drwav_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);\n#endif\n\n/* Frees data that was allocated internally by dr_wav. */\nDRWAV_API void drwav_free(void* p, const drwav_allocation_callbacks* pAllocationCallbacks);\n\n/* Converts bytes from a wav stream to a sized type of native endian. */\nDRWAV_API drwav_uint16 drwav_bytes_to_u16(const drwav_uint8* data);\nDRWAV_API drwav_int16 drwav_bytes_to_s16(const drwav_uint8* data);\nDRWAV_API drwav_uint32 drwav_bytes_to_u32(const drwav_uint8* data);\nDRWAV_API drwav_int32 drwav_bytes_to_s32(const drwav_uint8* data);\nDRWAV_API drwav_uint64 drwav_bytes_to_u64(const drwav_uint8* data);\nDRWAV_API drwav_int64 drwav_bytes_to_s64(const drwav_uint8* data);\n\n/* Compares a GUID for the purpose of checking the type of a Wave64 chunk. */\nDRWAV_API drwav_bool32 drwav_guid_equal(const drwav_uint8 a[16], const drwav_uint8 b[16]);\n\n/* Compares a four-character-code for the purpose of checking the type of a RIFF chunk. */\nDRWAV_API drwav_bool32 drwav_fourcc_equal(const drwav_uint8* a, const char* b);\n\n#ifdef __cplusplus\n}\n#endif\n#endif  /* dr_wav_h */\n\n\n/************************************************************************************************************************************************************\n ************************************************************************************************************************************************************\n\n IMPLEMENTATION\n\n ************************************************************************************************************************************************************\n ************************************************************************************************************************************************************/\n#if defined(DR_WAV_IMPLEMENTATION) || defined(DRWAV_IMPLEMENTATION)\n#ifndef dr_wav_c\n#define dr_wav_c\n\n#include <stdlib.h>\n#include <string.h> /* For memcpy(), memset() */\n#include <limits.h> /* For INT_MAX */\n\n#ifndef DR_WAV_NO_STDIO\n#include <stdio.h>\n#include <wchar.h>\n#endif\n\n/* Standard library stuff. */\n#ifndef DRWAV_ASSERT\n#include <assert.h>\n#define DRWAV_ASSERT(expression)           assert(expression)\n#endif\n#ifndef DRWAV_MALLOC\n#define DRWAV_MALLOC(sz)                   malloc((sz))\n#endif\n#ifndef DRWAV_REALLOC\n#define DRWAV_REALLOC(p, sz)               realloc((p), (sz))\n#endif\n#ifndef DRWAV_FREE\n#define DRWAV_FREE(p)                      free((p))\n#endif\n#ifndef DRWAV_COPY_MEMORY\n#define DRWAV_COPY_MEMORY(dst, src, sz)    memcpy((dst), (src), (sz))\n#endif\n#ifndef DRWAV_ZERO_MEMORY\n#define DRWAV_ZERO_MEMORY(p, sz)           memset((p), 0, (sz))\n#endif\n#ifndef DRWAV_ZERO_OBJECT\n#define DRWAV_ZERO_OBJECT(p)               DRWAV_ZERO_MEMORY((p), sizeof(*p))\n#endif\n\n#define drwav_countof(x)                   (sizeof(x) / sizeof(x[0]))\n#define drwav_align(x, a)                  ((((x) + (a) - 1) / (a)) * (a))\n#define drwav_min(a, b)                    (((a) < (b)) ? (a) : (b))\n#define drwav_max(a, b)                    (((a) > (b)) ? (a) : (b))\n#define drwav_clamp(x, lo, hi)             (drwav_max((lo), drwav_min((hi), (x))))\n\n#define DRWAV_MAX_SIMD_VECTOR_SIZE         64  /* 64 for AVX-512 in the future. */\n\n/* CPU architecture. */\n#if defined(__x86_64__) || defined(_M_X64)\n    #define DRWAV_X64\n#elif defined(__i386) || defined(_M_IX86)\n    #define DRWAV_X86\n#elif defined(__arm__) || defined(_M_ARM)\n    #define DRWAV_ARM\n#endif\n\n#ifdef _MSC_VER\n    #define DRWAV_INLINE __forceinline\n#elif defined(__GNUC__)\n    /*\n    I've had a bug report where GCC is emitting warnings about functions possibly not being inlineable. This warning happens when\n    the __attribute__((always_inline)) attribute is defined without an \"inline\" statement. I think therefore there must be some\n    case where \"__inline__\" is not always defined, thus the compiler emitting these warnings. When using -std=c89 or -ansi on the\n    command line, we cannot use the \"inline\" keyword and instead need to use \"__inline__\". In an attempt to work around this issue\n    I am using \"__inline__\" only when we're compiling in strict ANSI mode.\n    */\n    #if defined(__STRICT_ANSI__)\n        #define DRWAV_INLINE __inline__ __attribute__((always_inline))\n    #else\n        #define DRWAV_INLINE inline __attribute__((always_inline))\n    #endif\n#elif defined(__WATCOMC__)\n    #define DRWAV_INLINE __inline\n#else\n    #define DRWAV_INLINE\n#endif\n\n#if defined(SIZE_MAX)\n    #define DRWAV_SIZE_MAX  SIZE_MAX\n#else\n    #if defined(_WIN64) || defined(_LP64) || defined(__LP64__)\n        #define DRWAV_SIZE_MAX  ((drwav_uint64)0xFFFFFFFFFFFFFFFF)\n    #else\n        #define DRWAV_SIZE_MAX  0xFFFFFFFF\n    #endif\n#endif\n\n#if defined(_MSC_VER) && _MSC_VER >= 1400\n    #define DRWAV_HAS_BYTESWAP16_INTRINSIC\n    #define DRWAV_HAS_BYTESWAP32_INTRINSIC\n    #define DRWAV_HAS_BYTESWAP64_INTRINSIC\n#elif defined(__clang__)\n    #if defined(__has_builtin)\n        #if __has_builtin(__builtin_bswap16)\n            #define DRWAV_HAS_BYTESWAP16_INTRINSIC\n        #endif\n        #if __has_builtin(__builtin_bswap32)\n            #define DRWAV_HAS_BYTESWAP32_INTRINSIC\n        #endif\n        #if __has_builtin(__builtin_bswap64)\n            #define DRWAV_HAS_BYTESWAP64_INTRINSIC\n        #endif\n    #endif\n#elif defined(__GNUC__)\n    #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))\n        #define DRWAV_HAS_BYTESWAP32_INTRINSIC\n        #define DRWAV_HAS_BYTESWAP64_INTRINSIC\n    #endif\n    #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))\n        #define DRWAV_HAS_BYTESWAP16_INTRINSIC\n    #endif\n#endif\n\nDRWAV_API void drwav_version(drwav_uint32* pMajor, drwav_uint32* pMinor, drwav_uint32* pRevision)\n{\n    if (pMajor) {\n        *pMajor = DRWAV_VERSION_MAJOR;\n    }\n\n    if (pMinor) {\n        *pMinor = DRWAV_VERSION_MINOR;\n    }\n\n    if (pRevision) {\n        *pRevision = DRWAV_VERSION_REVISION;\n    }\n}\n\nDRWAV_API const char* drwav_version_string(void)\n{\n    return DRWAV_VERSION_STRING;\n}\n\n/*\nThese limits are used for basic validation when initializing the decoder. If you exceed these limits, first of all: what on Earth are\nyou doing?! (Let me know, I'd be curious!) Second, you can adjust these by #define-ing them before the dr_wav implementation.\n*/\n#ifndef DRWAV_MAX_SAMPLE_RATE\n#define DRWAV_MAX_SAMPLE_RATE       384000\n#endif\n#ifndef DRWAV_MAX_CHANNELS\n#define DRWAV_MAX_CHANNELS          256\n#endif\n#ifndef DRWAV_MAX_BITS_PER_SAMPLE\n#define DRWAV_MAX_BITS_PER_SAMPLE   64\n#endif\n\nstatic const drwav_uint8 drwavGUID_W64_RIFF[16] = {0x72,0x69,0x66,0x66, 0x2E,0x91, 0xCF,0x11, 0xA5,0xD6, 0x28,0xDB,0x04,0xC1,0x00,0x00};    /* 66666972-912E-11CF-A5D6-28DB04C10000 */\nstatic const drwav_uint8 drwavGUID_W64_WAVE[16] = {0x77,0x61,0x76,0x65, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A};    /* 65766177-ACF3-11D3-8CD1-00C04F8EDB8A */\n/*static const drwav_uint8 drwavGUID_W64_JUNK[16] = {0x6A,0x75,0x6E,0x6B, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A};*/    /* 6B6E756A-ACF3-11D3-8CD1-00C04F8EDB8A */\nstatic const drwav_uint8 drwavGUID_W64_FMT [16] = {0x66,0x6D,0x74,0x20, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A};    /* 20746D66-ACF3-11D3-8CD1-00C04F8EDB8A */\nstatic const drwav_uint8 drwavGUID_W64_FACT[16] = {0x66,0x61,0x63,0x74, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A};    /* 74636166-ACF3-11D3-8CD1-00C04F8EDB8A */\nstatic const drwav_uint8 drwavGUID_W64_DATA[16] = {0x64,0x61,0x74,0x61, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A};    /* 61746164-ACF3-11D3-8CD1-00C04F8EDB8A */\nstatic const drwav_uint8 drwavGUID_W64_SMPL[16] = {0x73,0x6D,0x70,0x6C, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A};    /* 6C706D73-ACF3-11D3-8CD1-00C04F8EDB8A */\n\nstatic DRWAV_INLINE drwav_bool32 drwav__guid_equal(const drwav_uint8 a[16], const drwav_uint8 b[16])\n{\n    int i;\n    for (i = 0; i < 16; i += 1) {\n        if (a[i] != b[i]) {\n            return DRWAV_FALSE;\n        }\n    }\n\n    return DRWAV_TRUE;\n}\n\nstatic DRWAV_INLINE drwav_bool32 drwav__fourcc_equal(const drwav_uint8* a, const char* b)\n{\n    return\n        a[0] == b[0] &&\n        a[1] == b[1] &&\n        a[2] == b[2] &&\n        a[3] == b[3];\n}\n\n\n\nstatic DRWAV_INLINE int drwav__is_little_endian(void)\n{\n#if defined(DRWAV_X86) || defined(DRWAV_X64)\n    return DRWAV_TRUE;\n#elif defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN\n    return DRWAV_TRUE;\n#else\n    int n = 1;\n    return (*(char*)&n) == 1;\n#endif\n}\n\nstatic DRWAV_INLINE drwav_uint16 drwav__bytes_to_u16(const drwav_uint8* data)\n{\n    return (data[0] << 0) | (data[1] << 8);\n}\n\nstatic DRWAV_INLINE drwav_int16 drwav__bytes_to_s16(const drwav_uint8* data)\n{\n    return (short)drwav__bytes_to_u16(data);\n}\n\nstatic DRWAV_INLINE drwav_uint32 drwav__bytes_to_u32(const drwav_uint8* data)\n{\n    return (data[0] << 0) | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);\n}\n\nstatic DRWAV_INLINE drwav_int32 drwav__bytes_to_s32(const drwav_uint8* data)\n{\n    return (drwav_int32)drwav__bytes_to_u32(data);\n}\n\nstatic DRWAV_INLINE drwav_uint64 drwav__bytes_to_u64(const drwav_uint8* data)\n{\n    return\n        ((drwav_uint64)data[0] <<  0) | ((drwav_uint64)data[1] <<  8) | ((drwav_uint64)data[2] << 16) | ((drwav_uint64)data[3] << 24) |\n        ((drwav_uint64)data[4] << 32) | ((drwav_uint64)data[5] << 40) | ((drwav_uint64)data[6] << 48) | ((drwav_uint64)data[7] << 56);\n}\n\nstatic DRWAV_INLINE drwav_int64 drwav__bytes_to_s64(const drwav_uint8* data)\n{\n    return (drwav_int64)drwav__bytes_to_u64(data);\n}\n\nstatic DRWAV_INLINE void drwav__bytes_to_guid(const drwav_uint8* data, drwav_uint8* guid)\n{\n    int i;\n    for (i = 0; i < 16; ++i) {\n        guid[i] = data[i];\n    }\n}\n\n\nstatic DRWAV_INLINE drwav_uint16 drwav__bswap16(drwav_uint16 n)\n{\n#ifdef DRWAV_HAS_BYTESWAP16_INTRINSIC\n    #if defined(_MSC_VER)\n        return _byteswap_ushort(n);\n    #elif defined(__GNUC__) || defined(__clang__)\n        return __builtin_bswap16(n);\n    #else\n        #error \"This compiler does not support the byte swap intrinsic.\"\n    #endif\n#else\n    return ((n & 0xFF00) >> 8) |\n           ((n & 0x00FF) << 8);\n#endif\n}\n\nstatic DRWAV_INLINE drwav_uint32 drwav__bswap32(drwav_uint32 n)\n{\n#ifdef DRWAV_HAS_BYTESWAP32_INTRINSIC\n    #if defined(_MSC_VER)\n        return _byteswap_ulong(n);\n    #elif defined(__GNUC__) || defined(__clang__)\n        #if defined(DRWAV_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 6) && !defined(DRWAV_64BIT)   /* <-- 64-bit inline assembly has not been tested, so disabling for now. */\n            /* Inline assembly optimized implementation for ARM. In my testing, GCC does not generate optimized code with __builtin_bswap32(). */\n            drwav_uint32 r;\n            __asm__ __volatile__ (\n            #if defined(DRWAV_64BIT)\n                \"rev %w[out], %w[in]\" : [out]\"=r\"(r) : [in]\"r\"(n)   /* <-- This is untested. If someone in the community could test this, that would be appreciated! */\n            #else\n                \"rev %[out], %[in]\" : [out]\"=r\"(r) : [in]\"r\"(n)\n            #endif\n            );\n            return r;\n        #else\n            return __builtin_bswap32(n);\n        #endif\n    #else\n        #error \"This compiler does not support the byte swap intrinsic.\"\n    #endif\n#else\n    return ((n & 0xFF000000) >> 24) |\n           ((n & 0x00FF0000) >>  8) |\n           ((n & 0x0000FF00) <<  8) |\n           ((n & 0x000000FF) << 24);\n#endif\n}\n\nstatic DRWAV_INLINE drwav_uint64 drwav__bswap64(drwav_uint64 n)\n{\n#ifdef DRWAV_HAS_BYTESWAP64_INTRINSIC\n    #if defined(_MSC_VER)\n        return _byteswap_uint64(n);\n    #elif defined(__GNUC__) || defined(__clang__)\n        return __builtin_bswap64(n);\n    #else\n        #error \"This compiler does not support the byte swap intrinsic.\"\n    #endif\n#else\n    /* Weird \"<< 32\" bitshift is required for C89 because it doesn't support 64-bit constants. Should be optimized out by a good compiler. */\n    return ((n & ((drwav_uint64)0xFF000000 << 32)) >> 56) |\n           ((n & ((drwav_uint64)0x00FF0000 << 32)) >> 40) |\n           ((n & ((drwav_uint64)0x0000FF00 << 32)) >> 24) |\n           ((n & ((drwav_uint64)0x000000FF << 32)) >>  8) |\n           ((n & ((drwav_uint64)0xFF000000      )) <<  8) |\n           ((n & ((drwav_uint64)0x00FF0000      )) << 24) |\n           ((n & ((drwav_uint64)0x0000FF00      )) << 40) |\n           ((n & ((drwav_uint64)0x000000FF      )) << 56);\n#endif\n}\n\n\nstatic DRWAV_INLINE drwav_int16 drwav__bswap_s16(drwav_int16 n)\n{\n    return (drwav_int16)drwav__bswap16((drwav_uint16)n);\n}\n\nstatic DRWAV_INLINE void drwav__bswap_samples_s16(drwav_int16* pSamples, drwav_uint64 sampleCount)\n{\n    drwav_uint64 iSample;\n    for (iSample = 0; iSample < sampleCount; iSample += 1) {\n        pSamples[iSample] = drwav__bswap_s16(pSamples[iSample]);\n    }\n}\n\n\nstatic DRWAV_INLINE void drwav__bswap_s24(drwav_uint8* p)\n{\n    drwav_uint8 t;\n    t = p[0];\n    p[0] = p[2];\n    p[2] = t;\n}\n\nstatic DRWAV_INLINE void drwav__bswap_samples_s24(drwav_uint8* pSamples, drwav_uint64 sampleCount)\n{\n    drwav_uint64 iSample;\n    for (iSample = 0; iSample < sampleCount; iSample += 1) {\n        drwav_uint8* pSample = pSamples + (iSample*3);\n        drwav__bswap_s24(pSample);\n    }\n}\n\n\nstatic DRWAV_INLINE drwav_int32 drwav__bswap_s32(drwav_int32 n)\n{\n    return (drwav_int32)drwav__bswap32((drwav_uint32)n);\n}\n\nstatic DRWAV_INLINE void drwav__bswap_samples_s32(drwav_int32* pSamples, drwav_uint64 sampleCount)\n{\n    drwav_uint64 iSample;\n    for (iSample = 0; iSample < sampleCount; iSample += 1) {\n        pSamples[iSample] = drwav__bswap_s32(pSamples[iSample]);\n    }\n}\n\n\nstatic DRWAV_INLINE float drwav__bswap_f32(float n)\n{\n    union {\n        drwav_uint32 i;\n        float f;\n    } x;\n    x.f = n;\n    x.i = drwav__bswap32(x.i);\n\n    return x.f;\n}\n\nstatic DRWAV_INLINE void drwav__bswap_samples_f32(float* pSamples, drwav_uint64 sampleCount)\n{\n    drwav_uint64 iSample;\n    for (iSample = 0; iSample < sampleCount; iSample += 1) {\n        pSamples[iSample] = drwav__bswap_f32(pSamples[iSample]);\n    }\n}\n\n\nstatic DRWAV_INLINE double drwav__bswap_f64(double n)\n{\n    union {\n        drwav_uint64 i;\n        double f;\n    } x;\n    x.f = n;\n    x.i = drwav__bswap64(x.i);\n\n    return x.f;\n}\n\nstatic DRWAV_INLINE void drwav__bswap_samples_f64(double* pSamples, drwav_uint64 sampleCount)\n{\n    drwav_uint64 iSample;\n    for (iSample = 0; iSample < sampleCount; iSample += 1) {\n        pSamples[iSample] = drwav__bswap_f64(pSamples[iSample]);\n    }\n}\n\n\nstatic DRWAV_INLINE void drwav__bswap_samples_pcm(void* pSamples, drwav_uint64 sampleCount, drwav_uint32 bytesPerSample)\n{\n    /* Assumes integer PCM. Floating point PCM is done in drwav__bswap_samples_ieee(). */\n    switch (bytesPerSample)\n    {\n        case 2: /* s16, s12 (loosely packed) */\n        {\n            drwav__bswap_samples_s16((drwav_int16*)pSamples, sampleCount);\n        } break;\n        case 3: /* s24 */\n        {\n            drwav__bswap_samples_s24((drwav_uint8*)pSamples, sampleCount);\n        } break;\n        case 4: /* s32 */\n        {\n            drwav__bswap_samples_s32((drwav_int32*)pSamples, sampleCount);\n        } break;\n        default:\n        {\n            /* Unsupported format. */\n            DRWAV_ASSERT(DRWAV_FALSE);\n        } break;\n    }\n}\n\nstatic DRWAV_INLINE void drwav__bswap_samples_ieee(void* pSamples, drwav_uint64 sampleCount, drwav_uint32 bytesPerSample)\n{\n    switch (bytesPerSample)\n    {\n    #if 0   /* Contributions welcome for f16 support. */\n        case 2: /* f16 */\n        {\n            drwav__bswap_samples_f16((drwav_float16*)pSamples, sampleCount);\n        } break;\n    #endif\n        case 4: /* f32 */\n        {\n            drwav__bswap_samples_f32((float*)pSamples, sampleCount);\n        } break;\n        case 8: /* f64 */\n        {\n            drwav__bswap_samples_f64((double*)pSamples, sampleCount);\n        } break;\n        default:\n        {\n            /* Unsupported format. */\n            DRWAV_ASSERT(DRWAV_FALSE);\n        } break;\n    }\n}\n\nstatic DRWAV_INLINE void drwav__bswap_samples(void* pSamples, drwav_uint64 sampleCount, drwav_uint32 bytesPerSample, drwav_uint16 format)\n{\n    switch (format)\n    {\n        case DR_WAVE_FORMAT_PCM:\n        {\n            drwav__bswap_samples_pcm(pSamples, sampleCount, bytesPerSample);\n        } break;\n\n        case DR_WAVE_FORMAT_IEEE_FLOAT:\n        {\n            drwav__bswap_samples_ieee(pSamples, sampleCount, bytesPerSample);\n        } break;\n\n        case DR_WAVE_FORMAT_ALAW:\n        case DR_WAVE_FORMAT_MULAW:\n        {\n            drwav__bswap_samples_s16((drwav_int16*)pSamples, sampleCount);\n        } break;\n\n        case DR_WAVE_FORMAT_ADPCM:\n        case DR_WAVE_FORMAT_DVI_ADPCM:\n        default:\n        {\n            /* Unsupported format. */\n            DRWAV_ASSERT(DRWAV_FALSE);\n        } break;\n    }\n}\n\n\nstatic void* drwav__malloc_default(size_t sz, void* pUserData)\n{\n    (void)pUserData;\n    return DRWAV_MALLOC(sz);\n}\n\nstatic void* drwav__realloc_default(void* p, size_t sz, void* pUserData)\n{\n    (void)pUserData;\n    return DRWAV_REALLOC(p, sz);\n}\n\nstatic void drwav__free_default(void* p, void* pUserData)\n{\n    (void)pUserData;\n    DRWAV_FREE(p);\n}\n\n\nstatic void* drwav__malloc_from_callbacks(size_t sz, const drwav_allocation_callbacks* pAllocationCallbacks)\n{\n    if (pAllocationCallbacks == NULL) {\n        return NULL;\n    }\n\n    if (pAllocationCallbacks->onMalloc != NULL) {\n        return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData);\n    }\n\n    /* Try using realloc(). */\n    if (pAllocationCallbacks->onRealloc != NULL) {\n        return pAllocationCallbacks->onRealloc(NULL, sz, pAllocationCallbacks->pUserData);\n    }\n\n    return NULL;\n}\n\nstatic void* drwav__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const drwav_allocation_callbacks* pAllocationCallbacks)\n{\n    if (pAllocationCallbacks == NULL) {\n        return NULL;\n    }\n\n    if (pAllocationCallbacks->onRealloc != NULL) {\n        return pAllocationCallbacks->onRealloc(p, szNew, pAllocationCallbacks->pUserData);\n    }\n\n    /* Try emulating realloc() in terms of malloc()/free(). */\n    if (pAllocationCallbacks->onMalloc != NULL && pAllocationCallbacks->onFree != NULL) {\n        void* p2;\n\n        p2 = pAllocationCallbacks->onMalloc(szNew, pAllocationCallbacks->pUserData);\n        if (p2 == NULL) {\n            return NULL;\n        }\n\n        if (p != NULL) {\n            DRWAV_COPY_MEMORY(p2, p, szOld);\n            pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);\n        }\n\n        return p2;\n    }\n\n    return NULL;\n}\n\nstatic void drwav__free_from_callbacks(void* p, const drwav_allocation_callbacks* pAllocationCallbacks)\n{\n    if (p == NULL || pAllocationCallbacks == NULL) {\n        return;\n    }\n\n    if (pAllocationCallbacks->onFree != NULL) {\n        pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);\n    }\n}\n\n\nstatic drwav_allocation_callbacks drwav_copy_allocation_callbacks_or_defaults(const drwav_allocation_callbacks* pAllocationCallbacks)\n{\n    if (pAllocationCallbacks != NULL) {\n        /* Copy. */\n        return *pAllocationCallbacks;\n    } else {\n        /* Defaults. */\n        drwav_allocation_callbacks allocationCallbacks;\n        allocationCallbacks.pUserData = NULL;\n        allocationCallbacks.onMalloc  = drwav__malloc_default;\n        allocationCallbacks.onRealloc = drwav__realloc_default;\n        allocationCallbacks.onFree    = drwav__free_default;\n        return allocationCallbacks;\n    }\n}\n\n\nstatic DRWAV_INLINE drwav_bool32 drwav__is_compressed_format_tag(drwav_uint16 formatTag)\n{\n    return\n        formatTag == DR_WAVE_FORMAT_ADPCM ||\n        formatTag == DR_WAVE_FORMAT_DVI_ADPCM;\n}\n\nstatic unsigned int drwav__chunk_padding_size_riff(drwav_uint64 chunkSize)\n{\n    return (unsigned int)(chunkSize % 2);\n}\n\nstatic unsigned int drwav__chunk_padding_size_w64(drwav_uint64 chunkSize)\n{\n    return (unsigned int)(chunkSize % 8);\n}\n\nstatic drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut);\nstatic drwav_uint64 drwav_read_pcm_frames_s16__ima(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut);\nstatic drwav_bool32 drwav_init_write__internal(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount);\n\nstatic drwav_result drwav__read_chunk_header(drwav_read_proc onRead, void* pUserData, drwav_container container, drwav_uint64* pRunningBytesReadOut, drwav_chunk_header* pHeaderOut)\n{\n    if (container == drwav_container_riff || container == drwav_container_rf64) {\n        drwav_uint8 sizeInBytes[4];\n\n        if (onRead(pUserData, pHeaderOut->id.fourcc, 4) != 4) {\n            return DRWAV_AT_END;\n        }\n\n        if (onRead(pUserData, sizeInBytes, 4) != 4) {\n            return DRWAV_INVALID_FILE;\n        }\n\n        pHeaderOut->sizeInBytes = drwav__bytes_to_u32(sizeInBytes);\n        pHeaderOut->paddingSize = drwav__chunk_padding_size_riff(pHeaderOut->sizeInBytes);\n        *pRunningBytesReadOut += 8;\n    } else {\n        drwav_uint8 sizeInBytes[8];\n\n        if (onRead(pUserData, pHeaderOut->id.guid, 16) != 16) {\n            return DRWAV_AT_END;\n        }\n\n        if (onRead(pUserData, sizeInBytes, 8) != 8) {\n            return DRWAV_INVALID_FILE;\n        }\n\n        pHeaderOut->sizeInBytes = drwav__bytes_to_u64(sizeInBytes) - 24;    /* <-- Subtract 24 because w64 includes the size of the header. */\n        pHeaderOut->paddingSize = drwav__chunk_padding_size_w64(pHeaderOut->sizeInBytes);\n        *pRunningBytesReadOut += 24;\n    }\n\n    return DRWAV_SUCCESS;\n}\n\nstatic drwav_bool32 drwav__seek_forward(drwav_seek_proc onSeek, drwav_uint64 offset, void* pUserData)\n{\n    drwav_uint64 bytesRemainingToSeek = offset;\n    while (bytesRemainingToSeek > 0) {\n        if (bytesRemainingToSeek > 0x7FFFFFFF) {\n            if (!onSeek(pUserData, 0x7FFFFFFF, drwav_seek_origin_current)) {\n                return DRWAV_FALSE;\n            }\n            bytesRemainingToSeek -= 0x7FFFFFFF;\n        } else {\n            if (!onSeek(pUserData, (int)bytesRemainingToSeek, drwav_seek_origin_current)) {\n                return DRWAV_FALSE;\n            }\n            bytesRemainingToSeek = 0;\n        }\n    }\n\n    return DRWAV_TRUE;\n}\n\nstatic drwav_bool32 drwav__seek_from_start(drwav_seek_proc onSeek, drwav_uint64 offset, void* pUserData)\n{\n    if (offset <= 0x7FFFFFFF) {\n        return onSeek(pUserData, (int)offset, drwav_seek_origin_start);\n    }\n\n    /* Larger than 32-bit seek. */\n    if (!onSeek(pUserData, 0x7FFFFFFF, drwav_seek_origin_start)) {\n        return DRWAV_FALSE;\n    }\n    offset -= 0x7FFFFFFF;\n\n    for (;;) {\n        if (offset <= 0x7FFFFFFF) {\n            return onSeek(pUserData, (int)offset, drwav_seek_origin_current);\n        }\n\n        if (!onSeek(pUserData, 0x7FFFFFFF, drwav_seek_origin_current)) {\n            return DRWAV_FALSE;\n        }\n        offset -= 0x7FFFFFFF;\n    }\n\n    /* Should never get here. */\n    /*return DRWAV_TRUE; */\n}\n\n\nstatic drwav_bool32 drwav__read_fmt(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, drwav_container container, drwav_uint64* pRunningBytesReadOut, drwav_fmt* fmtOut)\n{\n    drwav_chunk_header header;\n    drwav_uint8 fmt[16];\n\n    if (drwav__read_chunk_header(onRead, pUserData, container, pRunningBytesReadOut, &header) != DRWAV_SUCCESS) {\n        return DRWAV_FALSE;\n    }\n\n\n    /* Skip non-fmt chunks. */\n    while (((container == drwav_container_riff || container == drwav_container_rf64) && !drwav__fourcc_equal(header.id.fourcc, \"fmt \")) || (container == drwav_container_w64 && !drwav__guid_equal(header.id.guid, drwavGUID_W64_FMT))) {\n        if (!drwav__seek_forward(onSeek, header.sizeInBytes + header.paddingSize, pUserData)) {\n            return DRWAV_FALSE;\n        }\n        *pRunningBytesReadOut += header.sizeInBytes + header.paddingSize;\n\n        /* Try the next header. */\n        if (drwav__read_chunk_header(onRead, pUserData, container, pRunningBytesReadOut, &header) != DRWAV_SUCCESS) {\n            return DRWAV_FALSE;\n        }\n    }\n\n\n    /* Validation. */\n    if (container == drwav_container_riff || container == drwav_container_rf64) {\n        if (!drwav__fourcc_equal(header.id.fourcc, \"fmt \")) {\n            return DRWAV_FALSE;\n        }\n    } else {\n        if (!drwav__guid_equal(header.id.guid, drwavGUID_W64_FMT)) {\n            return DRWAV_FALSE;\n        }\n    }\n\n\n    if (onRead(pUserData, fmt, sizeof(fmt)) != sizeof(fmt)) {\n        return DRWAV_FALSE;\n    }\n    *pRunningBytesReadOut += sizeof(fmt);\n\n    fmtOut->formatTag      = drwav__bytes_to_u16(fmt + 0);\n    fmtOut->channels       = drwav__bytes_to_u16(fmt + 2);\n    fmtOut->sampleRate     = drwav__bytes_to_u32(fmt + 4);\n    fmtOut->avgBytesPerSec = drwav__bytes_to_u32(fmt + 8);\n    fmtOut->blockAlign     = drwav__bytes_to_u16(fmt + 12);\n    fmtOut->bitsPerSample  = drwav__bytes_to_u16(fmt + 14);\n\n    fmtOut->extendedSize       = 0;\n    fmtOut->validBitsPerSample = 0;\n    fmtOut->channelMask        = 0;\n    memset(fmtOut->subFormat, 0, sizeof(fmtOut->subFormat));\n\n    if (header.sizeInBytes > 16) {\n        drwav_uint8 fmt_cbSize[2];\n        int bytesReadSoFar = 0;\n\n        if (onRead(pUserData, fmt_cbSize, sizeof(fmt_cbSize)) != sizeof(fmt_cbSize)) {\n            return DRWAV_FALSE;    /* Expecting more data. */\n        }\n        *pRunningBytesReadOut += sizeof(fmt_cbSize);\n\n        bytesReadSoFar = 18;\n\n        fmtOut->extendedSize = drwav__bytes_to_u16(fmt_cbSize);\n        if (fmtOut->extendedSize > 0) {\n            /* Simple validation. */\n            if (fmtOut->formatTag == DR_WAVE_FORMAT_EXTENSIBLE) {\n                if (fmtOut->extendedSize != 22) {\n                    return DRWAV_FALSE;\n                }\n            }\n\n            if (fmtOut->formatTag == DR_WAVE_FORMAT_EXTENSIBLE) {\n                drwav_uint8 fmtext[22];\n                if (onRead(pUserData, fmtext, fmtOut->extendedSize) != fmtOut->extendedSize) {\n                    return DRWAV_FALSE;    /* Expecting more data. */\n                }\n\n                fmtOut->validBitsPerSample = drwav__bytes_to_u16(fmtext + 0);\n                fmtOut->channelMask        = drwav__bytes_to_u32(fmtext + 2);\n                drwav__bytes_to_guid(fmtext + 6, fmtOut->subFormat);\n            } else {\n                if (!onSeek(pUserData, fmtOut->extendedSize, drwav_seek_origin_current)) {\n                    return DRWAV_FALSE;\n                }\n            }\n            *pRunningBytesReadOut += fmtOut->extendedSize;\n\n            bytesReadSoFar += fmtOut->extendedSize;\n        }\n\n        /* Seek past any leftover bytes. For w64 the leftover will be defined based on the chunk size. */\n        if (!onSeek(pUserData, (int)(header.sizeInBytes - bytesReadSoFar), drwav_seek_origin_current)) {\n            return DRWAV_FALSE;\n        }\n        *pRunningBytesReadOut += (header.sizeInBytes - bytesReadSoFar);\n    }\n\n    if (header.paddingSize > 0) {\n        if (!onSeek(pUserData, header.paddingSize, drwav_seek_origin_current)) {\n            return DRWAV_FALSE;\n        }\n        *pRunningBytesReadOut += header.paddingSize;\n    }\n\n    return DRWAV_TRUE;\n}\n\n\nstatic size_t drwav__on_read(drwav_read_proc onRead, void* pUserData, void* pBufferOut, size_t bytesToRead, drwav_uint64* pCursor)\n{\n    size_t bytesRead;\n\n    DRWAV_ASSERT(onRead != NULL);\n    DRWAV_ASSERT(pCursor != NULL);\n\n    bytesRead = onRead(pUserData, pBufferOut, bytesToRead);\n    *pCursor += bytesRead;\n    return bytesRead;\n}\n\n#if 0\nstatic drwav_bool32 drwav__on_seek(drwav_seek_proc onSeek, void* pUserData, int offset, drwav_seek_origin origin, drwav_uint64* pCursor)\n{\n    DRWAV_ASSERT(onSeek != NULL);\n    DRWAV_ASSERT(pCursor != NULL);\n\n    if (!onSeek(pUserData, offset, origin)) {\n        return DRWAV_FALSE;\n    }\n\n    if (origin == drwav_seek_origin_start) {\n        *pCursor = offset;\n    } else {\n        *pCursor += offset;\n    }\n\n    return DRWAV_TRUE;\n}\n#endif\n\n\n\nstatic drwav_uint32 drwav_get_bytes_per_pcm_frame(drwav* pWav)\n{\n    /*\n    The bytes per frame is a bit ambiguous. It can be either be based on the bits per sample, or the block align. The way I'm doing it here\n    is that if the bits per sample is a multiple of 8, use floor(bitsPerSample*channels/8), otherwise fall back to the block align.\n    */\n    if ((pWav->bitsPerSample & 0x7) == 0) {\n        /* Bits per sample is a multiple of 8. */\n        return (pWav->bitsPerSample * pWav->fmt.channels) >> 3;\n    } else {\n        return pWav->fmt.blockAlign;\n    }\n}\n\nDRWAV_API drwav_uint16 drwav_fmt_get_format(const drwav_fmt* pFMT)\n{\n    if (pFMT == NULL) {\n        return 0;\n    }\n\n    if (pFMT->formatTag != DR_WAVE_FORMAT_EXTENSIBLE) {\n        return pFMT->formatTag;\n    } else {\n        return drwav__bytes_to_u16(pFMT->subFormat);    /* Only the first two bytes are required. */\n    }\n}\n\nstatic drwav_bool32 drwav_preinit(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pReadSeekUserData, const drwav_allocation_callbacks* pAllocationCallbacks)\n{\n    if (pWav == NULL || onRead == NULL || onSeek == NULL) {\n        return DRWAV_FALSE;\n    }\n\n    DRWAV_ZERO_MEMORY(pWav, sizeof(*pWav));\n    pWav->onRead    = onRead;\n    pWav->onSeek    = onSeek;\n    pWav->pUserData = pReadSeekUserData;\n    pWav->allocationCallbacks = drwav_copy_allocation_callbacks_or_defaults(pAllocationCallbacks);\n\n    if (pWav->allocationCallbacks.onFree == NULL || (pWav->allocationCallbacks.onMalloc == NULL && pWav->allocationCallbacks.onRealloc == NULL)) {\n        return DRWAV_FALSE;    /* Invalid allocation callbacks. */\n    }\n\n    return DRWAV_TRUE;\n}\n\nstatic drwav_bool32 drwav_init__internal(drwav* pWav, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags)\n{\n    /* This function assumes drwav_preinit() has been called beforehand. */\n\n    drwav_uint64 cursor;    /* <-- Keeps track of the byte position so we can seek to specific locations. */\n    drwav_bool32 sequential;\n    drwav_uint8 riff[4];\n    drwav_fmt fmt;\n    unsigned short translatedFormatTag;\n    drwav_bool32 foundDataChunk;\n    drwav_uint64 dataChunkSize = 0; /* <-- Important! Don't explicitly set this to 0 anywhere else. Calculation of the size of the data chunk is performed in different paths depending on the container. */\n    drwav_uint64 sampleCountFromFactChunk = 0;  /* Same as dataChunkSize - make sure this is the only place this is initialized to 0. */\n    drwav_uint64 chunkSize;\n\n    cursor = 0;\n    sequential = (flags & DRWAV_SEQUENTIAL) != 0;\n\n    /* The first 4 bytes should be the RIFF identifier. */\n    if (drwav__on_read(pWav->onRead, pWav->pUserData, riff, sizeof(riff), &cursor) != sizeof(riff)) {\n        return DRWAV_FALSE;\n    }\n\n    /*\n    The first 4 bytes can be used to identify the container. For RIFF files it will start with \"RIFF\" and for\n    w64 it will start with \"riff\".\n    */\n    if (drwav__fourcc_equal(riff, \"RIFF\")) {\n        pWav->container = drwav_container_riff;\n    } else if (drwav__fourcc_equal(riff, \"riff\")) {\n        int i;\n        drwav_uint8 riff2[12];\n\n        pWav->container = drwav_container_w64;\n\n        /* Check the rest of the GUID for validity. */\n        if (drwav__on_read(pWav->onRead, pWav->pUserData, riff2, sizeof(riff2), &cursor) != sizeof(riff2)) {\n            return DRWAV_FALSE;\n        }\n\n        for (i = 0; i < 12; ++i) {\n            if (riff2[i] != drwavGUID_W64_RIFF[i+4]) {\n                return DRWAV_FALSE;\n            }\n        }\n    } else if (drwav__fourcc_equal(riff, \"RF64\")) {\n        pWav->container = drwav_container_rf64;\n    } else {\n        return DRWAV_FALSE;   /* Unknown or unsupported container. */\n    }\n\n\n    if (pWav->container == drwav_container_riff || pWav->container == drwav_container_rf64) {\n        drwav_uint8 chunkSizeBytes[4];\n        drwav_uint8 wave[4];\n\n        /* RIFF/WAVE */\n        if (drwav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) {\n            return DRWAV_FALSE;\n        }\n\n        if (pWav->container == drwav_container_riff) {\n            if (drwav__bytes_to_u32(chunkSizeBytes) < 36) {\n                return DRWAV_FALSE;    /* Chunk size should always be at least 36 bytes. */\n            }\n        } else {\n            if (drwav__bytes_to_u32(chunkSizeBytes) != 0xFFFFFFFF) {\n                return DRWAV_FALSE;    /* Chunk size should always be set to -1/0xFFFFFFFF for RF64. The actual size is retrieved later. */\n            }\n        }\n\n        if (drwav__on_read(pWav->onRead, pWav->pUserData, wave, sizeof(wave), &cursor) != sizeof(wave)) {\n            return DRWAV_FALSE;\n        }\n\n        if (!drwav__fourcc_equal(wave, \"WAVE\")) {\n            return DRWAV_FALSE;    /* Expecting \"WAVE\". */\n        }\n    } else {\n        drwav_uint8 chunkSizeBytes[8];\n        drwav_uint8 wave[16];\n\n        /* W64 */\n        if (drwav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) {\n            return DRWAV_FALSE;\n        }\n\n        if (drwav__bytes_to_u64(chunkSizeBytes) < 80) {\n            return DRWAV_FALSE;\n        }\n\n        if (drwav__on_read(pWav->onRead, pWav->pUserData, wave, sizeof(wave), &cursor) != sizeof(wave)) {\n            return DRWAV_FALSE;\n        }\n\n        if (!drwav__guid_equal(wave, drwavGUID_W64_WAVE)) {\n            return DRWAV_FALSE;\n        }\n    }\n\n\n    /* For RF64, the \"ds64\" chunk must come next, before the \"fmt \" chunk. */\n    if (pWav->container == drwav_container_rf64) {\n        drwav_uint8 sizeBytes[8];\n        drwav_uint64 bytesRemainingInChunk;\n        drwav_chunk_header header;\n        drwav_result result = drwav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header);\n        if (result != DRWAV_SUCCESS) {\n            return DRWAV_FALSE;\n        }\n\n        if (!drwav__fourcc_equal(header.id.fourcc, \"ds64\")) {\n            return DRWAV_FALSE; /* Expecting \"ds64\". */\n        }\n\n        bytesRemainingInChunk = header.sizeInBytes + header.paddingSize;\n\n        /* We don't care about the size of the RIFF chunk - skip it. */\n        if (!drwav__seek_forward(pWav->onSeek, 8, pWav->pUserData)) {\n            return DRWAV_FALSE;\n        }\n        bytesRemainingInChunk -= 8;\n        cursor += 8;\n\n\n        /* Next 8 bytes is the size of the \"data\" chunk. */\n        if (drwav__on_read(pWav->onRead, pWav->pUserData, sizeBytes, sizeof(sizeBytes), &cursor) != sizeof(sizeBytes)) {\n            return DRWAV_FALSE;\n        }\n        bytesRemainingInChunk -= 8;\n        dataChunkSize = drwav__bytes_to_u64(sizeBytes);\n\n\n        /* Next 8 bytes is the same count which we would usually derived from the FACT chunk if it was available. */\n        if (drwav__on_read(pWav->onRead, pWav->pUserData, sizeBytes, sizeof(sizeBytes), &cursor) != sizeof(sizeBytes)) {\n            return DRWAV_FALSE;\n        }\n        bytesRemainingInChunk -= 8;\n        sampleCountFromFactChunk = drwav__bytes_to_u64(sizeBytes);\n\n\n        /* Skip over everything else. */\n        if (!drwav__seek_forward(pWav->onSeek, bytesRemainingInChunk, pWav->pUserData)) {\n            return DRWAV_FALSE;\n        }\n        cursor += bytesRemainingInChunk;\n    }\n\n\n    /* The next bytes should be the \"fmt \" chunk. */\n    if (!drwav__read_fmt(pWav->onRead, pWav->onSeek, pWav->pUserData, pWav->container, &cursor, &fmt)) {\n        return DRWAV_FALSE;    /* Failed to read the \"fmt \" chunk. */\n    }\n\n    /* Basic validation. */\n    if ((fmt.sampleRate    == 0 || fmt.sampleRate    > DRWAV_MAX_SAMPLE_RATE)     ||\n        (fmt.channels      == 0 || fmt.channels      > DRWAV_MAX_CHANNELS)        ||\n        (fmt.bitsPerSample == 0 || fmt.bitsPerSample > DRWAV_MAX_BITS_PER_SAMPLE) ||\n        fmt.blockAlign == 0) {\n        return DRWAV_FALSE; /* Probably an invalid WAV file. */\n    }\n\n\n    /* Translate the internal format. */\n    translatedFormatTag = fmt.formatTag;\n    if (translatedFormatTag == DR_WAVE_FORMAT_EXTENSIBLE) {\n        translatedFormatTag = drwav__bytes_to_u16(fmt.subFormat + 0);\n    }\n\n\n    /*\n    We need to enumerate over each chunk for two reasons:\n      1) The \"data\" chunk may not be the next one\n      2) We may want to report each chunk back to the client\n    \n    In order to correctly report each chunk back to the client we will need to keep looping until the end of the file.\n    */\n    foundDataChunk = DRWAV_FALSE;\n\n    /* The next chunk we care about is the \"data\" chunk. This is not necessarily the next chunk so we'll need to loop. */\n    for (;;)\n    {\n        drwav_chunk_header header;\n        drwav_result result = drwav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header);\n        if (result != DRWAV_SUCCESS) {\n            if (!foundDataChunk) {\n                return DRWAV_FALSE;\n            } else {\n                break;  /* Probably at the end of the file. Get out of the loop. */\n            }\n        }\n\n        /* Tell the client about this chunk. */\n        if (!sequential && onChunk != NULL) {\n            drwav_uint64 callbackBytesRead = onChunk(pChunkUserData, pWav->onRead, pWav->onSeek, pWav->pUserData, &header, pWav->container, &fmt);\n\n            /*\n            dr_wav may need to read the contents of the chunk, so we now need to seek back to the position before\n            we called the callback.\n            */\n            if (callbackBytesRead > 0) {\n                if (!drwav__seek_from_start(pWav->onSeek, cursor, pWav->pUserData)) {\n                    return DRWAV_FALSE;\n                }\n            }\n        }\n        \n\n        if (!foundDataChunk) {\n            pWav->dataChunkDataPos = cursor;\n        }\n\n        chunkSize = header.sizeInBytes;\n        if (pWav->container == drwav_container_riff || pWav->container == drwav_container_rf64) {\n            if (drwav__fourcc_equal(header.id.fourcc, \"data\")) {\n                foundDataChunk = DRWAV_TRUE;\n                if (pWav->container != drwav_container_rf64) {  /* The data chunk size for RF64 will always be set to 0xFFFFFFFF here. It was set to it's true value earlier. */\n                    dataChunkSize = chunkSize;\n                }\n            }\n        } else {\n            if (drwav__guid_equal(header.id.guid, drwavGUID_W64_DATA)) {\n                foundDataChunk = DRWAV_TRUE;\n                dataChunkSize = chunkSize;\n            }\n        }\n\n        /*\n        If at this point we have found the data chunk and we're running in sequential mode, we need to break out of this loop. The reason for\n        this is that we would otherwise require a backwards seek which sequential mode forbids.\n        */\n        if (foundDataChunk && sequential) {\n            break;\n        }\n\n        /* Optional. Get the total sample count from the FACT chunk. This is useful for compressed formats. */\n        if (pWav->container == drwav_container_riff) {\n            if (drwav__fourcc_equal(header.id.fourcc, \"fact\")) {\n                drwav_uint32 sampleCount;\n                if (drwav__on_read(pWav->onRead, pWav->pUserData, &sampleCount, 4, &cursor) != 4) {\n                    return DRWAV_FALSE;\n                }\n                chunkSize -= 4;\n\n                if (!foundDataChunk) {\n                    pWav->dataChunkDataPos = cursor;\n                }\n\n                /*\n                The sample count in the \"fact\" chunk is either unreliable, or I'm not understanding it properly. For now I am only enabling this\n                for Microsoft ADPCM formats.\n                */\n                if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) {\n                    sampleCountFromFactChunk = sampleCount;\n                } else {\n                    sampleCountFromFactChunk = 0;\n                }\n            }\n        } else if (pWav->container == drwav_container_w64) {\n            if (drwav__guid_equal(header.id.guid, drwavGUID_W64_FACT)) {\n                if (drwav__on_read(pWav->onRead, pWav->pUserData, &sampleCountFromFactChunk, 8, &cursor) != 8) {\n                    return DRWAV_FALSE;\n                }\n                chunkSize -= 8;\n\n                if (!foundDataChunk) {\n                    pWav->dataChunkDataPos = cursor;\n                }\n            }\n        } else if (pWav->container == drwav_container_rf64) {\n            /* We retrieved the sample count from the ds64 chunk earlier so no need to do that here. */\n        }\n\n        /* \"smpl\" chunk. */\n        if (pWav->container == drwav_container_riff || pWav->container == drwav_container_rf64) {\n            if (drwav__fourcc_equal(header.id.fourcc, \"smpl\")) {\n                drwav_uint8 smplHeaderData[36];    /* 36 = size of the smpl header section, not including the loop data. */\n                if (chunkSize >= sizeof(smplHeaderData)) {\n                    drwav_uint64 bytesJustRead = drwav__on_read(pWav->onRead, pWav->pUserData, smplHeaderData, sizeof(smplHeaderData), &cursor);\n                    chunkSize -= bytesJustRead;\n\n                    if (bytesJustRead == sizeof(smplHeaderData)) {\n                        drwav_uint32 iLoop;\n\n                        pWav->smpl.manufacturer      = drwav__bytes_to_u32(smplHeaderData+0);\n                        pWav->smpl.product           = drwav__bytes_to_u32(smplHeaderData+4);\n                        pWav->smpl.samplePeriod      = drwav__bytes_to_u32(smplHeaderData+8);\n                        pWav->smpl.midiUnityNotes    = drwav__bytes_to_u32(smplHeaderData+12);\n                        pWav->smpl.midiPitchFraction = drwav__bytes_to_u32(smplHeaderData+16);\n                        pWav->smpl.smpteFormat       = drwav__bytes_to_u32(smplHeaderData+20);\n                        pWav->smpl.smpteOffset       = drwav__bytes_to_u32(smplHeaderData+24);\n                        pWav->smpl.numSampleLoops    = drwav__bytes_to_u32(smplHeaderData+28);\n                        pWav->smpl.samplerData       = drwav__bytes_to_u32(smplHeaderData+32);\n\n                        for (iLoop = 0; iLoop < pWav->smpl.numSampleLoops && iLoop < drwav_countof(pWav->smpl.loops); ++iLoop) {\n                            drwav_uint8 smplLoopData[24];  /* 24 = size of a loop section in the smpl chunk. */\n                            bytesJustRead = drwav__on_read(pWav->onRead, pWav->pUserData, smplLoopData, sizeof(smplLoopData), &cursor);\n                            chunkSize -= bytesJustRead;\n\n                            if (bytesJustRead == sizeof(smplLoopData)) {\n                                pWav->smpl.loops[iLoop].cuePointId = drwav__bytes_to_u32(smplLoopData+0);\n                                pWav->smpl.loops[iLoop].type       = drwav__bytes_to_u32(smplLoopData+4);\n                                pWav->smpl.loops[iLoop].start      = drwav__bytes_to_u32(smplLoopData+8);\n                                pWav->smpl.loops[iLoop].end        = drwav__bytes_to_u32(smplLoopData+12);\n                                pWav->smpl.loops[iLoop].fraction   = drwav__bytes_to_u32(smplLoopData+16);\n                                pWav->smpl.loops[iLoop].playCount  = drwav__bytes_to_u32(smplLoopData+20);\n                            } else {\n                                break;  /* Break from the smpl loop for loop. */\n                            }\n                        }\n                    }\n                } else {\n                    /* Looks like invalid data. Ignore the chunk. */\n                }\n            }\n        } else {\n            if (drwav__guid_equal(header.id.guid, drwavGUID_W64_SMPL)) {\n                /*\n                This path will be hit when a W64 WAV file contains a smpl chunk. I don't have a sample file to test this path, so a contribution\n                is welcome to add support for this.\n                */\n            }\n        }\n\n        /* Make sure we seek past the padding. */\n        chunkSize += header.paddingSize;\n        if (!drwav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData)) {\n            break;\n        }\n        cursor += chunkSize;\n\n        if (!foundDataChunk) {\n            pWav->dataChunkDataPos = cursor;\n        }\n    }\n\n    /* If we haven't found a data chunk, return an error. */\n    if (!foundDataChunk) {\n        return DRWAV_FALSE;\n    }\n\n    /* We may have moved passed the data chunk. If so we need to move back. If running in sequential mode we can assume we are already sitting on the data chunk. */\n    if (!sequential) {\n        if (!drwav__seek_from_start(pWav->onSeek, pWav->dataChunkDataPos, pWav->pUserData)) {\n            return DRWAV_FALSE;\n        }\n        cursor = pWav->dataChunkDataPos;\n    }\n    \n\n    /* At this point we should be sitting on the first byte of the raw audio data. */\n\n    pWav->fmt                 = fmt;\n    pWav->sampleRate          = fmt.sampleRate;\n    pWav->channels            = fmt.channels;\n    pWav->bitsPerSample       = fmt.bitsPerSample;\n    pWav->bytesRemaining      = dataChunkSize;\n    pWav->translatedFormatTag = translatedFormatTag;\n    pWav->dataChunkDataSize   = dataChunkSize;\n\n    if (sampleCountFromFactChunk != 0) {\n        pWav->totalPCMFrameCount = sampleCountFromFactChunk;\n    } else {\n        pWav->totalPCMFrameCount = dataChunkSize / drwav_get_bytes_per_pcm_frame(pWav);\n\n        if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) {\n            drwav_uint64 totalBlockHeaderSizeInBytes;\n            drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign;\n\n            /* Make sure any trailing partial block is accounted for. */\n            if ((blockCount * fmt.blockAlign) < dataChunkSize) {\n                blockCount += 1;\n            }\n\n            /* We decode two samples per byte. There will be blockCount headers in the data chunk. This is enough to know how to calculate the total PCM frame count. */\n            totalBlockHeaderSizeInBytes = blockCount * (6*fmt.channels);\n            pWav->totalPCMFrameCount = ((dataChunkSize - totalBlockHeaderSizeInBytes) * 2) / fmt.channels;\n        }\n        if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {\n            drwav_uint64 totalBlockHeaderSizeInBytes;\n            drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign;\n\n            /* Make sure any trailing partial block is accounted for. */\n            if ((blockCount * fmt.blockAlign) < dataChunkSize) {\n                blockCount += 1;\n            }\n\n            /* We decode two samples per byte. There will be blockCount headers in the data chunk. This is enough to know how to calculate the total PCM frame count. */\n            totalBlockHeaderSizeInBytes = blockCount * (4*fmt.channels);\n            pWav->totalPCMFrameCount = ((dataChunkSize - totalBlockHeaderSizeInBytes) * 2) / fmt.channels;\n\n            /* The header includes a decoded sample for each channel which acts as the initial predictor sample. */\n            pWav->totalPCMFrameCount += blockCount;\n        }\n    }\n\n    /* Some formats only support a certain number of channels. */\n    if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {\n        if (pWav->channels > 2) {\n            return DRWAV_FALSE;\n        }\n    }\n\n#ifdef DR_WAV_LIBSNDFILE_COMPAT\n    /*\n    I use libsndfile as a benchmark for testing, however in the version I'm using (from the Windows installer on the libsndfile website),\n    it appears the total sample count libsndfile uses for MS-ADPCM is incorrect. It would seem they are computing the total sample count\n    from the number of blocks, however this results in the inclusion of extra silent samples at the end of the last block. The correct\n    way to know the total sample count is to inspect the \"fact\" chunk, which should always be present for compressed formats, and should\n    always include the sample count. This little block of code below is only used to emulate the libsndfile logic so I can properly run my\n    correctness tests against libsndfile, and is disabled by default.\n    */\n    if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) {\n        drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign;\n        pWav->totalPCMFrameCount = (((blockCount * (fmt.blockAlign - (6*pWav->channels))) * 2)) / fmt.channels;  /* x2 because two samples per byte. */\n    }\n    if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {\n        drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign;\n        pWav->totalPCMFrameCount = (((blockCount * (fmt.blockAlign - (4*pWav->channels))) * 2) + (blockCount * pWav->channels)) / fmt.channels;\n    }\n#endif\n\n    return DRWAV_TRUE;\n}\n\nDRWAV_API drwav_bool32 drwav_init(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks)\n{\n    return drwav_init_ex(pWav, onRead, onSeek, NULL, pUserData, NULL, 0, pAllocationCallbacks);\n}\n\nDRWAV_API drwav_bool32 drwav_init_ex(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_chunk_proc onChunk, void* pReadSeekUserData, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks)\n{\n    if (!drwav_preinit(pWav, onRead, onSeek, pReadSeekUserData, pAllocationCallbacks)) {\n        return DRWAV_FALSE;\n    }\n\n    return drwav_init__internal(pWav, onChunk, pChunkUserData, flags);\n}\n\n\nstatic drwav_uint32 drwav__riff_chunk_size_riff(drwav_uint64 dataChunkSize)\n{\n    drwav_uint64 chunkSize = 4 + 24 + dataChunkSize + drwav__chunk_padding_size_riff(dataChunkSize); /* 4 = \"WAVE\". 24 = \"fmt \" chunk. */\n    if (chunkSize > 0xFFFFFFFFUL) {\n        chunkSize = 0xFFFFFFFFUL;\n    }\n\n    return (drwav_uint32)chunkSize; /* Safe cast due to the clamp above. */\n}\n\nstatic drwav_uint32 drwav__data_chunk_size_riff(drwav_uint64 dataChunkSize)\n{\n    if (dataChunkSize <= 0xFFFFFFFFUL) {\n        return (drwav_uint32)dataChunkSize;\n    } else {\n        return 0xFFFFFFFFUL;\n    }\n}\n\nstatic drwav_uint64 drwav__riff_chunk_size_w64(drwav_uint64 dataChunkSize)\n{\n    drwav_uint64 dataSubchunkPaddingSize = drwav__chunk_padding_size_w64(dataChunkSize);\n\n    return 80 + 24 + dataChunkSize + dataSubchunkPaddingSize;   /* +24 because W64 includes the size of the GUID and size fields. */\n}\n\nstatic drwav_uint64 drwav__data_chunk_size_w64(drwav_uint64 dataChunkSize)\n{\n    return 24 + dataChunkSize;        /* +24 because W64 includes the size of the GUID and size fields. */\n}\n\nstatic drwav_uint64 drwav__riff_chunk_size_rf64(drwav_uint64 dataChunkSize)\n{\n    drwav_uint64 chunkSize = 4 + 36 + 24 + dataChunkSize + drwav__chunk_padding_size_riff(dataChunkSize); /* 4 = \"WAVE\". 36 = \"ds64\" chunk. 24 = \"fmt \" chunk. */\n    if (chunkSize > 0xFFFFFFFFUL) {\n        chunkSize = 0xFFFFFFFFUL;\n    }\n\n    return chunkSize;\n}\n\nstatic drwav_uint64 drwav__data_chunk_size_rf64(drwav_uint64 dataChunkSize)\n{\n    return dataChunkSize;\n}\n\n\nstatic size_t drwav__write(drwav* pWav, const void* pData, size_t dataSize)\n{\n    DRWAV_ASSERT(pWav          != NULL);\n    DRWAV_ASSERT(pWav->onWrite != NULL);\n\n    /* Generic write. Assumes no byte reordering required. */\n    return pWav->onWrite(pWav->pUserData, pData, dataSize);\n}\n\nstatic size_t drwav__write_u16ne_to_le(drwav* pWav, drwav_uint16 value)\n{\n    DRWAV_ASSERT(pWav          != NULL);\n    DRWAV_ASSERT(pWav->onWrite != NULL);\n\n    if (!drwav__is_little_endian()) {\n        value = drwav__bswap16(value);\n    }\n\n    return drwav__write(pWav, &value, 2);\n}\n\nstatic size_t drwav__write_u32ne_to_le(drwav* pWav, drwav_uint32 value)\n{\n    DRWAV_ASSERT(pWav          != NULL);\n    DRWAV_ASSERT(pWav->onWrite != NULL);\n\n    if (!drwav__is_little_endian()) {\n        value = drwav__bswap32(value);\n    }\n\n    return drwav__write(pWav, &value, 4);\n}\n\nstatic size_t drwav__write_u64ne_to_le(drwav* pWav, drwav_uint64 value)\n{\n    DRWAV_ASSERT(pWav          != NULL);\n    DRWAV_ASSERT(pWav->onWrite != NULL);\n\n    if (!drwav__is_little_endian()) {\n        value = drwav__bswap64(value);\n    }\n\n    return drwav__write(pWav, &value, 8);\n}\n\n\nstatic drwav_bool32 drwav_preinit_write(drwav* pWav, const drwav_data_format* pFormat, drwav_bool32 isSequential, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks)\n{\n    if (pWav == NULL || onWrite == NULL) {\n        return DRWAV_FALSE;\n    }\n\n    if (!isSequential && onSeek == NULL) {\n        return DRWAV_FALSE; /* <-- onSeek is required when in non-sequential mode. */\n    }\n\n    /* Not currently supporting compressed formats. Will need to add support for the \"fact\" chunk before we enable this. */\n    if (pFormat->format == DR_WAVE_FORMAT_EXTENSIBLE) {\n        return DRWAV_FALSE;\n    }\n    if (pFormat->format == DR_WAVE_FORMAT_ADPCM || pFormat->format == DR_WAVE_FORMAT_DVI_ADPCM) {\n        return DRWAV_FALSE;\n    }\n\n    DRWAV_ZERO_MEMORY(pWav, sizeof(*pWav));\n    pWav->onWrite   = onWrite;\n    pWav->onSeek    = onSeek;\n    pWav->pUserData = pUserData;\n    pWav->allocationCallbacks = drwav_copy_allocation_callbacks_or_defaults(pAllocationCallbacks);\n\n    if (pWav->allocationCallbacks.onFree == NULL || (pWav->allocationCallbacks.onMalloc == NULL && pWav->allocationCallbacks.onRealloc == NULL)) {\n        return DRWAV_FALSE;    /* Invalid allocation callbacks. */\n    }\n\n    pWav->fmt.formatTag = (drwav_uint16)pFormat->format;\n    pWav->fmt.channels = (drwav_uint16)pFormat->channels;\n    pWav->fmt.sampleRate = pFormat->sampleRate;\n    pWav->fmt.avgBytesPerSec = (drwav_uint32)((pFormat->bitsPerSample * pFormat->sampleRate * pFormat->channels) / 8);\n    pWav->fmt.blockAlign = (drwav_uint16)((pFormat->channels * pFormat->bitsPerSample) / 8);\n    pWav->fmt.bitsPerSample = (drwav_uint16)pFormat->bitsPerSample;\n    pWav->fmt.extendedSize = 0;\n    pWav->isSequentialWrite = isSequential;\n\n    return DRWAV_TRUE;\n}\n\nstatic drwav_bool32 drwav_init_write__internal(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount)\n{\n    /* The function assumes drwav_preinit_write() was called beforehand. */\n\n    size_t runningPos = 0;\n    drwav_uint64 initialDataChunkSize = 0;\n    drwav_uint64 chunkSizeFMT;\n\n    /*\n    The initial values for the \"RIFF\" and \"data\" chunks depends on whether or not we are initializing in sequential mode or not. In\n    sequential mode we set this to its final values straight away since they can be calculated from the total sample count. In non-\n    sequential mode we initialize it all to zero and fill it out in drwav_uninit() using a backwards seek.\n    */\n    if (pWav->isSequentialWrite) {\n        initialDataChunkSize = (totalSampleCount * pWav->fmt.bitsPerSample) / 8;\n\n        /*\n        The RIFF container has a limit on the number of samples. drwav is not allowing this. There's no practical limits for Wave64\n        so for the sake of simplicity I'm not doing any validation for that.\n        */\n        if (pFormat->container == drwav_container_riff) {\n            if (initialDataChunkSize > (0xFFFFFFFFUL - 36)) {\n                return DRWAV_FALSE; /* Not enough room to store every sample. */\n            }\n        }\n    }\n\n    pWav->dataChunkDataSizeTargetWrite = initialDataChunkSize;\n\n\n    /* \"RIFF\" chunk. */\n    if (pFormat->container == drwav_container_riff) {\n        drwav_uint32 chunkSizeRIFF = 28 + (drwav_uint32)initialDataChunkSize;   /* +28 = \"WAVE\" + [sizeof \"fmt \" chunk] */\n        runningPos += drwav__write(pWav, \"RIFF\", 4);\n        runningPos += drwav__write_u32ne_to_le(pWav, chunkSizeRIFF);\n        runningPos += drwav__write(pWav, \"WAVE\", 4);\n    } else if (pFormat->container == drwav_container_w64) {\n        drwav_uint64 chunkSizeRIFF = 80 + 24 + initialDataChunkSize;            /* +24 because W64 includes the size of the GUID and size fields. */\n        runningPos += drwav__write(pWav, drwavGUID_W64_RIFF, 16);\n        runningPos += drwav__write_u64ne_to_le(pWav, chunkSizeRIFF);\n        runningPos += drwav__write(pWav, drwavGUID_W64_WAVE, 16);\n    } else if (pFormat->container == drwav_container_rf64) {\n        runningPos += drwav__write(pWav, \"RF64\", 4);\n        runningPos += drwav__write_u32ne_to_le(pWav, 0xFFFFFFFF);               /* Always 0xFFFFFFFF for RF64. Set to a proper value in the \"ds64\" chunk. */\n        runningPos += drwav__write(pWav, \"WAVE\", 4);\n    }\n\n    \n    /* \"ds64\" chunk (RF64 only). */\n    if (pFormat->container == drwav_container_rf64) {\n        drwav_uint32 initialds64ChunkSize = 28;                                 /* 28 = [Size of RIFF (8 bytes)] + [Size of DATA (8 bytes)] + [Sample Count (8 bytes)] + [Table Length (4 bytes)]. Table length always set to 0. */\n        drwav_uint64 initialRiffChunkSize = 8 + initialds64ChunkSize + initialDataChunkSize;    /* +8 for the ds64 header. */\n\n        runningPos += drwav__write(pWav, \"ds64\", 4);\n        runningPos += drwav__write_u32ne_to_le(pWav, initialds64ChunkSize);     /* Size of ds64. */\n        runningPos += drwav__write_u64ne_to_le(pWav, initialRiffChunkSize);     /* Size of RIFF. Set to true value at the end. */\n        runningPos += drwav__write_u64ne_to_le(pWav, initialDataChunkSize);     /* Size of DATA. Set to true value at the end. */\n        runningPos += drwav__write_u64ne_to_le(pWav, totalSampleCount);         /* Sample count. */\n        runningPos += drwav__write_u32ne_to_le(pWav, 0);                        /* Table length. Always set to zero in our case since we're not doing any other chunks than \"DATA\". */\n    }\n\n\n    /* \"fmt \" chunk. */\n    if (pFormat->container == drwav_container_riff || pFormat->container == drwav_container_rf64) {\n        chunkSizeFMT = 16;\n        runningPos += drwav__write(pWav, \"fmt \", 4);\n        runningPos += drwav__write_u32ne_to_le(pWav, (drwav_uint32)chunkSizeFMT);\n    } else if (pFormat->container == drwav_container_w64) {\n        chunkSizeFMT = 40;\n        runningPos += drwav__write(pWav, drwavGUID_W64_FMT, 16);\n        runningPos += drwav__write_u64ne_to_le(pWav, chunkSizeFMT);\n    }\n\n    runningPos += drwav__write_u16ne_to_le(pWav, pWav->fmt.formatTag);\n    runningPos += drwav__write_u16ne_to_le(pWav, pWav->fmt.channels);\n    runningPos += drwav__write_u32ne_to_le(pWav, pWav->fmt.sampleRate);\n    runningPos += drwav__write_u32ne_to_le(pWav, pWav->fmt.avgBytesPerSec);\n    runningPos += drwav__write_u16ne_to_le(pWav, pWav->fmt.blockAlign);\n    runningPos += drwav__write_u16ne_to_le(pWav, pWav->fmt.bitsPerSample);\n\n    pWav->dataChunkDataPos = runningPos;\n\n    /* \"data\" chunk. */\n    if (pFormat->container == drwav_container_riff) {\n        drwav_uint32 chunkSizeDATA = (drwav_uint32)initialDataChunkSize;\n        runningPos += drwav__write(pWav, \"data\", 4);\n        runningPos += drwav__write_u32ne_to_le(pWav, chunkSizeDATA);\n    } else if (pFormat->container == drwav_container_w64) {\n        drwav_uint64 chunkSizeDATA = 24 + initialDataChunkSize;     /* +24 because W64 includes the size of the GUID and size fields. */\n        runningPos += drwav__write(pWav, drwavGUID_W64_DATA, 16);\n        runningPos += drwav__write_u64ne_to_le(pWav, chunkSizeDATA);\n    } else if (pFormat->container == drwav_container_rf64) {\n        runningPos += drwav__write(pWav, \"data\", 4);\n        runningPos += drwav__write_u32ne_to_le(pWav, 0xFFFFFFFF);   /* Always set to 0xFFFFFFFF for RF64. The true size of the data chunk is specified in the ds64 chunk. */\n    }\n\n    /*\n    The runningPos variable is incremented in the section above but is left unused which is causing some static analysis tools to detect it\n    as a dead store. I'm leaving this as-is for safety just in case I want to expand this function later to include other tags and want to\n    keep track of the running position for whatever reason. The line below should silence the static analysis tools.\n    */\n    (void)runningPos;\n\n    /* Set some properties for the client's convenience. */\n    pWav->container = pFormat->container;\n    pWav->channels = (drwav_uint16)pFormat->channels;\n    pWav->sampleRate = pFormat->sampleRate;\n    pWav->bitsPerSample = (drwav_uint16)pFormat->bitsPerSample;\n    pWav->translatedFormatTag = (drwav_uint16)pFormat->format;\n\n    return DRWAV_TRUE;\n}\n\n\nDRWAV_API drwav_bool32 drwav_init_write(drwav* pWav, const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks)\n{\n    if (!drwav_preinit_write(pWav, pFormat, DRWAV_FALSE, onWrite, onSeek, pUserData, pAllocationCallbacks)) {\n        return DRWAV_FALSE;\n    }\n\n    return drwav_init_write__internal(pWav, pFormat, 0);               /* DRWAV_FALSE = Not Sequential */\n}\n\nDRWAV_API drwav_bool32 drwav_init_write_sequential(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_write_proc onWrite, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks)\n{\n    if (!drwav_preinit_write(pWav, pFormat, DRWAV_TRUE, onWrite, NULL, pUserData, pAllocationCallbacks)) {\n        return DRWAV_FALSE;\n    }\n\n    return drwav_init_write__internal(pWav, pFormat, totalSampleCount); /* DRWAV_TRUE = Sequential */\n}\n\nDRWAV_API drwav_bool32 drwav_init_write_sequential_pcm_frames(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, drwav_write_proc onWrite, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks)\n{\n    if (pFormat == NULL) {\n        return DRWAV_FALSE;\n    }\n\n    return drwav_init_write_sequential(pWav, pFormat, totalPCMFrameCount*pFormat->channels, onWrite, pUserData, pAllocationCallbacks);\n}\n\nDRWAV_API drwav_uint64 drwav_target_write_size_bytes(const drwav_data_format* pFormat, drwav_uint64 totalSampleCount)\n{\n    /* Casting totalSampleCount to drwav_int64 for VC6 compatibility. No issues in practice because nobody is going to exhaust the whole 63 bits. */\n    drwav_uint64 targetDataSizeBytes = (drwav_uint64)((drwav_int64)totalSampleCount * pFormat->channels * pFormat->bitsPerSample/8.0);\n    drwav_uint64 riffChunkSizeBytes;\n    drwav_uint64 fileSizeBytes = 0;\n\n    if (pFormat->container == drwav_container_riff) {\n        riffChunkSizeBytes = drwav__riff_chunk_size_riff(targetDataSizeBytes);\n        fileSizeBytes = (8 + riffChunkSizeBytes);   /* +8 because WAV doesn't include the size of the ChunkID and ChunkSize fields. */\n    } else if (pFormat->container == drwav_container_w64) {\n        riffChunkSizeBytes = drwav__riff_chunk_size_w64(targetDataSizeBytes);\n        fileSizeBytes = riffChunkSizeBytes;\n    } else if (pFormat->container == drwav_container_rf64) {\n        riffChunkSizeBytes = drwav__riff_chunk_size_rf64(targetDataSizeBytes);\n        fileSizeBytes = (8 + riffChunkSizeBytes);   /* +8 because WAV doesn't include the size of the ChunkID and ChunkSize fields. */\n    }\n\n    return fileSizeBytes;\n}\n\n\n#ifndef DR_WAV_NO_STDIO\n\n/* drwav_result_from_errno() is only used for fopen() and wfopen() so putting it inside DR_WAV_NO_STDIO for now. If something else needs this later we can move it out. */\n#include <errno.h>\nstatic drwav_result drwav_result_from_errno(int e)\n{\n    switch (e)\n    {\n        case 0: return DRWAV_SUCCESS;\n    #ifdef EPERM\n        case EPERM: return DRWAV_INVALID_OPERATION;\n    #endif\n    #ifdef ENOENT\n        case ENOENT: return DRWAV_DOES_NOT_EXIST;\n    #endif\n    #ifdef ESRCH\n        case ESRCH: return DRWAV_DOES_NOT_EXIST;\n    #endif\n    #ifdef EINTR\n        case EINTR: return DRWAV_INTERRUPT;\n    #endif\n    #ifdef EIO\n        case EIO: return DRWAV_IO_ERROR;\n    #endif\n    #ifdef ENXIO\n        case ENXIO: return DRWAV_DOES_NOT_EXIST;\n    #endif\n    #ifdef E2BIG\n        case E2BIG: return DRWAV_INVALID_ARGS;\n    #endif\n    #ifdef ENOEXEC\n        case ENOEXEC: return DRWAV_INVALID_FILE;\n    #endif\n    #ifdef EBADF\n        case EBADF: return DRWAV_INVALID_FILE;\n    #endif\n    #ifdef ECHILD\n        case ECHILD: return DRWAV_ERROR;\n    #endif\n    #ifdef EAGAIN\n        case EAGAIN: return DRWAV_UNAVAILABLE;\n    #endif\n    #ifdef ENOMEM\n        case ENOMEM: return DRWAV_OUT_OF_MEMORY;\n    #endif\n    #ifdef EACCES\n        case EACCES: return DRWAV_ACCESS_DENIED;\n    #endif\n    #ifdef EFAULT\n        case EFAULT: return DRWAV_BAD_ADDRESS;\n    #endif\n    #ifdef ENOTBLK\n        case ENOTBLK: return DRWAV_ERROR;\n    #endif\n    #ifdef EBUSY\n        case EBUSY: return DRWAV_BUSY;\n    #endif\n    #ifdef EEXIST\n        case EEXIST: return DRWAV_ALREADY_EXISTS;\n    #endif\n    #ifdef EXDEV\n        case EXDEV: return DRWAV_ERROR;\n    #endif\n    #ifdef ENODEV\n        case ENODEV: return DRWAV_DOES_NOT_EXIST;\n    #endif\n    #ifdef ENOTDIR\n        case ENOTDIR: return DRWAV_NOT_DIRECTORY;\n    #endif\n    #ifdef EISDIR\n        case EISDIR: return DRWAV_IS_DIRECTORY;\n    #endif\n    #ifdef EINVAL\n        case EINVAL: return DRWAV_INVALID_ARGS;\n    #endif\n    #ifdef ENFILE\n        case ENFILE: return DRWAV_TOO_MANY_OPEN_FILES;\n    #endif\n    #ifdef EMFILE\n        case EMFILE: return DRWAV_TOO_MANY_OPEN_FILES;\n    #endif\n    #ifdef ENOTTY\n        case ENOTTY: return DRWAV_INVALID_OPERATION;\n    #endif\n    #ifdef ETXTBSY\n        case ETXTBSY: return DRWAV_BUSY;\n    #endif\n    #ifdef EFBIG\n        case EFBIG: return DRWAV_TOO_BIG;\n    #endif\n    #ifdef ENOSPC\n        case ENOSPC: return DRWAV_NO_SPACE;\n    #endif\n    #ifdef ESPIPE\n        case ESPIPE: return DRWAV_BAD_SEEK;\n    #endif\n    #ifdef EROFS\n        case EROFS: return DRWAV_ACCESS_DENIED;\n    #endif\n    #ifdef EMLINK\n        case EMLINK: return DRWAV_TOO_MANY_LINKS;\n    #endif\n    #ifdef EPIPE\n        case EPIPE: return DRWAV_BAD_PIPE;\n    #endif\n    #ifdef EDOM\n        case EDOM: return DRWAV_OUT_OF_RANGE;\n    #endif\n    #ifdef ERANGE\n        case ERANGE: return DRWAV_OUT_OF_RANGE;\n    #endif\n    #ifdef EDEADLK\n        case EDEADLK: return DRWAV_DEADLOCK;\n    #endif\n    #ifdef ENAMETOOLONG\n        case ENAMETOOLONG: return DRWAV_PATH_TOO_LONG;\n    #endif\n    #ifdef ENOLCK\n        case ENOLCK: return DRWAV_ERROR;\n    #endif\n    #ifdef ENOSYS\n        case ENOSYS: return DRWAV_NOT_IMPLEMENTED;\n    #endif\n    #ifdef ENOTEMPTY\n        case ENOTEMPTY: return DRWAV_DIRECTORY_NOT_EMPTY;\n    #endif\n    #ifdef ELOOP\n        case ELOOP: return DRWAV_TOO_MANY_LINKS;\n    #endif\n    #ifdef ENOMSG\n        case ENOMSG: return DRWAV_NO_MESSAGE;\n    #endif\n    #ifdef EIDRM\n        case EIDRM: return DRWAV_ERROR;\n    #endif\n    #ifdef ECHRNG\n        case ECHRNG: return DRWAV_ERROR;\n    #endif\n    #ifdef EL2NSYNC\n        case EL2NSYNC: return DRWAV_ERROR;\n    #endif\n    #ifdef EL3HLT\n        case EL3HLT: return DRWAV_ERROR;\n    #endif\n    #ifdef EL3RST\n        case EL3RST: return DRWAV_ERROR;\n    #endif\n    #ifdef ELNRNG\n        case ELNRNG: return DRWAV_OUT_OF_RANGE;\n    #endif\n    #ifdef EUNATCH\n        case EUNATCH: return DRWAV_ERROR;\n    #endif\n    #ifdef ENOCSI\n        case ENOCSI: return DRWAV_ERROR;\n    #endif\n    #ifdef EL2HLT\n        case EL2HLT: return DRWAV_ERROR;\n    #endif\n    #ifdef EBADE\n        case EBADE: return DRWAV_ERROR;\n    #endif\n    #ifdef EBADR\n        case EBADR: return DRWAV_ERROR;\n    #endif\n    #ifdef EXFULL\n        case EXFULL: return DRWAV_ERROR;\n    #endif\n    #ifdef ENOANO\n        case ENOANO: return DRWAV_ERROR;\n    #endif\n    #ifdef EBADRQC\n        case EBADRQC: return DRWAV_ERROR;\n    #endif\n    #ifdef EBADSLT\n        case EBADSLT: return DRWAV_ERROR;\n    #endif\n    #ifdef EBFONT\n        case EBFONT: return DRWAV_INVALID_FILE;\n    #endif\n    #ifdef ENOSTR\n        case ENOSTR: return DRWAV_ERROR;\n    #endif\n    #ifdef ENODATA\n        case ENODATA: return DRWAV_NO_DATA_AVAILABLE;\n    #endif\n    #ifdef ETIME\n        case ETIME: return DRWAV_TIMEOUT;\n    #endif\n    #ifdef ENOSR\n        case ENOSR: return DRWAV_NO_DATA_AVAILABLE;\n    #endif\n    #ifdef ENONET\n        case ENONET: return DRWAV_NO_NETWORK;\n    #endif\n    #ifdef ENOPKG\n        case ENOPKG: return DRWAV_ERROR;\n    #endif\n    #ifdef EREMOTE\n        case EREMOTE: return DRWAV_ERROR;\n    #endif\n    #ifdef ENOLINK\n        case ENOLINK: return DRWAV_ERROR;\n    #endif\n    #ifdef EADV\n        case EADV: return DRWAV_ERROR;\n    #endif\n    #ifdef ESRMNT\n        case ESRMNT: return DRWAV_ERROR;\n    #endif\n    #ifdef ECOMM\n        case ECOMM: return DRWAV_ERROR;\n    #endif\n    #ifdef EPROTO\n        case EPROTO: return DRWAV_ERROR;\n    #endif\n    #ifdef EMULTIHOP\n        case EMULTIHOP: return DRWAV_ERROR;\n    #endif\n    #ifdef EDOTDOT\n        case EDOTDOT: return DRWAV_ERROR;\n    #endif\n    #ifdef EBADMSG\n        case EBADMSG: return DRWAV_BAD_MESSAGE;\n    #endif\n    #ifdef EOVERFLOW\n        case EOVERFLOW: return DRWAV_TOO_BIG;\n    #endif\n    #ifdef ENOTUNIQ\n        case ENOTUNIQ: return DRWAV_NOT_UNIQUE;\n    #endif\n    #ifdef EBADFD\n        case EBADFD: return DRWAV_ERROR;\n    #endif\n    #ifdef EREMCHG\n        case EREMCHG: return DRWAV_ERROR;\n    #endif\n    #ifdef ELIBACC\n        case ELIBACC: return DRWAV_ACCESS_DENIED;\n    #endif\n    #ifdef ELIBBAD\n        case ELIBBAD: return DRWAV_INVALID_FILE;\n    #endif\n    #ifdef ELIBSCN\n        case ELIBSCN: return DRWAV_INVALID_FILE;\n    #endif\n    #ifdef ELIBMAX\n        case ELIBMAX: return DRWAV_ERROR;\n    #endif\n    #ifdef ELIBEXEC\n        case ELIBEXEC: return DRWAV_ERROR;\n    #endif\n    #ifdef EILSEQ\n        case EILSEQ: return DRWAV_INVALID_DATA;\n    #endif\n    #ifdef ERESTART\n        case ERESTART: return DRWAV_ERROR;\n    #endif\n    #ifdef ESTRPIPE\n        case ESTRPIPE: return DRWAV_ERROR;\n    #endif\n    #ifdef EUSERS\n        case EUSERS: return DRWAV_ERROR;\n    #endif\n    #ifdef ENOTSOCK\n        case ENOTSOCK: return DRWAV_NOT_SOCKET;\n    #endif\n    #ifdef EDESTADDRREQ\n        case EDESTADDRREQ: return DRWAV_NO_ADDRESS;\n    #endif\n    #ifdef EMSGSIZE\n        case EMSGSIZE: return DRWAV_TOO_BIG;\n    #endif\n    #ifdef EPROTOTYPE\n        case EPROTOTYPE: return DRWAV_BAD_PROTOCOL;\n    #endif\n    #ifdef ENOPROTOOPT\n        case ENOPROTOOPT: return DRWAV_PROTOCOL_UNAVAILABLE;\n    #endif\n    #ifdef EPROTONOSUPPORT\n        case EPROTONOSUPPORT: return DRWAV_PROTOCOL_NOT_SUPPORTED;\n    #endif\n    #ifdef ESOCKTNOSUPPORT\n        case ESOCKTNOSUPPORT: return DRWAV_SOCKET_NOT_SUPPORTED;\n    #endif\n    #ifdef EOPNOTSUPP\n        case EOPNOTSUPP: return DRWAV_INVALID_OPERATION;\n    #endif\n    #ifdef EPFNOSUPPORT\n        case EPFNOSUPPORT: return DRWAV_PROTOCOL_FAMILY_NOT_SUPPORTED;\n    #endif\n    #ifdef EAFNOSUPPORT\n        case EAFNOSUPPORT: return DRWAV_ADDRESS_FAMILY_NOT_SUPPORTED;\n    #endif\n    #ifdef EADDRINUSE\n        case EADDRINUSE: return DRWAV_ALREADY_IN_USE;\n    #endif\n    #ifdef EADDRNOTAVAIL\n        case EADDRNOTAVAIL: return DRWAV_ERROR;\n    #endif\n    #ifdef ENETDOWN\n        case ENETDOWN: return DRWAV_NO_NETWORK;\n    #endif\n    #ifdef ENETUNREACH\n        case ENETUNREACH: return DRWAV_NO_NETWORK;\n    #endif\n    #ifdef ENETRESET\n        case ENETRESET: return DRWAV_NO_NETWORK;\n    #endif\n    #ifdef ECONNABORTED\n        case ECONNABORTED: return DRWAV_NO_NETWORK;\n    #endif\n    #ifdef ECONNRESET\n        case ECONNRESET: return DRWAV_CONNECTION_RESET;\n    #endif\n    #ifdef ENOBUFS\n        case ENOBUFS: return DRWAV_NO_SPACE;\n    #endif\n    #ifdef EISCONN\n        case EISCONN: return DRWAV_ALREADY_CONNECTED;\n    #endif\n    #ifdef ENOTCONN\n        case ENOTCONN: return DRWAV_NOT_CONNECTED;\n    #endif\n    #ifdef ESHUTDOWN\n        case ESHUTDOWN: return DRWAV_ERROR;\n    #endif\n    #ifdef ETOOMANYREFS\n        case ETOOMANYREFS: return DRWAV_ERROR;\n    #endif\n    #ifdef ETIMEDOUT\n        case ETIMEDOUT: return DRWAV_TIMEOUT;\n    #endif\n    #ifdef ECONNREFUSED\n        case ECONNREFUSED: return DRWAV_CONNECTION_REFUSED;\n    #endif\n    #ifdef EHOSTDOWN\n        case EHOSTDOWN: return DRWAV_NO_HOST;\n    #endif\n    #ifdef EHOSTUNREACH\n        case EHOSTUNREACH: return DRWAV_NO_HOST;\n    #endif\n    #ifdef EALREADY\n        case EALREADY: return DRWAV_IN_PROGRESS;\n    #endif\n    #ifdef EINPROGRESS\n        case EINPROGRESS: return DRWAV_IN_PROGRESS;\n    #endif\n    #ifdef ESTALE\n        case ESTALE: return DRWAV_INVALID_FILE;\n    #endif\n    #ifdef EUCLEAN\n        case EUCLEAN: return DRWAV_ERROR;\n    #endif\n    #ifdef ENOTNAM\n        case ENOTNAM: return DRWAV_ERROR;\n    #endif\n    #ifdef ENAVAIL\n        case ENAVAIL: return DRWAV_ERROR;\n    #endif\n    #ifdef EISNAM\n        case EISNAM: return DRWAV_ERROR;\n    #endif\n    #ifdef EREMOTEIO\n        case EREMOTEIO: return DRWAV_IO_ERROR;\n    #endif\n    #ifdef EDQUOT\n        case EDQUOT: return DRWAV_NO_SPACE;\n    #endif\n    #ifdef ENOMEDIUM\n        case ENOMEDIUM: return DRWAV_DOES_NOT_EXIST;\n    #endif\n    #ifdef EMEDIUMTYPE\n        case EMEDIUMTYPE: return DRWAV_ERROR;\n    #endif\n    #ifdef ECANCELED\n        case ECANCELED: return DRWAV_CANCELLED;\n    #endif\n    #ifdef ENOKEY\n        case ENOKEY: return DRWAV_ERROR;\n    #endif\n    #ifdef EKEYEXPIRED\n        case EKEYEXPIRED: return DRWAV_ERROR;\n    #endif\n    #ifdef EKEYREVOKED\n        case EKEYREVOKED: return DRWAV_ERROR;\n    #endif\n    #ifdef EKEYREJECTED\n        case EKEYREJECTED: return DRWAV_ERROR;\n    #endif\n    #ifdef EOWNERDEAD\n        case EOWNERDEAD: return DRWAV_ERROR;\n    #endif\n    #ifdef ENOTRECOVERABLE\n        case ENOTRECOVERABLE: return DRWAV_ERROR;\n    #endif\n    #ifdef ERFKILL\n        case ERFKILL: return DRWAV_ERROR;\n    #endif\n    #ifdef EHWPOISON\n        case EHWPOISON: return DRWAV_ERROR;\n    #endif\n        default: return DRWAV_ERROR;\n    }\n}\n\nstatic drwav_result drwav_fopen(FILE** ppFile, const char* pFilePath, const char* pOpenMode)\n{\n#if _MSC_VER && _MSC_VER >= 1400\n    errno_t err;\n#endif\n\n    if (ppFile != NULL) {\n        *ppFile = NULL;  /* Safety. */\n    }\n\n    if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) {\n        return DRWAV_INVALID_ARGS;\n    }\n\n#if _MSC_VER && _MSC_VER >= 1400\n    err = fopen_s(ppFile, pFilePath, pOpenMode);\n    if (err != 0) {\n        return drwav_result_from_errno(err);\n    }\n#else\n#if defined(_WIN32) || defined(__APPLE__)\n    *ppFile = fopen(pFilePath, pOpenMode);\n#else\n    #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64 && defined(_LARGEFILE64_SOURCE)\n        *ppFile = fopen64(pFilePath, pOpenMode);\n    #else\n        *ppFile = fopen(pFilePath, pOpenMode);\n    #endif\n#endif\n    if (*ppFile == NULL) {\n        drwav_result result = drwav_result_from_errno(errno);\n        if (result == DRWAV_SUCCESS) {\n            result = DRWAV_ERROR;   /* Just a safety check to make sure we never ever return success when pFile == NULL. */\n        }\n\n        return result;\n    }\n#endif\n\n    return DRWAV_SUCCESS;\n}\n\n/*\n_wfopen() isn't always available in all compilation environments.\n\n    * Windows only.\n    * MSVC seems to support it universally as far back as VC6 from what I can tell (haven't checked further back).\n    * MinGW-64 (both 32- and 64-bit) seems to support it.\n    * MinGW wraps it in !defined(__STRICT_ANSI__).\n    * OpenWatcom wraps it in !defined(_NO_EXT_KEYS).\n\nThis can be reviewed as compatibility issues arise. The preference is to use _wfopen_s() and _wfopen() as opposed to the wcsrtombs()\nfallback, so if you notice your compiler not detecting this properly I'm happy to look at adding support.\n*/\n#if defined(_WIN32)\n    #if defined(_MSC_VER) || defined(__MINGW64__) || (!defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS))\n        #define DRWAV_HAS_WFOPEN\n    #endif\n#endif\n\nstatic drwav_result drwav_wfopen(FILE** ppFile, const wchar_t* pFilePath, const wchar_t* pOpenMode, const drwav_allocation_callbacks* pAllocationCallbacks)\n{\n    if (ppFile != NULL) {\n        *ppFile = NULL;  /* Safety. */\n    }\n\n    if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) {\n        return DRWAV_INVALID_ARGS;\n    }\n\n#if defined(DRWAV_HAS_WFOPEN)\n    {\n        /* Use _wfopen() on Windows. */\n    #if defined(_MSC_VER) && _MSC_VER >= 1400\n        errno_t err = _wfopen_s(ppFile, pFilePath, pOpenMode);\n        if (err != 0) {\n            return drwav_result_from_errno(err);\n        }\n    #else\n        *ppFile = _wfopen(pFilePath, pOpenMode);\n        if (*ppFile == NULL) {\n            return drwav_result_from_errno(errno);\n        }\n    #endif\n        (void)pAllocationCallbacks;\n    }\n#else\n    /*\n    Use fopen() on anything other than Windows. Requires a conversion. This is annoying because fopen() is locale specific. The only real way I can\n    think of to do this is with wcsrtombs(). Note that wcstombs() is apparently not thread-safe because it uses a static global mbstate_t object for\n    maintaining state. I've checked this with -std=c89 and it works, but if somebody get's a compiler error I'll look into improving compatibility.\n    */\n    {\n        mbstate_t mbs;\n        size_t lenMB;\n        const wchar_t* pFilePathTemp = pFilePath;\n        char* pFilePathMB = NULL;\n        char pOpenModeMB[32] = {0};\n\n        /* Get the length first. */\n        DRWAV_ZERO_OBJECT(&mbs);\n        lenMB = wcsrtombs(NULL, &pFilePathTemp, 0, &mbs);\n        if (lenMB == (size_t)-1) {\n            return drwav_result_from_errno(errno);\n        }\n\n        pFilePathMB = (char*)drwav__malloc_from_callbacks(lenMB + 1, pAllocationCallbacks);\n        if (pFilePathMB == NULL) {\n            return DRWAV_OUT_OF_MEMORY;\n        }\n\n        pFilePathTemp = pFilePath;\n        DRWAV_ZERO_OBJECT(&mbs);\n        wcsrtombs(pFilePathMB, &pFilePathTemp, lenMB + 1, &mbs);\n\n        /* The open mode should always consist of ASCII characters so we should be able to do a trivial conversion. */\n        {\n            size_t i = 0;\n            for (;;) {\n                if (pOpenMode[i] == 0) {\n                    pOpenModeMB[i] = '\\0';\n                    break;\n                }\n\n                pOpenModeMB[i] = (char)pOpenMode[i];\n                i += 1;\n            }\n        }\n\n        *ppFile = fopen(pFilePathMB, pOpenModeMB);\n\n        drwav__free_from_callbacks(pFilePathMB, pAllocationCallbacks);\n    }\n\n    if (*ppFile == NULL) {\n        return DRWAV_ERROR;\n    }\n#endif\n\n    return DRWAV_SUCCESS;\n}\n\n\nstatic size_t drwav__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead)\n{\n    return fread(pBufferOut, 1, bytesToRead, (FILE*)pUserData);\n}\n\nstatic size_t drwav__on_write_stdio(void* pUserData, const void* pData, size_t bytesToWrite)\n{\n    return fwrite(pData, 1, bytesToWrite, (FILE*)pUserData);\n}\n\nstatic drwav_bool32 drwav__on_seek_stdio(void* pUserData, int offset, drwav_seek_origin origin)\n{\n    return fseek((FILE*)pUserData, offset, (origin == drwav_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0;\n}\n\nDRWAV_API drwav_bool32 drwav_init_file(drwav* pWav, const char* filename, const drwav_allocation_callbacks* pAllocationCallbacks)\n{\n    return drwav_init_file_ex(pWav, filename, NULL, NULL, 0, pAllocationCallbacks);\n}\n\n\nstatic drwav_bool32 drwav_init_file__internal_FILE(drwav* pWav, FILE* pFile, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks)\n{\n    drwav_bool32 result;\n\n    result = drwav_preinit(pWav, drwav__on_read_stdio, drwav__on_seek_stdio, (void*)pFile, pAllocationCallbacks);\n    if (result != DRWAV_TRUE) {\n        fclose(pFile);\n        return result;\n    }\n\n    result = drwav_init__internal(pWav, onChunk, pChunkUserData, flags);\n    if (result != DRWAV_TRUE) {\n        fclose(pFile);\n        return result;\n    }\n\n    return DRWAV_TRUE;\n}\n\nDRWAV_API drwav_bool32 drwav_init_file_ex(drwav* pWav, const char* filename, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks)\n{\n    FILE* pFile;\n    if (drwav_fopen(&pFile, filename, \"rb\") != DRWAV_SUCCESS) {\n        return DRWAV_FALSE;\n    }\n\n    /* This takes ownership of the FILE* object. */\n    return drwav_init_file__internal_FILE(pWav, pFile, onChunk, pChunkUserData, flags, pAllocationCallbacks);\n}\n\nDRWAV_API drwav_bool32 drwav_init_file_w(drwav* pWav, const wchar_t* filename, const drwav_allocation_callbacks* pAllocationCallbacks)\n{\n    return drwav_init_file_ex_w(pWav, filename, NULL, NULL, 0, pAllocationCallbacks);\n}\n\nDRWAV_API drwav_bool32 drwav_init_file_ex_w(drwav* pWav, const wchar_t* filename, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks)\n{\n    FILE* pFile;\n    if (drwav_wfopen(&pFile, filename, L\"rb\", pAllocationCallbacks) != DRWAV_SUCCESS) {\n        return DRWAV_FALSE;\n    }\n\n    /* This takes ownership of the FILE* object. */\n    return drwav_init_file__internal_FILE(pWav, pFile, onChunk, pChunkUserData, flags, pAllocationCallbacks);\n}\n\n\nstatic drwav_bool32 drwav_init_file_write__internal_FILE(drwav* pWav, FILE* pFile, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, const drwav_allocation_callbacks* pAllocationCallbacks)\n{\n    drwav_bool32 result;\n\n    result = drwav_preinit_write(pWav, pFormat, isSequential, drwav__on_write_stdio, drwav__on_seek_stdio, (void*)pFile, pAllocationCallbacks);\n    if (result != DRWAV_TRUE) {\n        fclose(pFile);\n        return result;\n    }\n\n    result = drwav_init_write__internal(pWav, pFormat, totalSampleCount);\n    if (result != DRWAV_TRUE) {\n        fclose(pFile);\n        return result;\n    }\n\n    return DRWAV_TRUE;\n}\n\nstatic drwav_bool32 drwav_init_file_write__internal(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, const drwav_allocation_callbacks* pAllocationCallbacks)\n{\n    FILE* pFile;\n    if (drwav_fopen(&pFile, filename, \"wb\") != DRWAV_SUCCESS) {\n        return DRWAV_FALSE;\n    }\n\n    /* This takes ownership of the FILE* object. */\n    return drwav_init_file_write__internal_FILE(pWav, pFile, pFormat, totalSampleCount, isSequential, pAllocationCallbacks);\n}\n\nstatic drwav_bool32 drwav_init_file_write_w__internal(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, const drwav_allocation_callbacks* pAllocationCallbacks)\n{\n    FILE* pFile;\n    if (drwav_wfopen(&pFile, filename, L\"wb\", pAllocationCallbacks) != DRWAV_SUCCESS) {\n        return DRWAV_FALSE;\n    }\n\n    /* This takes ownership of the FILE* object. */\n    return drwav_init_file_write__internal_FILE(pWav, pFile, pFormat, totalSampleCount, isSequential, pAllocationCallbacks);\n}\n\nDRWAV_API drwav_bool32 drwav_init_file_write(drwav* pWav, const char* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks)\n{\n    return drwav_init_file_write__internal(pWav, filename, pFormat, 0, DRWAV_FALSE, pAllocationCallbacks);\n}\n\nDRWAV_API drwav_bool32 drwav_init_file_write_sequential(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks)\n{\n    return drwav_init_file_write__internal(pWav, filename, pFormat, totalSampleCount, DRWAV_TRUE, pAllocationCallbacks);\n}\n\nDRWAV_API drwav_bool32 drwav_init_file_write_sequential_pcm_frames(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks)\n{\n    if (pFormat == NULL) {\n        return DRWAV_FALSE;\n    }\n\n    return drwav_init_file_write_sequential(pWav, filename, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks);\n}\n\nDRWAV_API drwav_bool32 drwav_init_file_write_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks)\n{\n    return drwav_init_file_write_w__internal(pWav, filename, pFormat, 0, DRWAV_FALSE, pAllocationCallbacks);\n}\n\nDRWAV_API drwav_bool32 drwav_init_file_write_sequential_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks)\n{\n    return drwav_init_file_write_w__internal(pWav, filename, pFormat, totalSampleCount, DRWAV_TRUE, pAllocationCallbacks);\n}\n\nDRWAV_API drwav_bool32 drwav_init_file_write_sequential_pcm_frames_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks)\n{\n    if (pFormat == NULL) {\n        return DRWAV_FALSE;\n    }\n\n    return drwav_init_file_write_sequential_w(pWav, filename, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks);\n}\n#endif  /* DR_WAV_NO_STDIO */\n\n\nstatic size_t drwav__on_read_memory(void* pUserData, void* pBufferOut, size_t bytesToRead)\n{\n    drwav* pWav = (drwav*)pUserData;\n    size_t bytesRemaining;\n\n    DRWAV_ASSERT(pWav != NULL);\n    DRWAV_ASSERT(pWav->memoryStream.dataSize >= pWav->memoryStream.currentReadPos);\n\n    bytesRemaining = pWav->memoryStream.dataSize - pWav->memoryStream.currentReadPos;\n    if (bytesToRead > bytesRemaining) {\n        bytesToRead = bytesRemaining;\n    }\n\n    if (bytesToRead > 0) {\n        DRWAV_COPY_MEMORY(pBufferOut, pWav->memoryStream.data + pWav->memoryStream.currentReadPos, bytesToRead);\n        pWav->memoryStream.currentReadPos += bytesToRead;\n    }\n\n    return bytesToRead;\n}\n\nstatic drwav_bool32 drwav__on_seek_memory(void* pUserData, int offset, drwav_seek_origin origin)\n{\n    drwav* pWav = (drwav*)pUserData;\n    DRWAV_ASSERT(pWav != NULL);\n\n    if (origin == drwav_seek_origin_current) {\n        if (offset > 0) {\n            if (pWav->memoryStream.currentReadPos + offset > pWav->memoryStream.dataSize) {\n                return DRWAV_FALSE; /* Trying to seek too far forward. */\n            }\n        } else {\n            if (pWav->memoryStream.currentReadPos < (size_t)-offset) {\n                return DRWAV_FALSE; /* Trying to seek too far backwards. */\n            }\n        }\n\n        /* This will never underflow thanks to the clamps above. */\n        pWav->memoryStream.currentReadPos += offset;\n    } else {\n        if ((drwav_uint32)offset <= pWav->memoryStream.dataSize) {\n            pWav->memoryStream.currentReadPos = offset;\n        } else {\n            return DRWAV_FALSE; /* Trying to seek too far forward. */\n        }\n    }\n    \n    return DRWAV_TRUE;\n}\n\nstatic size_t drwav__on_write_memory(void* pUserData, const void* pDataIn, size_t bytesToWrite)\n{\n    drwav* pWav = (drwav*)pUserData;\n    size_t bytesRemaining;\n\n    DRWAV_ASSERT(pWav != NULL);\n    DRWAV_ASSERT(pWav->memoryStreamWrite.dataCapacity >= pWav->memoryStreamWrite.currentWritePos);\n\n    bytesRemaining = pWav->memoryStreamWrite.dataCapacity - pWav->memoryStreamWrite.currentWritePos;\n    if (bytesRemaining < bytesToWrite) {\n        /* Need to reallocate. */\n        void* pNewData;\n        size_t newDataCapacity = (pWav->memoryStreamWrite.dataCapacity == 0) ? 256 : pWav->memoryStreamWrite.dataCapacity * 2;\n\n        /* If doubling wasn't enough, just make it the minimum required size to write the data. */\n        if ((newDataCapacity - pWav->memoryStreamWrite.currentWritePos) < bytesToWrite) {\n            newDataCapacity = pWav->memoryStreamWrite.currentWritePos + bytesToWrite;\n        }\n\n        pNewData = drwav__realloc_from_callbacks(*pWav->memoryStreamWrite.ppData, newDataCapacity, pWav->memoryStreamWrite.dataCapacity, &pWav->allocationCallbacks);\n        if (pNewData == NULL) {\n            return 0;\n        }\n\n        *pWav->memoryStreamWrite.ppData = pNewData;\n        pWav->memoryStreamWrite.dataCapacity = newDataCapacity;\n    }\n\n    DRWAV_COPY_MEMORY(((drwav_uint8*)(*pWav->memoryStreamWrite.ppData)) + pWav->memoryStreamWrite.currentWritePos, pDataIn, bytesToWrite);\n\n    pWav->memoryStreamWrite.currentWritePos += bytesToWrite;\n    if (pWav->memoryStreamWrite.dataSize < pWav->memoryStreamWrite.currentWritePos) {\n        pWav->memoryStreamWrite.dataSize = pWav->memoryStreamWrite.currentWritePos;\n    }\n\n    *pWav->memoryStreamWrite.pDataSize = pWav->memoryStreamWrite.dataSize;\n\n    return bytesToWrite;\n}\n\nstatic drwav_bool32 drwav__on_seek_memory_write(void* pUserData, int offset, drwav_seek_origin origin)\n{\n    drwav* pWav = (drwav*)pUserData;\n    DRWAV_ASSERT(pWav != NULL);\n\n    if (origin == drwav_seek_origin_current) {\n        if (offset > 0) {\n            if (pWav->memoryStreamWrite.currentWritePos + offset > pWav->memoryStreamWrite.dataSize) {\n                offset = (int)(pWav->memoryStreamWrite.dataSize - pWav->memoryStreamWrite.currentWritePos);  /* Trying to seek too far forward. */\n            }\n        } else {\n            if (pWav->memoryStreamWrite.currentWritePos < (size_t)-offset) {\n                offset = -(int)pWav->memoryStreamWrite.currentWritePos;  /* Trying to seek too far backwards. */\n            }\n        }\n\n        /* This will never underflow thanks to the clamps above. */\n        pWav->memoryStreamWrite.currentWritePos += offset;\n    } else {\n        if ((drwav_uint32)offset <= pWav->memoryStreamWrite.dataSize) {\n            pWav->memoryStreamWrite.currentWritePos = offset;\n        } else {\n            pWav->memoryStreamWrite.currentWritePos = pWav->memoryStreamWrite.dataSize;  /* Trying to seek too far forward. */\n        }\n    }\n    \n    return DRWAV_TRUE;\n}\n\nDRWAV_API drwav_bool32 drwav_init_memory(drwav* pWav, const void* data, size_t dataSize, const drwav_allocation_callbacks* pAllocationCallbacks)\n{\n    return drwav_init_memory_ex(pWav, data, dataSize, NULL, NULL, 0, pAllocationCallbacks);\n}\n\nDRWAV_API drwav_bool32 drwav_init_memory_ex(drwav* pWav, const void* data, size_t dataSize, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks)\n{\n    if (data == NULL || dataSize == 0) {\n        return DRWAV_FALSE;\n    }\n\n    if (!drwav_preinit(pWav, drwav__on_read_memory, drwav__on_seek_memory, pWav, pAllocationCallbacks)) {\n        return DRWAV_FALSE;\n    }\n\n    pWav->memoryStream.data = (const drwav_uint8*)data;\n    pWav->memoryStream.dataSize = dataSize;\n    pWav->memoryStream.currentReadPos = 0;\n\n    return drwav_init__internal(pWav, onChunk, pChunkUserData, flags);\n}\n\n\nstatic drwav_bool32 drwav_init_memory_write__internal(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, const drwav_allocation_callbacks* pAllocationCallbacks)\n{\n    if (ppData == NULL || pDataSize == NULL) {\n        return DRWAV_FALSE;\n    }\n\n    *ppData = NULL; /* Important because we're using realloc()! */\n    *pDataSize = 0;\n\n    if (!drwav_preinit_write(pWav, pFormat, isSequential, drwav__on_write_memory, drwav__on_seek_memory_write, pWav, pAllocationCallbacks)) {\n        return DRWAV_FALSE;\n    }\n\n    pWav->memoryStreamWrite.ppData = ppData;\n    pWav->memoryStreamWrite.pDataSize = pDataSize;\n    pWav->memoryStreamWrite.dataSize = 0;\n    pWav->memoryStreamWrite.dataCapacity = 0;\n    pWav->memoryStreamWrite.currentWritePos = 0;\n\n    return drwav_init_write__internal(pWav, pFormat, totalSampleCount);\n}\n\nDRWAV_API drwav_bool32 drwav_init_memory_write(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks)\n{\n    return drwav_init_memory_write__internal(pWav, ppData, pDataSize, pFormat, 0, DRWAV_FALSE, pAllocationCallbacks);\n}\n\nDRWAV_API drwav_bool32 drwav_init_memory_write_sequential(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks)\n{\n    return drwav_init_memory_write__internal(pWav, ppData, pDataSize, pFormat, totalSampleCount, DRWAV_TRUE, pAllocationCallbacks);\n}\n\nDRWAV_API drwav_bool32 drwav_init_memory_write_sequential_pcm_frames(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks)\n{\n    if (pFormat == NULL) {\n        return DRWAV_FALSE;\n    }\n\n    return drwav_init_memory_write_sequential(pWav, ppData, pDataSize, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks);\n}\n\n\n\nDRWAV_API drwav_result drwav_uninit(drwav* pWav)\n{\n    drwav_result result = DRWAV_SUCCESS;\n\n    if (pWav == NULL) {\n        return DRWAV_INVALID_ARGS;\n    }\n\n    /*\n    If the drwav object was opened in write mode we'll need to finalize a few things:\n      - Make sure the \"data\" chunk is aligned to 16-bits for RIFF containers, or 64 bits for W64 containers.\n      - Set the size of the \"data\" chunk.\n    */\n    if (pWav->onWrite != NULL) {\n        drwav_uint32 paddingSize = 0;\n\n        /* Padding. Do not adjust pWav->dataChunkDataSize - this should not include the padding. */\n        if (pWav->container == drwav_container_riff || pWav->container == drwav_container_rf64) {\n            paddingSize = drwav__chunk_padding_size_riff(pWav->dataChunkDataSize);\n        } else {\n            paddingSize = drwav__chunk_padding_size_w64(pWav->dataChunkDataSize);\n        }\n        \n        if (paddingSize > 0) {\n            drwav_uint64 paddingData = 0;\n            drwav__write(pWav, &paddingData, paddingSize);  /* Byte order does not matter for this. */\n        }\n\n        /*\n        Chunk sizes. When using sequential mode, these will have been filled in at initialization time. We only need\n        to do this when using non-sequential mode.\n        */\n        if (pWav->onSeek && !pWav->isSequentialWrite) {\n            if (pWav->container == drwav_container_riff) {\n                /* The \"RIFF\" chunk size. */\n                if (pWav->onSeek(pWav->pUserData, 4, drwav_seek_origin_start)) {\n                    drwav_uint32 riffChunkSize = drwav__riff_chunk_size_riff(pWav->dataChunkDataSize);\n                    drwav__write_u32ne_to_le(pWav, riffChunkSize);\n                }\n\n                /* the \"data\" chunk size. */\n                if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos + 4, drwav_seek_origin_start)) {\n                    drwav_uint32 dataChunkSize = drwav__data_chunk_size_riff(pWav->dataChunkDataSize);\n                    drwav__write_u32ne_to_le(pWav, dataChunkSize);\n                }\n            } else if (pWav->container == drwav_container_w64) {\n                /* The \"RIFF\" chunk size. */\n                if (pWav->onSeek(pWav->pUserData, 16, drwav_seek_origin_start)) {\n                    drwav_uint64 riffChunkSize = drwav__riff_chunk_size_w64(pWav->dataChunkDataSize);\n                    drwav__write_u64ne_to_le(pWav, riffChunkSize);\n                }\n\n                /* The \"data\" chunk size. */\n                if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos + 16, drwav_seek_origin_start)) {\n                    drwav_uint64 dataChunkSize = drwav__data_chunk_size_w64(pWav->dataChunkDataSize);\n                    drwav__write_u64ne_to_le(pWav, dataChunkSize);\n                }\n            } else if (pWav->container == drwav_container_rf64) {\n                /* We only need to update the ds64 chunk. The \"RIFF\" and \"data\" chunks always have their sizes set to 0xFFFFFFFF for RF64. */\n                int ds64BodyPos = 12 + 8;\n\n                /* The \"RIFF\" chunk size. */\n                if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 0, drwav_seek_origin_start)) {\n                    drwav_uint64 riffChunkSize = drwav__riff_chunk_size_rf64(pWav->dataChunkDataSize);\n                    drwav__write_u64ne_to_le(pWav, riffChunkSize);\n                }\n\n                /* The \"data\" chunk size. */\n                if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 8, drwav_seek_origin_start)) {\n                    drwav_uint64 dataChunkSize = drwav__data_chunk_size_rf64(pWav->dataChunkDataSize);\n                    drwav__write_u64ne_to_le(pWav, dataChunkSize);\n                }\n            }\n        }\n\n        /* Validation for sequential mode. */\n        if (pWav->isSequentialWrite) {\n            if (pWav->dataChunkDataSize != pWav->dataChunkDataSizeTargetWrite) {\n                result = DRWAV_INVALID_FILE;\n            }\n        }\n    }\n\n#ifndef DR_WAV_NO_STDIO\n    /*\n    If we opened the file with drwav_open_file() we will want to close the file handle. We can know whether or not drwav_open_file()\n    was used by looking at the onRead and onSeek callbacks.\n    */\n    if (pWav->onRead == drwav__on_read_stdio || pWav->onWrite == drwav__on_write_stdio) {\n        fclose((FILE*)pWav->pUserData);\n    }\n#endif\n\n    return result;\n}\n\n\n\nDRWAV_API size_t drwav_read_raw(drwav* pWav, size_t bytesToRead, void* pBufferOut)\n{\n    size_t bytesRead;\n\n    if (pWav == NULL || bytesToRead == 0) {\n        return 0;\n    }\n\n    if (bytesToRead > pWav->bytesRemaining) {\n        bytesToRead = (size_t)pWav->bytesRemaining;\n    }\n\n    if (pBufferOut != NULL) {\n        bytesRead = pWav->onRead(pWav->pUserData, pBufferOut, bytesToRead);\n    } else {\n        /* We need to seek. If we fail, we need to read-and-discard to make sure we get a good byte count. */\n        bytesRead = 0;\n        while (bytesRead < bytesToRead) {\n            size_t bytesToSeek = (bytesToRead - bytesRead);\n            if (bytesToSeek > 0x7FFFFFFF) {\n                bytesToSeek = 0x7FFFFFFF;\n            }\n\n            if (pWav->onSeek(pWav->pUserData, (int)bytesToSeek, drwav_seek_origin_current) == DRWAV_FALSE) {\n                break;\n            }\n\n            bytesRead += bytesToSeek;\n        }\n\n        /* When we get here we may need to read-and-discard some data. */\n        while (bytesRead < bytesToRead) {\n            drwav_uint8 buffer[4096];\n            size_t bytesSeeked;\n            size_t bytesToSeek = (bytesToRead - bytesRead);\n            if (bytesToSeek > sizeof(buffer)) {\n                bytesToSeek = sizeof(buffer);\n            }\n\n            bytesSeeked = pWav->onRead(pWav->pUserData, buffer, bytesToSeek);\n            bytesRead += bytesSeeked;\n\n            if (bytesSeeked < bytesToSeek) {\n                break;  /* Reached the end. */\n            }\n        }\n    }\n\n    pWav->bytesRemaining -= bytesRead;\n    return bytesRead;\n}\n\n\n\nDRWAV_API drwav_uint64 drwav_read_pcm_frames_le(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut)\n{\n    drwav_uint32 bytesPerFrame;\n    drwav_uint64 bytesToRead;   /* Intentionally uint64 instead of size_t so we can do a check that we're not reading too much on 32-bit builds. */\n\n    if (pWav == NULL || framesToRead == 0) {\n        return 0;\n    }\n\n    /* Cannot use this function for compressed formats. */\n    if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) {\n        return 0;\n    }\n\n    bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);\n    if (bytesPerFrame == 0) {\n        return 0;\n    }\n\n    /* Don't try to read more samples than can potentially fit in the output buffer. */\n    bytesToRead = framesToRead * bytesPerFrame;\n    if (bytesToRead > DRWAV_SIZE_MAX) {\n        bytesToRead = (DRWAV_SIZE_MAX / bytesPerFrame) * bytesPerFrame; /* Round the number of bytes to read to a clean frame boundary. */\n    }\n\n    /*\n    Doing an explicit check here just to make it clear that we don't want to be attempt to read anything if there's no bytes to read. There\n    *could* be a time where it evaluates to 0 due to overflowing.\n    */\n    if (bytesToRead == 0) {\n        return 0;\n    }\n\n    return drwav_read_raw(pWav, (size_t)bytesToRead, pBufferOut) / bytesPerFrame;\n}\n\nDRWAV_API drwav_uint64 drwav_read_pcm_frames_be(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut)\n{\n    drwav_uint64 framesRead = drwav_read_pcm_frames_le(pWav, framesToRead, pBufferOut);\n\n    if (pBufferOut != NULL) {\n        drwav__bswap_samples(pBufferOut, framesRead*pWav->channels, drwav_get_bytes_per_pcm_frame(pWav)/pWav->channels, pWav->translatedFormatTag);\n    }\n\n    return framesRead;\n}\n\nDRWAV_API drwav_uint64 drwav_read_pcm_frames(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut)\n{\n    if (drwav__is_little_endian()) {\n        return drwav_read_pcm_frames_le(pWav, framesToRead, pBufferOut);\n    } else {\n        return drwav_read_pcm_frames_be(pWav, framesToRead, pBufferOut);\n    }\n}\n\n\n\nDRWAV_API drwav_bool32 drwav_seek_to_first_pcm_frame(drwav* pWav)\n{\n    if (pWav->onWrite != NULL) {\n        return DRWAV_FALSE; /* No seeking in write mode. */\n    }\n\n    if (!pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos, drwav_seek_origin_start)) {\n        return DRWAV_FALSE;\n    }\n\n    if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) {\n        pWav->compressed.iCurrentPCMFrame = 0;\n\n        /* Cached data needs to be cleared for compressed formats. */\n        if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) {\n            DRWAV_ZERO_OBJECT(&pWav->msadpcm);\n        } else if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {\n            DRWAV_ZERO_OBJECT(&pWav->ima);\n        } else {\n            DRWAV_ASSERT(DRWAV_FALSE);  /* If this assertion is triggered it means I've implemented a new compressed format but forgot to add a branch for it here. */\n        }\n    }\n    \n    pWav->bytesRemaining = pWav->dataChunkDataSize;\n    return DRWAV_TRUE;\n}\n\nDRWAV_API drwav_bool32 drwav_seek_to_pcm_frame(drwav* pWav, drwav_uint64 targetFrameIndex)\n{\n    /* Seeking should be compatible with wave files > 2GB. */\n\n    if (pWav == NULL || pWav->onSeek == NULL) {\n        return DRWAV_FALSE;\n    }\n\n    /* No seeking in write mode. */\n    if (pWav->onWrite != NULL) {\n        return DRWAV_FALSE;\n    }\n\n    /* If there are no samples, just return DRWAV_TRUE without doing anything. */\n    if (pWav->totalPCMFrameCount == 0) {\n        return DRWAV_TRUE;\n    }\n\n    /* Make sure the sample is clamped. */\n    if (targetFrameIndex >= pWav->totalPCMFrameCount) {\n        targetFrameIndex  = pWav->totalPCMFrameCount - 1;\n    }\n\n    /*\n    For compressed formats we just use a slow generic seek. If we are seeking forward we just seek forward. If we are going backwards we need\n    to seek back to the start.\n    */\n    if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) {\n        /* TODO: This can be optimized. */\n        \n        /*\n        If we're seeking forward it's simple - just keep reading samples until we hit the sample we're requesting. If we're seeking backwards,\n        we first need to seek back to the start and then just do the same thing as a forward seek.\n        */\n        if (targetFrameIndex < pWav->compressed.iCurrentPCMFrame) {\n            if (!drwav_seek_to_first_pcm_frame(pWav)) {\n                return DRWAV_FALSE;\n            }\n        }\n\n        if (targetFrameIndex > pWav->compressed.iCurrentPCMFrame) {\n            drwav_uint64 offsetInFrames = targetFrameIndex - pWav->compressed.iCurrentPCMFrame;\n\n            drwav_int16 devnull[2048];\n            while (offsetInFrames > 0) {\n                drwav_uint64 framesRead = 0;\n                drwav_uint64 framesToRead = offsetInFrames;\n                if (framesToRead > drwav_countof(devnull)/pWav->channels) {\n                    framesToRead = drwav_countof(devnull)/pWav->channels;\n                }\n\n                if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) {\n                    framesRead = drwav_read_pcm_frames_s16__msadpcm(pWav, framesToRead, devnull);\n                } else if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {\n                    framesRead = drwav_read_pcm_frames_s16__ima(pWav, framesToRead, devnull);\n                } else {\n                    DRWAV_ASSERT(DRWAV_FALSE);  /* If this assertion is triggered it means I've implemented a new compressed format but forgot to add a branch for it here. */\n                }\n\n                if (framesRead != framesToRead) {\n                    return DRWAV_FALSE;\n                }\n\n                offsetInFrames -= framesRead;\n            }\n        }\n    } else {\n        drwav_uint64 totalSizeInBytes;\n        drwav_uint64 currentBytePos;\n        drwav_uint64 targetBytePos;\n        drwav_uint64 offset;\n\n        totalSizeInBytes = pWav->totalPCMFrameCount * drwav_get_bytes_per_pcm_frame(pWav);\n        DRWAV_ASSERT(totalSizeInBytes >= pWav->bytesRemaining);\n\n        currentBytePos = totalSizeInBytes - pWav->bytesRemaining;\n        targetBytePos  = targetFrameIndex * drwav_get_bytes_per_pcm_frame(pWav);\n\n        if (currentBytePos < targetBytePos) {\n            /* Offset forwards. */\n            offset = (targetBytePos - currentBytePos);\n        } else {\n            /* Offset backwards. */\n            if (!drwav_seek_to_first_pcm_frame(pWav)) {\n                return DRWAV_FALSE;\n            }\n            offset = targetBytePos;\n        }\n\n        while (offset > 0) {\n            int offset32 = ((offset > INT_MAX) ? INT_MAX : (int)offset);\n            if (!pWav->onSeek(pWav->pUserData, offset32, drwav_seek_origin_current)) {\n                return DRWAV_FALSE;\n            }\n\n            pWav->bytesRemaining -= offset32;\n            offset -= offset32;\n        }\n    }\n\n    return DRWAV_TRUE;\n}\n\n\nDRWAV_API size_t drwav_write_raw(drwav* pWav, size_t bytesToWrite, const void* pData)\n{\n    size_t bytesWritten;\n\n    if (pWav == NULL || bytesToWrite == 0 || pData == NULL) {\n        return 0;\n    }\n\n    bytesWritten = pWav->onWrite(pWav->pUserData, pData, bytesToWrite);\n    pWav->dataChunkDataSize += bytesWritten;\n\n    return bytesWritten;\n}\n\n\nDRWAV_API drwav_uint64 drwav_write_pcm_frames_le(drwav* pWav, drwav_uint64 framesToWrite, const void* pData)\n{\n    drwav_uint64 bytesToWrite;\n    drwav_uint64 bytesWritten;\n    const drwav_uint8* pRunningData;\n\n    if (pWav == NULL || framesToWrite == 0 || pData == NULL) {\n        return 0;\n    }\n\n    bytesToWrite = ((framesToWrite * pWav->channels * pWav->bitsPerSample) / 8);\n    if (bytesToWrite > DRWAV_SIZE_MAX) {\n        return 0;\n    }\n\n    bytesWritten = 0;\n    pRunningData = (const drwav_uint8*)pData;\n\n    while (bytesToWrite > 0) {\n        size_t bytesJustWritten;\n        drwav_uint64 bytesToWriteThisIteration;\n\n        bytesToWriteThisIteration = bytesToWrite;\n        DRWAV_ASSERT(bytesToWriteThisIteration <= DRWAV_SIZE_MAX);  /* <-- This is checked above. */\n\n        bytesJustWritten = drwav_write_raw(pWav, (size_t)bytesToWriteThisIteration, pRunningData);\n        if (bytesJustWritten == 0) {\n            break;\n        }\n\n        bytesToWrite -= bytesJustWritten;\n        bytesWritten += bytesJustWritten;\n        pRunningData += bytesJustWritten;\n    }\n\n    return (bytesWritten * 8) / pWav->bitsPerSample / pWav->channels;\n}\n\nDRWAV_API drwav_uint64 drwav_write_pcm_frames_be(drwav* pWav, drwav_uint64 framesToWrite, const void* pData)\n{\n    drwav_uint64 bytesToWrite;\n    drwav_uint64 bytesWritten;\n    drwav_uint32 bytesPerSample;\n    const drwav_uint8* pRunningData;\n\n    if (pWav == NULL || framesToWrite == 0 || pData == NULL) {\n        return 0;\n    }\n\n    bytesToWrite = ((framesToWrite * pWav->channels * pWav->bitsPerSample) / 8);\n    if (bytesToWrite > DRWAV_SIZE_MAX) {\n        return 0;\n    }\n\n    bytesWritten = 0;\n    pRunningData = (const drwav_uint8*)pData;\n\n    bytesPerSample = drwav_get_bytes_per_pcm_frame(pWav) / pWav->channels;\n    \n    while (bytesToWrite > 0) {\n        drwav_uint8 temp[4096];\n        drwav_uint32 sampleCount;\n        size_t bytesJustWritten;\n        drwav_uint64 bytesToWriteThisIteration;\n\n        bytesToWriteThisIteration = bytesToWrite;\n        DRWAV_ASSERT(bytesToWriteThisIteration <= DRWAV_SIZE_MAX);  /* <-- This is checked above. */\n\n        /*\n        WAV files are always little-endian. We need to byte swap on big-endian architectures. Since our input buffer is read-only we need\n        to use an intermediary buffer for the conversion.\n        */\n        sampleCount = sizeof(temp)/bytesPerSample;\n\n        if (bytesToWriteThisIteration > ((drwav_uint64)sampleCount)*bytesPerSample) {\n            bytesToWriteThisIteration = ((drwav_uint64)sampleCount)*bytesPerSample;\n        }\n\n        DRWAV_COPY_MEMORY(temp, pRunningData, (size_t)bytesToWriteThisIteration);\n        drwav__bswap_samples(temp, sampleCount, bytesPerSample, pWav->translatedFormatTag);\n\n        bytesJustWritten = drwav_write_raw(pWav, (size_t)bytesToWriteThisIteration, temp);\n        if (bytesJustWritten == 0) {\n            break;\n        }\n\n        bytesToWrite -= bytesJustWritten;\n        bytesWritten += bytesJustWritten;\n        pRunningData += bytesJustWritten;\n    }\n\n    return (bytesWritten * 8) / pWav->bitsPerSample / pWav->channels;\n}\n\nDRWAV_API drwav_uint64 drwav_write_pcm_frames(drwav* pWav, drwav_uint64 framesToWrite, const void* pData)\n{\n    if (drwav__is_little_endian()) {\n        return drwav_write_pcm_frames_le(pWav, framesToWrite, pData);\n    } else {\n        return drwav_write_pcm_frames_be(pWav, framesToWrite, pData);\n    }\n}\n\n\nstatic drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut)\n{\n    drwav_uint64 totalFramesRead = 0;\n\n    DRWAV_ASSERT(pWav != NULL);\n    DRWAV_ASSERT(framesToRead > 0);\n\n    /* TODO: Lots of room for optimization here. */\n\n    while (framesToRead > 0 && pWav->compressed.iCurrentPCMFrame < pWav->totalPCMFrameCount) {\n        /* If there are no cached frames we need to load a new block. */\n        if (pWav->msadpcm.cachedFrameCount == 0 && pWav->msadpcm.bytesRemainingInBlock == 0) {\n            if (pWav->channels == 1) {\n                /* Mono. */\n                drwav_uint8 header[7];\n                if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) {\n                    return totalFramesRead;\n                }\n                pWav->msadpcm.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header);\n\n                pWav->msadpcm.predictor[0]     = header[0];\n                pWav->msadpcm.delta[0]         = drwav__bytes_to_s16(header + 1);\n                pWav->msadpcm.prevFrames[0][1] = (drwav_int32)drwav__bytes_to_s16(header + 3);\n                pWav->msadpcm.prevFrames[0][0] = (drwav_int32)drwav__bytes_to_s16(header + 5);\n                pWav->msadpcm.cachedFrames[2]  = pWav->msadpcm.prevFrames[0][0];\n                pWav->msadpcm.cachedFrames[3]  = pWav->msadpcm.prevFrames[0][1];\n                pWav->msadpcm.cachedFrameCount = 2;\n            } else {\n                /* Stereo. */\n                drwav_uint8 header[14];\n                if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) {\n                    return totalFramesRead;\n                }\n                pWav->msadpcm.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header);\n\n                pWav->msadpcm.predictor[0] = header[0];\n                pWav->msadpcm.predictor[1] = header[1];\n                pWav->msadpcm.delta[0] = drwav__bytes_to_s16(header + 2);\n                pWav->msadpcm.delta[1] = drwav__bytes_to_s16(header + 4);\n                pWav->msadpcm.prevFrames[0][1] = (drwav_int32)drwav__bytes_to_s16(header + 6);\n                pWav->msadpcm.prevFrames[1][1] = (drwav_int32)drwav__bytes_to_s16(header + 8);\n                pWav->msadpcm.prevFrames[0][0] = (drwav_int32)drwav__bytes_to_s16(header + 10);\n                pWav->msadpcm.prevFrames[1][0] = (drwav_int32)drwav__bytes_to_s16(header + 12);\n\n                pWav->msadpcm.cachedFrames[0] = pWav->msadpcm.prevFrames[0][0];\n                pWav->msadpcm.cachedFrames[1] = pWav->msadpcm.prevFrames[1][0];\n                pWav->msadpcm.cachedFrames[2] = pWav->msadpcm.prevFrames[0][1];\n                pWav->msadpcm.cachedFrames[3] = pWav->msadpcm.prevFrames[1][1];\n                pWav->msadpcm.cachedFrameCount = 2;\n            }\n        }\n\n        /* Output anything that's cached. */\n        while (framesToRead > 0 && pWav->msadpcm.cachedFrameCount > 0 && pWav->compressed.iCurrentPCMFrame < pWav->totalPCMFrameCount) {\n            if (pBufferOut != NULL) {\n                drwav_uint32 iSample = 0;\n                for (iSample = 0; iSample < pWav->channels; iSample += 1) {\n                    pBufferOut[iSample] = (drwav_int16)pWav->msadpcm.cachedFrames[(drwav_countof(pWav->msadpcm.cachedFrames) - (pWav->msadpcm.cachedFrameCount*pWav->channels)) + iSample];\n                }\n\n                pBufferOut += pWav->channels;\n            }\n\n            framesToRead    -= 1;\n            totalFramesRead += 1;\n            pWav->compressed.iCurrentPCMFrame += 1;\n            pWav->msadpcm.cachedFrameCount -= 1;\n        }\n\n        if (framesToRead == 0) {\n            return totalFramesRead;\n        }\n\n\n        /*\n        If there's nothing left in the cache, just go ahead and load more. If there's nothing left to load in the current block we just continue to the next\n        loop iteration which will trigger the loading of a new block.\n        */\n        if (pWav->msadpcm.cachedFrameCount == 0) {\n            if (pWav->msadpcm.bytesRemainingInBlock == 0) {\n                continue;\n            } else {\n                static drwav_int32 adaptationTable[] = { \n                    230, 230, 230, 230, 307, 409, 512, 614, \n                    768, 614, 512, 409, 307, 230, 230, 230 \n                };\n                static drwav_int32 coeff1Table[] = { 256, 512, 0, 192, 240, 460,  392 };\n                static drwav_int32 coeff2Table[] = { 0,  -256, 0, 64,  0,  -208, -232 };\n\n                drwav_uint8 nibbles;\n                drwav_int32 nibble0;\n                drwav_int32 nibble1;\n\n                if (pWav->onRead(pWav->pUserData, &nibbles, 1) != 1) {\n                    return totalFramesRead;\n                }\n                pWav->msadpcm.bytesRemainingInBlock -= 1;\n\n                /* TODO: Optimize away these if statements. */\n                nibble0 = ((nibbles & 0xF0) >> 4); if ((nibbles & 0x80)) { nibble0 |= 0xFFFFFFF0UL; }\n                nibble1 = ((nibbles & 0x0F) >> 0); if ((nibbles & 0x08)) { nibble1 |= 0xFFFFFFF0UL; }\n\n                if (pWav->channels == 1) {\n                    /* Mono. */\n                    drwav_int32 newSample0;\n                    drwav_int32 newSample1;\n\n                    newSample0  = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8;\n                    newSample0 += nibble0 * pWav->msadpcm.delta[0];\n                    newSample0  = drwav_clamp(newSample0, -32768, 32767);\n\n                    pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8;\n                    if (pWav->msadpcm.delta[0] < 16) {\n                        pWav->msadpcm.delta[0] = 16;\n                    }\n\n                    pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1];\n                    pWav->msadpcm.prevFrames[0][1] = newSample0;\n\n\n                    newSample1  = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8;\n                    newSample1 += nibble1 * pWav->msadpcm.delta[0];\n                    newSample1  = drwav_clamp(newSample1, -32768, 32767);\n\n                    pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[0]) >> 8;\n                    if (pWav->msadpcm.delta[0] < 16) {\n                        pWav->msadpcm.delta[0] = 16;\n                    }\n\n                    pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1];\n                    pWav->msadpcm.prevFrames[0][1] = newSample1;\n\n\n                    pWav->msadpcm.cachedFrames[2] = newSample0;\n                    pWav->msadpcm.cachedFrames[3] = newSample1;\n                    pWav->msadpcm.cachedFrameCount = 2;\n                } else {\n                    /* Stereo. */\n                    drwav_int32 newSample0;\n                    drwav_int32 newSample1;\n\n                    /* Left. */\n                    newSample0  = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8;\n                    newSample0 += nibble0 * pWav->msadpcm.delta[0];\n                    newSample0  = drwav_clamp(newSample0, -32768, 32767);\n\n                    pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8;\n                    if (pWav->msadpcm.delta[0] < 16) {\n                        pWav->msadpcm.delta[0] = 16;\n                    }\n\n                    pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1];\n                    pWav->msadpcm.prevFrames[0][1] = newSample0;\n\n\n                    /* Right. */\n                    newSample1  = ((pWav->msadpcm.prevFrames[1][1] * coeff1Table[pWav->msadpcm.predictor[1]]) + (pWav->msadpcm.prevFrames[1][0] * coeff2Table[pWav->msadpcm.predictor[1]])) >> 8;\n                    newSample1 += nibble1 * pWav->msadpcm.delta[1];\n                    newSample1  = drwav_clamp(newSample1, -32768, 32767);\n\n                    pWav->msadpcm.delta[1] = (adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[1]) >> 8;\n                    if (pWav->msadpcm.delta[1] < 16) {\n                        pWav->msadpcm.delta[1] = 16;\n                    }\n\n                    pWav->msadpcm.prevFrames[1][0] = pWav->msadpcm.prevFrames[1][1];\n                    pWav->msadpcm.prevFrames[1][1] = newSample1;\n\n                    pWav->msadpcm.cachedFrames[2] = newSample0;\n                    pWav->msadpcm.cachedFrames[3] = newSample1;\n                    pWav->msadpcm.cachedFrameCount = 1;\n                }\n            }\n        }\n    }\n\n    return totalFramesRead;\n}\n\n\nstatic drwav_uint64 drwav_read_pcm_frames_s16__ima(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut)\n{\n    drwav_uint64 totalFramesRead = 0;\n    drwav_uint32 iChannel;\n\n    static drwav_int32 indexTable[16] = {\n        -1, -1, -1, -1, 2, 4, 6, 8,\n        -1, -1, -1, -1, 2, 4, 6, 8\n    };\n\n    static drwav_int32 stepTable[89] = {\n        7,     8,     9,     10,    11,    12,    13,    14,    16,    17, \n        19,    21,    23,    25,    28,    31,    34,    37,    41,    45, \n        50,    55,    60,    66,    73,    80,    88,    97,    107,   118, \n        130,   143,   157,   173,   190,   209,   230,   253,   279,   307,\n        337,   371,   408,   449,   494,   544,   598,   658,   724,   796,\n        876,   963,   1060,  1166,  1282,  1411,  1552,  1707,  1878,  2066, \n        2272,  2499,  2749,  3024,  3327,  3660,  4026,  4428,  4871,  5358,\n        5894,  6484,  7132,  7845,  8630,  9493,  10442, 11487, 12635, 13899, \n        15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767 \n    };\n\n    DRWAV_ASSERT(pWav != NULL);\n    DRWAV_ASSERT(framesToRead > 0);\n\n    /* TODO: Lots of room for optimization here. */\n\n    while (framesToRead > 0 && pWav->compressed.iCurrentPCMFrame < pWav->totalPCMFrameCount) {\n        /* If there are no cached samples we need to load a new block. */\n        if (pWav->ima.cachedFrameCount == 0 && pWav->ima.bytesRemainingInBlock == 0) {\n            if (pWav->channels == 1) {\n                /* Mono. */\n                drwav_uint8 header[4];\n                if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) {\n                    return totalFramesRead;\n                }\n                pWav->ima.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header);\n\n                if (header[2] >= drwav_countof(stepTable)) {\n                    pWav->onSeek(pWav->pUserData, pWav->ima.bytesRemainingInBlock, drwav_seek_origin_current);\n                    pWav->ima.bytesRemainingInBlock = 0;\n                    return totalFramesRead; /* Invalid data. */\n                }\n\n                pWav->ima.predictor[0] = drwav__bytes_to_s16(header + 0);\n                pWav->ima.stepIndex[0] = header[2];\n                pWav->ima.cachedFrames[drwav_countof(pWav->ima.cachedFrames) - 1] = pWav->ima.predictor[0];\n                pWav->ima.cachedFrameCount = 1;\n            } else {\n                /* Stereo. */\n                drwav_uint8 header[8];\n                if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) {\n                    return totalFramesRead;\n                }\n                pWav->ima.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header);\n\n                if (header[2] >= drwav_countof(stepTable) || header[6] >= drwav_countof(stepTable)) {\n                    pWav->onSeek(pWav->pUserData, pWav->ima.bytesRemainingInBlock, drwav_seek_origin_current);\n                    pWav->ima.bytesRemainingInBlock = 0;\n                    return totalFramesRead; /* Invalid data. */\n                }\n\n                pWav->ima.predictor[0] = drwav__bytes_to_s16(header + 0);\n                pWav->ima.stepIndex[0] = header[2];\n                pWav->ima.predictor[1] = drwav__bytes_to_s16(header + 4);\n                pWav->ima.stepIndex[1] = header[6];\n\n                pWav->ima.cachedFrames[drwav_countof(pWav->ima.cachedFrames) - 2] = pWav->ima.predictor[0];\n                pWav->ima.cachedFrames[drwav_countof(pWav->ima.cachedFrames) - 1] = pWav->ima.predictor[1];\n                pWav->ima.cachedFrameCount = 1;\n            }\n        }\n\n        /* Output anything that's cached. */\n        while (framesToRead > 0 && pWav->ima.cachedFrameCount > 0 && pWav->compressed.iCurrentPCMFrame < pWav->totalPCMFrameCount) {\n            if (pBufferOut != NULL) {\n                drwav_uint32 iSample;\n                for (iSample = 0; iSample < pWav->channels; iSample += 1) {\n                    pBufferOut[iSample] = (drwav_int16)pWav->ima.cachedFrames[(drwav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + iSample];\n                }\n                pBufferOut += pWav->channels;\n            }\n\n            framesToRead    -= 1;\n            totalFramesRead += 1;\n            pWav->compressed.iCurrentPCMFrame += 1;\n            pWav->ima.cachedFrameCount -= 1;\n        }\n\n        if (framesToRead == 0) {\n            return totalFramesRead;\n        }\n\n        /*\n        If there's nothing left in the cache, just go ahead and load more. If there's nothing left to load in the current block we just continue to the next\n        loop iteration which will trigger the loading of a new block.\n        */\n        if (pWav->ima.cachedFrameCount == 0) {\n            if (pWav->ima.bytesRemainingInBlock == 0) {\n                continue;\n            } else {\n                /*\n                From what I can tell with stereo streams, it looks like every 4 bytes (8 samples) is for one channel. So it goes 4 bytes for the\n                left channel, 4 bytes for the right channel.\n                */\n                pWav->ima.cachedFrameCount = 8;\n                for (iChannel = 0; iChannel < pWav->channels; ++iChannel) {\n                    drwav_uint32 iByte;\n                    drwav_uint8 nibbles[4];\n                    if (pWav->onRead(pWav->pUserData, &nibbles, 4) != 4) {\n                        pWav->ima.cachedFrameCount = 0;\n                        return totalFramesRead;\n                    }\n                    pWav->ima.bytesRemainingInBlock -= 4;\n\n                    for (iByte = 0; iByte < 4; ++iByte) {\n                        drwav_uint8 nibble0 = ((nibbles[iByte] & 0x0F) >> 0);\n                        drwav_uint8 nibble1 = ((nibbles[iByte] & 0xF0) >> 4);\n\n                        drwav_int32 step      = stepTable[pWav->ima.stepIndex[iChannel]];\n                        drwav_int32 predictor = pWav->ima.predictor[iChannel];\n\n                        drwav_int32      diff  = step >> 3;\n                        if (nibble0 & 1) diff += step >> 2;\n                        if (nibble0 & 2) diff += step >> 1;\n                        if (nibble0 & 4) diff += step;\n                        if (nibble0 & 8) diff  = -diff;\n\n                        predictor = drwav_clamp(predictor + diff, -32768, 32767);\n                        pWav->ima.predictor[iChannel] = predictor;\n                        pWav->ima.stepIndex[iChannel] = drwav_clamp(pWav->ima.stepIndex[iChannel] + indexTable[nibble0], 0, (drwav_int32)drwav_countof(stepTable)-1);\n                        pWav->ima.cachedFrames[(drwav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + (iByte*2+0)*pWav->channels + iChannel] = predictor;\n\n\n                        step      = stepTable[pWav->ima.stepIndex[iChannel]];\n                        predictor = pWav->ima.predictor[iChannel];\n\n                                         diff  = step >> 3;\n                        if (nibble1 & 1) diff += step >> 2;\n                        if (nibble1 & 2) diff += step >> 1;\n                        if (nibble1 & 4) diff += step;\n                        if (nibble1 & 8) diff  = -diff;\n\n                        predictor = drwav_clamp(predictor + diff, -32768, 32767);\n                        pWav->ima.predictor[iChannel] = predictor;\n                        pWav->ima.stepIndex[iChannel] = drwav_clamp(pWav->ima.stepIndex[iChannel] + indexTable[nibble1], 0, (drwav_int32)drwav_countof(stepTable)-1);\n                        pWav->ima.cachedFrames[(drwav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + (iByte*2+1)*pWav->channels + iChannel] = predictor;\n                    }\n                }\n            }\n        }\n    }\n\n    return totalFramesRead;\n}\n\n\n#ifndef DR_WAV_NO_CONVERSION_API\nstatic unsigned short g_drwavAlawTable[256] = {\n    0xEA80, 0xEB80, 0xE880, 0xE980, 0xEE80, 0xEF80, 0xEC80, 0xED80, 0xE280, 0xE380, 0xE080, 0xE180, 0xE680, 0xE780, 0xE480, 0xE580, \n    0xF540, 0xF5C0, 0xF440, 0xF4C0, 0xF740, 0xF7C0, 0xF640, 0xF6C0, 0xF140, 0xF1C0, 0xF040, 0xF0C0, 0xF340, 0xF3C0, 0xF240, 0xF2C0, \n    0xAA00, 0xAE00, 0xA200, 0xA600, 0xBA00, 0xBE00, 0xB200, 0xB600, 0x8A00, 0x8E00, 0x8200, 0x8600, 0x9A00, 0x9E00, 0x9200, 0x9600, \n    0xD500, 0xD700, 0xD100, 0xD300, 0xDD00, 0xDF00, 0xD900, 0xDB00, 0xC500, 0xC700, 0xC100, 0xC300, 0xCD00, 0xCF00, 0xC900, 0xCB00, \n    0xFEA8, 0xFEB8, 0xFE88, 0xFE98, 0xFEE8, 0xFEF8, 0xFEC8, 0xFED8, 0xFE28, 0xFE38, 0xFE08, 0xFE18, 0xFE68, 0xFE78, 0xFE48, 0xFE58, \n    0xFFA8, 0xFFB8, 0xFF88, 0xFF98, 0xFFE8, 0xFFF8, 0xFFC8, 0xFFD8, 0xFF28, 0xFF38, 0xFF08, 0xFF18, 0xFF68, 0xFF78, 0xFF48, 0xFF58, \n    0xFAA0, 0xFAE0, 0xFA20, 0xFA60, 0xFBA0, 0xFBE0, 0xFB20, 0xFB60, 0xF8A0, 0xF8E0, 0xF820, 0xF860, 0xF9A0, 0xF9E0, 0xF920, 0xF960, \n    0xFD50, 0xFD70, 0xFD10, 0xFD30, 0xFDD0, 0xFDF0, 0xFD90, 0xFDB0, 0xFC50, 0xFC70, 0xFC10, 0xFC30, 0xFCD0, 0xFCF0, 0xFC90, 0xFCB0, \n    0x1580, 0x1480, 0x1780, 0x1680, 0x1180, 0x1080, 0x1380, 0x1280, 0x1D80, 0x1C80, 0x1F80, 0x1E80, 0x1980, 0x1880, 0x1B80, 0x1A80, \n    0x0AC0, 0x0A40, 0x0BC0, 0x0B40, 0x08C0, 0x0840, 0x09C0, 0x0940, 0x0EC0, 0x0E40, 0x0FC0, 0x0F40, 0x0CC0, 0x0C40, 0x0DC0, 0x0D40, \n    0x5600, 0x5200, 0x5E00, 0x5A00, 0x4600, 0x4200, 0x4E00, 0x4A00, 0x7600, 0x7200, 0x7E00, 0x7A00, 0x6600, 0x6200, 0x6E00, 0x6A00, \n    0x2B00, 0x2900, 0x2F00, 0x2D00, 0x2300, 0x2100, 0x2700, 0x2500, 0x3B00, 0x3900, 0x3F00, 0x3D00, 0x3300, 0x3100, 0x3700, 0x3500, \n    0x0158, 0x0148, 0x0178, 0x0168, 0x0118, 0x0108, 0x0138, 0x0128, 0x01D8, 0x01C8, 0x01F8, 0x01E8, 0x0198, 0x0188, 0x01B8, 0x01A8, \n    0x0058, 0x0048, 0x0078, 0x0068, 0x0018, 0x0008, 0x0038, 0x0028, 0x00D8, 0x00C8, 0x00F8, 0x00E8, 0x0098, 0x0088, 0x00B8, 0x00A8, \n    0x0560, 0x0520, 0x05E0, 0x05A0, 0x0460, 0x0420, 0x04E0, 0x04A0, 0x0760, 0x0720, 0x07E0, 0x07A0, 0x0660, 0x0620, 0x06E0, 0x06A0, \n    0x02B0, 0x0290, 0x02F0, 0x02D0, 0x0230, 0x0210, 0x0270, 0x0250, 0x03B0, 0x0390, 0x03F0, 0x03D0, 0x0330, 0x0310, 0x0370, 0x0350\n};\n\nstatic unsigned short g_drwavMulawTable[256] = {\n    0x8284, 0x8684, 0x8A84, 0x8E84, 0x9284, 0x9684, 0x9A84, 0x9E84, 0xA284, 0xA684, 0xAA84, 0xAE84, 0xB284, 0xB684, 0xBA84, 0xBE84, \n    0xC184, 0xC384, 0xC584, 0xC784, 0xC984, 0xCB84, 0xCD84, 0xCF84, 0xD184, 0xD384, 0xD584, 0xD784, 0xD984, 0xDB84, 0xDD84, 0xDF84, \n    0xE104, 0xE204, 0xE304, 0xE404, 0xE504, 0xE604, 0xE704, 0xE804, 0xE904, 0xEA04, 0xEB04, 0xEC04, 0xED04, 0xEE04, 0xEF04, 0xF004, \n    0xF0C4, 0xF144, 0xF1C4, 0xF244, 0xF2C4, 0xF344, 0xF3C4, 0xF444, 0xF4C4, 0xF544, 0xF5C4, 0xF644, 0xF6C4, 0xF744, 0xF7C4, 0xF844, \n    0xF8A4, 0xF8E4, 0xF924, 0xF964, 0xF9A4, 0xF9E4, 0xFA24, 0xFA64, 0xFAA4, 0xFAE4, 0xFB24, 0xFB64, 0xFBA4, 0xFBE4, 0xFC24, 0xFC64, \n    0xFC94, 0xFCB4, 0xFCD4, 0xFCF4, 0xFD14, 0xFD34, 0xFD54, 0xFD74, 0xFD94, 0xFDB4, 0xFDD4, 0xFDF4, 0xFE14, 0xFE34, 0xFE54, 0xFE74, \n    0xFE8C, 0xFE9C, 0xFEAC, 0xFEBC, 0xFECC, 0xFEDC, 0xFEEC, 0xFEFC, 0xFF0C, 0xFF1C, 0xFF2C, 0xFF3C, 0xFF4C, 0xFF5C, 0xFF6C, 0xFF7C, \n    0xFF88, 0xFF90, 0xFF98, 0xFFA0, 0xFFA8, 0xFFB0, 0xFFB8, 0xFFC0, 0xFFC8, 0xFFD0, 0xFFD8, 0xFFE0, 0xFFE8, 0xFFF0, 0xFFF8, 0x0000, \n    0x7D7C, 0x797C, 0x757C, 0x717C, 0x6D7C, 0x697C, 0x657C, 0x617C, 0x5D7C, 0x597C, 0x557C, 0x517C, 0x4D7C, 0x497C, 0x457C, 0x417C, \n    0x3E7C, 0x3C7C, 0x3A7C, 0x387C, 0x367C, 0x347C, 0x327C, 0x307C, 0x2E7C, 0x2C7C, 0x2A7C, 0x287C, 0x267C, 0x247C, 0x227C, 0x207C, \n    0x1EFC, 0x1DFC, 0x1CFC, 0x1BFC, 0x1AFC, 0x19FC, 0x18FC, 0x17FC, 0x16FC, 0x15FC, 0x14FC, 0x13FC, 0x12FC, 0x11FC, 0x10FC, 0x0FFC, \n    0x0F3C, 0x0EBC, 0x0E3C, 0x0DBC, 0x0D3C, 0x0CBC, 0x0C3C, 0x0BBC, 0x0B3C, 0x0ABC, 0x0A3C, 0x09BC, 0x093C, 0x08BC, 0x083C, 0x07BC, \n    0x075C, 0x071C, 0x06DC, 0x069C, 0x065C, 0x061C, 0x05DC, 0x059C, 0x055C, 0x051C, 0x04DC, 0x049C, 0x045C, 0x041C, 0x03DC, 0x039C, \n    0x036C, 0x034C, 0x032C, 0x030C, 0x02EC, 0x02CC, 0x02AC, 0x028C, 0x026C, 0x024C, 0x022C, 0x020C, 0x01EC, 0x01CC, 0x01AC, 0x018C, \n    0x0174, 0x0164, 0x0154, 0x0144, 0x0134, 0x0124, 0x0114, 0x0104, 0x00F4, 0x00E4, 0x00D4, 0x00C4, 0x00B4, 0x00A4, 0x0094, 0x0084, \n    0x0078, 0x0070, 0x0068, 0x0060, 0x0058, 0x0050, 0x0048, 0x0040, 0x0038, 0x0030, 0x0028, 0x0020, 0x0018, 0x0010, 0x0008, 0x0000\n};\n\nstatic DRWAV_INLINE drwav_int16 drwav__alaw_to_s16(drwav_uint8 sampleIn)\n{\n    return (short)g_drwavAlawTable[sampleIn];\n}\n\nstatic DRWAV_INLINE drwav_int16 drwav__mulaw_to_s16(drwav_uint8 sampleIn)\n{\n    return (short)g_drwavMulawTable[sampleIn];\n}\n\n\n\nstatic void drwav__pcm_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample)\n{\n    unsigned int i;\n\n    /* Special case for 8-bit sample data because it's treated as unsigned. */\n    if (bytesPerSample == 1) {\n        drwav_u8_to_s16(pOut, pIn, totalSampleCount);\n        return;\n    }\n\n\n    /* Slightly more optimal implementation for common formats. */\n    if (bytesPerSample == 2) {\n        for (i = 0; i < totalSampleCount; ++i) {\n           *pOut++ = ((const drwav_int16*)pIn)[i];\n        }\n        return;\n    }\n    if (bytesPerSample == 3) {\n        drwav_s24_to_s16(pOut, pIn, totalSampleCount);\n        return;\n    }\n    if (bytesPerSample == 4) {\n        drwav_s32_to_s16(pOut, (const drwav_int32*)pIn, totalSampleCount);\n        return;\n    }\n\n\n    /* Anything more than 64 bits per sample is not supported. */\n    if (bytesPerSample > 8) {\n        DRWAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut));\n        return;\n    }\n\n\n    /* Generic, slow converter. */\n    for (i = 0; i < totalSampleCount; ++i) {\n        drwav_uint64 sample = 0;\n        unsigned int shift  = (8 - bytesPerSample) * 8;\n\n        unsigned int j;\n        for (j = 0; j < bytesPerSample; j += 1) {\n            DRWAV_ASSERT(j < 8);\n            sample |= (drwav_uint64)(pIn[j]) << shift;\n            shift  += 8;\n        }\n\n        pIn += j;\n        *pOut++ = (drwav_int16)((drwav_int64)sample >> 48);\n    }\n}\n\nstatic void drwav__ieee_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample)\n{\n    if (bytesPerSample == 4) {\n        drwav_f32_to_s16(pOut, (const float*)pIn, totalSampleCount);\n        return;\n    } else if (bytesPerSample == 8) {\n        drwav_f64_to_s16(pOut, (const double*)pIn, totalSampleCount);\n        return;\n    } else {\n        /* Only supporting 32- and 64-bit float. Output silence in all other cases. Contributions welcome for 16-bit float. */\n        DRWAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut));\n        return;\n    }\n}\n\nstatic drwav_uint64 drwav_read_pcm_frames_s16__pcm(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut)\n{\n    drwav_uint32 bytesPerFrame;\n    drwav_uint64 totalFramesRead;\n    drwav_uint8 sampleData[4096];\n\n    /* Fast path. */\n    if ((pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM && pWav->bitsPerSample == 16) || pBufferOut == NULL) {\n        return drwav_read_pcm_frames(pWav, framesToRead, pBufferOut);\n    }\n    \n    bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);\n    if (bytesPerFrame == 0) {\n        return 0;\n    }\n\n    totalFramesRead = 0;\n    \n    while (framesToRead > 0) {\n        drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData);\n        if (framesRead == 0) {\n            break;\n        }\n\n        drwav__pcm_to_s16(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels), bytesPerFrame/pWav->channels);\n\n        pBufferOut      += framesRead*pWav->channels;\n        framesToRead    -= framesRead;\n        totalFramesRead += framesRead;\n    }\n\n    return totalFramesRead;\n}\n\nstatic drwav_uint64 drwav_read_pcm_frames_s16__ieee(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut)\n{\n    drwav_uint64 totalFramesRead;\n    drwav_uint8 sampleData[4096];\n    drwav_uint32 bytesPerFrame;\n\n    if (pBufferOut == NULL) {\n        return drwav_read_pcm_frames(pWav, framesToRead, NULL);\n    }\n\n    bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);\n    if (bytesPerFrame == 0) {\n        return 0;\n    }\n\n    totalFramesRead = 0;\n    \n    while (framesToRead > 0) {\n        drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData);\n        if (framesRead == 0) {\n            break;\n        }\n\n        drwav__ieee_to_s16(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels), bytesPerFrame/pWav->channels);\n\n        pBufferOut      += framesRead*pWav->channels;\n        framesToRead    -= framesRead;\n        totalFramesRead += framesRead;\n    }\n\n    return totalFramesRead;\n}\n\nstatic drwav_uint64 drwav_read_pcm_frames_s16__alaw(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut)\n{\n    drwav_uint64 totalFramesRead;\n    drwav_uint8 sampleData[4096];\n    drwav_uint32 bytesPerFrame;\n\n    if (pBufferOut == NULL) {\n        return drwav_read_pcm_frames(pWav, framesToRead, NULL);\n    }\n\n    bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);\n    if (bytesPerFrame == 0) {\n        return 0;\n    }\n\n    totalFramesRead = 0;\n    \n    while (framesToRead > 0) {\n        drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData);\n        if (framesRead == 0) {\n            break;\n        }\n\n        drwav_alaw_to_s16(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels));\n\n        pBufferOut      += framesRead*pWav->channels;\n        framesToRead    -= framesRead;\n        totalFramesRead += framesRead;\n    }\n\n    return totalFramesRead;\n}\n\nstatic drwav_uint64 drwav_read_pcm_frames_s16__mulaw(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut)\n{\n    drwav_uint64 totalFramesRead;\n    drwav_uint8 sampleData[4096];\n    drwav_uint32 bytesPerFrame;\n\n    if (pBufferOut == NULL) {\n        return drwav_read_pcm_frames(pWav, framesToRead, NULL);\n    }\n\n    bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);\n    if (bytesPerFrame == 0) {\n        return 0;\n    }\n\n    totalFramesRead = 0;\n\n    while (framesToRead > 0) {\n        drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData);\n        if (framesRead == 0) {\n            break;\n        }\n\n        drwav_mulaw_to_s16(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels));\n\n        pBufferOut      += framesRead*pWav->channels;\n        framesToRead    -= framesRead;\n        totalFramesRead += framesRead;\n    }\n\n    return totalFramesRead;\n}\n\nDRWAV_API drwav_uint64 drwav_read_pcm_frames_s16(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut)\n{\n    if (pWav == NULL || framesToRead == 0) {\n        return 0;\n    }\n\n    if (pBufferOut == NULL) {\n        return drwav_read_pcm_frames(pWav, framesToRead, NULL);\n    }\n\n    /* Don't try to read more samples than can potentially fit in the output buffer. */\n    if (framesToRead * pWav->channels * sizeof(drwav_int16) > DRWAV_SIZE_MAX) {\n        framesToRead = DRWAV_SIZE_MAX / sizeof(drwav_int16) / pWav->channels;\n    }\n\n    if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM) {\n        return drwav_read_pcm_frames_s16__pcm(pWav, framesToRead, pBufferOut);\n    }\n\n    if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT) {\n        return drwav_read_pcm_frames_s16__ieee(pWav, framesToRead, pBufferOut);\n    }\n\n    if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ALAW) {\n        return drwav_read_pcm_frames_s16__alaw(pWav, framesToRead, pBufferOut);\n    }\n\n    if (pWav->translatedFormatTag == DR_WAVE_FORMAT_MULAW) {\n        return drwav_read_pcm_frames_s16__mulaw(pWav, framesToRead, pBufferOut);\n    }\n\n    if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) {\n        return drwav_read_pcm_frames_s16__msadpcm(pWav, framesToRead, pBufferOut);\n    }\n\n    if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {\n        return drwav_read_pcm_frames_s16__ima(pWav, framesToRead, pBufferOut);\n    }\n\n    return 0;\n}\n\nDRWAV_API drwav_uint64 drwav_read_pcm_frames_s16le(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut)\n{\n    drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, framesToRead, pBufferOut);\n    if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_FALSE) {\n        drwav__bswap_samples_s16(pBufferOut, framesRead*pWav->channels);\n    }\n\n    return framesRead;\n}\n\nDRWAV_API drwav_uint64 drwav_read_pcm_frames_s16be(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut)\n{\n    drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, framesToRead, pBufferOut);\n    if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_TRUE) {\n        drwav__bswap_samples_s16(pBufferOut, framesRead*pWav->channels);\n    }\n\n    return framesRead;\n}\n\n\nDRWAV_API void drwav_u8_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount)\n{\n    int r;\n    size_t i;\n    for (i = 0; i < sampleCount; ++i) {\n        int x = pIn[i];\n        r = x << 8;\n        r = r - 32768;\n        pOut[i] = (short)r;\n    }\n}\n\nDRWAV_API void drwav_s24_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount)\n{\n    int r;\n    size_t i;\n    for (i = 0; i < sampleCount; ++i) {\n        int x = ((int)(((unsigned int)(((const drwav_uint8*)pIn)[i*3+0]) << 8) | ((unsigned int)(((const drwav_uint8*)pIn)[i*3+1]) << 16) | ((unsigned int)(((const drwav_uint8*)pIn)[i*3+2])) << 24)) >> 8;\n        r = x >> 8;\n        pOut[i] = (short)r;\n    }\n}\n\nDRWAV_API void drwav_s32_to_s16(drwav_int16* pOut, const drwav_int32* pIn, size_t sampleCount)\n{\n    int r;\n    size_t i;\n    for (i = 0; i < sampleCount; ++i) {\n        int x = pIn[i];\n        r = x >> 16;\n        pOut[i] = (short)r;\n    }\n}\n\nDRWAV_API void drwav_f32_to_s16(drwav_int16* pOut, const float* pIn, size_t sampleCount)\n{\n    int r;\n    size_t i;\n    for (i = 0; i < sampleCount; ++i) {\n        float x = pIn[i];\n        float c;\n        c = ((x < -1) ? -1 : ((x > 1) ? 1 : x));\n        c = c + 1;\n        r = (int)(c * 32767.5f);\n        r = r - 32768;\n        pOut[i] = (short)r;\n    }\n}\n\nDRWAV_API void drwav_f64_to_s16(drwav_int16* pOut, const double* pIn, size_t sampleCount)\n{\n    int r;\n    size_t i;\n    for (i = 0; i < sampleCount; ++i) {\n        double x = pIn[i];\n        double c;\n        c = ((x < -1) ? -1 : ((x > 1) ? 1 : x));\n        c = c + 1;\n        r = (int)(c * 32767.5);\n        r = r - 32768;\n        pOut[i] = (short)r;\n    }\n}\n\nDRWAV_API void drwav_alaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount)\n{\n    size_t i;\n    for (i = 0; i < sampleCount; ++i) {\n        pOut[i] = drwav__alaw_to_s16(pIn[i]);\n    }\n}\n\nDRWAV_API void drwav_mulaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount)\n{\n    size_t i;\n    for (i = 0; i < sampleCount; ++i) {\n        pOut[i] = drwav__mulaw_to_s16(pIn[i]);\n    }\n}\n\n\n\nstatic void drwav__pcm_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount, unsigned int bytesPerSample)\n{\n    unsigned int i;\n\n    /* Special case for 8-bit sample data because it's treated as unsigned. */\n    if (bytesPerSample == 1) {\n        drwav_u8_to_f32(pOut, pIn, sampleCount);\n        return;\n    }\n\n    /* Slightly more optimal implementation for common formats. */\n    if (bytesPerSample == 2) {\n        drwav_s16_to_f32(pOut, (const drwav_int16*)pIn, sampleCount);\n        return;\n    }\n    if (bytesPerSample == 3) {\n        drwav_s24_to_f32(pOut, pIn, sampleCount);\n        return;\n    }\n    if (bytesPerSample == 4) {\n        drwav_s32_to_f32(pOut, (const drwav_int32*)pIn, sampleCount);\n        return;\n    }\n\n\n    /* Anything more than 64 bits per sample is not supported. */\n    if (bytesPerSample > 8) {\n        DRWAV_ZERO_MEMORY(pOut, sampleCount * sizeof(*pOut));\n        return;\n    }\n\n\n    /* Generic, slow converter. */\n    for (i = 0; i < sampleCount; ++i) {\n        drwav_uint64 sample = 0;\n        unsigned int shift  = (8 - bytesPerSample) * 8;\n\n        unsigned int j;\n        for (j = 0; j < bytesPerSample; j += 1) {\n            DRWAV_ASSERT(j < 8);\n            sample |= (drwav_uint64)(pIn[j]) << shift;\n            shift  += 8;\n        }\n\n        pIn += j;\n        *pOut++ = (float)((drwav_int64)sample / 9223372036854775807.0);\n    }\n}\n\nstatic void drwav__ieee_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount, unsigned int bytesPerSample)\n{\n    if (bytesPerSample == 4) {\n        unsigned int i;\n        for (i = 0; i < sampleCount; ++i) {\n            *pOut++ = ((const float*)pIn)[i];\n        }\n        return;\n    } else if (bytesPerSample == 8) {\n        drwav_f64_to_f32(pOut, (const double*)pIn, sampleCount);\n        return;\n    } else {\n        /* Only supporting 32- and 64-bit float. Output silence in all other cases. Contributions welcome for 16-bit float. */\n        DRWAV_ZERO_MEMORY(pOut, sampleCount * sizeof(*pOut));\n        return;\n    }\n}\n\n\nstatic drwav_uint64 drwav_read_pcm_frames_f32__pcm(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut)\n{\n    drwav_uint64 totalFramesRead;\n    drwav_uint8 sampleData[4096];\n\n    drwav_uint32 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);\n    if (bytesPerFrame == 0) {\n        return 0;\n    }\n\n    totalFramesRead = 0;\n\n    while (framesToRead > 0) {\n        drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData);\n        if (framesRead == 0) {\n            break;\n        }\n\n        drwav__pcm_to_f32(pBufferOut, sampleData, (size_t)framesRead*pWav->channels, bytesPerFrame/pWav->channels);\n\n        pBufferOut      += framesRead*pWav->channels;\n        framesToRead    -= framesRead;\n        totalFramesRead += framesRead;\n    }\n\n    return totalFramesRead;\n}\n\nstatic drwav_uint64 drwav_read_pcm_frames_f32__msadpcm(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut)\n{\n    /*\n    We're just going to borrow the implementation from the drwav_read_s16() since ADPCM is a little bit more complicated than other formats and I don't\n    want to duplicate that code.\n    */\n    drwav_uint64 totalFramesRead = 0;\n    drwav_int16 samples16[2048];\n    while (framesToRead > 0) {\n        drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, drwav_min(framesToRead, drwav_countof(samples16)/pWav->channels), samples16);\n        if (framesRead == 0) {\n            break;\n        }\n\n        drwav_s16_to_f32(pBufferOut, samples16, (size_t)(framesRead*pWav->channels));   /* <-- Safe cast because we're clamping to 2048. */\n\n        pBufferOut      += framesRead*pWav->channels;\n        framesToRead    -= framesRead;\n        totalFramesRead += framesRead;\n    }\n\n    return totalFramesRead;\n}\n\nstatic drwav_uint64 drwav_read_pcm_frames_f32__ima(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut)\n{\n    /*\n    We're just going to borrow the implementation from the drwav_read_s16() since IMA-ADPCM is a little bit more complicated than other formats and I don't\n    want to duplicate that code.\n    */\n    drwav_uint64 totalFramesRead = 0;\n    drwav_int16 samples16[2048];\n    while (framesToRead > 0) {\n        drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, drwav_min(framesToRead, drwav_countof(samples16)/pWav->channels), samples16);\n        if (framesRead == 0) {\n            break;\n        }\n\n        drwav_s16_to_f32(pBufferOut, samples16, (size_t)(framesRead*pWav->channels));   /* <-- Safe cast because we're clamping to 2048. */\n\n        pBufferOut      += framesRead*pWav->channels;\n        framesToRead    -= framesRead;\n        totalFramesRead += framesRead;\n    }\n\n    return totalFramesRead;\n}\n\nstatic drwav_uint64 drwav_read_pcm_frames_f32__ieee(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut)\n{\n    drwav_uint64 totalFramesRead;\n    drwav_uint8 sampleData[4096];\n    drwav_uint32 bytesPerFrame;\n\n    /* Fast path. */\n    if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT && pWav->bitsPerSample == 32) {\n        return drwav_read_pcm_frames(pWav, framesToRead, pBufferOut);\n    }\n    \n    bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);\n    if (bytesPerFrame == 0) {\n        return 0;\n    }\n\n    totalFramesRead = 0;\n\n    while (framesToRead > 0) {\n        drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData);\n        if (framesRead == 0) {\n            break;\n        }\n\n        drwav__ieee_to_f32(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels), bytesPerFrame/pWav->channels);\n\n        pBufferOut      += framesRead*pWav->channels;\n        framesToRead    -= framesRead;\n        totalFramesRead += framesRead;\n    }\n\n    return totalFramesRead;\n}\n\nstatic drwav_uint64 drwav_read_pcm_frames_f32__alaw(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut)\n{\n    drwav_uint64 totalFramesRead;\n    drwav_uint8 sampleData[4096];\n    drwav_uint32 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);\n    if (bytesPerFrame == 0) {\n        return 0;\n    }\n\n    totalFramesRead = 0;\n\n    while (framesToRead > 0) {\n        drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData);\n        if (framesRead == 0) {\n            break;\n        }\n\n        drwav_alaw_to_f32(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels));\n\n        pBufferOut      += framesRead*pWav->channels;\n        framesToRead    -= framesRead;\n        totalFramesRead += framesRead;\n    }\n\n    return totalFramesRead;\n}\n\nstatic drwav_uint64 drwav_read_pcm_frames_f32__mulaw(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut)\n{\n    drwav_uint64 totalFramesRead;\n    drwav_uint8 sampleData[4096];\n\n    drwav_uint32 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);\n    if (bytesPerFrame == 0) {\n        return 0;\n    }\n\n    totalFramesRead = 0;\n\n    while (framesToRead > 0) {\n        drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData);\n        if (framesRead == 0) {\n            break;\n        }\n\n        drwav_mulaw_to_f32(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels));\n\n        pBufferOut      += framesRead*pWav->channels;\n        framesToRead    -= framesRead;\n        totalFramesRead += framesRead;\n    }\n\n    return totalFramesRead;\n}\n\nDRWAV_API drwav_uint64 drwav_read_pcm_frames_f32(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut)\n{\n    if (pWav == NULL || framesToRead == 0) {\n        return 0;\n    }\n\n    if (pBufferOut == NULL) {\n        return drwav_read_pcm_frames(pWav, framesToRead, NULL);\n    }\n\n    /* Don't try to read more samples than can potentially fit in the output buffer. */\n    if (framesToRead * pWav->channels * sizeof(float) > DRWAV_SIZE_MAX) {\n        framesToRead = DRWAV_SIZE_MAX / sizeof(float) / pWav->channels;\n    }\n\n    if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM) {\n        return drwav_read_pcm_frames_f32__pcm(pWav, framesToRead, pBufferOut);\n    }\n\n    if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) {\n        return drwav_read_pcm_frames_f32__msadpcm(pWav, framesToRead, pBufferOut);\n    }\n\n    if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT) {\n        return drwav_read_pcm_frames_f32__ieee(pWav, framesToRead, pBufferOut);\n    }\n\n    if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ALAW) {\n        return drwav_read_pcm_frames_f32__alaw(pWav, framesToRead, pBufferOut);\n    }\n\n    if (pWav->translatedFormatTag == DR_WAVE_FORMAT_MULAW) {\n        return drwav_read_pcm_frames_f32__mulaw(pWav, framesToRead, pBufferOut);\n    }\n\n    if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {\n        return drwav_read_pcm_frames_f32__ima(pWav, framesToRead, pBufferOut);\n    }\n\n    return 0;\n}\n\nDRWAV_API drwav_uint64 drwav_read_pcm_frames_f32le(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut)\n{\n    drwav_uint64 framesRead = drwav_read_pcm_frames_f32(pWav, framesToRead, pBufferOut);\n    if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_FALSE) {\n        drwav__bswap_samples_f32(pBufferOut, framesRead*pWav->channels);\n    }\n\n    return framesRead;\n}\n\nDRWAV_API drwav_uint64 drwav_read_pcm_frames_f32be(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut)\n{\n    drwav_uint64 framesRead = drwav_read_pcm_frames_f32(pWav, framesToRead, pBufferOut);\n    if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_TRUE) {\n        drwav__bswap_samples_f32(pBufferOut, framesRead*pWav->channels);\n    }\n\n    return framesRead;\n}\n\n\nDRWAV_API void drwav_u8_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount)\n{\n    size_t i;\n\n    if (pOut == NULL || pIn == NULL) {\n        return;\n    }\n\n#ifdef DR_WAV_LIBSNDFILE_COMPAT\n    /*\n    It appears libsndfile uses slightly different logic for the u8 -> f32 conversion to dr_wav, which in my opinion is incorrect. It appears\n    libsndfile performs the conversion something like \"f32 = (u8 / 256) * 2 - 1\", however I think it should be \"f32 = (u8 / 255) * 2 - 1\" (note\n    the divisor of 256 vs 255). I use libsndfile as a benchmark for testing, so I'm therefore leaving this block here just for my automated\n    correctness testing. This is disabled by default.\n    */\n    for (i = 0; i < sampleCount; ++i) {\n        *pOut++ = (pIn[i] / 256.0f) * 2 - 1;\n    }\n#else\n    for (i = 0; i < sampleCount; ++i) {\n        float x = pIn[i];\n        x = x * 0.00784313725490196078f;    /* 0..255 to 0..2 */\n        x = x - 1;                          /* 0..2 to -1..1 */\n\n        *pOut++ = x;\n    }\n#endif\n}\n\nDRWAV_API void drwav_s16_to_f32(float* pOut, const drwav_int16* pIn, size_t sampleCount)\n{\n    size_t i;\n\n    if (pOut == NULL || pIn == NULL) {\n        return;\n    }\n\n    for (i = 0; i < sampleCount; ++i) {\n        *pOut++ = pIn[i] * 0.000030517578125f;\n    }\n}\n\nDRWAV_API void drwav_s24_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount)\n{\n    size_t i;\n\n    if (pOut == NULL || pIn == NULL) {\n        return;\n    }\n\n    for (i = 0; i < sampleCount; ++i) {\n        double x;\n        drwav_uint32 a = ((drwav_uint32)(pIn[i*3+0]) <<  8);\n        drwav_uint32 b = ((drwav_uint32)(pIn[i*3+1]) << 16);\n        drwav_uint32 c = ((drwav_uint32)(pIn[i*3+2]) << 24);\n\n        x = (double)((drwav_int32)(a | b | c) >> 8);\n        *pOut++ = (float)(x * 0.00000011920928955078125);\n    }\n}\n\nDRWAV_API void drwav_s32_to_f32(float* pOut, const drwav_int32* pIn, size_t sampleCount)\n{\n    size_t i;\n    if (pOut == NULL || pIn == NULL) {\n        return;\n    }\n\n    for (i = 0; i < sampleCount; ++i) {\n        *pOut++ = (float)(pIn[i] / 2147483648.0);\n    }\n}\n\nDRWAV_API void drwav_f64_to_f32(float* pOut, const double* pIn, size_t sampleCount)\n{\n    size_t i;\n\n    if (pOut == NULL || pIn == NULL) {\n        return;\n    }\n\n    for (i = 0; i < sampleCount; ++i) {\n        *pOut++ = (float)pIn[i];\n    }\n}\n\nDRWAV_API void drwav_alaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount)\n{\n    size_t i;\n\n    if (pOut == NULL || pIn == NULL) {\n        return;\n    }\n\n    for (i = 0; i < sampleCount; ++i) {\n        *pOut++ = drwav__alaw_to_s16(pIn[i]) / 32768.0f;\n    }\n}\n\nDRWAV_API void drwav_mulaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount)\n{\n    size_t i;\n\n    if (pOut == NULL || pIn == NULL) {\n        return;\n    }\n\n    for (i = 0; i < sampleCount; ++i) {\n        *pOut++ = drwav__mulaw_to_s16(pIn[i]) / 32768.0f;\n    }\n}\n\n\n\nstatic void drwav__pcm_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample)\n{\n    unsigned int i;\n\n    /* Special case for 8-bit sample data because it's treated as unsigned. */\n    if (bytesPerSample == 1) {\n        drwav_u8_to_s32(pOut, pIn, totalSampleCount);\n        return;\n    }\n\n    /* Slightly more optimal implementation for common formats. */\n    if (bytesPerSample == 2) {\n        drwav_s16_to_s32(pOut, (const drwav_int16*)pIn, totalSampleCount);\n        return;\n    }\n    if (bytesPerSample == 3) {\n        drwav_s24_to_s32(pOut, pIn, totalSampleCount);\n        return;\n    }\n    if (bytesPerSample == 4) {\n        for (i = 0; i < totalSampleCount; ++i) {\n           *pOut++ = ((const drwav_int32*)pIn)[i];\n        }\n        return;\n    }\n\n\n    /* Anything more than 64 bits per sample is not supported. */\n    if (bytesPerSample > 8) {\n        DRWAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut));\n        return;\n    }\n\n\n    /* Generic, slow converter. */\n    for (i = 0; i < totalSampleCount; ++i) {\n        drwav_uint64 sample = 0;\n        unsigned int shift  = (8 - bytesPerSample) * 8;\n\n        unsigned int j;\n        for (j = 0; j < bytesPerSample; j += 1) {\n            DRWAV_ASSERT(j < 8);\n            sample |= (drwav_uint64)(pIn[j]) << shift;\n            shift  += 8;\n        }\n\n        pIn += j;\n        *pOut++ = (drwav_int32)((drwav_int64)sample >> 32);\n    }\n}\n\nstatic void drwav__ieee_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample)\n{\n    if (bytesPerSample == 4) {\n        drwav_f32_to_s32(pOut, (const float*)pIn, totalSampleCount);\n        return;\n    } else if (bytesPerSample == 8) {\n        drwav_f64_to_s32(pOut, (const double*)pIn, totalSampleCount);\n        return;\n    } else {\n        /* Only supporting 32- and 64-bit float. Output silence in all other cases. Contributions welcome for 16-bit float. */\n        DRWAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut));\n        return;\n    }\n}\n\n\nstatic drwav_uint64 drwav_read_pcm_frames_s32__pcm(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut)\n{\n    drwav_uint64 totalFramesRead;\n    drwav_uint8 sampleData[4096];\n    drwav_uint32 bytesPerFrame;\n\n    /* Fast path. */\n    if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM && pWav->bitsPerSample == 32) {\n        return drwav_read_pcm_frames(pWav, framesToRead, pBufferOut);\n    }\n    \n    bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);\n    if (bytesPerFrame == 0) {\n        return 0;\n    }\n\n    totalFramesRead = 0;\n\n    while (framesToRead > 0) {\n        drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData);\n        if (framesRead == 0) {\n            break;\n        }\n\n        drwav__pcm_to_s32(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels), bytesPerFrame/pWav->channels);\n\n        pBufferOut      += framesRead*pWav->channels;\n        framesToRead    -= framesRead;\n        totalFramesRead += framesRead;\n    }\n\n    return totalFramesRead;\n}\n\nstatic drwav_uint64 drwav_read_pcm_frames_s32__msadpcm(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut)\n{\n    /*\n    We're just going to borrow the implementation from the drwav_read_s16() since ADPCM is a little bit more complicated than other formats and I don't\n    want to duplicate that code.\n    */\n    drwav_uint64 totalFramesRead = 0;\n    drwav_int16 samples16[2048];\n    while (framesToRead > 0) {\n        drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, drwav_min(framesToRead, drwav_countof(samples16)/pWav->channels), samples16);\n        if (framesRead == 0) {\n            break;\n        }\n\n        drwav_s16_to_s32(pBufferOut, samples16, (size_t)(framesRead*pWav->channels));   /* <-- Safe cast because we're clamping to 2048. */\n\n        pBufferOut      += framesRead*pWav->channels;\n        framesToRead    -= framesRead;\n        totalFramesRead += framesRead;\n    }\n\n    return totalFramesRead;\n}\n\nstatic drwav_uint64 drwav_read_pcm_frames_s32__ima(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut)\n{\n    /*\n    We're just going to borrow the implementation from the drwav_read_s16() since IMA-ADPCM is a little bit more complicated than other formats and I don't\n    want to duplicate that code.\n    */\n    drwav_uint64 totalFramesRead = 0;\n    drwav_int16 samples16[2048];\n    while (framesToRead > 0) {\n        drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, drwav_min(framesToRead, drwav_countof(samples16)/pWav->channels), samples16);\n        if (framesRead == 0) {\n            break;\n        }\n\n        drwav_s16_to_s32(pBufferOut, samples16, (size_t)(framesRead*pWav->channels));   /* <-- Safe cast because we're clamping to 2048. */\n\n        pBufferOut      += framesRead*pWav->channels;\n        framesToRead    -= framesRead;\n        totalFramesRead += framesRead;\n    }\n\n    return totalFramesRead;\n}\n\nstatic drwav_uint64 drwav_read_pcm_frames_s32__ieee(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut)\n{\n    drwav_uint64 totalFramesRead;\n    drwav_uint8 sampleData[4096];\n\n    drwav_uint32 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);\n    if (bytesPerFrame == 0) {\n        return 0;\n    }\n\n    totalFramesRead = 0;\n\n    while (framesToRead > 0) {\n        drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData);\n        if (framesRead == 0) {\n            break;\n        }\n\n        drwav__ieee_to_s32(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels), bytesPerFrame/pWav->channels);\n\n        pBufferOut      += framesRead*pWav->channels;\n        framesToRead    -= framesRead;\n        totalFramesRead += framesRead;\n    }\n\n    return totalFramesRead;\n}\n\nstatic drwav_uint64 drwav_read_pcm_frames_s32__alaw(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut)\n{\n    drwav_uint64 totalFramesRead;\n    drwav_uint8 sampleData[4096];\n\n    drwav_uint32 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);\n    if (bytesPerFrame == 0) {\n        return 0;\n    }\n\n    totalFramesRead = 0;\n\n    while (framesToRead > 0) {\n        drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData);\n        if (framesRead == 0) {\n            break;\n        }\n\n        drwav_alaw_to_s32(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels));\n\n        pBufferOut      += framesRead*pWav->channels;\n        framesToRead    -= framesRead;\n        totalFramesRead += framesRead;\n    }\n\n    return totalFramesRead;\n}\n\nstatic drwav_uint64 drwav_read_pcm_frames_s32__mulaw(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut)\n{\n    drwav_uint64 totalFramesRead;\n    drwav_uint8 sampleData[4096];\n\n    drwav_uint32 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);\n    if (bytesPerFrame == 0) {\n        return 0;\n    }\n\n    totalFramesRead = 0;\n\n    while (framesToRead > 0) {\n        drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData);\n        if (framesRead == 0) {\n            break;\n        }\n\n        drwav_mulaw_to_s32(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels));\n\n        pBufferOut      += framesRead*pWav->channels;\n        framesToRead    -= framesRead;\n        totalFramesRead += framesRead;\n    }\n\n    return totalFramesRead;\n}\n\nDRWAV_API drwav_uint64 drwav_read_pcm_frames_s32(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut)\n{\n    if (pWav == NULL || framesToRead == 0) {\n        return 0;\n    }\n\n    if (pBufferOut == NULL) {\n        return drwav_read_pcm_frames(pWav, framesToRead, NULL);\n    }\n\n    /* Don't try to read more samples than can potentially fit in the output buffer. */\n    if (framesToRead * pWav->channels * sizeof(drwav_int32) > DRWAV_SIZE_MAX) {\n        framesToRead = DRWAV_SIZE_MAX / sizeof(drwav_int32) / pWav->channels;\n    }\n\n    if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM) {\n        return drwav_read_pcm_frames_s32__pcm(pWav, framesToRead, pBufferOut);\n    }\n\n    if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) {\n        return drwav_read_pcm_frames_s32__msadpcm(pWav, framesToRead, pBufferOut);\n    }\n\n    if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT) {\n        return drwav_read_pcm_frames_s32__ieee(pWav, framesToRead, pBufferOut);\n    }\n\n    if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ALAW) {\n        return drwav_read_pcm_frames_s32__alaw(pWav, framesToRead, pBufferOut);\n    }\n\n    if (pWav->translatedFormatTag == DR_WAVE_FORMAT_MULAW) {\n        return drwav_read_pcm_frames_s32__mulaw(pWav, framesToRead, pBufferOut);\n    }\n\n    if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {\n        return drwav_read_pcm_frames_s32__ima(pWav, framesToRead, pBufferOut);\n    }\n\n    return 0;\n}\n\nDRWAV_API drwav_uint64 drwav_read_pcm_frames_s32le(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut)\n{\n    drwav_uint64 framesRead = drwav_read_pcm_frames_s32(pWav, framesToRead, pBufferOut);\n    if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_FALSE) {\n        drwav__bswap_samples_s32(pBufferOut, framesRead*pWav->channels);\n    }\n\n    return framesRead;\n}\n\nDRWAV_API drwav_uint64 drwav_read_pcm_frames_s32be(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut)\n{\n    drwav_uint64 framesRead = drwav_read_pcm_frames_s32(pWav, framesToRead, pBufferOut);\n    if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_TRUE) {\n        drwav__bswap_samples_s32(pBufferOut, framesRead*pWav->channels);\n    }\n\n    return framesRead;\n}\n\n\nDRWAV_API void drwav_u8_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount)\n{\n    size_t i;\n\n    if (pOut == NULL || pIn == NULL) {\n        return;\n    }\n\n    for (i = 0; i < sampleCount; ++i) {\n        *pOut++ = ((int)pIn[i] - 128) << 24;\n    }\n}\n\nDRWAV_API void drwav_s16_to_s32(drwav_int32* pOut, const drwav_int16* pIn, size_t sampleCount)\n{\n    size_t i;\n\n    if (pOut == NULL || pIn == NULL) {\n        return;\n    }\n\n    for (i = 0; i < sampleCount; ++i) {\n        *pOut++ = pIn[i] << 16;\n    }\n}\n\nDRWAV_API void drwav_s24_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount)\n{\n    size_t i;\n\n    if (pOut == NULL || pIn == NULL) {\n        return;\n    }\n\n    for (i = 0; i < sampleCount; ++i) {\n        unsigned int s0 = pIn[i*3 + 0];\n        unsigned int s1 = pIn[i*3 + 1];\n        unsigned int s2 = pIn[i*3 + 2];\n\n        drwav_int32 sample32 = (drwav_int32)((s0 << 8) | (s1 << 16) | (s2 << 24));\n        *pOut++ = sample32;\n    }\n}\n\nDRWAV_API void drwav_f32_to_s32(drwav_int32* pOut, const float* pIn, size_t sampleCount)\n{\n    size_t i;\n\n    if (pOut == NULL || pIn == NULL) {\n        return;\n    }\n\n    for (i = 0; i < sampleCount; ++i) {\n        *pOut++ = (drwav_int32)(2147483648.0 * pIn[i]);\n    }\n}\n\nDRWAV_API void drwav_f64_to_s32(drwav_int32* pOut, const double* pIn, size_t sampleCount)\n{\n    size_t i;\n\n    if (pOut == NULL || pIn == NULL) {\n        return;\n    }\n\n    for (i = 0; i < sampleCount; ++i) {\n        *pOut++ = (drwav_int32)(2147483648.0 * pIn[i]);\n    }\n}\n\nDRWAV_API void drwav_alaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount)\n{\n    size_t i;\n\n    if (pOut == NULL || pIn == NULL) {\n        return;\n    }\n\n    for (i = 0; i < sampleCount; ++i) {\n        *pOut++ = ((drwav_int32)drwav__alaw_to_s16(pIn[i])) << 16;\n    }\n}\n\nDRWAV_API void drwav_mulaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount)\n{\n    size_t i;\n\n    if (pOut == NULL || pIn == NULL) {\n        return;\n    }\n\n    for (i= 0; i < sampleCount; ++i) {\n        *pOut++ = ((drwav_int32)drwav__mulaw_to_s16(pIn[i])) << 16;\n    }\n}\n\n\n\nstatic drwav_int16* drwav__read_pcm_frames_and_close_s16(drwav* pWav, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalFrameCount)\n{\n    drwav_uint64 sampleDataSize;\n    drwav_int16* pSampleData;\n    drwav_uint64 framesRead;\n\n    DRWAV_ASSERT(pWav != NULL);\n\n    sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(drwav_int16);\n    if (sampleDataSize > DRWAV_SIZE_MAX) {\n        drwav_uninit(pWav);\n        return NULL;    /* File's too big. */\n    }\n\n    pSampleData = (drwav_int16*)drwav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks); /* <-- Safe cast due to the check above. */\n    if (pSampleData == NULL) {\n        drwav_uninit(pWav);\n        return NULL;    /* Failed to allocate memory. */\n    }\n\n    framesRead = drwav_read_pcm_frames_s16(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData);\n    if (framesRead != pWav->totalPCMFrameCount) {\n        drwav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks);\n        drwav_uninit(pWav);\n        return NULL;    /* There was an error reading the samples. */\n    }\n\n    drwav_uninit(pWav);\n\n    if (sampleRate) {\n        *sampleRate = pWav->sampleRate;\n    }\n    if (channels) {\n        *channels = pWav->channels;\n    }\n    if (totalFrameCount) {\n        *totalFrameCount = pWav->totalPCMFrameCount;\n    }\n\n    return pSampleData;\n}\n\nstatic float* drwav__read_pcm_frames_and_close_f32(drwav* pWav, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalFrameCount)\n{\n    drwav_uint64 sampleDataSize;\n    float* pSampleData;\n    drwav_uint64 framesRead;\n\n    DRWAV_ASSERT(pWav != NULL);\n\n    sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(float);\n    if (sampleDataSize > DRWAV_SIZE_MAX) {\n        drwav_uninit(pWav);\n        return NULL;    /* File's too big. */\n    }\n\n    pSampleData = (float*)drwav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks); /* <-- Safe cast due to the check above. */\n    if (pSampleData == NULL) {\n        drwav_uninit(pWav);\n        return NULL;    /* Failed to allocate memory. */\n    }\n\n    framesRead = drwav_read_pcm_frames_f32(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData);\n    if (framesRead != pWav->totalPCMFrameCount) {\n        drwav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks);\n        drwav_uninit(pWav);\n        return NULL;    /* There was an error reading the samples. */\n    }\n\n    drwav_uninit(pWav);\n\n    if (sampleRate) {\n        *sampleRate = pWav->sampleRate;\n    }\n    if (channels) {\n        *channels = pWav->channels;\n    }\n    if (totalFrameCount) {\n        *totalFrameCount = pWav->totalPCMFrameCount;\n    }\n\n    return pSampleData;\n}\n\nstatic drwav_int32* drwav__read_pcm_frames_and_close_s32(drwav* pWav, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalFrameCount)\n{\n    drwav_uint64 sampleDataSize;\n    drwav_int32* pSampleData;\n    drwav_uint64 framesRead;\n\n    DRWAV_ASSERT(pWav != NULL);\n\n    sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(drwav_int32);\n    if (sampleDataSize > DRWAV_SIZE_MAX) {\n        drwav_uninit(pWav);\n        return NULL;    /* File's too big. */\n    }\n\n    pSampleData = (drwav_int32*)drwav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks); /* <-- Safe cast due to the check above. */\n    if (pSampleData == NULL) {\n        drwav_uninit(pWav);\n        return NULL;    /* Failed to allocate memory. */\n    }\n\n    framesRead = drwav_read_pcm_frames_s32(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData);\n    if (framesRead != pWav->totalPCMFrameCount) {\n        drwav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks);\n        drwav_uninit(pWav);\n        return NULL;    /* There was an error reading the samples. */\n    }\n\n    drwav_uninit(pWav);\n\n    if (sampleRate) {\n        *sampleRate = pWav->sampleRate;\n    }\n    if (channels) {\n        *channels = pWav->channels;\n    }\n    if (totalFrameCount) {\n        *totalFrameCount = pWav->totalPCMFrameCount;\n    }\n\n    return pSampleData;\n}\n\n\n\nDRWAV_API drwav_int16* drwav_open_and_read_pcm_frames_s16(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)\n{\n    drwav wav;\n\n    if (channelsOut) {\n        *channelsOut = 0;\n    }\n    if (sampleRateOut) {\n        *sampleRateOut = 0;\n    }\n    if (totalFrameCountOut) {\n        *totalFrameCountOut = 0;\n    }\n\n    if (!drwav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) {\n        return NULL;\n    }\n\n    return drwav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut);\n}\n\nDRWAV_API float* drwav_open_and_read_pcm_frames_f32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)\n{\n    drwav wav;\n\n    if (channelsOut) {\n        *channelsOut = 0;\n    }\n    if (sampleRateOut) {\n        *sampleRateOut = 0;\n    }\n    if (totalFrameCountOut) {\n        *totalFrameCountOut = 0;\n    }\n\n    if (!drwav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) {\n        return NULL;\n    }\n\n    return drwav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);\n}\n\nDRWAV_API drwav_int32* drwav_open_and_read_pcm_frames_s32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)\n{\n    drwav wav;\n\n    if (channelsOut) {\n        *channelsOut = 0;\n    }\n    if (sampleRateOut) {\n        *sampleRateOut = 0;\n    }\n    if (totalFrameCountOut) {\n        *totalFrameCountOut = 0;\n    }\n\n    if (!drwav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) {\n        return NULL;\n    }\n\n    return drwav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);\n}\n\n#ifndef DR_WAV_NO_STDIO\nDRWAV_API drwav_int16* drwav_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)\n{\n    drwav wav;\n\n    if (channelsOut) {\n        *channelsOut = 0;\n    }\n    if (sampleRateOut) {\n        *sampleRateOut = 0;\n    }\n    if (totalFrameCountOut) {\n        *totalFrameCountOut = 0;\n    }\n\n    if (!drwav_init_file(&wav, filename, pAllocationCallbacks)) {\n        return NULL;\n    }\n\n    return drwav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut);\n}\n\nDRWAV_API float* drwav_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)\n{\n    drwav wav;\n\n    if (channelsOut) {\n        *channelsOut = 0;\n    }\n    if (sampleRateOut) {\n        *sampleRateOut = 0;\n    }\n    if (totalFrameCountOut) {\n        *totalFrameCountOut = 0;\n    }\n\n    if (!drwav_init_file(&wav, filename, pAllocationCallbacks)) {\n        return NULL;\n    }\n\n    return drwav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);\n}\n\nDRWAV_API drwav_int32* drwav_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)\n{\n    drwav wav;\n\n    if (channelsOut) {\n        *channelsOut = 0;\n    }\n    if (sampleRateOut) {\n        *sampleRateOut = 0;\n    }\n    if (totalFrameCountOut) {\n        *totalFrameCountOut = 0;\n    }\n\n    if (!drwav_init_file(&wav, filename, pAllocationCallbacks)) {\n        return NULL;\n    }\n\n    return drwav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);\n}\n\n\nDRWAV_API drwav_int16* drwav_open_file_and_read_pcm_frames_s16_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)\n{\n    drwav wav;\n\n    if (sampleRateOut) {\n        *sampleRateOut = 0;\n    }\n    if (channelsOut) {\n        *channelsOut = 0;\n    }\n    if (totalFrameCountOut) {\n        *totalFrameCountOut = 0;\n    }\n\n    if (!drwav_init_file_w(&wav, filename, pAllocationCallbacks)) {\n        return NULL;\n    }\n\n    return drwav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut);\n}\n\nDRWAV_API float* drwav_open_file_and_read_pcm_frames_f32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)\n{\n    drwav wav;\n\n    if (sampleRateOut) {\n        *sampleRateOut = 0;\n    }\n    if (channelsOut) {\n        *channelsOut = 0;\n    }\n    if (totalFrameCountOut) {\n        *totalFrameCountOut = 0;\n    }\n\n    if (!drwav_init_file_w(&wav, filename, pAllocationCallbacks)) {\n        return NULL;\n    }\n\n    return drwav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);\n}\n\nDRWAV_API drwav_int32* drwav_open_file_and_read_pcm_frames_s32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)\n{\n    drwav wav;\n\n    if (sampleRateOut) {\n        *sampleRateOut = 0;\n    }\n    if (channelsOut) {\n        *channelsOut = 0;\n    }\n    if (totalFrameCountOut) {\n        *totalFrameCountOut = 0;\n    }\n\n    if (!drwav_init_file_w(&wav, filename, pAllocationCallbacks)) {\n        return NULL;\n    }\n\n    return drwav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);\n}\n#endif\n\nDRWAV_API drwav_int16* drwav_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)\n{\n    drwav wav;\n\n    if (channelsOut) {\n        *channelsOut = 0;\n    }\n    if (sampleRateOut) {\n        *sampleRateOut = 0;\n    }\n    if (totalFrameCountOut) {\n        *totalFrameCountOut = 0;\n    }\n\n    if (!drwav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) {\n        return NULL;\n    }\n\n    return drwav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut);\n}\n\nDRWAV_API float* drwav_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)\n{\n    drwav wav;\n\n    if (channelsOut) {\n        *channelsOut = 0;\n    }\n    if (sampleRateOut) {\n        *sampleRateOut = 0;\n    }\n    if (totalFrameCountOut) {\n        *totalFrameCountOut = 0;\n    }\n\n    if (!drwav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) {\n        return NULL;\n    }\n\n    return drwav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);\n}\n\nDRWAV_API drwav_int32* drwav_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)\n{\n    drwav wav;\n\n    if (channelsOut) {\n        *channelsOut = 0;\n    }\n    if (sampleRateOut) {\n        *sampleRateOut = 0;\n    }\n    if (totalFrameCountOut) {\n        *totalFrameCountOut = 0;\n    }\n\n    if (!drwav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) {\n        return NULL;\n    }\n\n    return drwav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);\n}\n#endif  /* DR_WAV_NO_CONVERSION_API */\n\n\nDRWAV_API void drwav_free(void* p, const drwav_allocation_callbacks* pAllocationCallbacks)\n{\n    if (pAllocationCallbacks != NULL) {\n        drwav__free_from_callbacks(p, pAllocationCallbacks);\n    } else {\n        drwav__free_default(p, NULL);\n    }\n}\n\nDRWAV_API drwav_uint16 drwav_bytes_to_u16(const drwav_uint8* data)\n{\n    return drwav__bytes_to_u16(data);\n}\n\nDRWAV_API drwav_int16 drwav_bytes_to_s16(const drwav_uint8* data)\n{\n    return drwav__bytes_to_s16(data);\n}\n\nDRWAV_API drwav_uint32 drwav_bytes_to_u32(const drwav_uint8* data)\n{\n    return drwav__bytes_to_u32(data);\n}\n\nDRWAV_API drwav_int32 drwav_bytes_to_s32(const drwav_uint8* data)\n{\n    return drwav__bytes_to_s32(data);\n}\n\nDRWAV_API drwav_uint64 drwav_bytes_to_u64(const drwav_uint8* data)\n{\n    return drwav__bytes_to_u64(data);\n}\n\nDRWAV_API drwav_int64 drwav_bytes_to_s64(const drwav_uint8* data)\n{\n    return drwav__bytes_to_s64(data);\n}\n\n\nDRWAV_API drwav_bool32 drwav_guid_equal(const drwav_uint8 a[16], const drwav_uint8 b[16])\n{\n    return drwav__guid_equal(a, b);\n}\n\nDRWAV_API drwav_bool32 drwav_fourcc_equal(const drwav_uint8* a, const char* b)\n{\n    return drwav__fourcc_equal(a, b);\n}\n\n#endif  /* dr_wav_c */\n#endif  /* DR_WAV_IMPLEMENTATION */\n\n/*\nRELEASE NOTES - v0.11.0\n=======================\nVersion 0.11.0 has breaking API changes.\n\nImproved Client-Defined Memory Allocation\n-----------------------------------------\nThe main change with this release is the addition of a more flexible way of implementing custom memory allocation routines. The\nexisting system of DRWAV_MALLOC, DRWAV_REALLOC and DRWAV_FREE are still in place and will be used by default when no custom\nallocation callbacks are specified.\n\nTo use the new system, you pass in a pointer to a drwav_allocation_callbacks object to drwav_init() and family, like this:\n\n    void* my_malloc(size_t sz, void* pUserData)\n    {\n        return malloc(sz);\n    }\n    void* my_realloc(void* p, size_t sz, void* pUserData)\n    {\n        return realloc(p, sz);\n    }\n    void my_free(void* p, void* pUserData)\n    {\n        free(p);\n    }\n\n    ...\n\n    drwav_allocation_callbacks allocationCallbacks;\n    allocationCallbacks.pUserData = &myData;\n    allocationCallbacks.onMalloc  = my_malloc;\n    allocationCallbacks.onRealloc = my_realloc;\n    allocationCallbacks.onFree    = my_free;\n    drwav_init_file(&wav, \"my_file.wav\", &allocationCallbacks);\n\nThe advantage of this new system is that it allows you to specify user data which will be passed in to the allocation routines.\n\nPassing in null for the allocation callbacks object will cause dr_wav to use defaults which is the same as DRWAV_MALLOC,\nDRWAV_REALLOC and DRWAV_FREE and the equivalent of how it worked in previous versions.\n\nEvery API that opens a drwav object now takes this extra parameter. These include the following:\n\n    drwav_init()\n    drwav_init_ex()\n    drwav_init_file()\n    drwav_init_file_ex()\n    drwav_init_file_w()\n    drwav_init_file_w_ex()\n    drwav_init_memory()\n    drwav_init_memory_ex()\n    drwav_init_write()\n    drwav_init_write_sequential()\n    drwav_init_write_sequential_pcm_frames()\n    drwav_init_file_write()\n    drwav_init_file_write_sequential()\n    drwav_init_file_write_sequential_pcm_frames()\n    drwav_init_file_write_w()\n    drwav_init_file_write_sequential_w()\n    drwav_init_file_write_sequential_pcm_frames_w()\n    drwav_init_memory_write()\n    drwav_init_memory_write_sequential()\n    drwav_init_memory_write_sequential_pcm_frames()\n    drwav_open_and_read_pcm_frames_s16()\n    drwav_open_and_read_pcm_frames_f32()\n    drwav_open_and_read_pcm_frames_s32()\n    drwav_open_file_and_read_pcm_frames_s16()\n    drwav_open_file_and_read_pcm_frames_f32()\n    drwav_open_file_and_read_pcm_frames_s32()\n    drwav_open_file_and_read_pcm_frames_s16_w()\n    drwav_open_file_and_read_pcm_frames_f32_w()\n    drwav_open_file_and_read_pcm_frames_s32_w()\n    drwav_open_memory_and_read_pcm_frames_s16()\n    drwav_open_memory_and_read_pcm_frames_f32()\n    drwav_open_memory_and_read_pcm_frames_s32()\n\nEndian Improvements\n-------------------\nPreviously, the following APIs returned little-endian audio data. These now return native-endian data. This improves compatibility\non big-endian architectures.\n\n    drwav_read_pcm_frames()\n    drwav_read_pcm_frames_s16()\n    drwav_read_pcm_frames_s32()\n    drwav_read_pcm_frames_f32()\n    drwav_open_and_read_pcm_frames_s16()\n    drwav_open_and_read_pcm_frames_s32()\n    drwav_open_and_read_pcm_frames_f32()\n    drwav_open_file_and_read_pcm_frames_s16()\n    drwav_open_file_and_read_pcm_frames_s32()\n    drwav_open_file_and_read_pcm_frames_f32()\n    drwav_open_file_and_read_pcm_frames_s16_w()\n    drwav_open_file_and_read_pcm_frames_s32_w()\n    drwav_open_file_and_read_pcm_frames_f32_w()\n    drwav_open_memory_and_read_pcm_frames_s16()\n    drwav_open_memory_and_read_pcm_frames_s32()\n    drwav_open_memory_and_read_pcm_frames_f32()\n\nAPIs have been added to give you explicit control over whether or not audio data is read or written in big- or little-endian byte\norder:\n\n    drwav_read_pcm_frames_le()\n    drwav_read_pcm_frames_be()\n    drwav_read_pcm_frames_s16le()\n    drwav_read_pcm_frames_s16be()\n    drwav_read_pcm_frames_f32le()\n    drwav_read_pcm_frames_f32be()\n    drwav_read_pcm_frames_s32le()\n    drwav_read_pcm_frames_s32be()\n    drwav_write_pcm_frames_le()\n    drwav_write_pcm_frames_be()\n\nRemoved APIs\n------------\nThe following APIs were deprecated in version 0.10.0 and have now been removed:\n\n    drwav_open()\n    drwav_open_ex()\n    drwav_open_write()\n    drwav_open_write_sequential()\n    drwav_open_file()\n    drwav_open_file_ex()\n    drwav_open_file_write()\n    drwav_open_file_write_sequential()\n    drwav_open_memory()\n    drwav_open_memory_ex()\n    drwav_open_memory_write()\n    drwav_open_memory_write_sequential()\n    drwav_close()\n\n\n\nRELEASE NOTES - v0.10.0\n=======================\nVersion 0.10.0 has breaking API changes. There are no significant bug fixes in this release, so if you are affected you do\nnot need to upgrade.\n\nRemoved APIs\n------------\nThe following APIs were deprecated in version 0.9.0 and have been completely removed in version 0.10.0:\n\n    drwav_read()\n    drwav_read_s16()\n    drwav_read_f32()\n    drwav_read_s32()\n    drwav_seek_to_sample()\n    drwav_write()\n    drwav_open_and_read_s16()\n    drwav_open_and_read_f32()\n    drwav_open_and_read_s32()\n    drwav_open_file_and_read_s16()\n    drwav_open_file_and_read_f32()\n    drwav_open_file_and_read_s32()\n    drwav_open_memory_and_read_s16()\n    drwav_open_memory_and_read_f32()\n    drwav_open_memory_and_read_s32()\n    drwav::totalSampleCount\n\nSee release notes for version 0.9.0 at the bottom of this file for replacement APIs.\n\nDeprecated APIs\n---------------\nThe following APIs have been deprecated. There is a confusing and completely arbitrary difference between drwav_init*() and\ndrwav_open*(), where drwav_init*() initializes a pre-allocated drwav object, whereas drwav_open*() will first allocated a\ndrwav object on the heap and then initialize it. drwav_open*() has been deprecated which means you must now use a pre-\nallocated drwav object with drwav_init*(). If you need the previous functionality, you can just do a malloc() followed by\na called to one of the drwav_init*() APIs.\n\n    drwav_open()\n    drwav_open_ex()\n    drwav_open_write()\n    drwav_open_write_sequential()\n    drwav_open_file()\n    drwav_open_file_ex()\n    drwav_open_file_write()\n    drwav_open_file_write_sequential()\n    drwav_open_memory()\n    drwav_open_memory_ex()\n    drwav_open_memory_write()\n    drwav_open_memory_write_sequential()\n    drwav_close()\n\nThese APIs will be removed completely in a future version. The rationale for this change is to remove confusion between the\ntwo different ways to initialize a drwav object.\n*/\n\n/*\nREVISION HISTORY\n================\nv0.12.16 - 2020-12-02\n  - Fix a bug when trying to read more bytes than can fit in a size_t.\n\nv0.12.15 - 2020-11-21\n  - Fix compilation with OpenWatcom.\n\nv0.12.14 - 2020-11-13\n  - Minor code clean up.\n\nv0.12.13 - 2020-11-01\n  - Improve compiler support for older versions of GCC.\n\nv0.12.12 - 2020-09-28\n  - Add support for RF64.\n  - Fix a bug in writing mode where the size of the RIFF chunk incorrectly includes the header section.\n\nv0.12.11 - 2020-09-08\n  - Fix a compilation error on older compilers.\n\nv0.12.10 - 2020-08-24\n  - Fix a bug when seeking with ADPCM formats.\n\nv0.12.9 - 2020-08-02\n  - Simplify sized types.\n\nv0.12.8 - 2020-07-25\n  - Fix a compilation warning.\n\nv0.12.7 - 2020-07-15\n  - Fix some bugs on big-endian architectures.\n  - Fix an error in s24 to f32 conversion.\n\nv0.12.6 - 2020-06-23\n  - Change drwav_read_*() to allow NULL to be passed in as the output buffer which is equivalent to a forward seek.\n  - Fix a buffer overflow when trying to decode invalid IMA-ADPCM files.\n  - Add include guard for the implementation section.\n\nv0.12.5 - 2020-05-27\n  - Minor documentation fix.\n\nv0.12.4 - 2020-05-16\n  - Replace assert() with DRWAV_ASSERT().\n  - Add compile-time and run-time version querying.\n    - DRWAV_VERSION_MINOR\n    - DRWAV_VERSION_MAJOR\n    - DRWAV_VERSION_REVISION\n    - DRWAV_VERSION_STRING\n    - drwav_version()\n    - drwav_version_string()\n\nv0.12.3 - 2020-04-30\n  - Fix compilation errors with VC6.\n\nv0.12.2 - 2020-04-21\n  - Fix a bug where drwav_init_file() does not close the file handle after attempting to load an erroneous file.\n\nv0.12.1 - 2020-04-13\n  - Fix some pedantic warnings.\n\nv0.12.0 - 2020-04-04\n  - API CHANGE: Add container and format parameters to the chunk callback.\n  - Minor documentation updates.\n\nv0.11.5 - 2020-03-07\n  - Fix compilation error with Visual Studio .NET 2003.\n\nv0.11.4 - 2020-01-29\n  - Fix some static analysis warnings.\n  - Fix a bug when reading f32 samples from an A-law encoded stream.\n\nv0.11.3 - 2020-01-12\n  - Minor changes to some f32 format conversion routines.\n  - Minor bug fix for ADPCM conversion when end of file is reached.\n\nv0.11.2 - 2019-12-02\n  - Fix a possible crash when using custom memory allocators without a custom realloc() implementation.\n  - Fix an integer overflow bug.\n  - Fix a null pointer dereference bug.\n  - Add limits to sample rate, channels and bits per sample to tighten up some validation.\n\nv0.11.1 - 2019-10-07\n  - Internal code clean up.\n\nv0.11.0 - 2019-10-06\n  - API CHANGE: Add support for user defined memory allocation routines. This system allows the program to specify their own memory allocation\n    routines with a user data pointer for client-specific contextual data. This adds an extra parameter to the end of the following APIs:\n    - drwav_init()\n    - drwav_init_ex()\n    - drwav_init_file()\n    - drwav_init_file_ex()\n    - drwav_init_file_w()\n    - drwav_init_file_w_ex()\n    - drwav_init_memory()\n    - drwav_init_memory_ex()\n    - drwav_init_write()\n    - drwav_init_write_sequential()\n    - drwav_init_write_sequential_pcm_frames()\n    - drwav_init_file_write()\n    - drwav_init_file_write_sequential()\n    - drwav_init_file_write_sequential_pcm_frames()\n    - drwav_init_file_write_w()\n    - drwav_init_file_write_sequential_w()\n    - drwav_init_file_write_sequential_pcm_frames_w()\n    - drwav_init_memory_write()\n    - drwav_init_memory_write_sequential()\n    - drwav_init_memory_write_sequential_pcm_frames()\n    - drwav_open_and_read_pcm_frames_s16()\n    - drwav_open_and_read_pcm_frames_f32()\n    - drwav_open_and_read_pcm_frames_s32()\n    - drwav_open_file_and_read_pcm_frames_s16()\n    - drwav_open_file_and_read_pcm_frames_f32()\n    - drwav_open_file_and_read_pcm_frames_s32()\n    - drwav_open_file_and_read_pcm_frames_s16_w()\n    - drwav_open_file_and_read_pcm_frames_f32_w()\n    - drwav_open_file_and_read_pcm_frames_s32_w()\n    - drwav_open_memory_and_read_pcm_frames_s16()\n    - drwav_open_memory_and_read_pcm_frames_f32()\n    - drwav_open_memory_and_read_pcm_frames_s32()\n    Set this extra parameter to NULL to use defaults which is the same as the previous behaviour. Setting this NULL will use\n    DRWAV_MALLOC, DRWAV_REALLOC and DRWAV_FREE.\n  - Add support for reading and writing PCM frames in an explicit endianness. New APIs:\n    - drwav_read_pcm_frames_le()\n    - drwav_read_pcm_frames_be()\n    - drwav_read_pcm_frames_s16le()\n    - drwav_read_pcm_frames_s16be()\n    - drwav_read_pcm_frames_f32le()\n    - drwav_read_pcm_frames_f32be()\n    - drwav_read_pcm_frames_s32le()\n    - drwav_read_pcm_frames_s32be()\n    - drwav_write_pcm_frames_le()\n    - drwav_write_pcm_frames_be()\n  - Remove deprecated APIs.\n  - API CHANGE: The following APIs now return native-endian data. Previously they returned little-endian data.\n    - drwav_read_pcm_frames()\n    - drwav_read_pcm_frames_s16()\n    - drwav_read_pcm_frames_s32()\n    - drwav_read_pcm_frames_f32()\n    - drwav_open_and_read_pcm_frames_s16()\n    - drwav_open_and_read_pcm_frames_s32()\n    - drwav_open_and_read_pcm_frames_f32()\n    - drwav_open_file_and_read_pcm_frames_s16()\n    - drwav_open_file_and_read_pcm_frames_s32()\n    - drwav_open_file_and_read_pcm_frames_f32()\n    - drwav_open_file_and_read_pcm_frames_s16_w()\n    - drwav_open_file_and_read_pcm_frames_s32_w()\n    - drwav_open_file_and_read_pcm_frames_f32_w()\n    - drwav_open_memory_and_read_pcm_frames_s16()\n    - drwav_open_memory_and_read_pcm_frames_s32()\n    - drwav_open_memory_and_read_pcm_frames_f32()\n\nv0.10.1 - 2019-08-31\n  - Correctly handle partial trailing ADPCM blocks.\n\nv0.10.0 - 2019-08-04\n  - Remove deprecated APIs.\n  - Add wchar_t variants for file loading APIs:\n      drwav_init_file_w()\n      drwav_init_file_ex_w()\n      drwav_init_file_write_w()\n      drwav_init_file_write_sequential_w()\n  - Add drwav_target_write_size_bytes() which calculates the total size in bytes of a WAV file given a format and sample count.\n  - Add APIs for specifying the PCM frame count instead of the sample count when opening in sequential write mode:\n      drwav_init_write_sequential_pcm_frames()\n      drwav_init_file_write_sequential_pcm_frames()\n      drwav_init_file_write_sequential_pcm_frames_w()\n      drwav_init_memory_write_sequential_pcm_frames()\n  - Deprecate drwav_open*() and drwav_close():\n      drwav_open()\n      drwav_open_ex()\n      drwav_open_write()\n      drwav_open_write_sequential()\n      drwav_open_file()\n      drwav_open_file_ex()\n      drwav_open_file_write()\n      drwav_open_file_write_sequential()\n      drwav_open_memory()\n      drwav_open_memory_ex()\n      drwav_open_memory_write()\n      drwav_open_memory_write_sequential()\n      drwav_close()\n  - Minor documentation updates.\n\nv0.9.2 - 2019-05-21\n  - Fix warnings.\n\nv0.9.1 - 2019-05-05\n  - Add support for C89.\n  - Change license to choice of public domain or MIT-0.\n\nv0.9.0 - 2018-12-16\n  - API CHANGE: Add new reading APIs for reading by PCM frames instead of samples. Old APIs have been deprecated and\n    will be removed in v0.10.0. Deprecated APIs and their replacements:\n      drwav_read()                     -> drwav_read_pcm_frames()\n      drwav_read_s16()                 -> drwav_read_pcm_frames_s16()\n      drwav_read_f32()                 -> drwav_read_pcm_frames_f32()\n      drwav_read_s32()                 -> drwav_read_pcm_frames_s32()\n      drwav_seek_to_sample()           -> drwav_seek_to_pcm_frame()\n      drwav_write()                    -> drwav_write_pcm_frames()\n      drwav_open_and_read_s16()        -> drwav_open_and_read_pcm_frames_s16()\n      drwav_open_and_read_f32()        -> drwav_open_and_read_pcm_frames_f32()\n      drwav_open_and_read_s32()        -> drwav_open_and_read_pcm_frames_s32()\n      drwav_open_file_and_read_s16()   -> drwav_open_file_and_read_pcm_frames_s16()\n      drwav_open_file_and_read_f32()   -> drwav_open_file_and_read_pcm_frames_f32()\n      drwav_open_file_and_read_s32()   -> drwav_open_file_and_read_pcm_frames_s32()\n      drwav_open_memory_and_read_s16() -> drwav_open_memory_and_read_pcm_frames_s16()\n      drwav_open_memory_and_read_f32() -> drwav_open_memory_and_read_pcm_frames_f32()\n      drwav_open_memory_and_read_s32() -> drwav_open_memory_and_read_pcm_frames_s32()\n      drwav::totalSampleCount          -> drwav::totalPCMFrameCount\n  - API CHANGE: Rename drwav_open_and_read_file_*() to drwav_open_file_and_read_*().\n  - API CHANGE: Rename drwav_open_and_read_memory_*() to drwav_open_memory_and_read_*().\n  - Add built-in support for smpl chunks.\n  - Add support for firing a callback for each chunk in the file at initialization time.\n    - This is enabled through the drwav_init_ex(), etc. family of APIs.\n  - Handle invalid FMT chunks more robustly.\n\nv0.8.5 - 2018-09-11\n  - Const correctness.\n  - Fix a potential stack overflow.\n\nv0.8.4 - 2018-08-07\n  - Improve 64-bit detection.\n\nv0.8.3 - 2018-08-05\n  - Fix C++ build on older versions of GCC.\n\nv0.8.2 - 2018-08-02\n  - Fix some big-endian bugs.\n\nv0.8.1 - 2018-06-29\n  - Add support for sequential writing APIs.\n  - Disable seeking in write mode.\n  - Fix bugs with Wave64.\n  - Fix typos.\n\nv0.8 - 2018-04-27\n  - Bug fix.\n  - Start using major.minor.revision versioning.\n\nv0.7f - 2018-02-05\n  - Restrict ADPCM formats to a maximum of 2 channels.\n\nv0.7e - 2018-02-02\n  - Fix a crash.\n\nv0.7d - 2018-02-01\n  - Fix a crash.\n\nv0.7c - 2018-02-01\n  - Set drwav.bytesPerSample to 0 for all compressed formats.\n  - Fix a crash when reading 16-bit floating point WAV files. In this case dr_wav will output silence for\n    all format conversion reading APIs (*_s16, *_s32, *_f32 APIs).\n  - Fix some divide-by-zero errors.\n\nv0.7b - 2018-01-22\n  - Fix errors with seeking of compressed formats.\n  - Fix compilation error when DR_WAV_NO_CONVERSION_API\n\nv0.7a - 2017-11-17\n  - Fix some GCC warnings.\n\nv0.7 - 2017-11-04\n  - Add writing APIs.\n\nv0.6 - 2017-08-16\n  - API CHANGE: Rename dr_* types to drwav_*.\n  - Add support for custom implementations of malloc(), realloc(), etc.\n  - Add support for Microsoft ADPCM.\n  - Add support for IMA ADPCM (DVI, format code 0x11).\n  - Optimizations to drwav_read_s16().\n  - Bug fixes.\n\nv0.5g - 2017-07-16\n  - Change underlying type for booleans to unsigned.\n\nv0.5f - 2017-04-04\n  - Fix a minor bug with drwav_open_and_read_s16() and family.\n\nv0.5e - 2016-12-29\n  - Added support for reading samples as signed 16-bit integers. Use the _s16() family of APIs for this.\n  - Minor fixes to documentation.\n\nv0.5d - 2016-12-28\n  - Use drwav_int* and drwav_uint* sized types to improve compiler support.\n\nv0.5c - 2016-11-11\n  - Properly handle JUNK chunks that come before the FMT chunk.\n\nv0.5b - 2016-10-23\n  - A minor change to drwav_bool8 and drwav_bool32 types.\n\nv0.5a - 2016-10-11\n  - Fixed a bug with drwav_open_and_read() and family due to incorrect argument ordering.\n  - Improve A-law and mu-law efficiency.\n\nv0.5 - 2016-09-29\n  - API CHANGE. Swap the order of \"channels\" and \"sampleRate\" parameters in drwav_open_and_read*(). Rationale for this is to\n    keep it consistent with dr_audio and dr_flac.\n\nv0.4b - 2016-09-18\n  - Fixed a typo in documentation.\n\nv0.4a - 2016-09-18\n  - Fixed a typo.\n  - Change date format to ISO 8601 (YYYY-MM-DD)\n\nv0.4 - 2016-07-13\n  - API CHANGE. Make onSeek consistent with dr_flac.\n  - API CHANGE. Rename drwav_seek() to drwav_seek_to_sample() for clarity and consistency with dr_flac.\n  - Added support for Sony Wave64.\n\nv0.3a - 2016-05-28\n  - API CHANGE. Return drwav_bool32 instead of int in onSeek callback.\n  - Fixed a memory leak.\n\nv0.3 - 2016-05-22\n  - Lots of API changes for consistency.\n\nv0.2a - 2016-05-16\n  - Fixed Linux/GCC build.\n\nv0.2 - 2016-05-11\n  - Added support for reading data as signed 32-bit PCM for consistency with dr_flac.\n\nv0.1a - 2016-05-07\n  - Fixed a bug in drwav_open_file() where the file handle would not be closed if the loader failed to initialize.\n\nv0.1 - 2016-05-04\n  - Initial versioned release.\n*/\n\n/*\nThis software is available as a choice of the following licenses. Choose\nwhichever you prefer.\n\n===============================================================================\nALTERNATIVE 1 - Public Domain (www.unlicense.org)\n===============================================================================\nThis is free and unencumbered software released into the public domain.\n\nAnyone is free to copy, modify, publish, use, compile, sell, or distribute this\nsoftware, either in source code form or as a compiled binary, for any purpose,\ncommercial or non-commercial, and by any means.\n\nIn jurisdictions that recognize copyright laws, the author or authors of this\nsoftware dedicate any and all copyright interest in the software to the public\ndomain. We make this dedication for the benefit of the public at large and to\nthe detriment of our heirs and successors. We intend this dedication to be an\novert act of relinquishment in perpetuity of all present and future rights to\nthis software under copyright law.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\nACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\nWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\nFor more information, please refer to <http://unlicense.org/>\n\n===============================================================================\nALTERNATIVE 2 - MIT No Attribution\n===============================================================================\nCopyright 2020 David Reid\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies\nof the Software, and to permit persons to whom the Software is furnished to do\nso.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n*/\n"
  },
  {
    "path": "Examples/OldMain/main.cpp",
    "content": "#include \"whisper.h\"\n\n// third-party utilities\n// use your favorite implementations\n#define DR_WAV_IMPLEMENTATION\n#include \"dr_wav.h\"\n\n#include <cmath>\n#include <fstream>\n#include <cstdio>\n#include <string>\n#include <thread>\n#include <vector>\n\n// Terminal color map. 10 colors grouped in ranges [0.0, 0.1, ..., 0.9]\n// Lowest is red, middle is yellow, highest is green.\nconst std::vector<std::string> k_colors = {\n    \"\\033[38;5;196m\", \"\\033[38;5;202m\", \"\\033[38;5;208m\", \"\\033[38;5;214m\", \"\\033[38;5;220m\",\n    \"\\033[38;5;226m\", \"\\033[38;5;190m\", \"\\033[38;5;154m\", \"\\033[38;5;118m\", \"\\033[38;5;82m\",\n};\n\n//  500 -> 00:05.000\n// 6000 -> 01:00.000\nstd::string to_timestamp(int64_t t, bool comma = false) {\n    int64_t msec = t * 10;\n    int64_t hr = msec / (1000 * 60 * 60);\n    msec = msec - hr * (1000 * 60 * 60);\n    int64_t min = msec / (1000 * 60);\n    msec = msec - min * (1000 * 60);\n    int64_t sec = msec / 1000;\n    msec = msec - sec * 1000;\n\n    char buf[32];\n    snprintf(buf, sizeof(buf), \"%02d:%02d:%02d%s%03d\", (int) hr, (int) min, (int) sec, comma ? \",\" : \".\", (int) msec);\n\n    return std::string(buf);\n}\n\nint timestamp_to_sample(int64_t t, int n_samples) {\n    return std::max(0, std::min((int) n_samples - 1, (int) ((t*WHISPER_SAMPLE_RATE)/100)));\n}\n\n// helper function to replace substrings\nvoid replace_all(std::string & s, const std::string & search, const std::string & replace) {\n    for (size_t pos = 0; ; pos += replace.length()) {\n        pos = s.find(search, pos);\n        if (pos == std::string::npos) break;\n        s.erase(pos, search.length());\n        s.insert(pos, replace);\n    }\n}\n\n// command-line parameters\nstruct whisper_params {\n    int32_t n_threads    = std::min(4, (int32_t) std::thread::hardware_concurrency());\n    int32_t n_processors = 1;\n    int32_t offset_t_ms  = 0;\n    int32_t offset_n     = 0;\n    int32_t duration_ms  = 0;\n    int32_t max_context  = -1;\n    int32_t max_len      = 0;\n\n    float word_thold = 0.01f;\n\n    bool speed_up       = false;\n    bool translate      = false;\n    bool diarize        = false;\n    bool output_txt     = false;\n    bool output_vtt     = false;\n    bool output_srt     = false;\n    bool output_wts     = false;\n    bool print_special  = false;\n    bool print_colors   = false;\n    bool print_progress = false;\n    bool no_timestamps  = false;\n\n    std::string language = \"en\";\n    std::string prompt;\n    std::string model    = \"models/ggml-base.en.bin\";\n\n    std::vector<std::string> fname_inp = {};\n};\n\nvoid whisper_print_usage(int argc, char ** argv, const whisper_params & params);\n\nbool whisper_params_parse(int argc, char ** argv, whisper_params & params) {\n    for (int i = 1; i < argc; i++) {\n        std::string arg = argv[i];\n\n        if (arg[0] != '-') {\n            params.fname_inp.push_back(arg);\n            continue;\n        }\n\n        if (arg == \"-h\" || arg == \"--help\") {\n            whisper_print_usage(argc, argv, params);\n            exit(0);\n        }\n        else if (arg == \"-t\"    || arg == \"--threads\")        { params.n_threads      = std::stoi(argv[++i]); }\n        else if (arg == \"-p\"    || arg == \"--processors\")     { params.n_processors   = std::stoi(argv[++i]); }\n        else if (arg == \"-ot\"   || arg == \"--offset-t\")       { params.offset_t_ms    = std::stoi(argv[++i]); }\n        else if (arg == \"-on\"   || arg == \"--offset-n\")       { params.offset_n       = std::stoi(argv[++i]); }\n        else if (arg == \"-d\"    || arg == \"--duration\")       { params.duration_ms    = std::stoi(argv[++i]); }\n        else if (arg == \"-mc\"   || arg == \"--max-context\")    { params.max_context    = std::stoi(argv[++i]); }\n        else if (arg == \"-ml\"   || arg == \"--max-len\")        { params.max_len        = std::stoi(argv[++i]); }\n        else if (arg == \"-wt\"   || arg == \"--word-thold\")     { params.word_thold     = std::stof(argv[++i]); }\n        else if (arg == \"-su\"   || arg == \"--speed-up\")       { params.speed_up       = true; }\n        else if (arg == \"-tr\"   || arg == \"--translate\")      { params.translate      = true; }\n        else if (arg == \"-di\"   || arg == \"--diarize\")        { params.diarize        = true; }\n        else if (arg == \"-otxt\" || arg == \"--output-txt\")     { params.output_txt     = true; }\n        else if (arg == \"-ovtt\" || arg == \"--output-vtt\")     { params.output_vtt     = true; }\n        else if (arg == \"-osrt\" || arg == \"--output-srt\")     { params.output_srt     = true; }\n        else if (arg == \"-owts\" || arg == \"--output-words\")   { params.output_wts     = true; }\n        else if (arg == \"-ps\"   || arg == \"--print-special\")  { params.print_special  = true; }\n        else if (arg == \"-pc\"   || arg == \"--print-colors\")   { params.print_colors   = true; }\n        else if (arg == \"-pp\"   || arg == \"--print-progress\") { params.print_progress = true; }\n        else if (arg == \"-nt\"   || arg == \"--no-timestamps\")  { params.no_timestamps  = true; }\n        else if (arg == \"-l\"    || arg == \"--language\")       { params.language       = argv[++i]; }\n        else if (                  arg == \"--prompt\")         { params.prompt         = argv[++i]; }\n        else if (arg == \"-m\"    || arg == \"--model\")          { params.model          = argv[++i]; }\n        else if (arg == \"-f\"    || arg == \"--file\")           { params.fname_inp.emplace_back(argv[++i]); }\n        else {\n            fprintf(stderr, \"error: unknown argument: %s\\n\", arg.c_str());\n            whisper_print_usage(argc, argv, params);\n            exit(0);\n        }\n    }\n\n    return true;\n}\n\nvoid whisper_print_usage(int /*argc*/, char ** argv, const whisper_params & params) {\n    fprintf(stderr, \"\\n\");\n    fprintf(stderr, \"usage: %s [options] file0.wav file1.wav ...\\n\", argv[0]);\n    fprintf(stderr, \"\\n\");\n    fprintf(stderr, \"options:\\n\");\n    fprintf(stderr, \"  -h,       --help           [default] show this help message and exit\\n\");\n    fprintf(stderr, \"  -t N,     --threads N      [%-7d] number of threads to use during computation\\n\",    params.n_threads);\n    fprintf(stderr, \"  -p N,     --processors N   [%-7d] number of processors to use during computation\\n\", params.n_processors);\n    fprintf(stderr, \"  -ot N,    --offset-t N     [%-7d] time offset in milliseconds\\n\",                    params.offset_t_ms);\n    fprintf(stderr, \"  -on N,    --offset-n N     [%-7d] segment index offset\\n\",                           params.offset_n);\n    fprintf(stderr, \"  -d  N,    --duration N     [%-7d] duration of audio to process in milliseconds\\n\",   params.duration_ms);\n    fprintf(stderr, \"  -mc N,    --max-context N  [%-7d] maximum number of text context tokens to store\\n\", params.max_context);\n    fprintf(stderr, \"  -ml N,    --max-len N      [%-7d] maximum segment length in characters\\n\",           params.max_len);\n    fprintf(stderr, \"  -wt N,    --word-thold N   [%-7.2f] word timestamp probability threshold\\n\",         params.word_thold);\n    fprintf(stderr, \"  -su,      --speed-up       [%-7s] speed up audio by x2 (reduced accuracy)\\n\",        params.speed_up ? \"true\" : \"false\");\n    fprintf(stderr, \"  -tr,      --translate      [%-7s] translate from source language to english\\n\",      params.translate ? \"true\" : \"false\");\n    fprintf(stderr, \"  -di,      --diarize        [%-7s] stereo audio diarization\\n\",                       params.diarize ? \"true\" : \"false\");\n    fprintf(stderr, \"  -otxt,    --output-txt     [%-7s] output result in a text file\\n\",                   params.output_txt ? \"true\" : \"false\");\n    fprintf(stderr, \"  -ovtt,    --output-vtt     [%-7s] output result in a vtt file\\n\",                    params.output_vtt ? \"true\" : \"false\");\n    fprintf(stderr, \"  -osrt,    --output-srt     [%-7s] output result in a srt file\\n\",                    params.output_srt ? \"true\" : \"false\");\n    fprintf(stderr, \"  -owts,    --output-words   [%-7s] output script for generating karaoke video\\n\",     params.output_wts ? \"true\" : \"false\");\n    fprintf(stderr, \"  -ps,      --print-special  [%-7s] print special tokens\\n\",                           params.print_special ? \"true\" : \"false\");\n    fprintf(stderr, \"  -pc,      --print-colors   [%-7s] print colors\\n\",                                   params.print_colors ? \"true\" : \"false\");\n    fprintf(stderr, \"  -pp,      --print-progress [%-7s] print progress\\n\",                                 params.print_progress ? \"true\" : \"false\");\n    fprintf(stderr, \"  -nt,      --no-timestamps  [%-7s] do not print timestamps\\n\",                        params.no_timestamps ? \"false\" : \"true\");\n    fprintf(stderr, \"  -l LANG,  --language LANG  [%-7s] spoken language ('auto' for auto-detect)\\n\",       params.language.c_str());\n    fprintf(stderr, \"            --prompt PROMPT  [%-7s] initial prompt\\n\",                                 params.prompt.c_str());\n    fprintf(stderr, \"  -m FNAME, --model FNAME    [%-7s] model path\\n\",                                     params.model.c_str());\n    fprintf(stderr, \"  -f FNAME, --file FNAME     [%-7s] input WAV file path\\n\",                            \"\");\n    fprintf(stderr, \"\\n\");\n}\n\nstruct whisper_print_user_data {\n    const whisper_params * params;\n\n    const std::vector<std::vector<float>> * pcmf32s;\n};\n\nvoid whisper_print_segment_callback(struct whisper_context * ctx, int n_new, void * user_data) {\n    const auto & params  = *((whisper_print_user_data *) user_data)->params;\n    const auto & pcmf32s = *((whisper_print_user_data *) user_data)->pcmf32s;\n\n    const int n_segments = whisper_full_n_segments(ctx);\n\n    // print the last n_new segments\n    const int s0 = n_segments - n_new;\n    if (s0 == 0) {\n        printf(\"\\n\");\n    }\n\n    for (int i = s0; i < n_segments; i++) {\n        if (params.no_timestamps) {\n            if (params.print_colors) {\n                for (int j = 0; j < whisper_full_n_tokens(ctx, i); ++j) {\n                    if (params.print_special == false) {\n                        const whisper_token id = whisper_full_get_token_id(ctx, i, j);\n                        if (id >= whisper_token_eot(ctx)) {\n                            continue;\n                        }\n                    }\n\n                    const char * text = whisper_full_get_token_text(ctx, i, j);\n                    const float  p    = whisper_full_get_token_p   (ctx, i, j);\n\n                    const int col = std::max(0, std::min((int) k_colors.size(), (int) (std::pow(p, 3)*float(k_colors.size()))));\n\n                    printf(\"%s%s%s\", k_colors[col].c_str(), text, \"\\033[0m\");\n                }\n            } else {\n                const char * text = whisper_full_get_segment_text(ctx, i);\n                printf(\"%s\", text);\n            }\n            fflush(stdout);\n        } else {\n            const int64_t t0 = whisper_full_get_segment_t0(ctx, i);\n            const int64_t t1 = whisper_full_get_segment_t1(ctx, i);\n\n            std::string speaker;\n\n            if (params.diarize && pcmf32s.size() == 2) {\n                const int64_t n_samples = pcmf32s[0].size();\n\n                const int64_t is0 = timestamp_to_sample(t0, n_samples);\n                const int64_t is1 = timestamp_to_sample(t1, n_samples);\n\n                double energy0 = 0.0f;\n                double energy1 = 0.0f;\n\n                for (int64_t j = is0; j < is1; j++) {\n                    energy0 += fabs(pcmf32s[0][j]);\n                    energy1 += fabs(pcmf32s[1][j]);\n                }\n\n                if (energy0 > 1.1*energy1) {\n                    speaker = \"(speaker 0)\";\n                } else if (energy1 > 1.1*energy0) {\n                    speaker = \"(speaker 1)\";\n                } else {\n                    speaker = \"(speaker ?)\";\n                }\n\n                //printf(\"is0 = %lld, is1 = %lld, energy0 = %f, energy1 = %f, %s\\n\", is0, is1, energy0, energy1, speaker.c_str());\n            }\n\n            if (params.print_colors) {\n                printf(\"[%s --> %s]  \", to_timestamp(t0).c_str(), to_timestamp(t1).c_str());\n                for (int j = 0; j < whisper_full_n_tokens(ctx, i); ++j) {\n                    if (params.print_special == false) {\n                        const whisper_token id = whisper_full_get_token_id(ctx, i, j);\n                        if (id >= whisper_token_eot(ctx)) {\n                            continue;\n                        }\n                    }\n\n                    const char * text = whisper_full_get_token_text(ctx, i, j);\n                    const float  p    = whisper_full_get_token_p   (ctx, i, j);\n\n                    const int col = std::max(0, std::min((int) k_colors.size(), (int) (std::pow(p, 3)*float(k_colors.size()))));\n\n                    printf(\"%s%s%s%s\", speaker.c_str(), k_colors[col].c_str(), text, \"\\033[0m\");\n                }\n                printf(\"\\n\");\n            } else {\n                const char * text = whisper_full_get_segment_text(ctx, i);\n\n                printf(\"[%s --> %s]  %s%s\\n\", to_timestamp(t0).c_str(), to_timestamp(t1).c_str(), speaker.c_str(), text);\n            }\n        }\n    }\n}\n\nbool output_txt(struct whisper_context * ctx, const char * fname) {\n    std::ofstream fout(fname);\n    if (!fout.is_open()) {\n        fprintf(stderr, \"%s: failed to open '%s' for writing\\n\", __func__, fname);\n        return false;\n    }\n\n    fprintf(stderr, \"%s: saving output to '%s'\\n\", __func__, fname);\n\n    const int n_segments = whisper_full_n_segments(ctx);\n    for (int i = 0; i < n_segments; ++i) {\n        const char * text = whisper_full_get_segment_text(ctx, i);\n        fout << text << \"\\n\";\n    }\n\n    return true;\n}\n\nbool output_vtt(struct whisper_context * ctx, const char * fname) {\n    std::ofstream fout(fname);\n    if (!fout.is_open()) {\n        fprintf(stderr, \"%s: failed to open '%s' for writing\\n\", __func__, fname);\n        return false;\n    }\n\n    fprintf(stderr, \"%s: saving output to '%s'\\n\", __func__, fname);\n\n    fout << \"WEBVTT\\n\\n\";\n\n    const int n_segments = whisper_full_n_segments(ctx);\n    for (int i = 0; i < n_segments; ++i) {\n        const char * text = whisper_full_get_segment_text(ctx, i);\n        const int64_t t0 = whisper_full_get_segment_t0(ctx, i);\n        const int64_t t1 = whisper_full_get_segment_t1(ctx, i);\n\n        fout << to_timestamp(t0) << \" --> \" << to_timestamp(t1) << \"\\n\";\n        fout << text << \"\\n\\n\";\n    }\n\n    return true;\n}\n\nbool output_srt(struct whisper_context * ctx, const char * fname, const whisper_params & params) {\n    std::ofstream fout(fname);\n    if (!fout.is_open()) {\n        fprintf(stderr, \"%s: failed to open '%s' for writing\\n\", __func__, fname);\n        return false;\n    }\n\n    fprintf(stderr, \"%s: saving output to '%s'\\n\", __func__, fname);\n\n    const int n_segments = whisper_full_n_segments(ctx);\n    for (int i = 0; i < n_segments; ++i) {\n        const char * text = whisper_full_get_segment_text(ctx, i);\n        const int64_t t0 = whisper_full_get_segment_t0(ctx, i);\n        const int64_t t1 = whisper_full_get_segment_t1(ctx, i);\n\n        fout << i + 1 + params.offset_n << \"\\n\";\n        fout << to_timestamp(t0, true) << \" --> \" << to_timestamp(t1, true) << \"\\n\";\n        fout << text << \"\\n\\n\";\n    }\n\n    return true;\n}\n\n// karaoke video generation\n// outputs a bash script that uses ffmpeg to generate a video with the subtitles\n// TODO: font parameter adjustments\nbool output_wts(struct whisper_context * ctx, const char * fname, const char * fname_inp, const whisper_params & /*params*/, float t_sec) {\n    std::ofstream fout(fname);\n\n    fprintf(stderr, \"%s: saving output to '%s'\\n\", __func__, fname);\n\n    // TODO: become parameter\n    static const char * font = \"/System/Library/Fonts/Supplemental/Courier New Bold.ttf\";\n\n    fout << \"#!/bin/bash\" << \"\\n\";\n    fout << \"\\n\";\n\n    fout << \"ffmpeg -i \" << fname_inp << \" -f lavfi -i color=size=1200x120:duration=\" << t_sec << \":rate=25:color=black -vf \\\"\";\n\n    for (int i = 0; i < whisper_full_n_segments(ctx); i++) {\n        const int64_t t0 = whisper_full_get_segment_t0(ctx, i);\n        const int64_t t1 = whisper_full_get_segment_t1(ctx, i);\n\n        const int n = whisper_full_n_tokens(ctx, i);\n\n        std::vector<whisper_token_data> tokens(n);\n        for (int j = 0; j < n; ++j) {\n            tokens[j] = whisper_full_get_token_data(ctx, i, j);\n        }\n\n        if (i > 0) {\n            fout << \",\";\n        }\n\n        // background text\n        fout << \"drawtext=fontfile='\" << font << \"':fontsize=24:fontcolor=gray:x=(w-text_w)/2:y=h/2:text='':enable='between(t,\" << t0/100.0 << \",\" << t0/100.0 << \")'\";\n\n        bool is_first = true;\n\n        for (int j = 0; j < n; ++j) {\n            const auto & token = tokens[j];\n\n            if (tokens[j].id >= whisper_token_eot(ctx)) {\n                continue;\n            }\n\n            std::string txt_bg;\n            std::string txt_fg; // highlight token\n            std::string txt_ul; // underline\n\n            txt_bg = \"> \";\n            txt_fg = \"> \";\n            txt_ul = \"\\\\ \\\\ \";\n\n            {\n                for (int k = 0; k < n; ++k) {\n                    const auto & token2 = tokens[k];\n\n                    if (tokens[k].id >= whisper_token_eot(ctx)) {\n                        continue;\n                    }\n\n                    const std::string txt = whisper_token_to_str(ctx, token2.id);\n\n                    txt_bg += txt;\n\n                    if (k == j) {\n                        for (int l = 0; l < (int) txt.size(); ++l) {\n                            txt_fg += txt[l];\n                            txt_ul += \"_\";\n                        }\n                        txt_fg += \"|\";\n                    } else {\n                        for (int l = 0; l < (int) txt.size(); ++l) {\n                            txt_fg += \"\\\\ \";\n                            txt_ul += \"\\\\ \";\n                        }\n                    }\n                }\n\n                ::replace_all(txt_bg, \"'\", \"\\u2019\");\n                ::replace_all(txt_bg, \"\\\"\", \"\\\\\\\"\");\n                ::replace_all(txt_fg, \"'\", \"\\u2019\");\n                ::replace_all(txt_fg, \"\\\"\", \"\\\\\\\"\");\n            }\n\n            if (is_first) {\n                // background text\n                fout << \",drawtext=fontfile='\" << font << \"':fontsize=24:fontcolor=gray:x=(w-text_w)/2:y=h/2:text='\" << txt_bg << \"':enable='between(t,\" << t0/100.0 << \",\" << t1/100.0 << \")'\";\n                is_first = false;\n            }\n\n            // foreground text\n            fout << \",drawtext=fontfile='\" << font << \"':fontsize=24:fontcolor=lightgreen:x=(w-text_w)/2+8:y=h/2:text='\" << txt_fg << \"':enable='between(t,\" << token.t0/100.0 << \",\" << token.t1/100.0 << \")'\";\n\n            // underline\n            fout << \",drawtext=fontfile='\" << font << \"':fontsize=24:fontcolor=lightgreen:x=(w-text_w)/2+8:y=h/2+16:text='\" << txt_ul << \"':enable='between(t,\" << token.t0/100.0 << \",\" << token.t1/100.0 << \")'\";\n        }\n    }\n\n    fout << \"\\\" -c:v libx264 -pix_fmt yuv420p -y \" << fname_inp << \".mp4\" << \"\\n\";\n\n    fout << \"\\n\\n\";\n    fout << \"echo \\\"Your video has been saved to \" << fname_inp << \".mp4\\\"\" << \"\\n\";\n    fout << \"\\n\";\n    fout << \"echo \\\"  ffplay \" << fname_inp << \".mp4\\\"\\n\";\n    fout << \"\\n\";\n\n    fout.close();\n\n    fprintf(stderr, \"%s: run 'source %s' to generate karaoke video\\n\", __func__, fname);\n\n    return true;\n}\n\nint main(int argc, char ** argv) {\n    whisper_params params;\n\n    if (whisper_params_parse(argc, argv, params) == false) {\n        return 1;\n    }\n\n    if (params.fname_inp.empty()) {\n        fprintf(stderr, \"error: no input files specified\\n\");\n        whisper_print_usage(argc, argv, params);\n        return 2;\n    }\n\n    if (params.language != \"auto\" && whisper_lang_id(params.language.c_str()) == -1) {\n        fprintf(stderr, \"error: unknown language '%s'\\n\", params.language.c_str());\n        whisper_print_usage(argc, argv, params);\n        exit(0);\n    }\n\n    // whisper init\n\n    struct whisper_context * ctx = whisper_init(params.model.c_str());\n\n    if (ctx == nullptr) {\n        fprintf(stderr, \"error: failed to initialize whisper context\\n\");\n        return 3;\n    }\n\n    // initial prompt\n    std::vector<whisper_token> prompt_tokens;\n\n    if (!params.prompt.empty()) {\n        prompt_tokens.resize(1024);\n        prompt_tokens.resize(whisper_tokenize(ctx, params.prompt.c_str(), prompt_tokens.data(), prompt_tokens.size()));\n\n        fprintf(stderr, \"\\n\");\n        fprintf(stderr, \"initial prompt: '%s'\\n\", params.prompt.c_str());\n        fprintf(stderr, \"initial tokens: [ \");\n        for (int i = 0; i < (int) prompt_tokens.size(); ++i) {\n            fprintf(stderr, \"%d \", prompt_tokens[i]);\n        }\n        fprintf(stderr, \"]\\n\");\n    }\n\n    for (int f = 0; f < (int) params.fname_inp.size(); ++f) {\n        const auto fname_inp = params.fname_inp[f];\n\n        std::vector<float> pcmf32; // mono-channel F32 PCM\n        std::vector<std::vector<float>> pcmf32s; // stereo-channel F32 PCM\n\n        // WAV input\n        {\n            drwav wav;\n            std::vector<uint8_t> wav_data; // used for pipe input from stdin\n\n            if (fname_inp == \"-\") {\n                {\n                    uint8_t buf[1024];\n                    while (true)\n                    {\n                        const size_t n = fread(buf, 1, sizeof(buf), stdin);\n                        if (n == 0) {\n                            break;\n                        }\n                        wav_data.insert(wav_data.end(), buf, buf + n);\n                    }\n                }\n\n                if (drwav_init_memory(&wav, wav_data.data(), wav_data.size(), nullptr) == false) {\n                    fprintf(stderr, \"error: failed to open WAV file from stdin\\n\");\n                    return 4;\n                }\n\n                fprintf(stderr, \"%s: read %zu bytes from stdin\\n\", __func__, wav_data.size());\n            }\n            else if (drwav_init_file(&wav, fname_inp.c_str(), nullptr) == false) {\n                fprintf(stderr, \"error: failed to open '%s' as WAV file\\n\", fname_inp.c_str());\n                return 5;\n            }\n\n            if (wav.channels != 1 && wav.channels != 2) {\n                fprintf(stderr, \"%s: WAV file '%s' must be mono or stereo\\n\", argv[0], fname_inp.c_str());\n                return 6;\n            }\n\n            if (params.diarize && wav.channels != 2 && params.no_timestamps == false) {\n                fprintf(stderr, \"%s: WAV file '%s' must be stereo for diarization and timestamps have to be enabled\\n\", argv[0], fname_inp.c_str());\n                return 6;\n            }\n\n            if (wav.sampleRate != WHISPER_SAMPLE_RATE) {\n                fprintf(stderr, \"%s: WAV file '%s' must be 16 kHz\\n\", argv[0], fname_inp.c_str());\n                return 8;\n            }\n\n            if (wav.bitsPerSample != 16) {\n                fprintf(stderr, \"%s: WAV file '%s' must be 16-bit\\n\", argv[0], fname_inp.c_str());\n                return 9;\n            }\n\n            const uint64_t n = wav_data.empty() ? wav.totalPCMFrameCount : wav_data.size()/(wav.channels*wav.bitsPerSample/8);\n\n            std::vector<int16_t> pcm16;\n            pcm16.resize(n*wav.channels);\n            drwav_read_pcm_frames_s16(&wav, n, pcm16.data());\n            drwav_uninit(&wav);\n\n            // convert to mono, float\n            pcmf32.resize(n);\n            if (wav.channels == 1) {\n                for (uint64_t i = 0; i < n; i++) {\n                    pcmf32[i] = float(pcm16[i])/32768.0f;\n                }\n            } else {\n                for (uint64_t i = 0; i < n; i++) {\n                    pcmf32[i] = float(pcm16[2*i] + pcm16[2*i + 1])/65536.0f;\n                }\n            }\n\n            if (params.diarize) {\n                // convert to stereo, float\n                pcmf32s.resize(2);\n\n                pcmf32s[0].resize(n);\n                pcmf32s[1].resize(n);\n                for (uint64_t i = 0; i < n; i++) {\n                    pcmf32s[0][i] = float(pcm16[2*i])/32768.0f;\n                    pcmf32s[1][i] = float(pcm16[2*i + 1])/32768.0f;\n                }\n            }\n        }\n\n        // print system information\n        {\n            fprintf(stderr, \"\\n\");\n            fprintf(stderr, \"system_info: n_threads = %d / %d | %s\\n\",\n                    params.n_threads*params.n_processors, std::thread::hardware_concurrency(), whisper_print_system_info());\n        }\n\n        // print some info about the processing\n        {\n            fprintf(stderr, \"\\n\");\n            if (!whisper_is_multilingual(ctx)) {\n                if (params.language != \"en\" || params.translate) {\n                    params.language = \"en\";\n                    params.translate = false;\n                    fprintf(stderr, \"%s: WARNING: model is not multilingual, ignoring language and translation options\\n\", __func__);\n                }\n            }\n            fprintf(stderr, \"%s: processing '%s' (%d samples, %.1f sec), %d threads, %d processors, lang = %s, task = %s, timestamps = %d ...\\n\",\n                    __func__, fname_inp.c_str(), int(pcmf32.size()), float(pcmf32.size())/WHISPER_SAMPLE_RATE,\n                    params.n_threads, params.n_processors,\n                    params.language.c_str(),\n                    params.translate ? \"translate\" : \"transcribe\",\n                    params.no_timestamps ? 0 : 1);\n\n            fprintf(stderr, \"\\n\");\n        }\n\n        // run the inference\n        {\n            whisper_full_params wparams = whisper_full_default_params(WHISPER_SAMPLING_GREEDY);\n\n            wparams.print_realtime   = false;\n            wparams.print_progress   = params.print_progress;\n            wparams.print_timestamps = !params.no_timestamps;\n            wparams.print_special    = params.print_special;\n            wparams.translate        = params.translate;\n            wparams.language         = params.language.c_str();\n            wparams.n_threads        = params.n_threads;\n            wparams.n_max_text_ctx   = params.max_context >= 0 ? params.max_context : wparams.n_max_text_ctx;\n            wparams.offset_ms        = params.offset_t_ms;\n            wparams.duration_ms      = params.duration_ms;\n\n            wparams.token_timestamps = params.output_wts || params.max_len > 0;\n            wparams.thold_pt         = params.word_thold;\n            wparams.max_len          = params.output_wts && params.max_len == 0 ? 60 : params.max_len;\n\n            wparams.speed_up         = params.speed_up;\n\n            wparams.prompt_tokens    = prompt_tokens.empty() ? nullptr : prompt_tokens.data();\n            wparams.prompt_n_tokens  = prompt_tokens.empty() ? 0       : prompt_tokens.size();\n\n            whisper_print_user_data user_data = { &params, &pcmf32s };\n\n            // this callback is called on each new segment\n            if (!wparams.print_realtime) {\n                wparams.new_segment_callback           = whisper_print_segment_callback;\n                wparams.new_segment_callback_user_data = &user_data;\n            }\n\n            // example for abort mechanism\n            // in this example, we do not abort the processing, but we could if the flag is set to true\n            // the callback is called before every encoder run - if it returns false, the processing is aborted\n            {\n                static bool is_aborted = false; // NOTE: this should be atomic to avoid data race\n\n                wparams.encoder_begin_callback = [](struct whisper_context * /*ctx*/, void * user_data) {\n                    bool is_aborted = *(bool*)user_data;\n                    return !is_aborted;\n                };\n                wparams.encoder_begin_callback_user_data = &is_aborted;\n            }\n\n            if (whisper_full_parallel(ctx, wparams, pcmf32.data(), pcmf32.size(), params.n_processors) != 0) {\n                fprintf(stderr, \"%s: failed to process audio\\n\", argv[0]);\n                return 10;\n            }\n        }\n\n        // output stuff\n        {\n            printf(\"\\n\");\n\n            // output to text file\n            if (params.output_txt) {\n                const auto fname_txt = fname_inp + \".txt\";\n                output_txt(ctx, fname_txt.c_str());\n            }\n\n            // output to VTT file\n            if (params.output_vtt) {\n                const auto fname_vtt = fname_inp + \".vtt\";\n                output_vtt(ctx, fname_vtt.c_str());\n            }\n\n            // output to SRT file\n            if (params.output_srt) {\n                const auto fname_srt = fname_inp + \".srt\";\n                output_srt(ctx, fname_srt.c_str(), params);\n            }\n\n            // output to WTS file\n            if (params.output_wts) {\n                const auto fname_wts = fname_inp + \".wts\";\n                output_wts(ctx, fname_wts.c_str(), fname_inp.c_str(), params, float(pcmf32.size() + 1000)/WHISPER_SAMPLE_RATE);\n            }\n        }\n    }\n\n    whisper_print_timings(ctx);\n    whisper_free(ctx);\n\n    return 0;\n}\n"
  },
  {
    "path": "Examples/TranscribeCS/AnsiCodes.cs",
    "content": "﻿using System.Runtime.InteropServices;\n\n/// <summary>Utility class to setup console coloring with ANSI codes.</summary>\n/// <remarks>The feature requires Windows 10 or newer</remarks>\nstatic class AnsiCodes\n{\n\tconst string dll = \"kernel32.dll\";\n\n\t[DllImport( dll, SetLastError = true )]\n\tstatic extern IntPtr GetStdHandle( int nStdHandle );\n\n\tconst int STD_OUTPUT_HANDLE = -11;\n\n\t[Flags]\n\tenum ConsoleModes: uint\n\t{\n\t\t// Input flags\n\t\tENABLE_PROCESSED_INPUT = 0x0001,\n\t\tENABLE_LINE_INPUT = 0x0002,\n\t\tENABLE_ECHO_INPUT = 0x0004,\n\t\tENABLE_WINDOW_INPUT = 0x0008,\n\t\tENABLE_MOUSE_INPUT = 0x0010,\n\t\tENABLE_INSERT_MODE = 0x0020,\n\t\tENABLE_QUICK_EDIT_MODE = 0x0040,\n\t\tENABLE_EXTENDED_FLAGS = 0x0080,\n\t\tENABLE_AUTO_POSITION = 0x0100,\n\t\tENABLE_VIRTUAL_TERMINAL_INPUT = 0x0200,\n\n\t\t// Output flags\n\t\tENABLE_PROCESSED_OUTPUT = 0x0001,\n\t\tENABLE_WRAP_AT_EOL_OUTPUT = 0x0002,\n\t\tENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004,\n\t\tDISABLE_NEWLINE_AUTO_RETURN = 0x0008,\n\t\tENABLE_LVB_GRID_WORLDWIDE = 0x0010\n\t}\n\n\t[DllImport( dll, SetLastError = true )]\n\tstatic extern bool GetConsoleMode( IntPtr hConsoleHandle, out ConsoleModes mode );\n\n\t[DllImport( dll, SetLastError = true )]\n\tstatic extern bool SetConsoleMode( IntPtr hConsoleHandle, ConsoleModes mode );\n\n\tstatic AnsiCodes()\n\t{\n\t\tIntPtr h = GetStdHandle( STD_OUTPUT_HANDLE );\n\t\tIntPtr INVALID_HANDLE_VALUE = (IntPtr)( -1 );\n\t\tif( h == INVALID_HANDLE_VALUE )\n\t\t\treturn;\n\n\t\tif( !GetConsoleMode( h, out ConsoleModes mode ) )\n\t\t\treturn;\n\n\t\tif( mode.HasFlag( ConsoleModes.ENABLE_VIRTUAL_TERMINAL_PROCESSING ) )\n\t\t{\n\t\t\tenabled = true;\n\t\t\treturn;\n\t\t}\n\n\t\tmode |= ConsoleModes.ENABLE_VIRTUAL_TERMINAL_PROCESSING;\n\t\tif( SetConsoleMode( h, mode ) )\n\t\t{\n\t\t\tenabled = true;\n\t\t\treturn;\n\t\t}\n\t}\n\n\tpublic static readonly bool enabled = false;\n}"
  },
  {
    "path": "Examples/TranscribeCS/CommandLineArgs.cs",
    "content": "﻿using System.Globalization;\nusing System.Reflection;\nusing Whisper;\n\nnamespace TranscribeCS\n{\n\tsealed record class CommandLineArgs\n\t{\n\t\tpublic int n_threads = Environment.ProcessorCount;\n\t\tpublic int offset_t_ms = 0;\n\t\tpublic int offset_n = 0;\n\t\tpublic int duration_ms = 0;\n\t\tpublic int max_context = -1;\n\t\tpublic int max_len = 0;\n\n\t\tpublic float word_thold = 0.01f;\n\n\t\tpublic bool speed_up = false;\n\t\tpublic bool translate = false;\n\t\tpublic bool diarize = false;\n\t\tpublic bool output_txt = false;\n\t\tpublic bool output_vtt = false;\n\t\tpublic bool output_srt = false;\n\t\tpublic bool print_special = false;\n\t\tpublic bool print_progress = false;\n\t\tpublic bool print_colors = true;\n\t\tpublic bool no_timestamps = false;\n\t\tpublic string? prompt = null;\n\n\t\tpublic eLanguage language = eLanguage.English;\n\t\tpublic string model = string.Empty;\n\t\tpublic readonly List<string> fileNames = new List<string>();\n\n\t\tconst bool output_wts = false;\n\t\tpublic void apply( ref Parameters p )\n\t\t{\n\t\t\tp.setFlag( eFullParamsFlags.PrintRealtime, false );\n\t\t\tp.setFlag( eFullParamsFlags.PrintProgress, print_progress );\n\t\t\tp.setFlag( eFullParamsFlags.PrintTimestamps, !no_timestamps );\n\t\t\tp.setFlag( eFullParamsFlags.PrintSpecial, print_special );\n\t\t\tp.setFlag( eFullParamsFlags.Translate, translate );\n\t\t\tp.language = language;\n\t\t\tp.cpuThreads = n_threads;\n\t\t\tif( max_context >= 0 )\n\t\t\t\tp.n_max_text_ctx = max_context;\n\t\t\tp.offset_ms = offset_t_ms;\n\t\t\tp.duration_ms = duration_ms;\n\t\t\tp.setFlag( eFullParamsFlags.TokenTimestamps, output_wts || max_len > 0 );\n\t\t\tp.thold_pt = word_thold;\n\t\t\tp.max_len = output_wts && max_len == 0 ? 60 : max_len;\n\t\t\tp.setFlag( eFullParamsFlags.SpeedupAudio, speed_up );\n\t\t}\n\n\t\tpublic eResultFlags resultFlags()\n\t\t{\n\t\t\teResultFlags flags = eResultFlags.None;\n\t\t\tbool wts = output_wts || max_len > 0;\n\t\t\tif( !no_timestamps || wts )\n\t\t\t\tflags |= eResultFlags.Timestamps;\n\t\t\tif( wts || print_colors )\n\t\t\t\tflags |= eResultFlags.Tokens;\n\t\t\treturn flags;\n\t\t}\n\n\t\tstatic eLanguage parseLanguage( string lang ) =>\n\t\t\tLibrary.languageFromCode( lang ) ?? throw new ArgumentException( $\"Unknown language code \\\"{lang}\\\"\" );\n\n\t\tpublic CommandLineArgs( string[] argv )\n\t\t{\n\t\t\tfor( int i = 0; i < argv.Length; i++ )\n\t\t\t{\n\t\t\t\tstring arg = argv[ i ];\n\t\t\t\tif( arg[ 0 ] != '-' )\n\t\t\t\t{\n\t\t\t\t\tfileNames.Add( arg );\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif( arg == \"-h\" || arg == \"--help\" )\n\t\t\t\t{\n\t\t\t\t\tprintUsage();\n\t\t\t\t\tthrow new OperationCanceledException();\n\t\t\t\t}\n\t\t\t\telse if( arg == \"-t\" || arg == \"--threads\" ) n_threads = int.Parse( argv[ ++i ] );\n\t\t\t\telse if( arg == \"-ot\" || arg == \"--offset-t\" ) offset_t_ms = int.Parse( argv[ ++i ] );\n\t\t\t\telse if( arg == \"-on\" || arg == \"--offset-n\" ) offset_n = int.Parse( argv[ ++i ] );\n\t\t\t\telse if( arg == \"-d\" || arg == \"--duration\" ) duration_ms = int.Parse( argv[ ++i ] );\n\t\t\t\telse if( arg == \"-mc\" || arg == \"--max-context\" ) max_context = int.Parse( argv[ ++i ] );\n\t\t\t\telse if( arg == \"-ml\" || arg == \"--max-len\" ) max_len = int.Parse( argv[ ++i ] );\n\t\t\t\telse if( arg == \"-wt\" || arg == \"--word-thold\" ) word_thold = float.Parse( argv[ ++i ], CultureInfo.InvariantCulture );\n\t\t\t\telse if( arg == \"-su\" || arg == \"--speed-up\" ) speed_up = true;\n\t\t\t\telse if( arg == \"-tr\" || arg == \"--translate\" ) translate = true;\n\t\t\t\telse if( arg == \"-di\" || arg == \"--diarize\" ) diarize = true;\n\t\t\t\telse if( arg == \"-otxt\" || arg == \"--output-txt\" ) output_txt = true;\n\t\t\t\telse if( arg == \"-ovtt\" || arg == \"--output-vtt\" ) output_vtt = true;\n\t\t\t\telse if( arg == \"-osrt\" || arg == \"--output-srt\" ) output_srt = true;\n\t\t\t\telse if( arg == \"-ps\" || arg == \"--print-special\" ) print_special = true;\n\t\t\t\telse if( arg == \"-nc\" || arg == \"--no-colors\" ) print_colors = false;\n\t\t\t\telse if( arg == \"-pp\" || arg == \"--print-progress\" ) print_progress = true;\n\t\t\t\telse if( arg == \"-nt\" || arg == \"--no-timestamps\" ) no_timestamps = true;\n\t\t\t\telse if( arg == \"-l\" || arg == \"--language\" ) language = parseLanguage( argv[ ++i ] );\n\t\t\t\telse if( arg == \"--prompt\" ) prompt = argv[ ++i ];\n\t\t\t\telse if( arg == \"-m\" || arg == \"--model\" ) model = argv[ ++i ];\n\t\t\t\telse if( arg == \"-f\" || arg == \"--file\" ) fileNames.Add( argv[ ++i ] );\n\t\t\t\telse\n\t\t\t\t\tthrow new ArgumentException( $\"Unknown argument: \\\"{arg}\\\"\" );\n\t\t\t}\n\t\t\tif( string.IsNullOrWhiteSpace( model ) )\n\t\t\t\tthrow new ArgumentException( \"The model file is not provided in the arguments\" );\n\t\t\tif( !File.Exists( model ) )\n\t\t\t\tthrow new FileNotFoundException( \"Model not found\", model );\n\t\t\tif( fileNames.Count <= 0 )\n\t\t\t\tthrow new ArgumentException( \"Please supply at least 1 input audio file to process\" );\n\t\t}\n\n\t\tstatic string cstr( bool b ) => b.ToString();\n\n\t\tvoid printUsage()\n\t\t{\n\t\t\tConsole.WriteLine();\n\n\t\t\tConsole.WriteLine( \"usage: {0} [options] file0.mp3 file1.wma ...\", Path.GetFileName( Assembly.GetExecutingAssembly().Location ) );\n\t\t\tConsole.WriteLine();\n\t\t\tConsole.WriteLine( \"options:\" );\n\t\t\tConsole.WriteLine( \"  -h,       --help          [default] show this help message and exit\" );\n\t\t\tConsole.WriteLine( \"  -t N,     --threads N     [{0,-7:D}] number of threads to use during computation\", n_threads );\n\t\t\tConsole.WriteLine( \"  -ot N,    --offset-t N    [{0,-7:D}] time offset in milliseconds\", offset_t_ms );\n\t\t\tConsole.WriteLine( \"  -on N,    --offset-n N    [{0,-7:D}] segment index offset\", offset_n );\n\t\t\tConsole.WriteLine( \"  -d  N,    --duration N    [{0,-7:D}] duration of audio to process in milliseconds\", duration_ms );\n\t\t\tConsole.WriteLine( \"  -mc N,    --max-context N [{0,-7:D}] maximum number of text context tokens to store\", max_context );\n\t\t\tConsole.WriteLine( \"  -ml N,    --max-len N     [{0,-7:D}] maximum segment length in characters\", max_len );\n\t\t\tConsole.WriteLine( \"  -wt N,    --word-thold N  [{0,-7:F2}] word timestamp probability threshold\", word_thold );\n\t\t\tConsole.WriteLine( \"  -su,      --speed-up      [{0,-7}] speed up audio by x2 (reduced accuracy)\", cstr( speed_up ) );\n\t\t\tConsole.WriteLine( \"  -tr,      --translate     [{0,-7}] translate from source language to english\", cstr( translate ) );\n\t\t\tConsole.WriteLine( \"  -di,      --diarize       [{0,-7}] stereo audio diarization\", cstr( diarize ) );\n\t\t\tConsole.WriteLine( \"  -otxt,    --output-txt    [{0,-7}] output result in a text file\", cstr( output_txt ) );\n\t\t\tConsole.WriteLine( \"  -ovtt,    --output-vtt    [{0,-7}] output result in a vtt file\", cstr( output_vtt ) );\n\t\t\tConsole.WriteLine( \"  -osrt,    --output-srt    [{0,-7}] output result in a srt file\", cstr( output_srt ) );\n\t\t\tConsole.WriteLine( \"  -ps,      --print-special [{0,-7}] print special tokens\", cstr( print_special ) );\n\t\t\tConsole.WriteLine( \"  -nc,      --no-colors     [{0,-7}] do not print colors\", cstr( !print_colors ) );\n\t\t\tConsole.WriteLine( \"  -nt,      --no-timestamps [{0,-7}] do not print timestamps\", cstr( no_timestamps ) );\n\t\t\tConsole.WriteLine( \"  -l LANG,  --language LANG [{0,-7}] spoken language\", language.getCode() );\n\t\t\tConsole.WriteLine( \"            --prompt PROMPT [       ] initial prompt\" );\n\t\t\tConsole.WriteLine( \"  -m FNAME, --model FNAME   [{0,-7}] model path\", model );\n\t\t\tConsole.WriteLine( \"  -f FNAME, --file FNAME    [{0,-7}] path of the input audio file\", \"\" );\n\t\t}\n\t}\n}"
  },
  {
    "path": "Examples/TranscribeCS/Readme.txt",
    "content": "﻿This example builds .NET 6 console application which shows how to transcribe or translate audio files with the .NET wrapper."
  },
  {
    "path": "Examples/TranscribeCS/Transcribe.cs",
    "content": "﻿using System.Globalization;\nusing Whisper;\n\nnamespace TranscribeCS\n{\n\t/// <summary>Implementation of Callbacks abstract class, to print these segments as soon as they’re produced by the library.</summary>\n\tsealed class Transcribe: Callbacks\n\t{\n\t\treadonly CommandLineArgs args;\n\t\treadonly eResultFlags resultFlags;\n\n\t\tpublic Transcribe( CommandLineArgs args )\n\t\t{\n\t\t\tthis.args = args;\n\t\t\tresultFlags = args.resultFlags();\n\t\t\tConsole.OutputEncoding = System.Text.Encoding.UTF8;\n\t\t}\n\n\t\t// Terminal color map. 10 colors grouped in ranges [0.0, 0.1, ..., 0.9]\n\t\t// Lowest is red, middle is yellow, highest is green.\n\t\treadonly string[] k_colors = new string[]\n\t\t{\n\t\t\t\"\\x1B[38;5;196m\", \"\\x1B[38;5;202m\", \"\\x1B[38;5;208m\", \"\\x1B[38;5;214m\", \"\\x1B[38;5;220m\",\n\t\t\t\"\\x1B[38;5;226m\", \"\\x1B[38;5;190m\", \"\\x1B[38;5;154m\", \"\\x1B[38;5;118m\", \"\\x1B[38;5;82m\"\n\t\t};\n\n\t\tint colorIndex( in sToken tok )\n\t\t{\n\t\t\tfloat p = tok.probability;\n\t\t\tfloat p3 = p * p * p;\n\t\t\tint col = (int)( p3 * k_colors.Length );\n\t\t\tcol = Math.Clamp( col, 0, k_colors.Length - 1 );\n\t\t\treturn col;\n\t\t}\n\n\t\tpublic static string printTime( TimeSpan ts ) =>\n\t\t\tts.ToString( \"hh':'mm':'ss'.'fff\", CultureInfo.InvariantCulture );\n\t\tpublic static string printTimeWithComma( TimeSpan ts ) =>\n\t\t\tts.ToString( \"hh':'mm':'ss','fff\", CultureInfo.InvariantCulture );\n\n\t\tprotected override void onNewSegment( Context sender, int countNew )\n\t\t{\n\t\t\tTranscribeResult res = sender.results( resultFlags );\n\t\t\tReadOnlySpan<sToken> tokens = res.tokens;\n\n\t\t\tint s0 = res.segments.Length - countNew;\n\t\t\tif( s0 == 0 )\n\t\t\t\tConsole.WriteLine();\n\n\t\t\tfor( int i = s0; i < res.segments.Length; i++ )\n\t\t\t{\n\t\t\t\tsSegment seg = res.segments[ i ];\n\n\t\t\t\tif( args.no_timestamps )\n\t\t\t\t{\n\t\t\t\t\tif( args.print_colors && AnsiCodes.enabled )\n\t\t\t\t\t{\n\t\t\t\t\t\tforeach( sToken tok in res.getTokens( seg ) )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif( !args.print_special && tok.hasFlag( eTokenFlags.Special ) )\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\tConsole.Write( \"{0}{1}{2}\", k_colors[ colorIndex( tok ) ], tok.text, \"\\x1B[0m\" );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tConsole.Write( seg.text );\n\t\t\t\t\tConsole.Out.Flush();\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tstring speaker = \"\";\n\t\t\t\tif( args.diarize )\n\t\t\t\t{\n\t\t\t\t\tspeaker = sender.detectSpeaker( seg.time ) switch\n\t\t\t\t\t{\n\t\t\t\t\t\teSpeakerChannel.Unsure => \"(speaker ?)\",\n\t\t\t\t\t\teSpeakerChannel.Left => \"(speaker 0)\",\n\t\t\t\t\t\teSpeakerChannel.Right => \"(speaker 1)\",\n\t\t\t\t\t\t_ => \"\"\n\t\t\t\t\t};\n\t\t\t\t}\n\n\t\t\t\tif( args.print_colors && AnsiCodes.enabled )\n\t\t\t\t{\n\t\t\t\t\tConsole.Write( \"[{0} --> {1}] {2} \", printTime( seg.time.begin ), printTime( seg.time.end ), speaker );\n\t\t\t\t\tforeach( sToken tok in res.getTokens( seg ) )\n\t\t\t\t\t{\n\t\t\t\t\t\tif( !args.print_special && tok.hasFlag( eTokenFlags.Special ) )\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\tConsole.Write( \"{0}{1}{2}\", k_colors[ colorIndex( tok ) ], tok.text, \"\\x1B[0m\" );\n\t\t\t\t\t}\n\t\t\t\t\tConsole.WriteLine();\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tConsole.WriteLine( \"[{0} --> {1}] {2} {3}\", printTime( seg.time.begin ), printTime( seg.time.end ), speaker, seg.text );\n\t\t\t}\n\t\t}\n\t}\n}"
  },
  {
    "path": "Examples/TranscribeCS/TranscribeCS.cs",
    "content": "﻿namespace TranscribeCS;\nusing Whisper;\n\nenum eFileOpenMode: byte\n{\n\t/// <summary>Decode chunks of audio directly from the file, as needed</summary>\n\tStreamFile,\n\n\t/// <summary>Decode the complete file into FP32 PCM buffer, transcribe from there</summary>\n\tBufferPCM,\n\n\t/// <summary>Load the complete input file into a buffer, decode chunks of audio from that memory buffer as needed</summary>\n\tBufferFile\n}\n\nstatic class Program\n{\n\tstatic readonly eFileOpenMode openMode = eFileOpenMode.StreamFile;\n\t// static readonly eFileOpenMode openMode = eFileOpenMode.BufferPCM;\n\t// static readonly eFileOpenMode openMode = eFileOpenMode.BufferFile;\n\n\tstatic int Main( string[] args )\n\t{\n\t\ttry\n\t\t{\n\t\t\t// dbgListGPUs();\n\n\t\t\tCommandLineArgs cla;\n\t\t\ttry\n\t\t\t{\n\t\t\t\tcla = new CommandLineArgs( args );\n\t\t\t}\n\t\t\tcatch( OperationCanceledException )\n\t\t\t{\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t\tconst eLoggerFlags loggerFlags = eLoggerFlags.UseStandardError | eLoggerFlags.SkipFormatMessage;\n\t\t\tLibrary.setLogSink( eLogLevel.Debug, loggerFlags );\n\n\t\t\tusing iModel model = Library.loadModel( cla.model );\n\t\t\tint[]? prompt = null;\n\t\t\tif( !string.IsNullOrEmpty( cla.prompt ) )\n\t\t\t\tprompt = model.tokenize( cla.prompt );\n\n\t\t\tusing Context context = model.createContext();\n\t\t\tcla.apply( ref context.parameters );\n\t\t\t// When there're multiple input files, assuming they're independent clips\n\t\t\tcontext.parameters.setFlag( eFullParamsFlags.NoContext, true );\n\t\t\tusing iMediaFoundation mf = Library.initMediaFoundation();\n\t\t\tTranscribe transcribe = new Transcribe( cla );\n\n\t\t\tforeach( string audioFile in cla.fileNames )\n\t\t\t{\n\t\t\t\tif( openMode == eFileOpenMode.StreamFile )\n\t\t\t\t{\n\t\t\t\t\tusing iAudioReader reader = mf.openAudioFile( audioFile, cla.diarize );\n\t\t\t\t\tcontext.runFull( reader, transcribe, null, prompt );\n\t\t\t\t}\n\t\t\t\telse if( openMode == eFileOpenMode.BufferPCM )\n\t\t\t\t{\n\t\t\t\t\tusing iAudioBuffer buffer = mf.loadAudioFile( audioFile, cla.diarize );\n\t\t\t\t\tcontext.runFull( buffer, transcribe, prompt );\n\t\t\t\t}\n\t\t\t\telse if( openMode == eFileOpenMode.BufferFile )\n\t\t\t\t{\n\t\t\t\t\tbyte[] buffer = File.ReadAllBytes( audioFile );\n\t\t\t\t\tusing iAudioReader reader = mf.loadAudioFileData( buffer, cla.diarize );\n\t\t\t\t\tcontext.runFull( reader, transcribe, null, prompt );\n\t\t\t\t}\n\n\t\t\t\t// When asked to, produce these text files\n\t\t\t\tif( cla.output_txt )\n\t\t\t\t\twriteTextFile( context, audioFile );\n\t\t\t\tif( cla.output_srt )\n\t\t\t\t\twriteSubRip( context, audioFile, cla );\n\t\t\t\tif( cla.output_vtt )\n\t\t\t\t\twriteWebVTT( context, audioFile );\n\t\t\t}\n\n\t\t\tcontext.timingsPrint();\n\t\t\treturn 0;\n\t\t}\n\t\tcatch( Exception ex )\n\t\t{\n\t\t\tConsole.WriteLine( ex.Message );\n\t\t\treturn ex.HResult;\n\t\t}\n\t}\n\n\tstatic void writeTextFile( Context context, string audioPath )\n\t{\n\t\tusing var stream = File.CreateText( Path.ChangeExtension( audioPath, \".txt\" ) );\n\t\tforeach( sSegment seg in context.results().segments )\n\t\t\tstream.WriteLine( seg.text );\n\t}\n\n\tstatic void writeSubRip( Context context, string audioPath, CommandLineArgs cliArgs )\n\t{\n\t\tusing var stream = File.CreateText( Path.ChangeExtension( audioPath, \".srt\" ) );\n\t\tvar segments = context.results( eResultFlags.Timestamps ).segments;\n\n\t\tfor( int i = 0; i < segments.Length; i++ )\n\t\t{\n\t\t\tstream.WriteLine( i + 1 + cliArgs.offset_n );\n\t\t\tsSegment seg = segments[ i ];\n\t\t\tstring begin = Transcribe.printTimeWithComma( seg.time.begin );\n\t\t\tstring end = Transcribe.printTimeWithComma( seg.time.end );\n\t\t\tstream.WriteLine( \"{0} --> {1}\", begin, end );\n\t\t\tstream.WriteLine( seg.text );\n\t\t\tstream.WriteLine();\n\t\t}\n\t}\n\n\tstatic void writeWebVTT( Context context, string audioPath )\n\t{\n\t\tusing var stream = File.CreateText( Path.ChangeExtension( audioPath, \".vtt\" ) );\n\t\tstream.WriteLine( \"WEBVTT\" );\n\t\tstream.WriteLine();\n\n\t\tforeach( sSegment seg in context.results( eResultFlags.Timestamps ).segments )\n\t\t{\n\t\t\tstring begin = Transcribe.printTime( seg.time.begin );\n\t\t\tstring end = Transcribe.printTime( seg.time.end );\n\t\t\tstream.WriteLine( \"{0} --> {1}\", begin, end );\n\t\t\tstream.WriteLine( seg.text );\n\t\t\tstream.WriteLine();\n\t\t}\n\t}\n\n\tstatic void dbgListGPUs()\n\t{\n\t\tstring[] list = Library.listGraphicAdapters();\n\t\tConsole.WriteLine( \"    Graphics Adapters:\\n{0}\", string.Join( \"\\n\", list ) );\n\t}\n}"
  },
  {
    "path": "Examples/TranscribeCS/TranscribeCS.csproj",
    "content": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\t<PropertyGroup>\n\t\t<OutputType>Exe</OutputType>\n\t\t<TargetFramework>net6.0-windows</TargetFramework>\n\t\t<ImplicitUsings>enable</ImplicitUsings>\n\t\t<Nullable>enable</Nullable>\n\t\t<CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>\n\t\t<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>\n\t\t<Platforms>x64</Platforms>\n\t</PropertyGroup>\n\t<ItemGroup>\n\t\t<Content Include=\"..\\..\\x64\\$(Configuration)\\Whisper.dll\" Link=\"Whisper.dll\">\n\t\t\t<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n\t\t</Content>\n\t</ItemGroup>\n\t<ItemGroup>\n\t\t<ProjectReference Include=\"..\\..\\WhisperNet\\WhisperNet.csproj\" />\n\t</ItemGroup>\n</Project>"
  },
  {
    "path": "Examples/WhisperDesktop/AppState.cpp",
    "content": "#include \"stdafx.h\"\n#include \"AppState.h\"\n#include \"Utils/miscUtils.h\"\n#include <commctrl.h>\n#pragma comment(lib, \"Comctl32.lib\")\n#include \"CircleIndicator.h\"\n\nnamespace\n{\n\tstatic const HKEY regKeyRoot = HKEY_CURRENT_USER;\n\tconst LPCTSTR regKey = LR\"(SOFTWARE\\const.me\\WhisperDesktop)\";\n\tconst LPCTSTR regValPath = L\"modelPath\";\n\tconst LPCTSTR regValImpl = L\"modelImpl\";\n\tconst LPCTSTR regValLang = L\"language\";\n\tconst LPCTSTR regValLastScreen = L\"screen\";\n\tconst LPCTSTR regValGpuFlags = L\"gpuFlags\";\n\n\tstatic HRESULT readString( CRegKey& k, LPCTSTR name, CString& rdi )\n\t{\n\t\tULONG nChars = 0;\n\t\tLSTATUS lss = k.QueryStringValue( name, nullptr, &nChars );\n\t\tif( lss != ERROR_SUCCESS )\n\t\t\treturn HRESULT_FROM_WIN32( lss );\n\t\tif( nChars == 0 )\n\t\t{\n\t\t\trdi = L\"\";\n\t\t\treturn S_FALSE;\n\t\t}\n\n\t\tlss = k.QueryStringValue( name, rdi.GetBufferSetLength( nChars ), &nChars );\n\t\trdi.ReleaseBuffer();\n\t\tif( lss != ERROR_SUCCESS )\n\t\t\treturn HRESULT_FROM_WIN32( lss );\n\n\t\treturn S_OK;\n\t}\n\n\tusing Whisper::eModelImplementation;\n}\n\nHRESULT AppState::startup()\n{\n\tHRESULT hr = CoInitializeEx( nullptr, COINIT_MULTITHREADED );\n\tif( FAILED( hr ) )\n\t{\n\t\treportFatalError( \"CoInitializeEx failed\", hr );\n\t\treturn hr;\n\t}\n\tcoInit = true;\n\n\tLSTATUS lss = registryKey.Create( regKeyRoot, regKey );\n\tif( lss != ERROR_SUCCESS )\n\t{\n\t\thr = HRESULT_FROM_WIN32( lss );\n\t\treportFatalError( \"Unable to open the registry key\", hr );\n\t\treturn hr;\n\t}\n\n\tINITCOMMONCONTROLSEX init;\n\tinit.dwSize = sizeof( init );\n\tinit.dwICC = ICC_LINK_CLASS | ICC_PROGRESS_CLASS | ICC_STANDARD_CLASSES | ICC_TAB_CLASSES;\n\tconst BOOL icc = InitCommonControlsEx( &init );\n\tif( !icc )\n\t{\n\t\treportFatalError( \"InitCommonControlsEx failed\", HRESULT_FROM_WIN32( GetLastError() ) );\n\t\treturn E_FAIL;\n\t}\n\n\thr = initMediaFoundation( &mediaFoundation );\n\tif( FAILED( hr ) )\n\t{\n\t\treportFatalError( \"Unable to initialize Media Foundation runtime\", hr );\n\t\treturn hr;\n\t}\n\n\thr = console.initialize();\n\tif( FAILED( hr ) )\n\t{\n\t\treportFatalError( \"Unable to initialize logging\", hr );\n\t\treturn hr;\n\t}\n\n\thr = CircleIndicator::registerClass();\n\tif( FAILED( hr ) )\n\t{\n\t\treportFatalError( \"Unable to register custom controls\", hr );\n\t\treturn hr;\n\t}\n\tappIcon.LoadIcon( IDI_WHISPERDESKTOP );\n\treturn S_OK;\n}\n\nAppState::~AppState()\n{\n\tif( coInit )\n\t{\n\t\tCoUninitialize();\n\t\tcoInit = false;\n\t}\n}\n\nHRESULT AppState::findModelSource()\n{\n\tCHECK( readString( registryKey, regValPath, source.path ) );\n\n\t{\n\t\tCAtlFile file;\n\t\tCHECK( file.Create( source.path, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING ) );\n\t\tULONGLONG len;\n\t\tCHECK( file.GetSize( len ) );\n\t\tsource.sizeInBytes = len;\n\t}\n\n\tCString impl;\n\tCHECK( readString( registryKey, regValImpl, impl ) );\n\tCHECK( implParse( impl, source.impl ) );\n\tsource.found = true;\n\treturn S_OK;\n}\n\nHRESULT AppState::saveModelSource()\n{\n\tLSTATUS lss = registryKey.SetStringValue( regValPath, source.path );\n\tif( lss != ERROR_SUCCESS )\n\t\treturn HRESULT_FROM_WIN32( lss );\n\n\tLPCTSTR impl = implString( source.impl );\n\tif( nullptr == impl )\n\t\treturn E_INVALIDARG;\n\tlss = registryKey.SetStringValue( regValImpl, impl );\n\tif( lss != ERROR_SUCCESS )\n\t\treturn HRESULT_FROM_WIN32( lss );\n\n\treturn S_OK;\n}\n\nuint32_t AppState::languageRead()\n{\n\tDWORD dw;\n\tLSTATUS lss = registryKey.QueryDWORDValue( regValLang, dw );\n\tif( lss == ERROR_SUCCESS )\n\t\treturn dw;\n\treturn UINT_MAX;\n}\n\nvoid AppState::languageWrite( uint32_t key )\n{\n\tregistryKey.SetDWORDValue( regValLang, key );\n}\n\nCString AppState::stringLoad( LPCTSTR name )\n{\n\tCString res;\n\treadString( registryKey, name, res );\n\treturn res;\n}\nvoid AppState::stringStore( LPCTSTR name, LPCTSTR value )\n{\n\tif( nullptr != value )\n\t\tregistryKey.SetStringValue( name, value );\n\telse\n\t\tregistryKey.DeleteValue( name );\n}\n\nuint32_t AppState::dwordLoad( LPCTSTR name, uint32_t fallback )\n{\n\tDWORD dw;\n\tLSTATUS lss = registryKey.QueryDWORDValue( name, dw );\n\tif( lss == ERROR_SUCCESS )\n\t\treturn dw;\n\treturn fallback;\n}\nvoid AppState::dwordStore( LPCTSTR name, uint32_t value )\n{\n\tregistryKey.SetDWORDValue( name, value );\n}\n\nvoid AppState::lastScreenSave( HRESULT code )\n{\n\tdwordStore( regValLastScreen, (uint32_t)code );\n}\n\nHRESULT AppState::lastScreenLoad()\n{\n\treturn (HRESULT)dwordLoad( regValLastScreen, SCREEN_TRANSCRIBE );\n}\n\nvoid AppState::setupIcon( CWindow* wnd )\n{\n\tHICON ic = appIcon;\n\tif( nullptr != ic )\n\t{\n\t\twnd->SendMessage( WM_SETICON, ICON_SMALL, (LPARAM)ic );\n\t\twnd->SendMessage( WM_SETICON, ICON_BIG, (LPARAM)ic );\n\t}\n}\n\nuint32_t AppState::gpuFlagsLoad()\n{\n\treturn dwordLoad( regValGpuFlags, 0 );\n}\n\nvoid AppState::gpuFlagsStore( uint32_t flags )\n{\n\tif( 0 == flags )\n\t\tregistryKey.DeleteValue( regValGpuFlags );\n\telse\n\t\tdwordStore( regValGpuFlags, flags );\n}\n\nbool AppState::boolLoad( LPCTSTR name )\n{\n\treturn dwordLoad( name, 0 ) != 0;\n}\n\nvoid AppState::boolStore( LPCTSTR name, bool val )\n{\n\tif( val )\n\t\tdwordStore( name, 1 );\n\telse\n\t\tregistryKey.DeleteValue( name );\n}"
  },
  {
    "path": "Examples/WhisperDesktop/AppState.h",
    "content": "#pragma once\n#include \"Utils/DebugConsole.h\"\n\nclass AppState\n{\n\tbool coInit = false;\n\tCRegKey registryKey;\n\tCIcon appIcon;\npublic:\n\n\tstruct ModelSource\n\t{\n\t\tCString path;\n\t\tbool found = false;\n\t\tWhisper::eModelImplementation impl = (Whisper::eModelImplementation)0;\n\t\tuint64_t sizeInBytes = 0;\n\t};\n\tModelSource source;\n\n\tDebugConsole console;\n\tCComPtr<Whisper::iMediaFoundation> mediaFoundation;\n\tCComPtr<Whisper::iModel> model;\n\n\t~AppState();\n\n\t// Setup the initial things\n\tHRESULT startup();\n\n\tHRESULT findModelSource();\n\n\tHRESULT saveModelSource();\n\n\tuint32_t languageRead();\n\tvoid languageWrite( uint32_t key );\n\n\tCString stringLoad( LPCTSTR name );\n\tvoid stringStore( LPCTSTR name, LPCTSTR value );\n\tuint32_t dwordLoad( LPCTSTR name, uint32_t fallback );\n\tvoid dwordStore( LPCTSTR name, uint32_t value );\n\tbool boolLoad( LPCTSTR name );\n\tvoid boolStore( LPCTSTR name, bool val );\n\n\tbool automaticallyLoadModel = true;\n\n\tvoid lastScreenSave( HRESULT code );\n\tHRESULT lastScreenLoad();\n\n\tvoid setupIcon( CWindow* wnd );\n\n\tuint32_t gpuFlagsLoad();\n\tvoid gpuFlagsStore( uint32_t flags );\n};\n\nconstexpr HRESULT SCREEN_MODEL = 1;\nconstexpr HRESULT SCREEN_TRANSCRIBE = 2;\nconstexpr HRESULT SCREEN_CAPTURE = 3;"
  },
  {
    "path": "Examples/WhisperDesktop/CaptureDlg.cpp",
    "content": "#include \"stdafx.h\"\n#include \"CaptureDlg.h\"\n\nHRESULT CaptureDlg::show()\n{\n\tauto res = DoModal( nullptr );\n\tif( res == -1 )\n\t\treturn HRESULT_FROM_WIN32( GetLastError() );\n\tswitch( res )\n\t{\n\tcase IDC_BACK:\n\t\treturn SCREEN_MODEL;\n\tcase IDC_TRANSCRIBE:\n\t\treturn SCREEN_TRANSCRIBE;\n\t}\n\treturn S_OK;\n}\n\nstatic const LPCTSTR regValDevice = L\"captureDevice\";\nstatic const LPCTSTR regValOutPath = L\"captureTextFile\";\nstatic const LPCTSTR regValOutFormat = L\"captureTextFlags\";\n\nenum struct CaptureDlg::eTextFlags : uint32_t\n{\n\tSave = 1,\n\tAppend = 2,\n\tTimestamps = 4,\n};\n\nLRESULT CaptureDlg::OnInitDialog( UINT nMessage, WPARAM wParam, LPARAM lParam, BOOL& bHandled )\n{\n\t// First DDX call, hooks up variables to controls.\n\tDoDataExchange( false );\n\n\tlanguageSelector.initialize( m_hWnd, IDC_LANGUAGE, appState );\n\tcbTranslate.initialize( m_hWnd, IDC_TRANSLATE, appState );\n\tcbConsole.initialize( m_hWnd, IDC_CONSOLE, appState );\n\n\tpendingState.initialize(\n\t\t// Controls to disable while pending, re-enable afterwards\n\t\t{\n\t\t\tlanguageSelector, GetDlgItem( IDC_TRANSLATE ),\n\t\t\tcbCaptureDevice,\n\t\t\tcheckSave, checkAppend, checkTimestamps, transcribeOutputPath, transcribeOutputBrowse,\n\t\t\tGetDlgItem( IDC_DEV_REFRESH ),\n\t\t\tGetDlgItem( IDC_BACK ),\n\t\t\tGetDlgItem( IDC_TRANSCRIBE ),\n\t\t\tGetDlgItem( IDCANCEL ),\n\t\t},\n\t\t// Controls to show while pending, hide afterwards\n\t\t{\n\t\t\tvoiceActivity, GetDlgItem( IDC_VOICE_ACTIVITY_LBL ),\n\t\t\ttranscribeActivity, GetDlgItem( IDC_TRANS_LBL ),\n\t\t\tstalled, GetDlgItem( IDC_STALL_LBL ),\n\t\t\tprogressBar,\n\t\t} );\n\n\tstalled.setActiveColor( flipRgb( 0xffcc33 ) );\n\n\tHRESULT hr = work.create( this );\n\tif( FAILED( hr ) )\n\t{\n\t\treportError( m_hWnd, L\"CreateThreadpoolWork failed\", nullptr, hr );\n\t\tEndDialog( IDCANCEL );\n\t}\n\n\tlistDevices();\n\tselectDevice( appState.stringLoad( regValDevice ) );\n\n\tconstexpr uint32_t defaultFlags = (uint32_t)eTextFlags::Append;\n\tuint32_t flags = appState.dwordLoad( regValOutFormat, defaultFlags );\n\tif( flags & (uint32_t)eTextFlags::Save )\n\t\tcheckSave.SetCheck( BST_CHECKED );\n\tif( flags & (uint32_t)eTextFlags::Append )\n\t\tcheckAppend.SetCheck( BST_CHECKED );\n\tif( flags & (uint32_t)eTextFlags::Timestamps )\n\t\tcheckTimestamps.SetCheck( BST_CHECKED );\n\n\ttranscribeOutputPath.SetWindowText( appState.stringLoad( regValOutPath ) );\n\tonSaveTextCheckbox();\n\n\tappState.lastScreenSave( SCREEN_CAPTURE );\n\tappState.setupIcon( this );\n\tATLVERIFY( CenterWindow() );\n\treturn 0;\n}\n\nHRESULT __stdcall CaptureDlg::listDevicesCallback( int len, const Whisper::sCaptureDevice* buffer, void* pv ) noexcept\n{\n\tstd::vector<sCaptureDevice>& devices = *( std::vector<sCaptureDevice> * )pv;\n\tdevices.resize( len );\n\tfor( int i = 0; i < len; i++ )\n\t{\n\t\tdevices[ i ].displayName = buffer[ i ].displayName;\n\t\tdevices[ i ].endpoint = buffer[ i ].endpoint;\n\t}\n\treturn S_OK;\n}\n\nbool CaptureDlg::listDevices()\n{\n\tappState.mediaFoundation->listCaptureDevices( &listDevicesCallback, &devices );\n\tcbCaptureDevice.ResetContent();\n\tfor( const auto& dev : devices )\n\t\tcbCaptureDevice.AddString( dev.displayName );\n\treturn !devices.empty();\n}\n\nvoid CaptureDlg::onDeviceRefresh()\n{\n\t// Save the current selection\n\tconst int curSel = cbCaptureDevice.GetCurSel();\n\tCString str;\n\tif( curSel >= 0 && curSel < (int)devices.size() )\n\t\tstr = std::move( devices[ curSel ].endpoint );\n\n\t// Refresh\n\tlistDevices();\n\n\t// Restore the selection\n\tselectDevice( str );\n\n\tconst size_t len = devices.size();\n\tif( len == 0 )\n\t{\n\t\tMessageBox( L\"No capture devices found on this computer.\\nIf you have a USB microphone, connect it to this PC,\\nand press refresh button.\",\n\t\t\tL\"Capture Devices\", MB_OK | MB_ICONWARNING );\n\t}\n\telse\n\t{\n\t\tconst char* suffix = ( len != 1 ) ? \"s\" : \"\";\n\t\tstr.Format( L\"Detected %zu audio capture device%S.\", len, suffix );\n\t\tMessageBox( str, L\"Capture Devices\", MB_OK | MB_ICONINFORMATION );\n\t}\n}\n\nbool CaptureDlg::selectDevice( LPCTSTR endpoint )\n{\n\tif( nullptr != endpoint && 0 != *endpoint )\n\t{\n\t\tfor( size_t i = 0; i < devices.size(); i++ )\n\t\t{\n\t\t\tif( devices[ i ].endpoint == endpoint )\n\t\t\t{\n\t\t\t\tcbCaptureDevice.SetCurSel( (int)i );\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t}\n\n\tif( !devices.empty() )\n\t\tcbCaptureDevice.SetCurSel( 0 );\n\treturn false;\n}\n\nvoid CaptureDlg::onSaveTextCheckbox()\n{\n\tconst BOOL enabled = ( checkSave.GetCheck() == BST_CHECKED );\n\tstd::array<HWND, 4> controls = { checkAppend, checkTimestamps, transcribeOutputPath, transcribeOutputBrowse };\n\tfor( HWND w : controls )\n\t\t::EnableWindow( w, enabled );\n}\n\nvoid CaptureDlg::onBrowseResult()\n{\n\tLPCTSTR title = L\"Output Text File\";\n\tLPCTSTR outputFilters = L\"Text files (*.txt)\\0*.txt\\0\\0\";\n\tCString path;\n\ttranscribeOutputPath.GetWindowText( path );\n\tif( !getSaveFileName( m_hWnd, title, outputFilters, path ) )\n\t\treturn;\n\n\tLPCTSTR ext = PathFindExtension( path );\n\tif( 0 == *ext )\n\t{\n\t\twchar_t* const buffer = path.GetBufferSetLength( path.GetLength() + 5 );\n\t\tPathRenameExtension( buffer, L\".txt\" );\n\t\tpath.ReleaseBuffer();\n\t}\n\n\ttranscribeOutputPath.SetWindowText( path );\n}\n\nCaptureDlg::eTextFlags CaptureDlg::getOutputFlags()\n{\n\tuint32_t flags = 0;\n\tif( checkSave.GetCheck() == BST_CHECKED )\n\t\tflags |= (uint32_t)eTextFlags::Save;\n\tif( checkAppend.GetCheck() == BST_CHECKED )\n\t\tflags |= (uint32_t)eTextFlags::Append;\n\tif( checkTimestamps.GetCheck() == BST_CHECKED )\n\t\tflags |= (uint32_t)eTextFlags::Timestamps;\n\treturn (eTextFlags)flags;\n}\n\nvoid CaptureDlg::setPending( bool nowPending )\n{\n\tpendingState.setPending( nowPending );\n\tif( nowPending )\n\t{\n\t\tprogressBar.SetMarquee( TRUE, 0 );\n\t\tbtnRunCapture.SetWindowText( L\"Stop\" );\n\t}\n\telse\n\t{\n\t\tprogressBar.SetMarquee( FALSE, 0 );\n\t\tbtnRunCapture.SetWindowText( L\"Capture\" );\n\t\tbtnRunCapture.EnableWindow( TRUE );\n\t\tcaptureRunning = false;\n\t}\n}\n\nvoid CaptureDlg::onRunCapture()\n{\n\tif( captureRunning )\n\t{\n\t\tthreadState.stopRequested = true;\n\t\tbtnRunCapture.EnableWindow( FALSE );\n\t\treturn;\n\t}\n\n\tint dev = cbCaptureDevice.GetCurSel();\n\tif( dev < 0 || dev >= (int)devices.size() )\n\t{\n\t\tshowError( L\"Please select a capture device\", S_FALSE );\n\t\treturn;\n\t}\n\tthreadState.endpoint = devices[ dev ].endpoint;\n\tthreadState.language = languageSelector.selectedLanguage();\n\tthreadState.translate = cbTranslate.checked();\n\tif( isInvalidTranslate( m_hWnd, threadState.language, threadState.translate ) )\n\t\treturn;\n\n\tthreadState.flags = getOutputFlags();\n\tif( (uint32_t)threadState.flags & (uint32_t)eTextFlags::Save )\n\t{\n\t\ttranscribeOutputPath.GetWindowText( threadState.textOutputPath );\n\t\tif( threadState.textOutputPath.GetLength() <= 0 )\n\t\t{\n\t\t\tshowError( L\"Please specify the output text file\", S_FALSE );\n\t\t\treturn;\n\t\t}\n\t\tappState.stringStore( regValOutPath, threadState.textOutputPath );\n\t}\n\telse\n\t\tcbConsole.ensureChecked();\n\n\tlanguageSelector.saveSelection( appState );\n\tcbTranslate.saveSelection( appState );\n\tappState.stringStore( regValDevice, threadState.endpoint );\n\tappState.dwordStore( regValOutFormat, (uint32_t)threadState.flags );\n\n\tcaptureRunning = true;\n\tthreadState.errorMessage = L\"\";\n\tthreadState.stopRequested = false;\n\tthreadState.captureParams.minDuration = 7;\n\tthreadState.captureParams.maxDuration = 11;\n\tsetPending( true );\n\twork.post();\n}\n\nvoid __declspec( noinline ) CaptureDlg::getThreadError()\n{\n\tgetLastError( threadState.errorMessage );\n}\n\n#define CHECK_EX( hr ) { const HRESULT __hr = ( hr ); if( FAILED( __hr ) ) { getThreadError(); return __hr; } }\n\nstatic HRESULT appendDate( CString& str, const SYSTEMTIME& time )\n{\n\tconstexpr DWORD dateFlags = DATE_LONGDATE;\n\tint cc = GetDateFormatEx( LOCALE_NAME_USER_DEFAULT, dateFlags, &time, nullptr, nullptr, 0, nullptr );\n\tif( 0 == cc )\n\t\treturn getLastHr();\n\n\tconst int oldLength = str.GetLength();\n\twchar_t* const buffer = str.GetBufferSetLength( oldLength + cc );\n\tcc = GetDateFormatEx( LOCALE_NAME_USER_DEFAULT, dateFlags, &time, nullptr, buffer + oldLength, cc, nullptr );\n\tif( 0 != cc )\n\t{\n\t\tstr.ReleaseBuffer();\n\t\treturn S_OK;\n\t}\n\tHRESULT hr = getLastHr();\n\tstr.ReleaseBuffer();\n\treturn hr;\n}\n\nstatic HRESULT appendTime( CString& str, const SYSTEMTIME& time )\n{\n\tconstexpr DWORD timeFlags = 0;\n\tint cc = GetTimeFormatEx( LOCALE_NAME_USER_DEFAULT, timeFlags, &time, nullptr, nullptr, 0 );\n\tif( 0 == cc )\n\t\treturn getLastHr();\n\n\tconst int oldLength = str.GetLength();\n\twchar_t* const buffer = str.GetBufferSetLength( oldLength + cc );\n\tcc = GetTimeFormatEx( LOCALE_NAME_USER_DEFAULT, timeFlags, &time, nullptr, buffer + oldLength, cc );\n\tif( 0 != cc )\n\t{\n\t\tstr.ReleaseBuffer();\n\t\treturn S_OK;\n\t}\n\tHRESULT hr = getLastHr();\n\tstr.ReleaseBuffer();\n\treturn hr;\n}\n\nstatic HRESULT printDateTime( CAtlFile& file )\n{\n\tSYSTEMTIME time;\n\tGetLocalTime( &time );\n\n\tCString str;\n\tstr = L\"==== Captured on \";\n\tCHECK( appendDate( str, time ) );\n\tstr += L\", \";\n\tCHECK( appendTime( str, time ) );\n\tstr += L\" ====\\r\\n\";\n\n\tCStringA u8;\n\tmakeUtf8( u8, str );\n\treturn file.Write( cstr( u8 ), (DWORD)u8.GetLength() );\n}\n\ninline HRESULT CaptureDlg::runCapture()\n{\n\tclearLastError();\n\tusing namespace Whisper;\n\tCComPtr<iAudioCapture> capture;\n\tCHECK_EX( appState.mediaFoundation->openCaptureDevice( threadState.endpoint, threadState.captureParams, &capture ) );\n\n\tHRESULT hr;\n\tCAtlFile file;\n\tconst uint32_t flags = (uint32_t)threadState.flags;\n\tif( flags & (uint32_t)eTextFlags::Save )\n\t{\n\t\tconst bool append = 0 != ( flags & (uint32_t)eTextFlags::Append );\n\t\tconst DWORD creation = append ? OPEN_ALWAYS : CREATE_ALWAYS;\n\t\thr = file.Create( threadState.textOutputPath, GENERIC_WRITE, FILE_SHARE_READ, creation );\n\t\tif( FAILED( hr ) )\n\t\t{\n\t\t\tthreadState.errorMessage = L\"Unable to create the output text file\";\n\t\t\treturn hr;\n\t\t}\n\t\tif( append )\n\t\t{\n\t\t\tULONGLONG size;\n\t\t\tCHECK( file.GetSize( size ) );\n\t\t\tif( size == 0 )\n\t\t\t\tCHECK( writeUtf8Bom( file ) )\n\t\t\telse\n\t\t\t\tCHECK( file.Seek( 0, SEEK_END ) );\n\t\t}\n\t\telse\n\t\t{\n\t\t\tCHECK( writeUtf8Bom( file ) );\n\t\t}\n\n\t\tif( flags & (uint32_t)eTextFlags::Timestamps )\n\t\t\tCHECK( printDateTime( file ) );\n\n\t\tthreadState.file = &file;\n\t}\n\telse\n\t\tthreadState.file = nullptr;\n\n\tCComPtr<iContext> context;\n\tCHECK_EX( appState.model->createContext( &context ) );\n\n\tsFullParams fullParams;\n\tCHECK_EX( context->fullDefaultParams( eSamplingStrategy::Greedy, &fullParams ) );\n\tfullParams.language = threadState.language;\n\tfullParams.setFlag( eFullParamsFlags::Translate, threadState.translate );\n\tfullParams.resetFlag( eFullParamsFlags::PrintRealtime );\n\tfullParams.new_segment_callback = &newSegmentCallback;\n\tfullParams.new_segment_callback_user_data = this;\n\n\tsCaptureCallbacks callbacks;\n\tcallbacks.shouldCancel = &cbCancel;\n\tcallbacks.captureStatus = &cbStatus;\n\tcallbacks.pv = this;\n\n\tCHECK_EX( context->runCapture( fullParams, callbacks, capture ) );\n\tthreadState.file = nullptr;\n\n\tcontext->timingsPrint();\n\treturn S_OK;\n}\n\nvoid __stdcall CaptureDlg::poolCallback() noexcept\n{\n\tconst HRESULT hr = runCapture();\n\tPostMessage( WM_CALLBACK_COMPLETION, hr );\n}\n\nvoid CaptureDlg::showError( LPCTSTR text, HRESULT hr )\n{\n\treportError( m_hWnd, text, L\"Capture failed\", hr );\n}\n\nLRESULT CaptureDlg::onThreadQuit( UINT nMessage, WPARAM wParam, LPARAM lParam, BOOL& bHandled )\n{\n\tsetPending( false );\n\n\tconst HRESULT hr = (HRESULT)wParam;\n\tif( FAILED( hr ) )\n\t{\n\t\tLPCTSTR failMessage = L\"Capture failed\";\n\n\t\tif( threadState.errorMessage.GetLength() > 0 )\n\t\t{\n\t\t\tCString tmp = failMessage;\n\t\t\ttmp += L\"\\n\";\n\t\t\ttmp += threadState.errorMessage;\n\t\t\tshowError( tmp, hr );\n\t\t}\n\t\telse\n\t\t\tshowError( failMessage, hr );\n\n\t\treturn 0;\n\t}\n\telse\n\t{\n\t\tif( (uint32_t)threadState.flags & (uint32_t)eTextFlags::Save )\n\t\t\tShellExecute( NULL, L\"open\", threadState.textOutputPath, NULL, NULL, SW_SHOW );\n\t}\n\n\treturn 0;\n}\n\nLRESULT CaptureDlg::onThreadStatus( UINT nMessage, WPARAM wParam, LPARAM lParam, BOOL& bHandled )\n{\n\tusing namespace Whisper;\n\tconst uint8_t newStatus = (uint8_t)wParam;\n\t// Update the GUI\n\tvoiceActivity.setActive( 0 != ( newStatus & (uint8_t)eCaptureStatus::Voice ) );\n\ttranscribeActivity.setActive( 0 != ( newStatus & (uint8_t)eCaptureStatus::Transcribing ) );\n\tstalled.setActive( 0 != ( newStatus & (uint8_t)eCaptureStatus::Stalled ) );\n\treturn 0;\n}\n\nHRESULT __stdcall CaptureDlg::cbCancel( void* pv ) noexcept\n{\n\tconst bool stopRequested = ( (CaptureDlg*)pv )->threadState.stopRequested;\n\treturn stopRequested ? S_FALSE : S_OK;\n}\n\nHRESULT __stdcall CaptureDlg::cbStatus( void* pv, Whisper::eCaptureStatus status ) noexcept\n{\n\tCaptureDlg& dialog = *(CaptureDlg*)pv;\n\tif( dialog.PostMessage( WM_CALLBACK_STATUS, (uint8_t)status ) )\n\t\treturn S_OK;\n\treturn getLastHr();\n}\n\nHRESULT __cdecl CaptureDlg::newSegmentCallback( Whisper::iContext* ctx, uint32_t n_new, void* user_data ) noexcept\n{\n\tusing namespace Whisper;\n\tCComPtr<iTranscribeResult> result;\n\tconst eResultFlags flags = eResultFlags::Timestamps | eResultFlags::Tokens;\n\tCHECK( ctx->getResults( flags, &result ) );\n\tCHECK( logNewSegments( result, n_new ) );\n\n\tCaptureDlg& dialog = *(CaptureDlg*)user_data;\n\treturn dialog.appendTextFile( result, n_new );\n}\n\nHRESULT CaptureDlg::appendTextFile( Whisper::iTranscribeResult* results, uint32_t newSegments )\n{\n\tif( nullptr == threadState.file || 0 == newSegments )\n\t\treturn S_OK;\n\n\tusing namespace Whisper;\n\tsTranscribeLength length;\n\tCHECK( results->getSize( length ) );\n\n\tconst size_t len = length.countSegments;\n\tsize_t i = len - newSegments;\n\n\tconst sSegment* const segments = results->getSegments();\n\tCStringA str;\n\tfor( ; i < len; i++ )\n\t{\n\t\tconst sSegment& seg = segments[ i ];\n\t\tif( 0 != ( (uint32_t)threadState.flags & (uint32_t)eTextFlags::Timestamps ) )\n\t\t{\n\t\t\tstr = \"[\";\n\t\t\tprintTime( str, seg.time.begin );\n\t\t\tstr += \" --> \";\n\t\t\tprintTime( str, seg.time.end );\n\t\t\tstr += \"]  \";\n\t\t}\n\t\telse\n\t\t\tstr = \"\";\n\n\t\tstr += seg.text;\n\t\tstr += \"\\r\\n\";\n\n\t\tCHECK( threadState.file->Write( cstr( str ), (DWORD)str.GetLength() ) );\n\t}\n\n\tCHECK( threadState.file->Flush() );\n\treturn S_OK;\n}"
  },
  {
    "path": "Examples/WhisperDesktop/CaptureDlg.h",
    "content": "#pragma once\n#include \"AppState.h\"\n#include \"Utils/WTL/atlddx.h\"\n#include \"Utils/miscUtils.h\"\n#include \"Utils/LanguageDropdown.h\"\n#include \"Utils/TranslateCheckbox.h\"\n#include \"Utils/PendingState.h\"\n#include \"CircleIndicator.h\"\n\nclass CaptureDlg :\n\tpublic CDialogImpl<CaptureDlg>,\n\tpublic CWinDataExchange<CaptureDlg>,\n\tpublic iThreadPoolCallback\n{\n\tAppState& appState;\n\npublic:\n\tstatic constexpr UINT IDD = IDD_CAPTURE_DIALOG;\n\tstatic constexpr UINT WM_CALLBACK_COMPLETION = WM_APP + 1;\n\tstatic constexpr UINT WM_CALLBACK_STATUS = WM_APP + 2;\n\n\tCaptureDlg( AppState& app ) : appState( app ) { }\n\n\tHRESULT show();\n\n\tBEGIN_MSG_MAP( CaptureDlg )\n\t\tMESSAGE_HANDLER( WM_INITDIALOG, OnInitDialog )\n\t\tON_BUTTON_CLICK( IDC_CONSOLE, cbConsole.click )\n\t\tON_BUTTON_CLICK( IDC_DEV_REFRESH, onDeviceRefresh );\n\t\tON_BUTTON_CLICK( IDC_BROWSE_RESULT, onBrowseResult );\n\t\tON_BUTTON_CLICK( IDC_SAVE_TEXT, onSaveTextCheckbox );\n\t\tON_BUTTON_CLICK( IDC_RUN_CAPTURE, onRunCapture );\n\n\t\tON_BUTTON_CLICK( IDCANCEL, onClose )\n\t\tON_BUTTON_CLICK( IDC_BACK, onBack )\n\t\tON_BUTTON_CLICK( IDC_TRANSCRIBE, onTranscribe );\n\n\t\tMESSAGE_HANDLER( WM_CALLBACK_COMPLETION, onThreadQuit );\n\t\tMESSAGE_HANDLER( WM_CALLBACK_STATUS, onThreadStatus );\n\tEND_MSG_MAP()\n\n\tBEGIN_DDX_MAP( CaptureDlg )\n\t\tDDX_CONTROL_HANDLE( IDC_DEVICE, cbCaptureDevice )\n\t\tDDX_CONTROL_HANDLE( IDC_RUN_CAPTURE, btnRunCapture );\n\t\tDDX_CONTROL_HANDLE( IDC_TRANSCRIBE_PROGRESS, progressBar );\n\t\tDDX_CONTROL_HANDLE( IDC_SAVE_TEXT, checkSave )\n\t\tDDX_CONTROL_HANDLE( IDC_SAVE_APPEND, checkAppend )\n\t\tDDX_CONTROL_HANDLE( IDC_SAVE_TIMESTAMPS, checkTimestamps )\n\t\tDDX_CONTROL_HANDLE( IDC_PATH_RESULT, transcribeOutputPath )\n\t\tDDX_CONTROL_HANDLE( IDC_BROWSE_RESULT, transcribeOutputBrowse );\n\n\t\tDDX_CONTROL( IDC_VOICE_ACTIVITY, voiceActivity );\n\t\tDDX_CONTROL( IDC_TRANS_STATUS, transcribeActivity );\n\t\tDDX_CONTROL( IDC_STALL_STATUS, stalled );\n\t\t\n\tEND_DDX_MAP()\n\nprivate:\n\tPendingState pendingState;\n\tvoid setPending( bool nowPending );\n\n\tLRESULT OnInitDialog( UINT nMessage, WPARAM wParam, LPARAM lParam, BOOL& bHandled );\n\n\tvoid onClose()\n\t{\n\t\tATLVERIFY( EndDialog( IDCANCEL ) );\n\t}\n\tvoid onBack()\n\t{\n\t\tATLVERIFY( EndDialog( IDC_BACK ) );\n\t}\n\tvoid onTranscribe()\n\t{\n\t\tATLVERIFY( EndDialog( IDC_TRANSCRIBE ) );\n\t}\n\n\t// List capture devices, and populate the combobox\n\tbool listDevices();\n\tvoid onDeviceRefresh();\n\tbool selectDevice( LPCTSTR endpoint );\n\n\tstatic HRESULT __stdcall listDevicesCallback( int len, const Whisper::sCaptureDevice* buffer, void* pv ) noexcept;\n\tConsoleCheckbox cbConsole;\n\tLanguageDropdown languageSelector;\n\tTranslateCheckbox cbTranslate;\n\tCComboBox cbCaptureDevice;\n\n\tvoid onBrowseResult();\n\n\tenum struct eTextFlags : uint32_t;\n\tCButton\tcheckSave, checkAppend, checkTimestamps;\n\tCEdit transcribeOutputPath;\n\tCButton transcribeOutputBrowse;\n\tvoid onSaveTextCheckbox();\n\teTextFlags getOutputFlags();\n\n\tCButton btnRunCapture;\n\tCProgressBarCtrl progressBar;\n\tThreadPoolWork work;\n\n\tstruct sCaptureDevice\n\t{\n\t\tCString displayName;\n\t\tCString endpoint;\n\t};\n\tstd::vector<sCaptureDevice> devices;\n\n\tvoid showError( LPCTSTR text, HRESULT hr );\n\n\tCircleIndicator voiceActivity;\n\tCircleIndicator transcribeActivity;\n\tCircleIndicator stalled;\n\n\tstruct sThreadState\n\t{\n\t\tvolatile bool stopRequested;\n\t\tbool translate;\n\t\teTextFlags flags;\n\t\tCAtlFile* file;\n\t\tuint32_t language;\n\t\tWhisper::sCaptureParams captureParams;\n\t\tCString endpoint;\n\t\tCString textOutputPath;\n\t\tCString errorMessage;\n\t};\n\tsThreadState threadState;\n\tbool captureRunning = false;\n\n\tvoid getThreadError();\n\tvoid onRunCapture();\n\tHRESULT runCapture();\n\tvoid __stdcall poolCallback() noexcept override final;\n\n\tLRESULT onThreadQuit( UINT nMessage, WPARAM wParam, LPARAM lParam, BOOL& bHandled );\n\tLRESULT onThreadStatus( UINT nMessage, WPARAM wParam, LPARAM lParam, BOOL& bHandled );\n\n\tstatic HRESULT __stdcall cbCancel( void* pv ) noexcept;\n\tstatic HRESULT __stdcall cbStatus( void* pv, Whisper::eCaptureStatus status ) noexcept;\n\n\tstatic HRESULT __cdecl newSegmentCallback( Whisper::iContext* ctx, uint32_t n_new, void* user_data ) noexcept;\n\n\tHRESULT appendTextFile( Whisper::iTranscribeResult* results, uint32_t newSegments );\n};"
  },
  {
    "path": "Examples/WhisperDesktop/CircleIndicator.cpp",
    "content": "﻿#include \"stdafx.h\"\n#include \"CircleIndicator.h\"\n#include <atltypes.h>\n#include \"AppState.h\"\n\nnamespace\n{\n\tstatic const LPCTSTR windowClassName = L\"CircleIndicator\";\n\n\t// Font with these symbols, shipped with Windows since forever:\n\t// https://learn.microsoft.com/en-us/typography/font-list/segoe-ui-symbol\n\tstatic const LPCTSTR fontName = L\"Segoe UI Symbol\";\n\n\t// Outlined circle\n\tstatic const LPCTSTR circleOutline = L\"⚪\";\n\t// Filled circle\n\tstatic const LPCTSTR circleFilled = L\"⚫\";\n\n\t// Font size for that symbol font, in points\n\tconstexpr int fontSizePoints = 14;\n\n\t// Default active color for the indicator\n\tconstexpr uint32_t defaultActiveColor = 0x7FFF7F;\n}\n\nCircleIndicator::CircleIndicator() :\n\tactiveColor( defaultActiveColor )\n{ }\n\nATL::CWndClassInfo& CircleIndicator::GetWndClassInfo()\n{\n\t// Use custom class style with CS_PARENTDC, and COLOR_3DFACE for the background\n\tstatic ATL::CWndClassInfo wc = \n\t{\n\t\t{ sizeof( WNDCLASSEX ),\n\t\tCS_HREDRAW | CS_VREDRAW | CS_PARENTDC,\n\t\tStartWindowProc,\n\t\t0, 0, NULL, NULL, NULL, (HBRUSH)( COLOR_3DFACE + 1 ), NULL, windowClassName, NULL },\n\t\tNULL, NULL, IDC_ARROW, TRUE, 0, _T( \"\" )\n\t};\n\treturn wc;\n}\n\n// Class registration\nHRESULT CircleIndicator::registerClass()\n{\n\tWNDPROC pUnusedWndSuperProc = nullptr;\n\tATOM a = GetWndClassInfo().Register( &pUnusedWndSuperProc );\n\tif( 0 != a )\n\t\treturn S_OK;\n\treturn getLastHr();\n}\n\nHRESULT CircleIndicator::createFont( int height )\n{\n\tLOGFONT logFont;\n\tmemset( &logFont, 0, sizeof( logFont ) );\n\tlogFont.lfHeight = height;\n\tlogFont.lfCharSet = ANSI_CHARSET;\n\tlogFont.lfOutPrecision = OUT_TT_PRECIS;\n\tlogFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;\n\twcsncpy_s( logFont.lfFaceName, fontName, _TRUNCATE );\n\tfont.CreateFontIndirect( &logFont );\n\tif( font )\n\t\treturn S_OK;\n\treturn E_FAIL;\n}\n\nvoid CircleIndicator::onDestroy()\n{\n\tif( font )\n\t\tfont.DeleteObject();\n}\n\nvoid CircleIndicator::onPaint( CDCHandle dc )\n{\n\tCRect rectInt32;\n\tGetClientRect( &rectInt32 );\n\n\tCPaintDC pdc( m_hWnd );\n\n\tconst int logPixels = pdc.GetDeviceCaps( LOGPIXELSY );\n\tint fontSize = -MulDiv( fontSizePoints, logPixels, 72 );\n\tif( !font || fontHeight != fontSize )\n\t{\n\t\tif( font )\n\t\t\tfont.DeleteObject();\n\t\tHRESULT hr = createFont( fontSize );\n\t\tif( FAILED( hr ) )\n\t\t\treturn;\n\t\tfontHeight = fontSize;\n\t}\n\n\tpdc.SetBkColor( GetSysColor( COLOR_3DFACE ) );\n\tpdc.SelectFont( font );\n\tpdc.SetBkMode( OPAQUE );\n\tconstexpr UINT textFormat = DT_CENTER | DT_VCENTER;\n\n\tif( isActive )\n\t{\n\t\tpdc.SetTextColor( activeColor );\n\t\tpdc.DrawText( circleFilled, 1, rectInt32, textFormat );\n\t\tpdc.SetBkMode( TRANSPARENT );\n\t}\n\n\tpdc.SetTextColor( 0 );\n\tpdc.DrawText( circleOutline, 1, rectInt32, textFormat );\n}\n\nvoid CircleIndicator::setActive( bool nowActive )\n{\n\tif( nowActive == isActive )\n\t\treturn;\n\n\t// Repaint the control\n\tisActive = nowActive;\n\tInvalidateRect( nullptr );\n}"
  },
  {
    "path": "Examples/WhisperDesktop/CircleIndicator.h",
    "content": "#pragma once\n#include \"Utils/miscUtils.h\"\n#include \"Utils/WTL/atlcrack.h\"\n\n// This control renders a black circle, and in the active state, the circle is filled with a bright color.\nclass CircleIndicator: public CWindowImpl<CircleIndicator>\n{\npublic:\n\tstatic ATL::CWndClassInfo& GetWndClassInfo();\n\n\tBEGIN_MSG_MAP( CircleIndicator )\n\t\tMSG_WM_PAINT( onPaint )\n\t\tMSG_WM_DESTROY( onDestroy )\n\tEND_MSG_MAP()\n\n\t// Class registration\n\tstatic HRESULT registerClass();\n\n\tvoid setActive( bool nowActive );\n\n\tvoid setActiveColor( uint32_t col )\n\t{\n\t\tactiveColor = col;\n\t}\n\tCircleIndicator();\n\nprivate:\n\tbool isActive = false;\n\tuint32_t activeColor;\n\tint fontHeight = 0;\n\tCFont font;\n\tHRESULT createFont( int height );\n\n\tvoid onDestroy();\n\tvoid onPaint( CDCHandle dc );\n};"
  },
  {
    "path": "Examples/WhisperDesktop/LoadModelDlg.cpp",
    "content": "#include \"stdafx.h\"\n#include \"LoadModelDlg.h\"\n#include \"Utils/miscUtils.h\"\n#include \"Utils/logger.h\"\n#include \"ModelAdvancedDlg.h\"\n\nconstexpr int progressMaxInteger = 1024 * 8;\n\nHRESULT LoadModelDlg::show()\n{\n\tauto res = DoModal( nullptr );\n\tif( res == -1 )\n\t\treturn HRESULT_FROM_WIN32( GetLastError() );\n\tif( res == IDOK )\n\t{\n\t\tHRESULT hr = appState.lastScreenLoad();\n\t\tswitch( hr )\n\t\t{\n\t\tcase SCREEN_TRANSCRIBE:\n\t\tcase SCREEN_CAPTURE:\n\t\t\treturn hr;\n\t\tdefault:\n\t\t\treturn SCREEN_TRANSCRIBE;\n\t\t}\n\t}\n\treturn S_OK;\n}\n\nLRESULT LoadModelDlg::OnInitDialog( UINT nMessage, WPARAM wParam, LPARAM lParam, BOOL& bHandled )\n{\n\t// First DDX call, hooks up variables to controls.\n\tDoDataExchange( false );\n\n\tcbConsole.initialize( m_hWnd, IDC_CONSOLE, appState );\n\timplPopulateCombobox( cbModelType, appState.source.impl );\n\tmodelPath.SetWindowTextW( appState.source.path );\n\n\tHRESULT hr = work.create( this );\n\tif( FAILED( hr ) )\n\t{\n\t\tCString text = L\"CreateThreadpoolWork failed\\n\";\n\t\ttext += formatErrorMessage( hr );\n\t\t::MessageBox( m_hWnd, text, L\"Unable to load the model\", MB_OK | MB_ICONWARNING );\n\t\treturn TRUE;\n\t}\n\n\teditorsWindows.reserve( 6 );\n\teditorsWindows = { modelPath, cbModelType, GetDlgItem( IDC_BROWSE ), GetDlgItem( IDC_MODEL_ADV ), GetDlgItem( IDOK ), GetDlgItem( IDCANCEL ) };\n\tpendingWindows.reserve( 2 );\n\tpendingWindows = { GetDlgItem( IDC_PENDING_TEXT ), progressBar };\n\n\tprogressBar.SetRange32( 0, progressMaxInteger );\n\tprogressBar.SetStep( 1 );\n\n\tappState.setupIcon( this );\n\tATLVERIFY( CenterWindow() );\n\tif( !appState.source.found || !appState.automaticallyLoadModel )\n\t\treturn 0;\n\n\t// AppState.findModelSource() method has located model parameters in registry;\n\t// Post a notification identical to the \"OK\" button click event.\n\tPostMessage( WM_COMMAND, IDOK, (LPARAM)( GetDlgItem( IDOK ).m_hWnd ) );\n\n\treturn 0;\n}\n\nLRESULT LoadModelDlg::OnBrowse( UINT, INT, HWND, BOOL& bHandled )\n{\n\tbHandled = TRUE;\n\n\tCString path;\n\tmodelPath.GetWindowText( path );\n\tif( !getOpenFileName( m_hWnd, L\"Select a GGML Model File\", L\"Binary files (*.bin)\\0*.bin\\0\\0\", path ) )\n\t\treturn 0;\n\n\tmodelPath.SetWindowText( path );\n\tappState.source.path = path;\n\treturn 0;\n}\n\nLRESULT LoadModelDlg::validationError( LPCTSTR message )\n{\n\treportError( m_hWnd, message, L\"Unable to load the model\" );\n\treturn 0;\n}\n\nLRESULT LoadModelDlg::validationError( LPCTSTR message, HRESULT hr )\n{\n\treportError( m_hWnd, message, L\"Unable to load the model\", hr );\n\treturn 0;\n}\n\nvoid LoadModelDlg::setPending( bool nowPending )\n{\n\tconst BOOL enable = nowPending ? FALSE : TRUE;\n\tfor( HWND w : editorsWindows )\n\t\t::EnableWindow( w, enable );\n\n\tconst int show = nowPending ? SW_NORMAL : SW_HIDE;\n\tfor( HWND w : pendingWindows )\n\t\t::ShowWindow( w, show );\n\n\tif( nowPending )\n\t\tprogressBar.SetMarquee( TRUE, 0 );\n\telse\n\t\tprogressBar.SetMarquee( FALSE, 0 );\n}\n\nLRESULT LoadModelDlg::OnOk( UINT, INT, HWND, BOOL& bHandled )\n{\n\tmodelPath.GetWindowText( path );\n\tif( path.GetLength() <= 0 )\n\t\treturn validationError( L\"Please select a model GGML file\" );\n\n\t{\n\t\tCAtlFile file;\n\t\tHRESULT hr = file.Create( path, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING );\n\t\tif( FAILED( hr ) )\n\t\t\treturn validationError( L\"Unable to open the model file\", hr );\n\n\t\tULONGLONG cb = 0;\n\t\tfile.GetSize( cb );\n\t\tappState.source.sizeInBytes = cb;\n\t}\n\n\timpl = implGetValue( cbModelType );\n\tif( impl == (Whisper::eModelImplementation)0 )\n\t\treturn validationError( L\"Please select a model type\" );\n\n\tsetPending( true );\n\twork.post();\n\treturn 0;\n}\n\nstatic HRESULT loadModel( const wchar_t* path, Whisper::sModelSetup setup, const Whisper::sLoadModelCallbacks* callbacks, Whisper::iModel** pp )\n{\n\tconstexpr bool dbgTestClone = false;\n\n\tif constexpr( dbgTestClone )\n\t\tsetup.flags |= (uint32_t)Whisper::eGpuModelFlags::Cloneable;\n\n\tCComPtr<Whisper::iModel> res;\n\tCHECK( Whisper::loadModel( path, setup, callbacks, &res ) );\n\n\tif constexpr( dbgTestClone )\n\t{\n\t\tCComPtr<Whisper::iModel> clone;\n\t\tCHECK( res->clone( &clone ) );\n\t\t*pp = clone.Detach();\n\t\treturn S_OK;\n\t}\n\telse\n\t{\n\t\t*pp = res.Detach();\n\t\treturn S_OK;\n\t}\n}\n\nvoid __stdcall LoadModelDlg::poolCallback() noexcept\n{\n\tCComPtr<Whisper::iModel> model;\n\tclearLastError();\n\tloadError = L\"\";\n\tWhisper::sLoadModelCallbacks lmcb;\n\tlmcb.cancel = nullptr;\n\tlmcb.progress = &LoadModelDlg::progressCallback;\n\tlmcb.pv = this;\n\tWhisper::sModelSetup setup;\n\tsetup.impl = impl;\n\tsetup.flags = appState.gpuFlagsLoad();\n\tCString adapter = appState.stringLoad( L\"gpu\" );\n\tsetup.adapter = adapter;\n\tHRESULT hr = ::loadModel( path, setup, &lmcb, &model );\n\tif( SUCCEEDED( hr ) )\n\t\tappState.model = model;\n\telse\n\t\tgetLastError( loadError );\n\n\tthis->PostMessage( WM_CALLBACK_STATUS, (WPARAM)hr );\n}\n\nHRESULT __stdcall LoadModelDlg::progressCallback( double val, void* pv ) noexcept\n{\n\tLoadModelDlg& dialog = *(LoadModelDlg*)pv;\n\tconstexpr double mul = progressMaxInteger;\n\tint pos = lround( mul * val );\n\tdialog.progressBar.PostMessage( PBM_SETPOS, pos, 0 );\n\treturn S_OK;\n}\n\nLRESULT LoadModelDlg::OnCallbackStatus( UINT, WPARAM wParam, LPARAM, BOOL& bHandled )\n{\n\tsetPending( false );\n\n\tbHandled = TRUE;\n\tconst HRESULT hr = (HRESULT)wParam;\n\tif( FAILED( hr ) )\n\t{\n\t\tLPCTSTR failMessage = L\"Error loading the model\";\n\t\tif( loadError.GetLength() > 0 )\n\t\t{\n\t\t\tCString tmp = failMessage;\n\t\t\ttmp += L\"\\n\";\n\t\t\ttmp += loadError;\n\t\t\treturn validationError( tmp, hr );\n\t\t}\n\t\telse\n\t\t\treturn validationError( failMessage, hr );\n\t}\n\n\tappState.source.path = path;\n\tappState.source.impl = impl;\n\tappState.saveModelSource();\n\n\tEndDialog( IDOK );\n\treturn 0;\n}\n\nLRESULT LoadModelDlg::OnHyperlink( int idCtrl, LPNMHDR pnmh, BOOL& bHandled )\n{\n\tconst UINT code = pnmh->code;\n\tswitch( code )\n\t{\n\tcase NM_CLICK:\n\tcase NM_RETURN:\n\t\tbreak;\n\tdefault:\n\t\treturn 0;\n\t}\n\n\tPNMLINK pNMLink = (PNMLINK)pnmh;\n\tLPCTSTR url = pNMLink->item.szUrl;\n\tShellExecute( NULL, L\"open\", url, NULL, NULL, SW_SHOW );\n\tbHandled = TRUE;\n\treturn 0;\n}\n\nvoid LoadModelDlg::onModelAdvanced()\n{\n\tModelAdvancedDlg dlg{ appState };\n\tdlg.show( m_hWnd );\n}"
  },
  {
    "path": "Examples/WhisperDesktop/LoadModelDlg.h",
    "content": "#pragma once\n#include \"AppState.h\"\n#include \"Utils/WTL/atlddx.h\"\n#include \"Utils/miscUtils.h\"\n\nclass LoadModelDlg:\n\tpublic CDialogImpl<LoadModelDlg>,\n\tpublic CWinDataExchange<LoadModelDlg>,\n\tpublic iThreadPoolCallback\n{\n\tAppState& appState;\npublic:\n\tstatic constexpr UINT IDD = IDD_OPEN_MODEL;\n\tstatic constexpr UINT WM_CALLBACK_STATUS = WM_APP + 1;\n\n\tLoadModelDlg( AppState& app ) : appState( app ) { }\n\n\tHRESULT show();\n\n\tBEGIN_MSG_MAP( LoadModelDlg )\n\t\tMESSAGE_HANDLER( WM_INITDIALOG, OnInitDialog )\n\t\tON_BUTTON_CLICK( IDC_CONSOLE, cbConsole.click )\n\t\tCOMMAND_ID_HANDLER( IDCANCEL, OnCommand )\n\t\tCOMMAND_ID_HANDLER( IDOK, OnOk )\n\t\tCOMMAND_ID_HANDLER( IDC_BROWSE, OnBrowse )\n\t\tMESSAGE_HANDLER( WM_CALLBACK_STATUS, OnCallbackStatus )\n\t\tNOTIFY_ID_HANDLER( IDC_HYPERLINKS, OnHyperlink )\n\t\tON_BUTTON_CLICK( IDC_MODEL_ADV, onModelAdvanced )\n\tEND_MSG_MAP()\n\n\tBEGIN_DDX_MAP( LoadModelDlg )\n\t\tDDX_CONTROL_HANDLE( IDC_PATH, modelPath )\n\t\tDDX_CONTROL_HANDLE( IDC_MODEL_TYPE, cbModelType )\n\t\tDDX_CONTROL_HANDLE( IDC_PROGRESS, progressBar );\n\tEND_DDX_MAP()\n\t\nprivate:\n\tstd::vector<HWND> editorsWindows;\n\tstd::vector<HWND> pendingWindows;\n\tvoid setPending( bool nowPending );\n\n\tLRESULT OnInitDialog( UINT nMessage, WPARAM wParam, LPARAM lParam, BOOL& bHandled );\n\tLRESULT OnCallbackStatus( UINT, WPARAM wParam, LPARAM, BOOL& bHandled );\n\n\tLRESULT OnCommand( UINT, INT nIdentifier, HWND, BOOL& bHandled )\n\t{\n\t\tATLVERIFY( EndDialog( nIdentifier ) );\n\t\treturn 0;\n\t}\n\tLRESULT OnBrowse( UINT, INT, HWND, BOOL& bHandled );\n\tLRESULT OnOk( UINT, INT, HWND, BOOL& bHandled );\n\n\tConsoleCheckbox cbConsole;\n\tCComboBox cbModelType;\n\tCEdit modelPath;\n\tCProgressBarCtrl progressBar;\n\n\tLRESULT validationError( LPCTSTR message );\n\tLRESULT validationError( LPCTSTR message, HRESULT hr );\n\n\tThreadPoolWork work;\n\tCString path;\n\tWhisper::eModelImplementation impl;\n\tCString loadError;\n\tvoid __stdcall poolCallback() noexcept override final;\n\n\tLRESULT OnHyperlink( int idCtrl, LPNMHDR pnmh, BOOL& bHandled );\n\n\tstatic HRESULT __stdcall progressCallback( double val, void* pv ) noexcept;\n\n\tvoid onModelAdvanced();\n};"
  },
  {
    "path": "Examples/WhisperDesktop/ModelAdvancedDlg.cpp",
    "content": "﻿#include \"stdafx.h\"\n#include \"ModelAdvancedDlg.h\"\nusing Whisper::eGpuModelFlags;\n\nstatic void __stdcall addGpu( const wchar_t* name, void* pv )\n{\n\tCComboBox& cb = *(CComboBox*)pv;\n\tcb.AddString( name );\n}\n\nLRESULT ModelAdvancedDlg::onInitDialog( UINT nMessage, WPARAM wParam, LPARAM lParam, BOOL& bHandled )\n{\n\tcbWave = GetDlgItem( IDC_WAVE );\n\tcbReshapedMatMul = GetDlgItem( IDC_RESHAPED_MAT_MUL );\n\tcbAdapter = GetDlgItem( IDC_GPU );\n\tconst uint32_t flags = appState.gpuFlagsLoad();\n\n\t// Setup the \"Compute shaders\" combobox\n\tcbWave.AddString( L\"Wave64 shaders on AMD\" );\n\tcbWave.AddString( L\"Wave32, always\" );\n\tcbWave.AddString( L\"Wave64, always\" );\n\tint i = 0;\n\tif( 0 != ( flags & (uint32_t)eGpuModelFlags::Wave32 ) )\n\t\ti = 1;\n\telse if( 0 != ( flags & (uint32_t)eGpuModelFlags::Wave64 ) )\n\t\ti = 2;\n\tcbWave.SetCurSel( i );\n\n\t// Setup the \"Reshaped multiply\" combobox\n\tcbReshapedMatMul.AddString( L\"Reshape on AMD\" );\n\tcbReshapedMatMul.AddString( L\"Don’t reshape tensors\" );\n\tcbReshapedMatMul.AddString( L\"Reshape some tensors\" );\n\ti = 0;\n\tif( 0 != ( flags & (uint32_t)eGpuModelFlags::NoReshapedMatMul ) )\n\t\ti = 1;\n\telse if( 0 != ( flags & (uint32_t)eGpuModelFlags::UseReshapedMatMul ) )\n\t\ti = 2;\n\tcbReshapedMatMul.SetCurSel( i );\n\n\tcbAdapter.AddString( L\"Use default\" );\n\tHRESULT hr = Whisper::listGPUs( &addGpu, &cbAdapter );\n\tif( FAILED( hr ) )\n\t{\n\t\treportError( m_hWnd, L\"Unable to enumerate GPUs\", L\"\", hr );\n\t\treturn 0;\n\t}\n\tconst CString setting = appState.stringLoad( L\"gpu\" );\n\tif( setting.GetLength() <= 0 )\n\t\tcbAdapter.SetCurSel( 0 );\n\telse\n\t\tcbAdapter.SetCurSel( cbAdapter.FindStringExact( 1, setting ) );\n\n\treturn 0;\n}\n\nbool ModelAdvancedDlg::show( HWND owner )\n{\n\tauto res = DoModal( owner );\n\treturn res == IDOK;\n}\n\nvoid ModelAdvancedDlg::onOk()\n{\n\t// Gather values from these comboboxes\n\tuint32_t flags = 0;\n\n\tint i = cbWave.GetCurSel();\n\tif( 1 == i )\n\t\tflags |= (uint32_t)eGpuModelFlags::Wave32;\n\telse if( 2 == i )\n\t\tflags |= (uint32_t)eGpuModelFlags::Wave64;\n\n\ti = cbReshapedMatMul.GetCurSel();\n\tif( 1 == i )\n\t\tflags |= (uint32_t)eGpuModelFlags::NoReshapedMatMul;\n\telse if( 2 == i )\n\t\tflags |= (uint32_t)eGpuModelFlags::UseReshapedMatMul;\n\n\t// Save to registry\n\tappState.gpuFlagsStore( flags );\n\n\tint gpuIndex = cbAdapter.GetCurSel();\n\tif( gpuIndex <= 0 )\n\t\tappState.stringStore( L\"gpu\", nullptr );\n\telse\n\t{\n\t\tCString gpu;\n\t\tcbAdapter.GetLBText( gpuIndex, gpu );\n\t\tappState.stringStore( L\"gpu\", gpu );\n\t}\n\n\tEndDialog( IDOK );\n}"
  },
  {
    "path": "Examples/WhisperDesktop/ModelAdvancedDlg.h",
    "content": "#pragma once\n#include \"AppState.h\"\n#include \"Utils/WTL/atlddx.h\"\n#include \"Utils/miscUtils.h\"\n\nclass ModelAdvancedDlg :\n\tpublic CDialogImpl<ModelAdvancedDlg>\n{\n\tCComboBox cbWave, cbReshapedMatMul, cbAdapter;\n\tAppState& appState;\n\npublic:\n\tstatic constexpr UINT IDD = IDD_MODEL_ADV;\n\n\tModelAdvancedDlg( AppState& app ) : appState( app ) { }\n\n\tBEGIN_MSG_MAP( ModelAdvancedDlg )\n\t\tMESSAGE_HANDLER( WM_INITDIALOG, onInitDialog )\n\t\tON_BUTTON_CLICK( IDOK, onOk )\n\t\tON_BUTTON_CLICK( IDCANCEL, onCancel )\n\tEND_MSG_MAP()\n\n\tbool show( HWND owner );\n\nprivate:\n\n\tLRESULT onInitDialog( UINT nMessage, WPARAM wParam, LPARAM lParam, BOOL& bHandled );\n\n\tvoid onOk();\n\n\tvoid onCancel()\n\t{\n\t\tEndDialog( IDCANCEL );\n\t}\n};"
  },
  {
    "path": "Examples/WhisperDesktop/Readme.txt",
    "content": "﻿This example shows how to consume the DLL from a C++ GUI application.\n\nThe GUI is implemented with ATL and WTL libraries."
  },
  {
    "path": "Examples/WhisperDesktop/Resource.h",
    "content": "//{{NO_DEPENDENCIES}}\n// Microsoft Visual C++ generated include file.\n// Used by WhisperDesktop.rc\n//\n#define IDC_MYICON                      2\n#define IDD_WHISPERDESKTOP_DIALOG       102\n#define IDM_ABOUT                       104\n#define IDI_WHISPERDESKTOP              107\n#define IDI_SMALL                       108\n#define IDR_MAINFRAME                   128\n#define IDD_OPEN_MODEL                  129\n#define IDD_MAIN_DIALOG                 130\n#define IDD_TRANSCRIBE_DIALOG           130\n#define IDD_CAPTURE_DIALOG              131\n#define IDD_MODEL_ADV                   132\n#define IDC_PATH                        1000\n#define IDC_BROWSE                      1001\n#define IDC_MODEL_TYPE                  1002\n#define IDC_PATH_RESULT                 1002\n#define IDC_PROGRESS                    1003\n#define IDC_BROWSE_RESULT               1003\n#define IDC_SYSLINK1                    1004\n#define IDC_HYPERLINKS                  1004\n#define IDC_TRANSCRIBE_PROGRESS         1004\n#define IDC_PENDING_TEXT                1005\n#define IDC_MODEL_DESC                  1006\n#define IDC_LANGUAGE                    1007\n#define IDC_OUTPUT_FORMAT               1008\n#define IDC_PATH_MEDIA                  1009\n#define IDC_DEVICE                      1009\n#define IDC_BROWSE_MEDIA                1010\n#define IDC_TRANSCRIBE                  1011\n#define IDC_BACK                        1012\n#define IDC_CHECK1                      1013\n#define IDC_CONSOLE                     1013\n#define IDC_CAPTURE                     1014\n#define IDC_DEV_REFRESH                 1015\n#define IDC_SAVE_TEXT                   1016\n#define IDC_SAVE_APPEND                 1017\n#define IDC_SAVE_TIMESTAMPS             1018\n#define IDC_RUN_CAPTURE                 1019\n#define IDC_VOICE_ACTIVITY              1020\n#define IDC_VOICE_ACTIVITY_LBL          1021\n#define IDC_TRANS_STATUS                1022\n#define IDC_TRANS_LBL                   1023\n#define IDC_STALL_STATUS                1024\n#define IDC_STALL_LBL                   1025\n#define IDC_TRANSLATE                   1026\n#define IDC_MODEL_ADV                   1027\n#define IDC_WAVE                        1028\n#define IDC_RESHAPED_MAT_MUL            1029\n#define IDC_CHECK2                      1029\n#define IDC_USE_INPUT_FOLDER            1029\n#define IDC_RESHAPED_MAT_MUL2           1030\n#define IDC_GPU                         1030\n#define IDC_STATIC                      -1\n\n// Next default values for new objects\n// \n#ifdef APSTUDIO_INVOKED\n#ifndef APSTUDIO_READONLY_SYMBOLS\n#define _APS_NO_MFC                     1\n#define _APS_NEXT_RESOURCE_VALUE        131\n#define _APS_NEXT_COMMAND_VALUE         32771\n#define _APS_NEXT_CONTROL_VALUE         1030\n#define _APS_NEXT_SYMED_VALUE           110\n#endif\n#endif\n"
  },
  {
    "path": "Examples/WhisperDesktop/TranscribeDlg.cpp",
    "content": "#include \"stdafx.h\"\n#include \"TranscribeDlg.h\"\n#include \"Utils/logger.h\"\n\nHRESULT TranscribeDlg::show()\n{\n\tauto res = DoModal( nullptr );\n\tif( res == -1 )\n\t\treturn HRESULT_FROM_WIN32( GetLastError() );\n\tswitch( res )\n\t{\n\tcase IDC_BACK:\n\t\treturn SCREEN_MODEL;\n\tcase IDC_CAPTURE:\n\t\treturn SCREEN_CAPTURE;\n\t}\n\treturn S_OK;\n}\n\nconstexpr int progressMaxInteger = 1024 * 8;\n\nstatic const LPCTSTR regValInput = L\"sourceMedia\";\nstatic const LPCTSTR regValOutFormat = L\"resultFormat\";\nstatic const LPCTSTR regValOutPath = L\"resultPath\";\nstatic const LPCTSTR regValUseInputFolder = L\"useInputFolder\";\n\nLRESULT TranscribeDlg::OnInitDialog( UINT nMessage, WPARAM wParam, LPARAM lParam, BOOL& bHandled )\n{\n\t// First DDX call, hooks up variables to controls.\n\tDoDataExchange( false );\n\tprintModelDescription();\n\tlanguageSelector.initialize( m_hWnd, IDC_LANGUAGE, appState );\n\tcbConsole.initialize( m_hWnd, IDC_CONSOLE, appState );\n\tcbTranslate.initialize( m_hWnd, IDC_TRANSLATE, appState );\n\tpopulateOutputFormats();\n\n\tpendingState.initialize(\n\t\t{\n\t\t\tlanguageSelector, GetDlgItem( IDC_TRANSLATE ),\n\t\t\tsourceMediaPath, GetDlgItem( IDC_BROWSE_MEDIA ),\n\t\t\ttranscribeOutFormat, useInputFolder,\n\t\t\ttranscribeOutputPath, GetDlgItem( IDC_BROWSE_RESULT ),\n\t\t\tGetDlgItem( IDCANCEL ),\n\t\t\tGetDlgItem( IDC_BACK ),\n\t\t\tGetDlgItem( IDC_CAPTURE )\n\t\t},\n\t\t{\n\t\t\tprogressBar, GetDlgItem( IDC_PENDING_TEXT )\n\t\t} );\n\n\tHRESULT hr = work.create( this );\n\tif( FAILED( hr ) )\n\t{\n\t\treportError( m_hWnd, L\"CreateThreadpoolWork failed\", nullptr, hr );\n\t\tEndDialog( IDCANCEL );\n\t}\n\n\tprogressBar.SetRange32( 0, progressMaxInteger );\n\tprogressBar.SetStep( 1 );\n\n\tsourceMediaPath.SetWindowText( appState.stringLoad( regValInput ) );\n\ttranscribeOutFormat.SetCurSel( (int)appState.dwordLoad( regValOutFormat, 0 ) );\n\ttranscribeOutputPath.SetWindowText( appState.stringLoad( regValOutPath ) );\n\tif( appState.boolLoad( regValUseInputFolder ) )\n\t\tuseInputFolder.SetCheck( BST_CHECKED );\n\tBOOL unused;\n\tonOutFormatChange( 0, 0, nullptr, unused );\n\n\tappState.lastScreenSave( SCREEN_TRANSCRIBE );\n\tappState.setupIcon( this );\n\tATLVERIFY( CenterWindow() );\n\treturn 0;\n}\n\nvoid TranscribeDlg::printModelDescription()\n{\n\tCString text;\n\tif( S_OK == appState.model->isMultilingual() )\n\t\ttext = L\"Multilingual\";\n\telse\n\t\ttext = L\"Single-language\";\n\ttext += L\" model \\\"\";\n\tLPCTSTR path = appState.source.path;\n\tpath = ::PathFindFileName( path );\n\ttext += path;\n\ttext += L\"\\\", \";\n\tconst int64_t cb = appState.source.sizeInBytes;\n\tif( cb < 1 << 30 )\n\t{\n\t\tconstexpr double mul = 1.0 / ( 1 << 20 );\n\t\tdouble mb = (double)cb * mul;\n\t\ttext.AppendFormat( L\"%.1f MB\", mb );\n\t}\n\telse\n\t{\n\t\tconstexpr double mul = 1.0 / ( 1 << 30 );\n\t\tdouble gb = (double)cb * mul;\n\t\ttext.AppendFormat( L\"%.2f GB\", gb );\n\t}\n\ttext += L\" on disk, \";\n\ttext += implString( appState.source.impl );\n\ttext += L\" implementation\";\n\n\tmodelDesc.SetWindowText( text );\n}\n\n// Populate the \"Output Format\" combobox\nvoid TranscribeDlg::populateOutputFormats()\n{\n\ttranscribeOutFormat.AddString( L\"None\" );\n\ttranscribeOutFormat.AddString( L\"Text file\" );\n\ttranscribeOutFormat.AddString( L\"Text with timestamps\" );\n\ttranscribeOutFormat.AddString( L\"SubRip subtitles\" );\n\ttranscribeOutFormat.AddString( L\"WebVTT subtitles\" );\n}\n\n// The enum values should match 0-based indices of the combobox items\nenum struct TranscribeDlg::eOutputFormat : uint8_t\n{\n\tNone = 0,\n\tText = 1,\n\tTextTimestamps = 2,\n\tSubRip = 3,\n\tWebVTT = 4,\n};\n\nenum struct TranscribeDlg::eVisualState : uint8_t\n{\n\tIdle = 0,\n\tRunning = 1,\n\tStopping = 2\n};\n\n// CBN_SELCHANGE notification for IDC_OUTPUT_FORMAT combobox\nLRESULT TranscribeDlg::onOutFormatChange( UINT, INT, HWND, BOOL& bHandled )\n{\n\tBOOL enabled = transcribeOutFormat.GetCurSel() != 0;\n\tuseInputFolder.EnableWindow( enabled );\n\n\tif( isChecked( useInputFolder ) && enabled )\n\t{\n\t\tenabled = FALSE;\n\t\tsetOutputPath();\n\t}\n\ttranscribeOutputPath.EnableWindow( enabled );\n\ttranscribeOutputBrowse.EnableWindow( enabled );\n\n\treturn 0;\n}\n\n// EN_CHANGE notification for IDC_PATH_MEDIA edit box\nLRESULT TranscribeDlg::onInputChange( UINT, INT, HWND, BOOL& )\n{\n\tif( !useInputFolder.IsWindowEnabled() )\n\t\treturn 0;\n\tif( !isChecked( useInputFolder ) )\n\t\treturn 0;\n\tsetOutputPath();\n\treturn 0;\n}\n\nvoid TranscribeDlg::onBrowseMedia()\n{\n\tLPCTSTR title = L\"Input audio file to transcribe\";\n\tLPCTSTR filters = L\"Multimedia Files\\0*.wav;*.wave;*.mp3;*.wma;*.mp4;*.mpeg4;*.mkv;*.m4a\\0\\0\";\n\n\tCString path;\n\tsourceMediaPath.GetWindowText( path );\n\tif( !getOpenFileName( m_hWnd, title, filters, path ) )\n\t\treturn;\n\tsourceMediaPath.SetWindowText( path );\n\tif( useInputFolder.IsWindowEnabled() && useInputFolder.GetCheck() == BST_CHECKED )\n\t\tsetOutputPath( path );\n}\n\nstatic const LPCTSTR outputFilters = L\"Text files (*.txt)\\0*.txt\\0Text with timestamps (*.txt)\\0*.txt\\0SubRip subtitles (*.srt)\\0*.srt\\0WebVTT subtitles (*.vtt)\\0*.vtt\\0\\0\";\nstatic const std::array<LPCTSTR, 4> outputExtensions =\n{\n\tL\".txt\", L\".txt\", L\".srt\", L\".vtt\"\n};\n\nvoid TranscribeDlg::setOutputPath( const CString& input )\n{\n\tconst int format = transcribeOutFormat.GetCurSel() - 1;\n\tif( format < 0 || format >= outputExtensions.size() )\n\t\treturn;\n\tconst LPCTSTR ext = outputExtensions[ format ];\n\tCString path = input;\n\tpath.Trim();\n\tconst bool renamed = PathRenameExtension( path.GetBufferSetLength( path.GetLength() + 4 ), ext );\n\tpath.ReleaseBuffer();\n\tif( !renamed )\n\t\treturn;\n\ttranscribeOutputPath.SetWindowText( path );\n}\n\nvoid TranscribeDlg::setOutputPath()\n{\n\tCString path;\n\tif( !sourceMediaPath.GetWindowText( path ) )\n\t\treturn;\n\tif( path.GetLength() <= 0 )\n\t\treturn;\n\tsetOutputPath( path );\n}\n\nvoid TranscribeDlg::onInputFolderCheck()\n{\n\tconst bool checked = isChecked( useInputFolder );\n\n\tBOOL enableOutput = checked ? FALSE : TRUE;\n\ttranscribeOutputPath.EnableWindow( enableOutput );\n\ttranscribeOutputBrowse.EnableWindow( enableOutput );\n\n\tif( !checked )\n\t\treturn;\n\tsetOutputPath();\n}\n\nvoid TranscribeDlg::onBrowseOutput()\n{\n\tconst DWORD origFilterIndex = (DWORD)transcribeOutFormat.GetCurSel() - 1;\n\n\tLPCTSTR title = L\"Output Text File\";\n\tCString path;\n\ttranscribeOutputPath.GetWindowText( path );\n\tDWORD filterIndex = origFilterIndex;\n\tif( !getSaveFileName( m_hWnd, title, outputFilters, path, &filterIndex ) )\n\t\treturn;\n\n\tLPCTSTR ext = PathFindExtension( path );\n\tif( 0 == *ext && filterIndex < outputExtensions.size() )\n\t{\n\t\twchar_t* const buffer = path.GetBufferSetLength( path.GetLength() + 5 );\n\t\tPathRenameExtension( buffer, outputExtensions[ filterIndex ] );\n\t\tpath.ReleaseBuffer();\n\t}\n\n\ttranscribeOutputPath.SetWindowText( path );\n\tif( filterIndex != origFilterIndex )\n\t\ttranscribeOutFormat.SetCurSel( filterIndex + 1 );\n}\n\nvoid TranscribeDlg::setPending( bool nowPending )\n{\n\tpendingState.setPending( nowPending );\n}\n\nvoid TranscribeDlg::transcribeError( LPCTSTR text, HRESULT hr )\n{\n\treportError( m_hWnd, text, L\"Unable to transcribe audio\", hr );\n}\n\nvoid TranscribeDlg::onTranscribe()\n{\n\tswitch( transcribeArgs.visualState )\n\t{\n\tcase eVisualState::Running:\n\t\ttranscribeArgs.visualState = eVisualState::Stopping;\n\t\ttranscribeButton.EnableWindow( FALSE );\n\t\treturn;\n\tcase eVisualState::Stopping:\n\t\treturn;\n\t}\n\n\t// Validate input\n\tsourceMediaPath.GetWindowText( transcribeArgs.pathMedia );\n\tif( transcribeArgs.pathMedia.GetLength() <= 0 )\n\t{\n\t\ttranscribeError( L\"Please select an input audio file\" );\n\t\treturn;\n\t}\n\n\tif( !PathFileExists( transcribeArgs.pathMedia ) )\n\t{\n\t\ttranscribeError( L\"Input audio file does not exist\", HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND ) );\n\t\treturn;\n\t}\n\n\ttranscribeArgs.language = languageSelector.selectedLanguage();\n\ttranscribeArgs.translate = cbTranslate.checked();\n\tif( isInvalidTranslate( m_hWnd, transcribeArgs.language, transcribeArgs.translate ) )\n\t\treturn;\n\n\ttranscribeArgs.format = (eOutputFormat)(uint8_t)transcribeOutFormat.GetCurSel();\n\tif( transcribeArgs.format != eOutputFormat::None )\n\t{\n\t\ttranscribeOutputPath.GetWindowText( transcribeArgs.pathOutput );\n\t\tif( transcribeArgs.pathOutput.GetLength() <= 0 )\n\t\t{\n\t\t\ttranscribeError( L\"Please select an output text file\" );\n\t\t\treturn;\n\t\t}\n\t\tif( PathFileExists( transcribeArgs.pathOutput ) )\n\t\t{\n\t\t\tconst int resp = MessageBox( L\"The output file is already there.\\nOverwrite the file?\", L\"Confirm Overwrite\", MB_ICONQUESTION | MB_YESNO );\n\t\t\tif( resp != IDYES )\n\t\t\t\treturn;\n\t\t}\n\t\tappState.stringStore( regValOutPath, transcribeArgs.pathOutput );\n\t}\n\telse\n\t\tcbConsole.ensureChecked();\n\n\tappState.dwordStore( regValOutFormat, (uint32_t)(int)transcribeArgs.format );\n\tappState.boolStore( regValUseInputFolder, isChecked( useInputFolder ) );\n\tlanguageSelector.saveSelection( appState );\n\tcbTranslate.saveSelection( appState );\n\tappState.stringStore( regValInput, transcribeArgs.pathMedia );\n\n\tsetPending( true );\n\ttranscribeArgs.visualState = eVisualState::Running;\n\ttranscribeButton.SetWindowText( L\"Stop\" );\n\twork.post();\n}\n\nvoid __stdcall TranscribeDlg::poolCallback() noexcept\n{\n\tHRESULT hr = transcribe();\n\tPostMessage( WM_CALLBACK_STATUS, (WPARAM)hr );\n}\n\nstatic void printTime( CString& rdi, int64_t ticks )\n{\n\tconst Whisper::sTimeSpan ts{ (uint64_t)ticks };\n\tconst Whisper::sTimeSpanFields fields = ts;\n\n\tif( fields.days != 0 )\n\t{\n\t\trdi.AppendFormat( L\"%i days, %i hours\", fields.days, (int)fields.hours );\n\t\treturn;\n\t}\n\tif( ( fields.hours | fields.minutes ) != 0 )\n\t{\n\t\trdi.AppendFormat( L\"%02d:%02d:%02d\", (int)fields.hours, (int)fields.minutes, (int)fields.seconds );\n\t\treturn;\n\t}\n\trdi.AppendFormat( L\"%.3f seconds\", (double)ticks / 1E7 );\n}\n\nLRESULT TranscribeDlg::onCallbackStatus( UINT, WPARAM wParam, LPARAM, BOOL& bHandled )\n{\n\tsetPending( false );\n\ttranscribeButton.SetWindowText( L\"Transcribe\" );\n\ttranscribeButton.EnableWindow( TRUE );\n\tconst bool prematurely = ( transcribeArgs.visualState == eVisualState::Stopping );\n\ttranscribeArgs.visualState = eVisualState::Idle;\n\n\tconst HRESULT hr = (HRESULT)wParam;\n\tif( FAILED( hr ) )\n\t{\n\t\tLPCTSTR failMessage = L\"Transcribe failed\";\n\n\t\tif( transcribeArgs.errorMessage.GetLength() > 0 )\n\t\t{\n\t\t\tCString tmp = failMessage;\n\t\t\ttmp += L\"\\n\";\n\t\t\ttmp += transcribeArgs.errorMessage;\n\t\t\ttranscribeError( tmp, hr );\n\t\t}\n\t\telse\n\t\t\ttranscribeError( failMessage, hr );\n\n\t\treturn 0;\n\t}\n\n\tconst int64_t elapsed = ( GetTickCount64() - transcribeArgs.startTime ) * 10'000;\n\tconst int64_t media = transcribeArgs.mediaDuration;\n\tCString message;\n\tif( prematurely )\n\t\tmessage = L\"Transcribed an initial portion of the audio\";\n\telse\n\t\tmessage = L\"Transcribed the audio\";\n\tmessage += L\"\\nMedia duration: \";\n\tprintTime( message, media );\n\tmessage += L\"\\nProcessing time: \";\n\tprintTime( message, elapsed );\n\tmessage += L\"\\nRelative processing speed: \";\n\tdouble mul = (double)media / (double)elapsed;\n\tmessage.AppendFormat( L\"%g\", mul );\n\n\tMessageBox( message, L\"Transcribe Completed\", MB_OK | MB_ICONINFORMATION );\n\treturn 0;\n}\n\nvoid TranscribeDlg::getThreadError()\n{\n\tgetLastError( transcribeArgs.errorMessage );\n}\n\n#define CHECK_EX( hr ) { const HRESULT __hr = ( hr ); if( FAILED( __hr ) ) { getThreadError(); return __hr; } }\n\nHRESULT TranscribeDlg::transcribe()\n{\n\ttranscribeArgs.startTime = GetTickCount64();\n\tclearLastError();\n\ttranscribeArgs.errorMessage = L\"\";\n\n\tusing namespace Whisper;\n\tCComPtr<iAudioReader> reader;\n\n\tCHECK_EX( appState.mediaFoundation->openAudioFile( transcribeArgs.pathMedia, false, &reader ) );\n\n\tconst eOutputFormat format = transcribeArgs.format;\n\tCAtlFile outputFile;\n\tif( format != eOutputFormat::None )\n\t\tCHECK( outputFile.Create( transcribeArgs.pathOutput, GENERIC_WRITE, 0, CREATE_ALWAYS ) );\n\n\ttranscribeArgs.resultFlags = eResultFlags::Timestamps | eResultFlags::Tokens;\n\n\tCComPtr<iContext> context;\n\tCHECK_EX( appState.model->createContext( &context ) );\n\n\tsFullParams fullParams;\n\tCHECK_EX( context->fullDefaultParams( eSamplingStrategy::Greedy, &fullParams ) );\n\tfullParams.language = transcribeArgs.language;\n\tfullParams.setFlag( eFullParamsFlags::Translate, transcribeArgs.translate );\n\tfullParams.resetFlag( eFullParamsFlags::PrintRealtime );\n\n\t// Setup the callbacks\n\tfullParams.new_segment_callback = &newSegmentCallbackStatic;\n\tfullParams.new_segment_callback_user_data = this;\n\tfullParams.encoder_begin_callback = &encoderBeginCallback;\n\tfullParams.encoder_begin_callback_user_data = this;\n\n\t// Setup the progress indication sink\n\tsProgressSink progressSink{ &progressCallbackStatic, this };\n\t// Run the transcribe\n\tCHECK_EX( context->runStreamed( fullParams, progressSink, reader ) );\n\n\t// Once finished, query duration of the audio.\n\t// The duration before the processing is sometimes different, by 20 seconds for the file in that issue:\n\t// https://github.com/Const-me/Whisper/issues/4\n\tCHECK_EX( reader->getDuration( transcribeArgs.mediaDuration ) );\n\n\tcontext->timingsPrint();\n\n\tif( format == eOutputFormat::None )\n\t\treturn S_OK;\n\n\tCComPtr<iTranscribeResult> result;\n\tCHECK_EX( context->getResults( transcribeArgs.resultFlags, &result ) );\n\n\tsTranscribeLength len;\n\tCHECK_EX( result->getSize( len ) );\n\tconst sSegment* const segments = result->getSegments();\n\n\tswitch( format )\n\t{\n\tcase eOutputFormat::Text:\n\t\treturn writeTextFile( segments, len.countSegments, outputFile, false );\n\tcase eOutputFormat::TextTimestamps:\n\t\treturn writeTextFile( segments, len.countSegments, outputFile, true );\n\tcase eOutputFormat::SubRip:\n\t\treturn writeSubRip( segments, len.countSegments, outputFile );\n\tcase eOutputFormat::WebVTT:\n\t\treturn writeWebVTT( segments, len.countSegments, outputFile );\n\tdefault:\n\t\treturn E_FAIL;\n\t}\n}\n\n#undef CHECK_EX\n\ninline HRESULT TranscribeDlg::progressCallback( double p ) noexcept\n{\n\tconstexpr double mul = progressMaxInteger;\n\tint pos = lround( mul * p );\n\tprogressBar.PostMessage( PBM_SETPOS, pos, 0 );\n\treturn S_OK;\n}\n\nHRESULT __cdecl TranscribeDlg::progressCallbackStatic( double p, Whisper::iContext* ctx, void* pv ) noexcept\n{\n\tTranscribeDlg* dlg = (TranscribeDlg*)pv;\n\treturn dlg->progressCallback( p );\n}\n\nnamespace\n{\n\tHRESULT write( CAtlFile& file, const CStringA& line )\n\t{\n\t\tif( line.GetLength() > 0 )\n\t\t\tCHECK( file.Write( cstr( line ), (DWORD)line.GetLength() ) );\n\t\treturn S_OK;\n\t}\n\n\tconst char* skipBlank( const char* rsi )\n\t{\n\t\twhile( true )\n\t\t{\n\t\t\tconst char c = *rsi;\n\t\t\tif( c == ' ' || c == '\\t' )\n\t\t\t{\n\t\t\t\trsi++;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\treturn rsi;\n\t\t}\n\t}\n}\n\nusing Whisper::sSegment;\n\n\nHRESULT TranscribeDlg::writeTextFile( const sSegment* const segments, const size_t length, CAtlFile& file, bool timestamps )\n{\n\tusing namespace Whisper;\n\tCHECK( writeUtf8Bom( file ) );\n\tCStringA line;\n\tfor( size_t i = 0; i < length; i++ )\n\t{\n\t\tconst sSegment& seg = segments[ i ];\n\n\t\tif( timestamps )\n\t\t{\n\t\t\tline = \"[\";\n\t\t\tprintTime( line, seg.time.begin );\n\t\t\tline += \" --> \";\n\t\t\tprintTime( line, seg.time.end );\n\t\t\tline += \"]  \";\n\t\t}\n\t\telse\n\t\t\tline = \"\";\n\n\t\tline += skipBlank( seg.text );\n\t\tline += \"\\r\\n\";\n\t\tCHECK( write( file, line ) );\n\t}\n\treturn S_OK;\n}\n\nHRESULT TranscribeDlg::writeSubRip( const sSegment* const segments, const size_t length, CAtlFile& file )\n{\n\tCHECK( writeUtf8Bom( file ) );\n\tCStringA line;\n\tfor( size_t i = 0; i < length; i++ )\n\t{\n\t\tconst sSegment& seg = segments[ i ];\n\n\t\tline.Format( \"%zu\\r\\n\", i + 1 );\n\t\tprintTime( line, seg.time.begin, true );\n\t\tline += \" --> \";\n\t\tprintTime( line, seg.time.end, true );\n\t\tline += \"\\r\\n\";\n\t\tline += skipBlank( seg.text );\n\t\tline += \"\\r\\n\\r\\n\";\n\t\tCHECK( write( file, line ) );\n\t}\n\treturn S_OK;\n}\n\nHRESULT TranscribeDlg::writeWebVTT( const sSegment* const segments, const size_t length, CAtlFile& file )\n{\n\tCHECK( writeUtf8Bom( file ) );\n\tCStringA line;\n\tline = \"WEBVTT\\r\\n\\r\\n\";\n\tCHECK( write( file, line ) );\n\n\tfor( size_t i = 0; i < length; i++ )\n\t{\n\t\tconst sSegment& seg = segments[ i ];\n\t\tline = \"\";\n\n\t\tprintTime( line, seg.time.begin, false );\n\t\tline += \" --> \";\n\t\tprintTime( line, seg.time.end, false );\n\t\tline += \"\\r\\n\";\n\t\tline += skipBlank( seg.text );\n\t\tline += \"\\r\\n\\r\\n\";\n\t\tCHECK( write( file, line ) );\n\t}\n\treturn S_OK;\n}\n\ninline HRESULT TranscribeDlg::newSegmentCallback( Whisper::iContext* ctx, uint32_t n_new )\n{\n\tusing namespace Whisper;\n\tCComPtr<iTranscribeResult> result;\n\tCHECK( ctx->getResults( transcribeArgs.resultFlags, &result ) );\n\treturn logNewSegments( result, n_new );\n}\n\nHRESULT __cdecl TranscribeDlg::newSegmentCallbackStatic( Whisper::iContext* ctx, uint32_t n_new, void* user_data ) noexcept\n{\n\tTranscribeDlg* dlg = (TranscribeDlg*)user_data;\n\treturn dlg->newSegmentCallback( ctx, n_new );\n}\n\nHRESULT __cdecl TranscribeDlg::encoderBeginCallback( Whisper::iContext* ctx, void* user_data ) noexcept\n{\n\tTranscribeDlg* dlg = (TranscribeDlg*)user_data;\n\tconst eVisualState visualState = dlg->transcribeArgs.visualState;\n\tswitch( visualState )\n\t{\n\tcase eVisualState::Idle:\n\t\treturn E_NOT_VALID_STATE;\n\tcase eVisualState::Running:\n\t\treturn S_OK;\n\tcase eVisualState::Stopping:\n\t\treturn S_FALSE;\n\tdefault:\n\t\treturn E_UNEXPECTED;\n\t}\n}\n\nvoid TranscribeDlg::onWmClose()\n{\n\tif( GetDlgItem( IDCANCEL ).IsWindowEnabled() )\n\t{\n\t\tEndDialog( IDCANCEL );\n\t\treturn;\n\t}\n\n\tconstexpr UINT flags = MB_YESNO | MB_ICONQUESTION | MB_DEFBUTTON2;\n\tconst int res = this->MessageBox( L\"Transcribe is in progress.\\nDo you want to quit anyway?\", L\"Confirm exit\", flags );\n\tif( res != IDYES )\n\t\treturn;\n\n\t// TODO: instead of ExitProcess(), implement another callback in the DLL API, for proper cancellation of the background task\n\tExitProcess( 1 );\n}"
  },
  {
    "path": "Examples/WhisperDesktop/TranscribeDlg.h",
    "content": "#pragma once\n#include \"AppState.h\"\n#include \"Utils/WTL/atlddx.h\"\n#include \"Utils/WTL/atlcrack.h\"\n#include \"Utils/miscUtils.h\"\n#include \"Utils/LanguageDropdown.h\"\n#include \"Utils/TranslateCheckbox.h\"\n#include \"Utils/PendingState.h\"\n\nclass TranscribeDlg :\n\tpublic CDialogImpl<TranscribeDlg>,\n\tpublic CWinDataExchange<TranscribeDlg>,\n\tpublic iThreadPoolCallback\n{\n\tAppState& appState;\n\npublic:\n\tstatic constexpr UINT IDD = IDD_TRANSCRIBE_DIALOG;\n\tstatic constexpr UINT WM_CALLBACK_STATUS = WM_APP + 1;\n\n\tTranscribeDlg( AppState& app ) : appState( app ) { }\n\n\t// Show this dialog modally, without parent.\n\tHRESULT show();\n\n\tBEGIN_MSG_MAP( LoadModelDlg )\n\t\tMESSAGE_HANDLER( WM_INITDIALOG, OnInitDialog )\n\t\tON_BUTTON_CLICK( IDC_CONSOLE, cbConsole.click )\n\t\tON_BUTTON_CLICK( IDCANCEL, onClose )\n\t\tON_BUTTON_CLICK( IDC_BACK, onBack )\n\t\tON_BUTTON_CLICK( IDC_USE_INPUT_FOLDER, onInputFolderCheck )\n\t\tON_BUTTON_CLICK( IDC_BROWSE_MEDIA, onBrowseMedia )\n\t\tON_BUTTON_CLICK( IDC_BROWSE_RESULT, onBrowseOutput )\n\t\tON_BUTTON_CLICK( IDC_TRANSCRIBE, onTranscribe )\n\t\tON_BUTTON_CLICK( IDC_CAPTURE, onCapture )\n\t\tCOMMAND_HANDLER( IDC_OUTPUT_FORMAT, CBN_SELCHANGE, onOutFormatChange )\n\t\tCOMMAND_HANDLER( IDC_PATH_MEDIA, EN_CHANGE, onInputChange )\n\t\tMESSAGE_HANDLER( WM_CALLBACK_STATUS, onCallbackStatus )\n\t\tMSG_WM_CLOSE( onWmClose )\n\tEND_MSG_MAP()\n\n\tBEGIN_DDX_MAP( LoadModelDlg )\n\t\tDDX_CONTROL_HANDLE( IDC_MODEL_DESC, modelDesc )\n\t\tDDX_CONTROL_HANDLE( IDC_PATH_MEDIA, sourceMediaPath )\n\t\tDDX_CONTROL_HANDLE( IDC_OUTPUT_FORMAT, transcribeOutFormat )\n\t\tDDX_CONTROL_HANDLE( IDC_USE_INPUT_FOLDER, useInputFolder )\n\t\tDDX_CONTROL_HANDLE( IDC_PATH_RESULT, transcribeOutputPath )\n\t\tDDX_CONTROL_HANDLE( IDC_BROWSE_RESULT, transcribeOutputBrowse );\n\t\tDDX_CONTROL_HANDLE( IDC_TRANSCRIBE, transcribeButton );\n\t\tDDX_CONTROL_HANDLE( IDC_TRANSCRIBE_PROGRESS, progressBar );\n\tEND_DDX_MAP()\n\nprivate:\n\tPendingState pendingState;\n\tvoid setPending( bool nowPending );\n\tvoid transcribeError( LPCTSTR text, HRESULT hr = S_FALSE );\n\n\tLRESULT OnInitDialog( UINT nMessage, WPARAM wParam, LPARAM lParam, BOOL& bHandled );\n\n\tvoid onClose()\n\t{\n\t\tATLVERIFY( EndDialog( IDCANCEL ) );\n\t}\n\tvoid onBack()\n\t{\n\t\tATLVERIFY( EndDialog( IDC_BACK ) );\n\t}\n\n\tvoid printModelDescription();\n\tCStatic modelDesc;\n\tConsoleCheckbox cbConsole;\n\n\tLanguageDropdown languageSelector;\n\tTranslateCheckbox cbTranslate;\n\n\tCEdit sourceMediaPath;\n\tCButton useInputFolder;\n\tCEdit transcribeOutputPath;\n\tCButton transcribeOutputBrowse;\n\tCComboBox transcribeOutFormat;\n\tCButton transcribeButton;\n\tCProgressBarCtrl progressBar;\n\tvoid populateOutputFormats();\n\n\tLRESULT onOutFormatChange( UINT, INT, HWND, BOOL& bHandled );\n\tLRESULT onInputChange( UINT, INT, HWND, BOOL& );\n\tvoid onInputFolderCheck();\n\tvoid onBrowseMedia();\n\tvoid onBrowseOutput();\n\t// Despite the name, the method also handles the \"Stop\" button\n\tvoid onTranscribe();\n\tvoid onCapture()\n\t{\n\t\tEndDialog( IDC_CAPTURE );\n\t}\n\n\tThreadPoolWork work;\n\n\tenum struct eOutputFormat : uint8_t;\n\tenum struct eVisualState : uint8_t;\n\n\tstruct TranscribeArgs\n\t{\n\t\tCString pathMedia;\n\t\tCString pathOutput;\n\t\tuint32_t language;\n\t\tbool translate;\n\t\teOutputFormat format;\n\t\tWhisper::eResultFlags resultFlags;\n\t\tvolatile eVisualState visualState = (eVisualState)0;\n\t\tuint64_t startTime;\n\t\tint64_t mediaDuration;\n\t\tCString errorMessage;\n\t};\n\tTranscribeArgs transcribeArgs;\n\n\tvoid __stdcall poolCallback() noexcept override final;\n\n\tLRESULT onCallbackStatus( UINT, WPARAM wParam, LPARAM, BOOL& bHandled );\n\n\tHRESULT transcribe();\n\tvoid getThreadError();\n\t\n\tstatic HRESULT writeTextFile( const Whisper::sSegment* const segments, const size_t length, CAtlFile& file, bool timestamps );\n\tstatic HRESULT writeSubRip( const Whisper::sSegment* const segments, const size_t length, CAtlFile& file );\n\tstatic HRESULT writeWebVTT( const Whisper::sSegment* const segments, const size_t length, CAtlFile& file );\n\n\tstatic HRESULT __cdecl newSegmentCallbackStatic( Whisper::iContext* ctx, uint32_t n_new, void* user_data ) noexcept;\n\tstatic HRESULT __cdecl encoderBeginCallback( Whisper::iContext* ctx, void* user_data ) noexcept;\n\tHRESULT newSegmentCallback( Whisper::iContext* ctx, uint32_t n_new );\n\n\tstatic HRESULT __cdecl progressCallbackStatic( double p, Whisper::iContext* ctx, void* pv ) noexcept;\n\tHRESULT progressCallback( double p ) noexcept;\n\n\tvoid onWmClose();\n\t// Populate output path based on the provided input media path\n\tvoid setOutputPath( const CString& input );\n\t// Populate output path based on the input media path in the edit box\n\tvoid setOutputPath();\n};"
  },
  {
    "path": "Examples/WhisperDesktop/Utils/DebugConsole.cpp",
    "content": "// https://github.com/Const-me/vis_avs_dx/blob/master/avs_dx/DxVisuals/Interop/ConsoleLogger.cpp\n#include \"stdafx.h\"\n#include \"DebugConsole.h\"\n#include \"miscUtils.h\"\n#include \"../AppState.h\"\n#include \"logger.h\"\n#include <string>\n\nnamespace\n{\n\tusing Whisper::eLogLevel;\n\n\tconstexpr uint16_t defaultAttributes = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;\n\n\tinline uint16_t textAttributes( eLogLevel lvl )\n\t{\n\t\tswitch( lvl )\n\t\t{\n\t\tcase eLogLevel::Error:\n\t\t\treturn FOREGROUND_RED | FOREGROUND_INTENSITY;\n\t\tcase eLogLevel::Warning:\n\t\t\treturn FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY;\n\t\tcase eLogLevel::Info:\n\t\t\treturn FOREGROUND_GREEN | FOREGROUND_INTENSITY;\n\t\tcase eLogLevel::Debug:\n\t\t\treturn FOREGROUND_BLUE | FOREGROUND_INTENSITY;\n\t\t}\n\t\treturn defaultAttributes;\n\t}\n\n\t// Background stuff: accumulate messages in a small buffer, in case user will want to see them in the console.\n\t// Ideally, we should accumulate them in a more efficient data structure, maybe a circular buffer.\n\t// However, we don't have that many messages per second, this simple solution that uses std::deque is probably good enough for the job.\n\tstatic constexpr uint16_t bufferSize = 64;\n\n\tusing Lock = CComCritSecLock<CComAutoCriticalSection>;\n#define LOCK() Lock __lock{ critSec }\n\n\tthread_local std::string threadError;\n}\n\nHRESULT DebugConsole::Entry::print( HANDLE hConsole, CString& tempString ) const\n{\n\tif( !SetConsoleTextAttribute( hConsole, textAttributes( level ) ) )\n\t\treturn getLastHr();\n\n\tmakeUtf16( tempString, message );\n\ttempString += L\"\\r\\n\";\n\tif( !WriteConsoleW( hConsole, tempString, (DWORD)tempString.GetLength(), nullptr, nullptr ) )\n\t\treturn getLastHr();\n\treturn S_OK;\n}\n\nvoid clearLastError()\n{\n\tthreadError.clear();\n}\n\nbool getLastError( CString& rdi )\n{\n\tif( threadError.empty() )\n\t{\n\t\trdi = L\"\";\n\t\treturn false;\n\t}\n\telse\n\t{\n\t\tmakeUtf16( rdi, threadError.c_str() );\n\t\tthreadError.clear();\n\t\treturn true;\n\t}\n}\n\ninline void DebugConsole::logSink( eLogLevel lvl, const char* message )\n{\n\tLOCK();\n\n\t// Add to the buffer\n\twhile( buffer.size() >= bufferSize )\n\t\tbuffer.pop_front();\n\tbuffer.emplace_back( Entry{ lvl, message } );\n\n\t// If the console window is shown, print there, too.\n\tif( output )\n\t\tbuffer.rbegin()->print( output, tempString );\n}\n\nvoid __stdcall DebugConsole::logSinkStatic( void* context, eLogLevel lvl, const char* message )\n{\n\tif( lvl == eLogLevel::Error )\n\t\tthreadError = message;\n\n\tDebugConsole* con = (DebugConsole*)context;\n\tcon->logSink( lvl, message );\n}\n\nHRESULT DebugConsole::initialize( Whisper::eLogLevel level )\n{\n\tif( nullptr != pGlobalInstance )\n\t\treturn HRESULT_FROM_WIN32( ERROR_ALREADY_INITIALIZED );\n\tpGlobalInstance = this;\n\n\tWhisper::sLoggerSetup setup;\n\tsetup.sink = &logSinkStatic;\n\tsetup.context = this;\n\tsetup.level = level;\n\tsetup.flags = Whisper::eLoggerFlags::SkipFormatMessage;\n\treturn Whisper::setupLogger( setup );\n}\n\nDebugConsole::~DebugConsole()\n{\n\thide();\n\n\tWhisper::sLoggerSetup setup;\n\tmemset( &setup, 0, sizeof( setup ) );\n\tWhisper::setupLogger( setup );\n\n\tpGlobalInstance = nullptr;\n}\n\nDebugConsole* DebugConsole::pGlobalInstance = nullptr;\n\nvoid DebugConsole::windowClosed()\n{\n\tLOCK();\n\tif( FreeConsole() )\n\t{\n\t\t// Apparently, FreeConsole already closes that handle: https://stackoverflow.com/q/12676312/126995\n\t\toutput.Detach();\n\t}\n\toutput.Close();\n\n\tfor( CButton* b : checkboxes )\n\t{\n\t\tif( !*b )\n\t\t\tcontinue;\n\t\tif( !b->IsWindow() )\n\t\t\tcontinue;\n\t\tPostMessage( *b, BM_SETCHECK, BST_UNCHECKED, 0 );\n\t}\n}\n\nBOOL __stdcall DebugConsole::consoleHandlerRoutine( DWORD dwCtrlType )\n{\n\tswitch( dwCtrlType )\n\t{\n\tcase CTRL_CLOSE_EVENT:\n\tcase CTRL_C_EVENT:\n\tcase CTRL_BREAK_EVENT:\n\t\tpGlobalInstance->windowClosed();\n\t\treturn TRUE;\n\t}\n\treturn TRUE;\n}\n\nHRESULT DebugConsole::show()\n{\n\tHWND hWnd = GetConsoleWindow();\n\tif( nullptr != hWnd )\n\t{\n\t\tShowWindow( hWnd, SW_RESTORE );\n\t\tSetForegroundWindow( hWnd );\n\t\treturn S_FALSE;\n\t}\n\n\tif( !AllocConsole() )\n\t\treturn getLastHr();\n\n\toutput.Close();\n\toutput.Attach( GetStdHandle( STD_OUTPUT_HANDLE ) );\n\tif( !output )\n\t\treturn getLastHr();\n\n\tconstexpr UINT cp = CP_UTF8;\n\tif( IsValidCodePage( cp ) )\n\t\tSetConsoleOutputCP( cp );\n\n\t// Enable ANSI color coding\n\tDWORD mode = 0;\n\tif( !GetConsoleMode( output, &mode ) )\n\t\treturn getLastHr();\n\tif( 0 == ( mode & ENABLE_VIRTUAL_TERMINAL_PROCESSING ) )\n\t{\n\t\tmode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;\n\t\tif( !SetConsoleMode( output, mode ) )\n\t\t\treturn getLastHr();\n\t}\n\n\tSetConsoleTitle( L\"Whisper Desktop Debug Console\" );\n\n\tSetConsoleCtrlHandler( &consoleHandlerRoutine, TRUE );\n\n\t// Disable close command in the sys.menu of the new console, otherwise the whole process will quit: https://stackoverflow.com/a/12015131/126995\n\tHWND hwnd = ::GetConsoleWindow();\n\tif( hwnd != nullptr )\n\t{\n\t\tHMENU hMenu = ::GetSystemMenu( hwnd, FALSE );\n\t\tif( hMenu != NULL )\n\t\t\tDeleteMenu( hMenu, SC_CLOSE, MF_BYCOMMAND );\n\t}\n\n\t// Print old log entries\n\tfor( const auto& e : buffer )\n\t\tCHECK( e.print( output, tempString ) );\n\n\tconst CStringA msg = \"Press Control+C or Control+Break to close this window\\r\\n\";\n\tif( !SetConsoleTextAttribute( output, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY ) )\n\t\treturn getLastHr();\n\tif( !WriteConsoleA( output, cstr( msg ), msg.GetLength(), nullptr, nullptr ) )\n\t\treturn getLastHr();\n\n\treturn S_OK;\n}\n\nHRESULT DebugConsole::hide()\n{\n\tLOCK();\n\tif( !output )\n\t\treturn S_FALSE;\n\twindowClosed();\n\treturn S_OK;\n}\n\nvoid DebugConsole::addCheckbox( CButton& cb )\n{\n\tcheckboxes.emplace( &cb );\n}\nvoid DebugConsole::removeCheckbox( CButton& cb )\n{\n\tcheckboxes.erase( &cb );\n}\n\nHRESULT ConsoleCheckbox::initialize( HWND dialog, int idc, AppState& state )\n{\n\tcontrol = GetDlgItem( dialog, idc );\n\tassert( control );\n\n\tconsole = &state.console;\n\tif( state.console.isVisible() )\n\t\tcontrol.SetCheck( BST_CHECKED );\n\n\tstate.console.addCheckbox( control );\n\treturn S_OK;\n}\n\nvoid ConsoleCheckbox::click()\n{\n\tconst int state = control.GetCheck();\n\tif( state == BST_CHECKED )\n\t\tconsole->show();\n\telse\n\t\tconsole->hide();\n}\n\nvoid ConsoleCheckbox::ensureChecked()\n{\n\tconst int state = control.GetCheck();\n\tif( state == BST_CHECKED )\n\t\treturn;\n\tcontrol.SetCheck( BST_CHECKED );\n\tconsole->show();\n}\n\nvoid DebugConsole::log( eLogLevel lvl, const char* pszFormat, va_list args )\n{\n\tLOCK();\n\t// Add to the buffer\n\twhile( buffer.size() >= bufferSize )\n\t\tbuffer.pop_front();\n\n\ttempStringA.FormatV( pszFormat, args );\n\tbuffer.emplace_back( Entry{ lvl, tempStringA } );\n\n\t// If the console window is shown, print there, too.\n\tif( output )\n\t\tbuffer.rbegin()->print( output, tempString );\n}\n\nvoid DebugConsole::logMessage( eLogLevel lvl, const char* pszFormat, va_list args )\n{\n\tif( nullptr == pGlobalInstance )\n\t\treturn;\n\tpGlobalInstance->log( lvl, pszFormat, args );\n}\n\nvoid logMessage( Whisper::eLogLevel lvl, const char8_t* pczFormat, va_list args )\n{\n\tDebugConsole::logMessage( lvl, (const char*)pczFormat, args );\n}"
  },
  {
    "path": "Examples/WhisperDesktop/Utils/DebugConsole.h",
    "content": "#pragma once\n#include <whisperWindows.h>\n#include <deque>\n#include <unordered_set>\n\nclass AppState;\nclass DebugConsole\n{\n\tusing eLogLevel = Whisper::eLogLevel;\n\n\tstruct Entry\n\t{\n\t\teLogLevel level;\n\t\tCStringA message;\n\t\tHRESULT print( HANDLE hConsole, CString& tempString ) const;\n\t};\n\n\tCComAutoCriticalSection critSec;\n\tstd::deque<Entry> buffer;\n\tCString tempString;\n\tCHandle output;\n\n\tinline void logSink( eLogLevel lvl, const char* message );\n\tstatic void __stdcall logSinkStatic( void* context, eLogLevel lvl, const char* message );\n\n\tstatic BOOL __stdcall consoleHandlerRoutine( DWORD dwCtrlType );\n\n\tstatic DebugConsole* pGlobalInstance;\n\tvoid windowClosed();\n\n\tstd::unordered_set<CButton*> checkboxes;\n\n\tCStringA tempStringA;\n\tvoid log( eLogLevel lvl, const char* pszFormat, va_list args );\n\npublic:\n\tHRESULT initialize( eLogLevel level = eLogLevel::Debug );\n\t~DebugConsole();\n\n\tHRESULT show();\n\tHRESULT hide();\n\tbool isVisible() const { return output; }\n\n\tvoid addCheckbox( CButton& cb );\n\tvoid removeCheckbox( CButton& cb );\n\n\tstatic void logMessage( eLogLevel lvl, const char* pszFormat, va_list args );\n};\n\nclass ConsoleCheckbox\n{\n\tCButton control;\n\tDebugConsole* console = nullptr;\n\npublic:\n\tHRESULT initialize( HWND dialog, int idc, AppState& state );\n\tvoid click();\n\t~ConsoleCheckbox()\n\t{\n\t\tif( nullptr != console )\n\t\t\tconsole->removeCheckbox( control );\n\t}\n\tvoid ensureChecked();\n};"
  },
  {
    "path": "Examples/WhisperDesktop/Utils/LanguageDropdown.cpp",
    "content": "#include \"stdafx.h\"\n#include \"LanguageDropdown.h\"\n#include \"miscUtils.h\"\n\nnamespace\n{\n\tinline wchar_t toUpper( wchar_t c )\n\t{\n\t\tsize_t st = (uint16_t)c;\n\t\tst = reinterpret_cast<size_t>( CharUpperW( reinterpret_cast<LPWSTR>( st ) ) );\n\t\treturn (wchar_t)(uint16_t)st;\n\t}\n\n\tvoid makeTitleCase( CString& s )\n\t{\n\t\tbool cap = true;\n\t\tfor( int i = 0; i < s.GetLength(); i++ )\n\t\t{\n\t\t\twchar_t c = s[ i ];\n\t\t\tif( cap )\n\t\t\t{\n\t\t\t\tc = toUpper( c );\n\t\t\t\ts.SetAt( i, c );\n\t\t\t}\n\t\t\tcap = false;\n\t\t\tif( c == ' ' )\n\t\t\t\tcap = true;\n\t\t}\n\t}\n}\n\nint LanguageDropdown::getInitialSelection( AppState& state ) const\n{\n\tconstexpr uint32_t english = 0x6E65;\n\n\t// Load preference from the registry\n\tuint32_t id = state.languageRead();\n\tif( id == UINT_MAX )\n\t\tid = english;\n\n\tauto it = std::find( keys.begin(), keys.end(), id );\n\tif( it == keys.end() )\n\t{\n\t\tid = english;\n\t\tit = std::find( keys.begin(), keys.end(), id );\n\t\tassert( it != keys.end() );\n\t}\n\n\tptrdiff_t idx = it - keys.begin();\n\treturn (int)idx;\n}\n\nvoid LanguageDropdown::initialize( HWND owner, int idc, AppState& state )\n{\n\tm_hWnd = GetDlgItem( owner, idc );\n\tassert( nullptr != m_hWnd );\n\n\tWhisper::sLanguageList list;\n\tWhisper::getSupportedLanguages( list );\n\n\tconst size_t length = list.length;\n\tkeys.resize( length );\n\tCString utf16;\n\tfor( size_t i = 0; i < length; i++ )\n\t{\n\t\tkeys[ i ] = list.pointer[ i ].key;\n\t\tmakeUtf16( utf16, list.pointer[ i ].name );\n\t\tmakeTitleCase( utf16 );\n\t\tSendMessage( m_hWnd, CB_ADDSTRING, 0, (LPARAM)cstr( utf16 ) );\n\t}\n\n\tconst int curSel = getInitialSelection( state );\n\tSendMessage( m_hWnd, CB_SETCURSEL, curSel, 0 );\n}\n\nuint32_t LanguageDropdown::selectedLanguage()\n{\n\tconst int cs = (int)SendMessage( m_hWnd, CB_GETCURSEL, 0, 0 );\n\tif( cs < 0 || cs >= keys.size() )\n\t\treturn UINT_MAX;\n\treturn keys[ cs ];\n}\n\nvoid LanguageDropdown::saveSelection( AppState& state )\n{\n\tstate.languageWrite( selectedLanguage() );\n}"
  },
  {
    "path": "Examples/WhisperDesktop/Utils/LanguageDropdown.h",
    "content": "#pragma once\n#include \"../AppState.h\"\n\n// Dropdown list which implements language selector control\nclass LanguageDropdown\n{\n\tHWND m_hWnd = nullptr;\n\tstd::vector<uint32_t> keys;\n\tint getInitialSelection( AppState& state ) const;\n\npublic:\n\toperator HWND() const\n\t{\n\t\treturn m_hWnd;\n\t}\n\n\t// Query language list form the native library, populate the combo box\n\t// Then load the last saved language selection from registry, and preselect an item.\n\tvoid initialize( HWND owner, int idc, AppState& state );\n\n\t// Get the ID of the currently selected language, or UINT_MAX if nothing's selected\n\tuint32_t selectedLanguage();\n\n\t// Get the ID of the currently selected language, and store in registry\n\tvoid saveSelection( AppState& state );\n};"
  },
  {
    "path": "Examples/WhisperDesktop/Utils/PendingState.cpp",
    "content": "#include \"stdafx.h\"\n#include \"PendingState.h\"\n\nvoid PendingState::initialize( std::initializer_list<HWND> editors, std::initializer_list<HWND> pending )\n{\n\teditorsWindows = editors;\n\twasEnabled.resize( editorsWindows.size() );\n\tpendingWindows = pending;\n}\n\nvoid PendingState::setPending( bool nowPending )\n{\n\tif( nowPending )\n\t{\n\t\tfor( size_t i = 0; i < editorsWindows.size(); i++ )\n\t\t{\n\t\t\tBOOL e = IsWindowEnabled( editorsWindows[ i ] );\n\t\t\tif( e )\n\t\t\t{\n\t\t\t\twasEnabled[ i ] = true;\n\t\t\t\tEnableWindow( editorsWindows[ i ], FALSE );\n\t\t\t}\n\t\t\telse\n\t\t\t\twasEnabled[ i ] = false;\n\t\t}\n\t}\n\telse\n\t{\n\t\tfor( size_t i = 0; i < editorsWindows.size(); i++ )\n\t\t{\n\t\t\tif( !wasEnabled[ i ] )\n\t\t\t\tcontinue;\n\t\t\tEnableWindow( editorsWindows[ i ], TRUE );\n\t\t}\n\t}\n\n\tconst int show = nowPending ? SW_NORMAL : SW_HIDE;\n\tfor( HWND w : pendingWindows )\n\t\t::ShowWindow( w, show );\n}"
  },
  {
    "path": "Examples/WhisperDesktop/Utils/PendingState.h",
    "content": "#pragma once\n\n// Utility class to switch visual state of dialog controls between idle and pending\nclass PendingState\n{\n\tstd::vector<HWND> editorsWindows;\n\tstd::vector<bool> wasEnabled;\n\tstd::vector<HWND> pendingWindows;\npublic:\n\tvoid initialize( std::initializer_list<HWND> editors, std::initializer_list<HWND> pending );\n\tvoid setPending( bool nowPending );\n};"
  },
  {
    "path": "Examples/WhisperDesktop/Utils/TranslateCheckbox.cpp",
    "content": "#include \"stdafx.h\"\n#include \"TranslateCheckbox.h\"\n\nstatic const LPCTSTR regValTranslate = L\"translate\";\n\nvoid TranslateCheckbox::initialize( HWND owner, int idc, AppState& state )\n{\n\tm_hWnd = GetDlgItem( owner, idc );\n\tassert( nullptr != m_hWnd );\n\n\tif( state.boolLoad( regValTranslate ) )\n\t\t::SendMessage( m_hWnd, BM_SETCHECK, BST_CHECKED, 0L );\n}\n\nbool TranslateCheckbox::checked()\n{\n\tassert( nullptr != m_hWnd );\n\tconst int state = ( int )::SendMessage( m_hWnd, BM_GETCHECK, 0, 0 );\n\treturn state == BST_CHECKED;\n}\n\nvoid TranslateCheckbox::saveSelection( AppState& state )\n{\n\tstate.boolStore( regValTranslate, checked() );\n}"
  },
  {
    "path": "Examples/WhisperDesktop/Utils/TranslateCheckbox.h",
    "content": "#pragma once\n#include \"../AppState.h\"\n\nclass TranslateCheckbox\n{\n\tHWND m_hWnd = nullptr;\npublic:\n\toperator HWND() const\n\t{\n\t\treturn m_hWnd;\n\t}\n\n\tvoid initialize( HWND owner, int idc, AppState& state );\n\n\tbool checked();\n\n\tvoid saveSelection( AppState& state );\n};"
  },
  {
    "path": "Examples/WhisperDesktop/Utils/WTL/MS-PL.txt",
    "content": "Microsoft Public License (MS-PL)\n\nThis license governs use of the accompanying software. If you use the software, you\naccept this license. If you do not accept the license, do not use the software.\n\n1. Definitions\nThe terms \"reproduce,\" \"reproduction,\" \"derivative works,\" and \"distribution\" have the\nsame meaning here as under U.S. copyright law.\nA \"contribution\" is the original software, or any additions or changes to the software.\nA \"contributor\" is any person that distributes its contribution under this license.\n\"Licensed patents\" are a contributor's patent claims that read directly on its contribution.\n\n2. Grant of Rights\n(A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create.\n(B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software.\n\n3. Conditions and Limitations\n(A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks.\n(B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically.\n(C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software.\n(D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license.\n(E) The software is licensed \"as-is.\" You bear the risk of using it. The contributors give no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement.\n"
  },
  {
    "path": "Examples/WhisperDesktop/Utils/WTL/ReadMe.html",
    "content": "<html>\n\n<head>\n\t<meta http-equiv=\"Content-Language\" content=\"en-us\">\n\t<meta http-equiv=\"Content-Type\" content=\"text/html; charset=windows-1252\">\n\t<title>Windows Template Library</title>\n\t<style type=\"text/css\">\n\t\thtml, body {\n\t\t\twidth: 800px;\n\t\t\tfont-family: Arial;\n\t\t\tfont-size: 10pt;\n\t\t}\n\t\t.wtl10 {\n\t\t\tfont-family: Arial;\n\t\t\tfont-size: 12pt;\n\t\t}\n\t\t.table1 {\n\t\t\tfont-family: Arial;\n\t\t\tfont-size: 10pt;\n\t\t}\n\t\t.code1 {\n\t\t\tfont-family: Courier;\n\t\t\tfont-size: 10pt;\n\t\t\tmargin-left:4ch\n\t\t}\n\t</style>\n</head>\n\n<body>\n\n<table class=\"table1\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" style=\"border-collapse: collapse\" bordercolor=\"#111111\">\n\t<tr>\n\t\t<td class=\"wtl10\">\n\t\t\t<b>Windows Template Library - WTL 10</b>\n\t\t</td>\n\t\t<td align=right>\n\t\t\tversion 10.0.10320 Release (2020-11-15)\n\t\t</td>\n\t</tr>\n\t<tr>\n\t\t<td colspan=2><hr></td>\n\t</tr>\n\t<tr>\n\t\t<td colspan=2>Copyright &#169; 2020 Microsoft Corporation, WTL Team. All rights reserved.</td>\n\t</tr>\n\t<tr>\n\t\t<td colspan=2><br></td>\n\t</tr>\n\t<tr>\n\t\t<td colspan=2>\n\t\t\tThis file is a part of the Windows Template Library. \n\t\t\tThe use and distribution terms for this software are covered by the<br>\n\t\t\tMicrosoft Public License (<a target=\"_blank\" href=\"http://opensource.org/licenses/MS-PL\">http://opensource.org/licenses/MS-PL</a>) \n\t\t\twhich can be found in the file MS-PL.txt at the root folder.\n\t\t</td>\n\t</tr>\n\t<tr>\n\t\t<td colspan=2><hr></td>\n\t</tr>\n</table>\n<p style=margin:0px><br></p>\n\n<p>Welcome to the Windows Template Library, version 10. This document contains the following topics:</p>\n\n<ul>\n\t<li><a href=\"#Introduction\">Introduction</a></li>\n\t<li><a href=\"#Features and Installation\">Features and Installation</a></li>\n\t<li><a href=\"#Packing List\">Packing List</a></li>\n\t<li><a href=\"#Class Overview\">Class Overview</a></li>\n\t<li><a href=\"#ATL/WTL AppWizard\">ATL/WTL AppWizard</a></li>\n\t<li><a href=\"#WTL in MFC\">How to use WTL in an MFC project</a></li>\n\t<li><a href=\"#WTL Releases\">WTL Releases</a></li>\n</ul>\n<p style=margin:0px><br></p>\n\n<h3 style=margin:0px>Introduction</h3>\n<p>\n\tWindows Template Library, or WTL, is a set of \n\tclasses that extend ATL to support more complex user interfaces for either \n\tapplications or various UI components, while maintaining the big advantage of \n\tATL - small and fast code. WTL classes were designed to be the best and the \n\teasiest way to implement rich Win32 based UI for ATL based applications, \n\tservers, components, and controls.\n</p>\n\n<p>\n\tWTL provides support for implementing many \n\tuser interface elements, from frame and popup windows, to MDI, standard and \n\tcommon controls, common dialogs, property sheets and pages, GDI objects, UI \n\tupdating, scrollable windows, splitter windows, command bars, etc. The WTL \n\tclasses are mostly templated and use minimal instance data and inline functions. \n\tThey were not designed as a framework, so they do not force a particular \n\tapplication model, and can accommodate any. The classes do not use hooks or \n\tthread local storage, so they have no restrictions that those techniques impose. \n\tThey also have no inter-dependencies and can be freely mixed with straight SDK \n\tcode. In summary, WTL delivers very small and efficient code, very close in size \n\tand speed to SDK programs, while presenting a more logical, object oriented \n\tmodel to a programmer.\n</p>\n<p style=margin:0px><br></p>\n\n<h3 style=margin:0px><a name=\"Features and Installation\"></a>Features and Installation</h3>\n\n<p>\n\tThis is the ninth public release of WTL. This version is released \n\tunder the Microsoft Public License, enabling developers from the WTL community to \n\tcontribute to the library.\n</p>\n<p>\n\tWTL classes can be used with all versions of VC++ from 2005 to the newest, 2019. \n\tAppWizard for Visual Studio is included.\n</p>\n<p>\n\tThe WTL classes are provided in header files located in the include directory. \n\tThe only header files that must be included is atlapp.h, while others can be used when needed. \n\tThe name of the file doesn't mean that you have to create an application, just that \n\tatlapp.h contains base definitions required for WTL projects.\n</p>\n<p>\n\tTo install WTL, just copy the whole directory structure, or unpack the archive file, \n\tto the location of your choice. Please be sure to <b>add the WTL\\include directory</b> \n\tto the list of include directories in VC++, so that the compiler \n\tcan find them when you include them in your projects.\n</p>\n<p>\n\tSetup programs for the AppWizard are provided. After executing the setup script, \n\tATL/WTL AppWizard will appear in the list of AppWizards when you select File.New.Project \n\tin VC++ IDE. The file AppWiz\\setup.js is the setup script for all supported versions of Visual Studio.\n</p>\n<p>\n\tTo manually install AppWizard for VC++ 2005, copy all WTLAppWiz.* files from AppWiz\\Files to VC++ \n\tprojects directory, %VCDIR%\\VC\\vcprojects, where %VCDIR% is the directory \n\twhere VC++ 2005 is installed. After that, open WTL10AppWiz.vsz and modify the \n\tline that contains ABSOLUTE_PATH to contain %WTLDIR%\\AppWiz\\Files, where \n\t%WTLDIR% is the directory where WTL files are.\n</p>\n\n<p style=margin:0px>Compiler/IDE/ATL support:</p>\n<ul style=margin:0px>\n\t<li>Visual C++ 2005&nbsp;&nbsp;&nbsp; (ATL 8.0)</li>\n\t<li>Visual C++ 2008&nbsp;&nbsp;&nbsp; (ATL 9.0)</li>\n\t<li>Visual C++ 2010&nbsp;&nbsp;&nbsp; (ATL 10.0)</li>\n\t<li>Visual C++ 2012&nbsp;&nbsp;&nbsp; (ATL 11.0)</li>\n\t<li>Visual C++ 2013&nbsp;&nbsp;&nbsp; (ATL 12.0)</li>\n\t<li>Visual C++ 2015&nbsp;&nbsp;&nbsp; (ATL 14.0)</li>\n\t<li>Visual C++ 2017&nbsp;&nbsp;&nbsp; (ATL 14.0)</li>\n\t<li>Visual C++ 2019&nbsp;&nbsp;&nbsp; (ATL 14.0)</li>\n</ul>\n<p style=margin:0px><br></p>\n\n<p style=margin:0px>Windows SDK support (optional):</p>\n<ul style=margin:0px>\n\t<li>Windows SDK 6.0 or newer</li>\n</ul>\n<p>\n\tNote: Visual C++ 2005 is the only version that requires use of external Windows SDK, \n\tall other versions of Visual C++ can use bundled Windows SDK.\n</p>\n<p style=margin:0px><br></p>\n<p style=margin:0px><br></p>\n\n<h3 style=margin:0px><a name=\"Packing List\"></a>Packing List</h3>\n<p style=margin:0px><br></p>\n\n<table class=\"table1\" border=\"0\" cellspacing=\"0\" style=\"border-collapse: collapse\" bordercolor=\"#111111\" width=\"65%\">\n  <tr>\n    <td width=\"30%\">File Name:</td>\n    <td>Description:</td>\n  </tr>\n  <tr>\n    <td colspan=\"2\"><hr></td>\n  </tr>\n  <tr>\n    <td>readme.html</td>\n    <td>this file</td>\n  </tr>\n  <tr>\n    <td>MS-PL.txt</td>\n    <td>Microsoft Public License</td>\n  </tr>\n  <tr>\n    <td colspan=\"2\">&nbsp;</td>\n  </tr>\n  <tr>\n    <td colspan=\"2\">include\\</td>\n  </tr>\n  <tr>\n    <td>&nbsp;&nbsp;&nbsp; atlapp.h</td>\n    <td>message loop, interfaces, \n    general app stuff</td>\n  </tr>\n  <tr>\n    <td>&nbsp;&nbsp;&nbsp; atlcrack.h</td>\n    <td>message cracker macros</td>\n  </tr>\n  <tr>\n    <td>&nbsp;&nbsp;&nbsp; atlctrls.h</td>\n    <td>standard and common control \n    classes</td>\n  </tr>\n  <tr>\n    <td>&nbsp;&nbsp;&nbsp; atlctrlw.h</td>\n    <td>command bar class</td>\n  </tr>\n  <tr>\n    <td>&nbsp;&nbsp;&nbsp; atlctrlx.h</td>\n    <td>bitmap button, check list view, \n    and other controls</td>\n  </tr>\n  <tr>\n    <td>&nbsp;&nbsp;&nbsp; atlddx.h</td>\n    <td>data exchange for dialogs and \n    windows</td>\n  </tr>\n  <tr>\n    <td>&nbsp;&nbsp;&nbsp; atldlgs.h</td>\n    <td>common dialog classes, property \n    sheet and page classes</td>\n  </tr>\n  <tr>\n    <td>&nbsp;&nbsp;&nbsp; atldwm.h</td>\n    <td>DWM support classes</td>\n  </tr>\n  <tr>\n    <td>&nbsp;&nbsp;&nbsp; atlfind.h</td>\n    <td>Find/Replace support for Edit \n\tand RichEdit</td>\n  </tr>\n  <tr>\n    <td>&nbsp;&nbsp;&nbsp; atlframe.h</td>\n    <td>frame window classes, MDI, \n    update UI classes</td>\n  </tr>\n  <tr>\n    <td>&nbsp;&nbsp;&nbsp; atlgdi.h</td>\n    <td>DC classes, GDI object classes</td>\n  </tr>\n  <tr>\n    <td>&nbsp;&nbsp;&nbsp; atlmisc.h</td>\n    <td>WTL ports of CPoint, CRect, \n    CSize, CString, etc.</td>\n  </tr>\n  <tr>\n    <td>&nbsp;&nbsp;&nbsp; atlprint.h</td>\n    <td>printing and print preview</td>\n  </tr>\n  <tr>\n    <td>&nbsp;&nbsp;&nbsp; atlres.h</td>\n    <td>standard resource IDs</td>\n  </tr>\n  <tr>\n    <td>&nbsp;&nbsp;&nbsp; atlribbon.h</td>\n    <td>RibbonUI support</td>\n  </tr>\n  <tr>\n    <td>&nbsp;&nbsp;&nbsp; atlscrl.h</td>\n    <td>scrollable windows</td>\n  </tr>\n  <tr>\n    <td>&nbsp;&nbsp;&nbsp; atlsplit.h</td>\n    <td>splitter windows</td>\n  </tr>\n  <tr>\n    <td>&nbsp;&nbsp;&nbsp; atltheme.h</td>\n    <td>Windows XP theme classes</td>\n  </tr>\n  <tr>\n    <td>&nbsp;&nbsp;&nbsp; atluser.h</td>\n    <td>menu class, USER object classes</td>\n  </tr>\n  <tr>\n    <td>&nbsp;&nbsp;&nbsp; atlwinx.h</td>\n    <td>extensions of ATL windowing \n    support</td>\n  </tr>\n  <tr>\n    <td colspan=\"2\">&nbsp;</td>\n  </tr>\n  <tr>\n    <td colspan=\"2\">Samples\\</td>\n  </tr>\n  <tr>\n    <td>&nbsp;&nbsp;&nbsp; Aero\\...</td>\n    <td>Vista Aero glass showcase</td>\n  </tr>\n  <tr>\n    <td>&nbsp;&nbsp;&nbsp; Alpha\\...</td>\n    <td>Windows XP 32-bit (alpha) \n    toolbar images</td>\n  </tr>\n  <tr>\n    <td>&nbsp;&nbsp;&nbsp; BmpView\\...</td>\n    <td>bitmap file view sample</td>\n  </tr>\n  <tr>\n    <td>&nbsp;&nbsp;&nbsp; GuidGen\\...</td>\n    <td>WTL version of the GuidGen \n    sample</td>\n  </tr>\n\t<tr>\n    <td>&nbsp;&nbsp;&nbsp; MDIDocVw\\...</td>\n    <td>WTL version of the MDI sample</td>\n  </tr>\n\t<tr>\n    <td>&nbsp;&nbsp;&nbsp; MemDlg\\...</td>\n    <td>In-memory dialog sample</td>\n  </tr>\n\t<tr>\n    <td>&nbsp;&nbsp;&nbsp; MTPad\\...</td>\n    <td>multithreaded notepad sample</td>\n  </tr>\n\t<tr>\n    <td>&nbsp;&nbsp;&nbsp; MTPad7\\...</td>\n    <td>MTPad with RibbonUI</td>\n  </tr>\n  <tr>\n    <td>&nbsp;&nbsp;&nbsp; TabBrowser\\...</td>\n    <td>Web browser using TabView</td>\n  </tr>\n  <tr>\n    <td>&nbsp;&nbsp;&nbsp; Wizard97Test\\...</td>\n    <td>Wizard97 showcase \n    sample</td>\n  </tr>\n  <tr>\n    <td>&nbsp;&nbsp;&nbsp; WTLExplorer\\...</td>\n    <td>Explorer-like application \n    sample</td>\n  </tr>\n  <tr>\n    <td colspan=\"2\">&nbsp;</td>\n  </tr>\n\t<tr>\n    <td colspan=\"2\">AppWiz\\</td>\n  </tr>\n\t<tr>\n    <td>&nbsp;&nbsp;&nbsp; setup.js</td>\n    <td>AppWizard setup program for all versions of \n    Visual Studio</td>\n  </tr>\n\t<tr>\n    <td>&nbsp;&nbsp;&nbsp; Files\\...</td>\n    <td>WTL AppWizard files</td>\n  </tr>\n  </table>\n<p style=margin:0px><br></p>\n<p style=margin:0px><br></p>\n\n<h3 style=margin:0px><a name=\"Class Overview\"></a>Class Overview</h3>\n<p style=margin:0px><br></p>\n\n<p style=margin:0px>usage:</p>\n<table class=\"table1\" style=margin-left:6ch border=\"0\" cellpadding=\"0\" cellspacing=\"0\" style=\"border-collapse: collapse\" bordercolor=\"#111111\" width=\"65%\">\n  <tr>\n    <td width=\"12%\"><b>mi base</b></td>\n    <td>- &nbsp;&nbsp; a base class (multiple inheritance)</td>\n  </tr>\n  <tr>\n    <td><b>client</b></td>\n    <td>- &nbsp;&nbsp; wrapper class for a handle</td>\n  </tr>\n  <tr>\n    <td><b>as-is</b></td>\n    <td>- &nbsp;&nbsp; to be used directly</td>\n  </tr>\n  <tr>\n    <td><b>impl</b></td>\n    <td>- &nbsp;&nbsp; implements a window (has WindowProc) or other support</td>\n  </tr>\n  <tr>\n    <td><b>helper</b></td>\n    <td>- &nbsp;&nbsp; a helper class</td>\n  </tr>\n  <tr>\n    <td><b>base</b></td>\n    <td>- &nbsp;&nbsp; implementation base class</td>\n  </tr>\n</table>\n<p style=margin:0px><br></p>\n\n<table class=\"table1\" border=\"1\" cellspacing=\"0\" style=\"border-collapse: collapse\" bordercolor=\"#111111\" width=\"75%\">\n  <tr>\n    <td width=\"37%\"><b>class name:</b></td>\n    <td width=\"20%\"><b>usage:</b></td>\n    <td><b>description:</b></td>\n  </tr>\n  <tr>\n    <td colspan=\"3\"><br>App/module support</td>\n  </tr>\n  <tr>\n    <td><b>CAppModule</b></td>\n    <td>as-is</td>\n    <td>app support, CComModule derived</td>\n  </tr>\n  <tr>\n    <td><b>CServerAppModule</b></td>\n    <td>as-is</td>\n    <td>module for COM servers</td>\n  </tr>\n  <tr>\n    <td><b>CMessageLoop</b></td>\n    <td>as-is</td>\n    <td>message loop</td>\n  </tr>\n  <tr>\n    <td><b>CMessageFilter</b></td>\n    <td>mi base</td>\n    <td>message filter interface</td>\n  </tr>\n  <tr>\n    <td><b>CIdleHandler</b></td>\n    <td>mi base</td>\n    <td>idle time handler interface</td>\n  </tr>\n  <tr>\n    <td colspan=\"3\"><br>Frame windows</td>\n  </tr>\n  <tr>\n    <td><b>CFrameWindowImplBase</b></td>\n    <td>base</td>\n    <td>&nbsp;</td>\n  </tr>\n  <tr>\n    <td><b>CFrameWindowImpl</b></td>\n    <td>impl</td>\n    <td>frame window support</td>\n  </tr>\n  <tr>\n    <td><b>COwnerDraw</b></td>\n    <td>impl mi base</td>\n    <td>owner-draw msg map and handlers</td>\n  </tr>\n  <tr>\n    <td><b>CDialogResize\n    </b></td>\n    <td>impl mi base</td>\n    <td>support for resizing dialogs</td>\n  </tr>\n\t<tr>\n    <td><b>CDoubleBufferImpl\n    </b></td>\n    <td>impl mi</td>\n    <td>double-buffer painting support</td>\n  </tr>\n  <tr>\n    <td><b>CDoubleBufferWindowImpl\n    </b></td>\n    <td>impl</td>\n    <td>double-buffer painting window</td>\n  </tr>\n  <tr>\n    <td colspan=\"3\"><br>MDI windows</td>\n  </tr>\n  <tr>\n    <td><b>CMDIWindow</b></td>\n    <td>client</td>\n    <td>MDI methods</td>\n  </tr>\n  <tr>\n    <td><b>CMDIFrameWindowImpl</b></td>\n    <td>impl</td>\n    <td>MDI frame window</td>\n  </tr>\n  <tr>\n    <td><b>CMDIChildWindowImpl</b></td>\n    <td>impl</td>\n    <td>MDI child window</td>\n  </tr>\n  <tr>\n    <td colspan=\"3\"><br>Update UI</td>\n  </tr>\n  <tr>\n    <td><b>CUpdateUIBase</b></td>\n    <td>base</td>\n    <td>&nbsp;</td>\n  </tr>\n  <tr>\n    <td><b>CUpdateUI</b></td>\n    <td>mi base class</td>\n    <td>provides support for UI update</td>\n  </tr>\n  <tr>\n    <td><b>CDynamicUpdateUI</b></td>\n    <td>mi base class</td>\n    <td>provides dynamic support for UI update</td>\n  </tr>\n  <tr>\n    <td colspan=\"3\"><br>Standard controls</td>\n  </tr>\n  <tr>\n    <td><b>CStatic</b></td>\n    <td>client</td>\n    <td>static ctrl</td>\n  </tr>\n  <tr>\n    <td><b>CButton</b></td>\n    <td>client</td>\n    <td>button ctrl</td>\n  </tr>\n  <tr>\n    <td><b>CListBox</b></td>\n    <td>client</td>\n    <td>list box ctrl</td>\n  </tr>\n  <tr>\n    <td><b>CComboBox</b></td>\n    <td>client</td>\n    <td>combo box ctrl</td>\n  </tr>\n  <tr>\n    <td><b>CEdit</b></td>\n    <td>client</td>\n    <td>edit ctrl</td>\n  </tr>\n  <tr>\n    <td><b>CEditCommands</b></td>\n    <td>mi</td>\n    <td>standard edit command support</td>\n  </tr>\n  <tr>\n    <td><b>CScrollBar</b></td>\n    <td>client</td>\n    <td>scroll bar ctrl</td>\n  </tr>\n  <tr>\n    <td colspan=\"3\"><br>Common controls</td>\n  </tr>\n  <tr>\n    <td><b>CImageList</b></td>\n    <td>client</td>\n    <td>image list</td>\n  </tr>\n  <tr>\n    <td><b>CListViewCtrl</b></td>\n    <td>client</td>\n    <td>list view ctrl</td>\n  </tr>\n  <tr>\n    <td><b>CTreeViewCtrl</b></td>\n    <td>client</td>\n    <td>tree view ctrl</td>\n  </tr>\n  <tr>\n    <td><b>CTreeItem</b></td>\n    <td>helper</td>\n    <td>&nbsp;</td>\n  </tr>\n  <tr>\n    <td><b>CTreeViewCtrlEx</b></td>\n    <td>client</td>\n    <td>uses CTreeItem</td>\n  </tr>\n  <tr>\n    <td><b>CHeaderCtrl</b></td>\n    <td>client</td>\n    <td>header bar ctrl</td>\n  </tr>\n  <tr>\n    <td><b>CToolBarCtrl</b></td>\n    <td>client</td>\n    <td>toolbar ctrl</td>\n  </tr>\n  <tr>\n    <td><b>CStatusBarCtrl</b></td>\n    <td>client</td>\n    <td>status bar ctrl</td>\n  </tr>\n  <tr>\n    <td><b>CTabCtrl</b></td>\n    <td>client</td>\n    <td>tab ctrl</td>\n  </tr>\n  <tr>\n    <td><b>CToolTipCtrl</b></td>\n    <td>client</td>\n    <td>tool tip ctrl</td>\n  </tr>\n  <tr>\n    <td><b>CToolInfo</b></td>\n    <td>helper</td>\n    <td>&nbsp;</td>\n  </tr>\n  <tr>\n    <td><b>CTrackBarCtrl</b></td>\n    <td>client</td>\n    <td>trackbar ctrl</td>\n  </tr>\n  <tr>\n    <td><b>CUpDownCtrl</b></td>\n    <td>client</td>\n    <td>up-down ctrl</td>\n  </tr>\n  <tr>\n    <td><b>CProgressBarCtrl</b></td>\n    <td>client</td>\n    <td>progress bar ctrl</td>\n  </tr>\n  <tr>\n    <td><b>CHotKeyCtrl</b></td>\n    <td>client</td>\n    <td>hot key ctrl</td>\n  </tr>\n  <tr>\n    <td><b>CAnimateCtrl</b></td>\n    <td>client</td>\n    <td>animation ctrl</td>\n  </tr>\n  <tr>\n    <td><b>CRichEditCtrl</b></td>\n    <td>client</td>\n    <td>rich edit ctrl</td>\n  </tr>\n  <tr>\n    <td><b>CRichEditCommands</b></td>\n    <td>mi</td>\n    <td>std rich edit commands support</td>\n  </tr>\n  <tr>\n    <td><b>CDragListBox</b></td>\n    <td>client</td>\n    <td>drag list box</td>\n  </tr>\n  <tr>\n    <td><b>CDragListNotifyImpl</b></td>\n    <td>impl mi class</td>\n    <td>support for notifications</td>\n  </tr>\n  <tr>\n    <td><b>CReBarCtrl</b></td>\n    <td>client</td>\n    <td>rebar ctrl</td>\n  </tr>\n  <tr>\n    <td><b>CComboBoxEx</b></td>\n    <td>client</td>\n    <td>extended combo box</td>\n  </tr>\n  <tr>\n    <td><b>CDateTimePickerCtrl</b></td>\n    <td>client</td>\n    <td>date-time ctrl</td>\n  </tr>\n  <tr>\n    <td><b>CFlatScrollBarImpl</b></td>\n    <td>mi impl</td>\n    <td>flat scroll bars support</td>\n  </tr>\n  <tr>\n    <td><b>CFlatScrollBar</b></td>\n    <td>as-is</td>\n    <td>flat scroll bars support</td>\n  </tr>\n  <tr>\n    <td><b>CIPAddressCtrl</b></td>\n    <td>client</td>\n    <td>IP address ctrl</td>\n  </tr>\n  <tr>\n    <td><b>CMonthCalendarCtrl</b></td>\n    <td>client</td>\n    <td>month calendar ctrl</td>\n  </tr>\n  <tr>\n    <td><b>CCustomDraw</b></td>\n    <td>impl mi class</td>\n    <td>custom draw handling support</td>\n  </tr>\n  <tr>\n    <td colspan=\"3\"><br>Property sheet &amp; page</td>\n  </tr>\n  <tr>\n    <td><b>CPropertySheetWindow</b></td>\n    <td>client</td>\n    <td>&nbsp;</td>\n  </tr>\n  <tr>\n    <td><b>CPropertySheetImpl</b></td>\n    <td>impl</td>\n    <td>property sheet </td>\n  </tr>\n  <tr>\n    <td><b>CPropertySheet</b></td>\n    <td>as-is</td>\n    <td>&nbsp;</td>\n  </tr>\n  <tr>\n    <td><b>CPropertyPageWindow</b></td>\n    <td>client</td>\n    <td>&nbsp;</td>\n  </tr>\n  <tr>\n    <td><b>CPropertyPageImpl</b></td>\n    <td>impl</td>\n    <td>property page</td>\n  </tr>\n  <tr>\n    <td><b>CPropertyPage</b></td>\n    <td>as-is</td>\n    <td>&nbsp;</td>\n  </tr>\n  <tr>\n    <td><b>CAxPropertyPageImpl</b></td>\n    <td>impl</td>\n    <td>property page with ActiveX</td>\n  </tr>\n  <tr>\n    <td><b>CAxPropertyPage</b></td>\n    <td>as-is</td>\n    <td>&nbsp;</td>\n  </tr>\n\t<tr>\n    <td><b>CWizard97SheetWindow</b></td>\n    <td>client</td>\n    <td>&nbsp;</td>\n  </tr>\n\t<tr>\n    <td><b>CWizard97SheetImpl</b></td>\n    <td>impl</td>\n    <td>Wizard97 property sheet</td>\n  </tr>\n\t<tr>\n    <td><b>CWizard97Sheet</b></td>\n    <td>as-is</td>\n    <td>&nbsp;</td>\n  </tr>\n\t<tr>\n    <td><b>CWizard97PageWindow</b></td>\n    <td>client</td>\n    <td>&nbsp;</td>\n  </tr>\n\t<tr>\n    <td><b>CWizard97PageImpl</b></td>\n    <td>impl</td>\n    <td>Wizard97 property page</td>\n  </tr>\n\t<tr>\n    <td><b>CWizard97ExteriorPageImpl</b></td>\n    <td>impl</td>\n    <td>Wizard97 exterior page</td>\n  </tr>\n\t<tr>\n    <td><b>CWizard97InteriorPageImpl</b></td>\n    <td>impl</td>\n    <td>Wizard97 interior page</td>\n  </tr>\n\t<tr>\n    <td><b>CAeroWizardFrameWindow</b></td>\n    <td>client</td>\n    <td>&nbsp;</td>\n  </tr>\n\t<tr>\n    <td><b>CAeroWizardFrameImpl</b></td>\n    <td>impl</td>\n    <td>Aero Wizard frame</td>\n  </tr>\n\t<tr>\n    <td><b>CAeroWizardFrame</b></td>\n    <td>as-is</td>\n    <td>&nbsp;</td>\n  </tr>\n\t<tr>\n    <td><b>CAeroWizardPageWindow</b></td>\n    <td>client</td>\n    <td>&nbsp;</td>\n  </tr>\n\t<tr>\n    <td><b>CAeroWizardPageImpl</b></td>\n    <td>impl</td>\n    <td>Aero Wizard page</td>\n  </tr>\n\t<tr>\n    <td><b>CAeroWizardPage</b></td>\n    <td>as-is</td>\n    <td>&nbsp;</td>\n  </tr>\n\t<tr>\n    <td><b>CAeroWizardAxPageImpl</b></td>\n    <td>impl</td>\n    <td>Aero Wizard page with ActiveX</td>\n  </tr>\n\t<tr>\n    <td><b>CAeroWizardAxPage</b></td>\n    <td>as-is</td>\n    <td>&nbsp;</td>\n  </tr>\n  <tr>\n    <td colspan=\"3\"><br>Common dialogs</td>\n  </tr>\n  <tr>\n    <td><b>CFileDialogImpl</b></td>\n    <td>impl</td>\n    <td>GetOpenFileName/GetSaveFileName</td>\n  </tr>\n  <tr>\n    <td><b>CFileDialog</b></td>\n    <td>as-is</td>\n    <td>&nbsp;</td>\n  </tr>\n  <tr>\n    <td><b>CSimpleFileDialog</b></td>\n    <td>as-is</td>\n    <td>no customization</td>\n  </tr>\n  <tr>\n    <td><b>CMultiFileDialogImpl</b></td>\n    <td>impl</td>\n    <td>Multi-select GetOpenFileName</td>\n  </tr>\n  <tr>\n    <td><b>CMultiFileDialog</b></td>\n    <td>as-is</td>\n    <td>&nbsp;</td>\n  </tr>\n\t<tr>\n    <td><b>CShellFileDialogImpl</b></td>\n    <td>base</td>\n    <td>&nbsp;</td>\n  </tr>\n\t<tr>\n    <td><b>CShellFileOpenDialogImpl</b></td>\n    <td>impl</td>\n    <td>Shell File Open dialog</td>\n  </tr>\n\t<tr>\n    <td><b>CShellFileOpenDialog</b></td>\n    <td>as-is</td>\n    <td>&nbsp;</td>\n  </tr>\n\t<tr>\n    <td><b>CShellFileSaveDialogImpl</b></td>\n    <td>impl</td>\n    <td>Shell File Save dialog</td>\n  </tr>\n  <tr>\n    <td><b>CShellFileSaveDialog</b></td>\n    <td>as-is</td>\n    <td>&nbsp;</td>\n  </tr>\n  <tr>\n    <td><b>CFolderDialogImpl</b></td>\n    <td>impl</td>\n    <td>directory picker</td>\n  </tr>\n  <tr>\n    <td><b>CFolderDialog</b></td>\n    <td>as-is</td>\n    <td>&nbsp;</td>\n  </tr>\n  <tr>\n    <td><b>CFontDialogImpl</b></td>\n    <td>impl</td>\n    <td>ChooseFont common dialog</td>\n  </tr>\n  <tr>\n    <td><b>CFontDialog</b></td>\n    <td>as-is</td>\n    <td>&nbsp;</td>\n  </tr>\n  <tr>\n    <td><b>CRichEditFontDialogImpl</b></td>\n    <td>impl</td>\n    <td>ChooseFont for rich edit</td>\n  </tr>\n  <tr>\n    <td><b>CRichEditFontDialog</b></td>\n    <td>as-is</td>\n    <td>&nbsp;</td>\n  </tr>\n  <tr>\n    <td><b>CColorDialogImpl</b></td>\n    <td>impl</td>\n    <td>ChooseColor common dialog</td>\n  </tr>\n  <tr>\n    <td><b>CColorDialog</b></td>\n    <td>as-is</td>\n    <td>&nbsp;</td>\n  </tr>\n  <tr>\n    <td><b>CPrintDialogImpl</b></td>\n    <td>impl</td>\n    <td>PrintDlg common dialog</td>\n  </tr>\n  <tr>\n    <td><b>CPrintDialog</b></td>\n    <td>as-is</td>\n    <td>&nbsp;</td>\n  </tr>\n  <tr>\n    <td><b>CPrintDialogExImpl</b></td>\n    <td>impl</td>\n    <td>new Win2000 print dialog</td>\n  </tr>\n  <tr>\n    <td><b>CPrintDialogEx</b></td>\n    <td>as-is</td>\n    <td>&nbsp;</td>\n  </tr>\n  <tr>\n    <td><b>CPageSetupDialogImpl</b></td>\n    <td>impl</td>\n    <td>PageSetupDlg common dialog</td>\n  </tr>\n  <tr>\n    <td><b>CPageSetupDialog</b></td>\n    <td>as-is</td>\n    <td>&nbsp;</td>\n  </tr>\n  <tr>\n    <td><b>CFindReplaceDialogImpl</b></td>\n    <td>impl</td>\n    <td>FindText/ReplaceText</td>\n  </tr>\n  <tr>\n    <td><b>CFindReplaceDialog</b></td>\n    <td>as-is</td>\n    <td>&nbsp;</td>\n  </tr>\n  <tr>\n    <td colspan=\"3\"><br>User support</td>\n  </tr>\n  <tr>\n    <td><b>CMenu</b></td>\n    <td>client</td>\n    <td>menu support</td>\n  </tr>\n\t<tr>\n    <td><b>CMenuItemInfo</b></td>\n    <td>as-is</td>\n    <td>MENUITEMINFO wrapper</td>\n  </tr>\n\t<tr>\n    <td><b>CAccelerator</b></td>\n    <td>client</td>\n    <td>accelerator table</td>\n  </tr>\n\t<tr>\n    <td><b>CIcon</b></td>\n    <td>client</td>\n    <td>icon object</td>\n  </tr>\n\t<tr>\n    <td><b>CCursor</b></td>\n    <td>client</td>\n    <td>cursor object</td>\n  </tr>\n\t<tr>\n    <td><b>CResource</b></td>\n    <td>client</td>\n    <td>generic resource object</td>\n  </tr>\n  <tr>\n    <td colspan=\"3\"><br>GDI support</td>\n  </tr>\n  <tr>\n    <td><b>CDC</b></td>\n    <td>client</td>\n    <td>DC support</td>\n  </tr>\n  <tr>\n    <td><b>CPaintDC</b></td>\n    <td>client</td>\n    <td>for handling WM_PAINT</td>\n  </tr>\n  <tr>\n    <td><b>CClientDC</b></td>\n    <td>client</td>\n    <td>for GetDC</td>\n  </tr>\n  <tr>\n    <td><b>CWindowDC</b></td>\n    <td>client</td>\n    <td>for GetWindowDC</td>\n  </tr>\n  <tr>\n    <td><b>CMemoryDC</b></td>\n    <td>client</td>\n    <td>in-memory DC</td>\n  </tr>\n  <tr>\n    <td><b>CPen</b></td>\n    <td>client</td>\n    <td>GDI pen object</td>\n  </tr>\n  <tr>\n    <td><b>CBrush</b></td>\n    <td>client</td>\n    <td>GDI brush object</td>\n  </tr>\n  <tr>\n    <td><b>CLogFont</b></td>\n    <td>as-is</td>\n    <td>LOGFONT wrapper</td>\n  </tr>\n  <tr>\n    <td><b>CFont</b></td>\n    <td>client</td>\n    <td>GDI font object</td>\n  </tr>\n  <tr>\n    <td><b>CBitmap</b></td>\n    <td>client</td>\n    <td>GDI bitmap object</td>\n  </tr>\n  <tr>\n    <td><b>CPalette</b></td>\n    <td>client</td>\n    <td>GDI palette object</td>\n  </tr>\n  <tr>\n    <td><b>CRgn</b></td>\n    <td>client</td>\n    <td>GDI region object</td>\n  </tr>\n  <tr>\n    <td colspan=\"3\"><br>Enhanced controls</td>\n  </tr>\n  <tr>\n    <td><b>CCommandBarCtrlImpl</b></td>\n    <td>impl</td>\n    <td>command bar</td>\n  </tr>\n  <tr>\n    <td><b>CCommandBarCtrl</b></td>\n    <td>as-is</td>\n    <td>&nbsp;</td>\n  </tr>\n  <tr>\n    <td><b>CBitmapButtonImpl</b></td>\n    <td>impl</td>\n    <td>bitmap button</td>\n  </tr>\n  <tr>\n    <td><b>CBitmapButton</b></td>\n    <td>as-is</td>\n    <td>&nbsp;</td>\n  </tr>\n  <tr>\n    <td><b>CCheckListViewCtrlImpl</b></td>\n    <td>impl</td>\n    <td>check list box</td>\n  </tr>\n  <tr>\n    <td><b>CCheckListViewCtrl</b></td>\n    <td>as-is</td>\n    <td>&nbsp;</td>\n  </tr>\n  <tr>\n    <td><b>CHyperLinkImpl</b></td>\n    <td>impl</td>\n    <td>hyper link control</td>\n  </tr>\n  <tr>\n    <td><b>CHyperLink</b></td>\n    <td>as-is</td>\n    <td>&nbsp;</td>\n  </tr>\n  <tr>\n    <td><b>CWaitCursor</b></td>\n    <td>as-is</td>\n    <td>wait cursor</td>\n  </tr>\n  <tr>\n    <td><b>CCustomWaitCursor</b></td>\n    <td>as-is</td>\n    <td>custom and animated wait cursor</td>\n  </tr>\n  <tr>\n    <td><b>CMultiPaneStatusBarCtrlImpl</b></td>\n    <td>impl</td>\n    <td>status bar with multiple panes</td>\n  </tr>\n  <tr>\n    <td><b>CMultiPaneStatusBarCtrl</b></td>\n    <td>as-is</td>\n    <td>&nbsp;</td>\n  </tr>\n  <tr>\n    <td><b>CPaneContainerImpl</b></td>\n    <td>impl</td>\n    <td>pane window container</td>\n  </tr>\n  <tr>\n    <td><b>CPaneContainer</b></td>\n    <td>as-is</td>\n    <td>&nbsp;</td>\n  </tr>\n\t<tr>\n    <td><b>CSortListViewImpl</b></td>\n    <td>impl</td>\n    <td>sorting list view control</td>\n  </tr>\n\t<tr>\n    <td><b>CSortListViewCtrlImpl</b></td>\n    <td>impl</td>\n    <td>&nbsp;</td>\n  </tr>\n\t<tr>\n    <td><b>CSortListViewCtrl</b></td>\n    <td>as-is</td>\n    <td>&nbsp;</td>\n  </tr>\n\t<tr>\n    <td><b>CTabViewImpl;</b></td>\n    <td>impl</td>\n    <td>tab view window</td>\n  </tr>\n  <tr>\n    <td><b>CTabView</b></td>\n    <td>as-is</td>\n    <td>&nbsp;</td>\n  </tr>\n  <tr>\n    <td colspan=\"3\"><br>Scrolling window support</td>\n  </tr>\n  <tr>\n    <td><b>CScrollImpl</b></td>\n    <td>impl mi</td>\n    <td>scrolling support</td>\n  </tr>\n  <tr>\n    <td><b>CScrollWindowImpl</b></td>\n    <td>impl</td>\n    <td>scrollable window</td>\n  </tr>\n  <tr>\n    <td><b>CMapScrollImpl</b></td>\n    <td>impl mi</td>\n    <td>scrolling support with map modes</td>\n  </tr>\n  <tr>\n    <td><b>CMapScrollWindowImpl</b></td>\n    <td>impl</td>\n    <td>scrollable window with map modes</td>\n  </tr>\n  <tr>\n    <td><b>CZoomScrollImpl</b></td>\n    <td>impl mi</td>\n    <td>zooming support</td>\n  </tr>\n\t<tr>\n    <td><b>CZoomScrollWindowImpl</b></td>\n    <td>impl</td>\n    <td>zooming window</td>\n  </tr>\n  <tr>\n    <td><b>CScrollContainerImpl</b></td>\n    <td>impl</td>\n    <td>scroll container window</td>\n  </tr>\n\t<tr>\n    <td><b>CScrollContainer</b></td>\n    <td>as-is</td>\n    <td>&nbsp;</td>\n  </tr>\n  <tr>\n    <td colspan=\"3\"><br>Splitter window support</td>\n  </tr>\n  <tr>\n    <td><b>CSplitterImpl</b></td>\n    <td>impl mi</td>\n    <td>splitter support</td>\n  </tr>\n  <tr>\n    <td><b>CSplitterWindowImpl</b></td>\n    <td>impl</td>\n    <td>splitter window</td>\n  </tr>\n  <tr>\n    <td><b>CSplitterWindow</b></td>\n    <td>as-is</td>\n    <td>&nbsp;</td>\n  </tr>\n  <tr>\n    <td colspan=\"3\"><br>Theming support</td>\n  </tr>\n  <tr>\n    <td><b>CTheme</b></td>\n    <td>client</td>\n    <td>Windows XP theme</td>\n  </tr>\n  <tr>\n    <td><b>CThemeImpl</b></td>\n    <td>impl</td>\n    <td>theming support for a window</td>\n  </tr>\n  <tr>\n    <td colspan=\"3\"><br>Buffered paint and animation support</td>\n  </tr>\n\t<tr>\n    <td><b>CBufferedPaint</b></td>\n    <td>as-is</td>\n    <td>buffered paint</td>\n  </tr>\n\t<tr>\n    <td><b>CBufferedPaintImpl</b></td>\n    <td>impl mi</td>\n    <td>buffered paint support</td>\n  </tr>\n\t<tr>\n    <td><b>CBufferedPaintWindowImpl</b></td>\n    <td>impl</td>\n    <td>window with buffered paint</td>\n  </tr>\n\t<tr>\n    <td><b>CBufferedAnimation</b></td>\n    <td>as-is</td>\n    <td>buffered animation</td>\n  </tr>\n\t<tr>\n    <td><b>CBufferedAnimationImpl</b></td>\n    <td>impl mi</td>\n    <td>buffered animation support</td>\n  </tr>\n\t<tr>\n    <td><b>CBufferedAnimationWindowImpl</b></td>\n    <td>impl</td>\n    <td>window with buffered animation</td>\n  </tr>\n  <tr>\n    <td colspan=\"3\"><br>Edit and RichEdit Find/Replace support</td>\n  </tr>\n\t<tr>\n    <td><b>CEditFindReplaceImplBase</b></td>\n    <td>base</td>\n    <td>&nbsp;</td>\n  </tr>\n\t<tr>\n    <td><b>CEditFindReplaceImpl</b></td>\n    <td>mi</td>\n    <td>Edit Find/Replace support</td>\n  </tr>\n\t<tr>\n    <td><b>CRichEditFindReplaceImpl</b></td>\n    <td>mi</td>\n    <td>RichEdit Find/Replace support</td>\n  </tr>\n  <tr>\n    <td colspan=\"3\"><br>Printing support</td>\n  </tr>\n  <tr>\n    <td><b>CPrinterInfo</b></td>\n    <td>as-is</td>\n    <td>print info support</td>\n  </tr>\n  <tr>\n    <td><b>CPrinter</b></td>\n    <td>client</td>\n    <td>printer handle wrapper</td>\n  </tr>\n  <tr>\n    <td><b>CDevMode</b></td>\n    <td>client</td>\n    <td>DEVMODE wrapper</td>\n  </tr>\n  <tr>\n    <td><b>CPrinterDC</b></td>\n    <td>client</td>\n    <td>printing DC support</td>\n  </tr>\n  <tr>\n    <td><b>CPrintJobInfo</b></td>\n    <td>client</td>\n    <td>print job info</td>\n  </tr>\n  <tr>\n    <td><b>CPrintJob</b></td>\n    <td>client</td>\n    <td>print job support</td>\n  </tr>\n  <tr>\n    <td><b>CPrintPreview</b></td>\n    <td>mi</td>\n    <td>print preview support</td>\n  </tr>\n  <tr>\n    <td><b>CPrintPreviewWindowImpl</b></td>\n    <td>impl</td>\n    <td>print preview window</td>\n  </tr>\n  <tr>\n    <td><b>CPrintPreviewWindow</b></td>\n    <td>as-is</td>\n    <td>&nbsp;</td>\n  </tr>\n  <tr>\n    <td><b>CZoomPrintPreviewWindowImpl</b></td>\n    <td>impl</td>\n    <td>zooming print preview window</td>\n  </tr>\n  <tr>\n    <td><b>CZoomPrintPreviewWindow</b></td>\n    <td>as-is</td>\n    <td>&nbsp;</td>\n  </tr>\n  <tr>\n    <td colspan=\"3\"><br>Miscellaneous</td>\n  </tr>\n  <tr>\n    <td><b>CWinDataExchange</b></td>\n    <td>mi</td>\n    <td>data exchange for controls</td>\n  </tr>\n  <tr>\n    <td><b>CRecentDocumentList</b></td>\n    <td>mi or as-is</td>\n    <td>support for MRU list</td>\n  </tr>\n  <tr>\n    <td><b>CFindFile</b></td>\n    <td>as-is</td>\n    <td>file search support</td>\n  </tr>\n  <tr>\n    <td><b>CRegProperty</b></td>\n    <td>as-is</td>\n    <td>registry properties support</td>\n  </tr>\n  <tr>\n    <td><b>CRegPropertyImpl</b></td>\n    <td>impl</td>\n    <td>registry properties via map</td>\n  </tr>\n  <tr>\n    <td colspan=\"3\"><br>In-memory dialog</td>\n  </tr>\n  <tr>\n    <td><b>CDialogBaseUnits</b></td>\n    <td>helper</td>\n    <td>dialog units helper</td>\n  </tr>\n  <tr>\n    <td><b>CMemDlgTemplate</b></td>\n    <td>as-is</td>\n    <td>In-memory dialog template</td>\n  </tr>\n  <tr>\n    <td><b>CIndirectDialogImpl</b></td>\n    <td>impl</td>\n    <td>In-memory dialog class</td>\n  </tr>\n  <tr>\n    <td colspan=\"3\"><br>Task dialog</td>\n  </tr>\n  <tr>\n    <td><b>CTaskDialogImpl</b></td>\n    <td>impl</td>\n    <td>Task Dialog in Vista</td>\n  </tr>\n  <tr>\n    <td><b>CTaskDialog</b></td>\n    <td>as-is</td>\n    <td>&nbsp;</td>\n  </tr>\n  <tr>\n    <td colspan=\"3\"><br>DWM classes</td>\n  </tr>\n  <tr>\n    <td><b>CDwm</b></td>\n    <td>client</td>\n    <td>DWM handle warapper</td>\n  </tr>\n  <tr>\n    <td><b>CDwmImpl</b></td>\n    <td>impl base</td>\n    <td>DWM support</td>\n  </tr>\n  <tr>\n    <td><b>CDwmWindow</b></td>\n    <td>impl</td>\n    <td>DWM window support</td>\n  </tr>\n  <tr>\n    <td><b>CDwmThumbnail</b></td>\n    <td>client</td>\n    <td>DWM thumbnail wrapper</td>\n  </tr>\n  <tr>\n    <td><b>CAeroControlImpl</b></td>\n    <td>impl</td>\n    <td>support for Aero controls</td>\n  </tr>\n  <tr>\n    <td colspan=\"3\"><br>Ribbon classes</td>\n  </tr>\n  <tr>\n    <td><b>CRibbonUpdateUI</b></td>\n    <td>mi base</td>\n    <td>automatic mapping of ribbon UI elements</td>\n  </tr>\n  <tr>\n    <td><b>RibbonUI::CtrlImpl</b></td>\n    <td>base impl</td>\n    <td>base class for all ribbon controls</td>\n  </tr>\n  <tr>\n    <td><b>RibbonUI::CommandCtrlImpl</b></td>\n    <td>base impl</td>\n    <td>base class for ribbon controls</td>\n  </tr>\n  <tr>\n    <td><b>RibbonUI::CollectionImplBase</b></td>\n    <td>base</td>\n    <td>base class for all RibbonUI collections</td>\n  </tr>\n  <tr>\n    <td><b>RibbonUI::CollectionImpl</b></td>\n    <td>impl</td>\n    <td>RibbonUI collections</td>\n  </tr>\n  <tr>\n    <td><b>RibbonUI::CollectionCtrlImpl</b></td>\n    <td>impl</td>\n    <td>for ribbon collection controls</td>\n  </tr>\n  <tr>\n    <td><b>RibbonUI::ToolbarGalleryCtrlImpl</b></td>\n    <td>base impl</td>\n    <td>for ribbon toolbar gallery controls</td>\n  </tr>\n  <tr>\n    <td><b>RibbonUI::CRibbonImpl</b></td>\n    <td>impl</td>\n    <td>Ribbon implementation class</td>\n  </tr>\n  <tr>\n    <td><b>CRibbonFrameWindowImplBase</b></td>\n    <td>base</td>\n    <td>base frame class for Ribbon</td>\n  </tr>\n  <tr>\n    <td><b>CRibbonFrameWindowImpl</b></td>\n    <td>impl</td>\n    <td>Ribbon frame window class</td>\n  </tr>\n  <tr>\n    <td><b>CRibbonMDIFrameWindowImpl</b></td>\n    <td>impl</td>\n    <td>Ribbon MDI frame window class</td>\n  </tr>\n  <tr>\n    <td><b>CRibbonPersist</b></td>\n    <td>as-is</td>\n    <td>Ribbon persistance support</td>\n  </tr>\n</table>\n<p style=margin:0px><br></p>\n<p style=margin:0px><br></p>\n\n<h3 style=margin:0px><a name=\"ATL/WTL AppWizard\"></a>ATL/WTL AppWizard</h3>\n\n<p>ATL/WTL AppWizard generates starting code for a WTL application. It has options to create code for different application types and features.</p>\n<p style=margin:0px>You can choose the following options:</p>\n<ul style='margin-top:0px;margin-bottom:0px'>\n\t<li>Application type (SDI, multi thread SDI, MDI, TabView, Explorer, dialog based)</li>\n\t<li>Support for hosting ActiveX controls</li>\n\t<li>COM server support</li>\n\t<li>Class implementation in .CPP files</li>\n\t<li>Common Control manifest</li>\n\t<li>Unicode character set</li>\n\t<li>Toolbar, rebar, command bar, status bar</li>\n\t<li>View window, and it's type (generic, dialog form, or a list box, edit, list view, tree view, rich edit, HTML page, scroll window)</li>\n\t<li>For dialog based apps or a form based view window - support for hosting ActiveX controls in the dialog</li>\n</ul>\n<p style=margin:0px><br></p>\n\n<p style=margin:0px>ATL/WTL AppWizard supports VC++ 2005, 2008, 2010, 2012, 2013, 2015, 2017, and 2019.</p>\n<p style=margin:0px><br></p>\n<p style=margin:0px><br></p>\n\n<h3 style=margin:0px><a name=\"WTL in MFC\"></a>How to use WTL in an MFC project</h3>\n\n<p>If you want to use WTL in an MFC project, you need to put these 2 lines before including atlapp.h:</p>\n<p class=\"code1\">\n\tnamespace ATL { using ::CString; };<br>\n\t#define _WTL_NO_AUTOMATIC_NAMESPACE\n</p>\n<p>The first line tells WTL to use CString from global namespace, because CString is defined that way in MFC. \nThe second line prevents name collisions between WTL and MFC. Use the WTL namespace prefix explicitly.</p>\n\n<p style=margin:0px><br></p>\n<p style=margin:0px><br></p>\n\n<p><hr></p>\n\n<h3 style=margin:0px><a name=\"WTL Releases\"></a>WTL Releases</h3>\n<p style=margin:0px><br></p>\n\n<h4>History</h4>\n<table class=\"table1\" style=margin-left:4ch border=\"0\" cellpadding=\"0\" cellspacing=\"0\" style=\"border-collapse: collapse\" bordercolor=\"#111111\" width=\"50%\">\n\t<tr>\n\t\t<td width=\"22%\">WTL 10</td>\n\t\t<td>2020</td>\n\t</tr>\n\t<tr>\n\t\t<td>WTL 9.1</td>\n\t\t<td>2015</td>\n\t</tr>\n\t<tr>\n\t\t<td>WTL 9.0</td>\n\t\t<td>2014</td>\n\t</tr>\n\t<tr>\n\t\t<td>WTL 8.0</td>\n\t\t<td>2007</td>\n\t</tr>\n\t<tr>\n\t\t<td>WTL 7.5</td>\n\t\t<td>2005</td>\n\t</tr>\n\t<tr>\n\t\t<td>WTL 7.1</td>\n\t\t<td>2003</td>\n\t</tr>\n\t<tr>\n\t\t<td>WTL 7.0</td>\n\t\t<td>2002</td>\n\t</tr>\n\t<tr>\n\t\t<td>WTL 3.1</td>\n\t\t<td>2000</td>\n\t</tr>\n\t<tr>\n\t\t<td>WTL 3.0</td>\n\t\t<td>1999</td>\n\t</tr>\n</table>\n<p style=margin:0px><br></p>\n\n<h4>Changes Between WTL 10 and 9.1</h4>\n<p style=margin:0px>New and improved:</p>\n<blockquote style='margin-top:0px;margin-bottom:0px'>\n\t<p style=margin:0px>\n\t\tFull compatibility with VS2017 and VS2019<br>\n\t\tFull C++ Standards compliance and support for compiling with /permissive- flag<br>\n\t\tFull support for strict const-qualification conformance (/Zc:strictStrings)<br>\n\t\tNew classes: CRegProperty and CRegPropertyImpl<> for properties stored in registry<br>\n\t\tNew class: CSimpleFileDialog - fixed common dialog that does not use OFN_ENABLEHOOK<br>\n\t\tAdded support for MFC Dynamic Dialog Layout resource format<br>\n\t\tApp Wizard:\n\t</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>Added support for VS2017 and VS2019</li>\n\t\t<li>Removed options for manifest (always there) and Unicode (always on)</li>\n\t\t<li>Added option for WinXP support</li>\n\t\t<li>Added support for _NO_AUTOMATIC_NAMESPACE</li>\n\t</ul>\n</blockquote>\n<p style=margin:0px><br></p>\n\n<p style=margin:0px>Fixes and enhancements:</p>\n<blockquote style='margin-top:0px;margin-bottom:0px'>\n\t<p style=margin:0px>General:</p>\n\t\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t\t<li>Added AtlGetStringPtr() function - get a pointer to read-only resource string</li>\n\t\t\t<li>Changed CMessageLoop::IsIdleMessage() to be virtual, so it can be overridden in a derived class</li>\n\t\t\t<li>Added DECLARE_FRAME_WND_CLASS2(), DECLARE_FRAME_WND_CLASS_EX2(), and DECLARE_FRAME_WND_SUPERCLASS2() for templated classes, and used DECLARE_FRAME_WND_CLASS2() for CFrameWindowImplBase</li>\n\t\t\t<li>CMemDlgTemplateT: Added new window traits for dialog controls to avoid painting problems with WS_CLIPCHILDREN and WS_CLIPSIBLINGS</li>\n\t\t\t<li>CWindowEx: Added methods for Dialog-only messages</li>\n\t\t\t<li>Fix for #315 WTL::RunTimeHelper::IsWin7 works incorrectly on Windows 10 if versionhelpers.h is not used</li>\n\t\t\t<li>Fix for bug #300 Error in CZoomScrollWindowImpl with setting SetZoomMode(ZOOMMODE_IN)</li>\n\t\t\t<li>Fix for bug #298 InitDialogBaseUnits takes LOGFONT by value</li>\n\t\t\t<li>Fix for CResource::LoadEx() - wrong order for parameters to ::FindResourceEx()</li>\n\t\t\t<li>Fix for warning C4555: expression has no effect when using BEGIN_MSG_MAP_EX and BEGIN_DDX_MAP</li>\n\t\t\t<li>Fix for bug #266 Icon loading for hi-dpi environments</li>\n\t\t\t<li>Fix for bug #319 atlprint.h: Incorrect offsets in DEVNAMES struct</li>\n\t\t\t<li>Samples: Updated for WTL10, renamed project files to indicate VS version, code and file cleanup</li>\n\t\t</ul>\n\t<p style=margin:0px><br></p>\n\n\t<p style=margin:0px>Controls:</p>\n\t\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t\t<li>Added CListViewCtrl::SelectAllItems()</li>\n\t\t\t<li>Improved CListViewCtrl::SelectItem() to call SetSelectionMark() and remove selection</li>\n\t\t\t<li>Added another variant of CListViewCtrl::Scroll()</li>\n\t\t\t<li>Fix for bug #321 Missing HTREEITEM parameter to TreeView GetNextSelected() methods</li>\n\t\t\t<li>Added CEdit methods for new Edit messages added in Windows 10.0.17763</li>\n\t\t\t<li>CRichEditCtrl: Added GetTypographyOptions() and SetTypographyOptions() that were missing</li>\n\t\t\t<li>Added CString variant of CMultiPaneStatusBarCtrlImpl::GetPaneText()</li>\n\t\t\t<li>Fix for bug #268 CImageListManaged throws ATL assert when using attach or operator =</li>\n\t\t</ul>\n\t<p style=margin:0px><br></p>\n\n\t<p style=margin:0px>Splitter:</p>\n\t\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t\t<li>CSplitterImpl: Increased m_nPropMax to handle super-high resolutions</li>\n\t\t\t<li>CSplitterWindowImpl: Added new 'flat' splitter bar drawing style</li>\n\t\t\t<li>Used DECLARE_WND_CLASS_EX2() for CSplitterWindowT</li>\n\t\t</ul>\n\t<p style=margin:0px><br></p>\n\n\t<p style=margin:0px>TabView:</p>\n\t\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t\t<li>Added support for auto scroll when dragging tabs to reposition them</li>\n\t\t\t<li>Extended drag area to the whole client area of CTabViewImpl</li>\n\t\t\t<li>Added hover close buttons to tabs</li>\n\t\t\t<li>SetActivePage() - set focus only if main window is active</li>\n\t\t\t<li>OnTabContextMenu() to pass the correct tab item to OnContextMenu()</li>\n\t\t\t<li>CTabViewImpl: Uses ShowWindow() with TRUE/FALSE instead of SW_SHOW/SW_HIDE</li>\n\t\t</ul>\n\t<p style=margin:0px><br></p>\n\n\t<p style=margin:0px>Ribbon:</p>\n\t\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t\t<li>Fix for bug #317 SpinnerCtrlImpl - Failed to update string properties</li>\n\t\t\t<li>Don't use function pointers to templated functions</li>\n\t\t</ul>\n\n\t<p style=margin:0px>Cracked Handlers:</p>\n\t\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t\t<li>Added MSG_WM_NCMOUSEHOVER() and MSG_WM_NCMOUSELEAVE()</li>\n\t\t\t<li>Added MSG_WM_GESTURE, MSG_WM_GESTURENOTIFY, MSG_WM_DPICHANGED, MSG_WM_APPCOMMAND</li>\n\t\t\t<li>Fix for bug #322 atlcrack.h: Incorrect signature in comment for MSG_WM_MDIACTIVATE</li>\n\t\t\t<li>Fix for bug #302 MSG_WM_WTSSESSION_CHANGE should crack lParam as session Id</li>\n\t\t\t<li>Fix for bug #284 MSG_WM_XBUTTONDOWN inconsistent with MSDN</li>\n\t\t\t<li>Fix for bug #286 MSG_WM_KEYDOWN cracks wParam as TCHAR, should be a virtual key</li>\n\t\t</ul>\n\t<p style=margin:0px><br></p>\n\n\t<p style=margin:0px>Clang specific fixes:</p>\n\t\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t\t<li>Use C++-style instead of C-style struct initialization</li>\n\t\t\t<li>Remove superfluous semicolons</li>\n\t\t\t<li>Add comment for implicit fallthrough in switch statements</li>\n\t\t</ul>\n\t<p style=margin:0px><br></p>\n\n\t<p style=margin:0px>AppWizard:</p>\n\t\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t\t<li>Removed project name from the view file and class name</li>\n\t\t\t<li>Fixed warning for different types in x64 for modal dialog project</li>\n\t\t\t<li>Improved Setup.js to handle multiple installations of VS2017 and VS2019</li>\n\t\t\t<li>Setup.js - Fix for VS2019 Community setup which does not create vcprojects folder</li>\n\t\t\t<li>Removed deprecated MinimalRebuild compiler option for VS2017 and higher</li>\n\t\t</ul>\n</blockquote>\n<p style=margin:0px><br></p>\n\n<p style=margin:0px>Removed legacy features:</p>\n<blockquote style='margin-top:0px;margin-bottom:0px'>\n\t<p style=margin:0px>\n\tRemoved support for older version of Visual Studio and old SDKs<br>\n\tRemoved support for WinCE (and AppWizardCE)<br>\n\tRemoved support for _ATL_MIN_CRT and use of MinCrtHelper<br>\n\tRemoved WTL implementation of CPoint/CSize/CRect/CString (use ATL)<br>\n\tRemoved use of _WTYPES_NS and _CSTRING_NS<br>\n\tDefine _WTL_NEW_PAGE_NOTIFY_HANDLERS always<br>\n\t&nbsp;&nbsp;&nbsp;&nbsp;(added _WTL_FORCE_OLD_PAGE_NOTIFY_HANDLERS to turn it off)<br>\n\tRemoved use of _ATL_NO_OLD_NAMES, _ATL_USE_NEW_PRINTER_INFO, _ATL_NO_COM<br>\n\tRemoved _ATL_USE_CSTRING_FLOAT and _ATL_USE_DDX_FLOAT (use float always)<br>\n\tRemoved use of _ATL_NO_MSIMG<br>\n\tRemoved support for RichEdit 1.0<br>\n\tatlfind.h: Removed shadow buffer and added a warning/assert instead<br>\n\tRemoved use of _TrackMouseEvent() and used TrackMouseEvent() directly<br>\n\tRemoved use of CRegKeyEx (not needed any more)<br>\n\tRemoved support for _SECURE_ATL and use of SecureHelper functions (now always secure)<br>\n\tAlways use themes - Moved uxtheme.h and uxtheme.lib to atlapp.h<br>\n\tRemoved AtlIsOldWindows() and its use in the code<br>\n\tAppWizard:\n\t</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'></li>\n\t\t<li>Removed support for older versions of Visual Studio and VC++ Express</li>\n\t\t<li>Removed support and comments for obsolete stuff</li>\n\t\t<li>Renamed files to WTL10AppWiz.*</li>\n\t</ul>\n</blockquote>\n<p style=margin:0px><br></p>\n\n\n<h4>Changes Between WTL 9.1 and 9.0</h4>\n<p style=margin:0px>New and improved:</p>\n<blockquote style='margin-top:0px;margin-bottom:0px'>\n\t<p style=margin:0px>\n\t\tFull compatibility with VS2015<br>\n\t\tNuGet support and package<br>\n\t\tMicrosoft Public License (MS-PL)<br>\n\t\tNew sample: MemDlg - demonstrates use of in-memory dialogs\n\t</p>\n</blockquote>\n<p style=margin:0px><br></p>\n\n<p style=margin:0px>Fixes and enhancements:</p>\n<blockquote style='margin-top:0px;margin-bottom:0px'>\n\t<p style=margin:0px>\n\t\tFixes for code analysis warnings<br>\n\t\tFixes for strict const-qualification conformance (/Zc:strictStrings)<br>\n\t\tCEditFindReplaceImpl::UseShadowBuffer(): Use AtlGetCommCtrlVersion() instead of GetProcAddress()<br>\n\t\tMisc improvements: missing initialization, undefined messages, better #ifdefs<br>\n\t\tCFrameWndClassInfo: Use GetSystemMetrics() for icon sizes<br>\n\t\tBEGIN_MSG_MAP_EX and BEGIN_DDX_MAP: Fix for C4555: expression has no effect<br>\n\t\tCResource::LoadEx(): Fix for the wrong order for parameters to ::FindResourceEx()<br>\n\t\tCPaneContainerImpl:\n\t</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>New extended styles: PANECNT_DIVIDER and PANECNT_GRADIENT</li>\n\t\t<li>Fixed background drawing for close button</li>\n\t</ul>\n\t<p style=margin:0px>\n\t\tCImageListManaged: Fix for assert when using attach or operator =<br>\n\t\tWTLExplorer sample cleanup<br>\n\t\tGenericWndClass::Register(): Fix for Windows CE<br>\n\t\tApp Wizard: Improved code for generating project configurations<br>\n\t\tCSplitterImpl::OnCaptureChanged(): Fixed so it moves splitter bar only if move was in progress<br>\n\t\tCDynamicUpdateUI::UIRemoveUpdateElement() leaks memory if UPDUI_TEXT is set<br>\n\t\tCToolInfo and CToolTipCtrl: nIDTool argument should be UINT_PTR instead of UINT<br>\n\t\tCSplitterImpl: Added GetSplitterPosPct()<br>\n\t\tCCommandBarCtrlImpl: Fixed incorrect use of m_wndParent when AttachToWindow() is used\n\t</p>\n</blockquote>\n<p style=margin:0px><br></p>\n\n\n<h4>Changes Between WTL 9.0 and 8.0</h4>\n<p style=margin:0px>New and improved:</p>\n<blockquote style='margin-top:0px;margin-bottom:0px'>\n\t<p style=margin:0px>\n\t\tFull compatibility with VS2008, VS2010, VS2012, and VS2013<br>\n\t\tNew CRegKeyEx class for uniform support for registry<br>\n\t\tNew MinCrtHelper functions for uniform support for _ATL_MIN_CRT<br>\n\t\tNew DWM classes in atldwm.h<br>\n\t\tNew Ribbon classes in atlribbon.h<br>\n\t\tNew CDialogBaseUnits class<br>\n\t\tExtended DDX support to TabCtrl, ComboBox, ListBox and ListView selection index<br>\n\t\tImproved font handling in CHyperLink, CPaneContainer, CTabView<br>\n\t\tCHyperlink: Added options for auto-create link font and single-line mode<br>\n\t\tCBitmapButtonImpl: Added checked state, GetCheck()/SetCheck(), and check mode extended styles<br>\n\t\tUpdateUI: Added support for radio menu items for popup menus<br>\n\t\tAdded support for new VersionHelpers.h in WinSDK 8.1 - GetVersionEx() is now deprecated<br>\n\t\tImproved global support for old SDK headers, and for original headers in VC6 and VC7.x<br>\n\t\tGlobal support for builds with NOMINMAX defined<br>\n\t\tGlobal support for builds with STRICT_TYPED_ITEMIDS defined<br>\n\t\tGlobal support for builds with _ATL_ALL_USER_WARNINGS defined<br>\n\t\tSplitter Window:\n\t</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>Added keyboard handling</li>\n\t\t<li>Added default position for splitter bar</li>\n\t\t<li>Changed orientation from template argument to data member to reduce memory use</li>\n\t\t<li>Added SPLIT_GRADIENTBAR and SPLIT_FIXEDBARSIZE extended styles</li>\n\t</ul>\n\t<p style=margin:0px>\n\t\tAdded CImageListManaged to manage the lifetime of wrapped image list<br>\n\t\tAdded Vista standard menu bar look option for Command bar<br>\n\t\tAdded new Rich Edit wrappers for _RICHEDIT_VER >= 0x0800<br>\n\t\tAdded new Win8 methods to Theme classes<br>\n\t\tAdded override of SubclassWindow() to CSplitterWindowImpl, CPaneContainerImpl, CTabViewImpl,<br>\n\t\t&nbsp;&nbsp;CScrollImpl, CMapScrollImpl, CZoomScrollImpl, and CScrollContainerImpl<br>\n\t\tCZoomScrollImpl:\n\t</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>Added zoom child windows option</li>\n\t\t<li>Added zoom scale max limit</li>\n\t</ul>\n\t<p style=margin:0px>\n\t\tAppWizard:\n\t</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>Support for VS2008, VS2010, VS2012, and VS2013</li>\n\t\t<li>New universal setup for all versions of Visual Studio</li>\n\t\t<li>Support for ribbon control</li>\n\t</ul>\n\t<p style=margin:0px>\n\t\tUpdated samples and added VS2005 project files<br>\n\t\tNew sample: MTPad7 - demonstrates Ribbon UI\n\t</p>\n</blockquote>\n<p style=margin:0px><br></p>\n\n<p style=margin:0px>Fixes and enhancements:</p>\n<blockquote style='margin-top:0px;margin-bottom:0px'>\n\t<p style=margin:0px>General:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>Fixed security warning for _vstprintf in atlapp.h</li>\n\t\t<li>Added RunTimeHelper::IsThemeAvailable that detects if themes can be used in the app</li>\n\t\t<li>VS2012: DLL version functions are defined as they are removed from ATL11</li>\n\t\t<li>Added CWndProcThunk initialization for _ATL_VER >= 0x0800</li>\n\t\t<li>Added RunTimeHelper::SizeOf_TOOLINFO() for different Windows versions at runtime</li>\n\t\t<li>Added AtlCreateControlFont()</li>\n\t</ul>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>Controls:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>Extended CListViewCtrl::SelectItem() to multi-selection list view controls</li>\n\t\t<li>Added another variant of CListViewCtrl::FindItem for strings</li>\n\t\t<li>Added new CToolBarCtrl methods - InsertSeparator() and AddSeparator()</li>\n\t\t<li>Added CToolBarCtrl::GetItemDropDownRect()</li>\n\t\t<li>Added another variant of CToolTipCtrl::TrackActivate()</li>\n\t</ul>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>Cracked Handlers:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>Fixed handlers with menu arguments</li>\n\t\t<li>Fixed MSG_WM_SYSCOMMAND handler</li>\n\t\t<li>Added MSG_WM_MOUSEHWHEEL handler</li>\n\t</ul>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>App Wizard:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>Fix for TabView project code generation</li>\n\t\t<li>Improved generated code for VC++ Express to support various versions of ATL</li>\n\t\t<li>Fix for missing UIUpdateChildWindows() in dialog projects</li>\n\t</ul>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>App Wizard CE / App Wizard Mobile:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>Updated AppWizCE for VS2008 - used different CLSID for Platforms object</li>\n\t\t<li>Fix: VS2008 uses _SECURE_ATL code only</li>\n\t\t<li>Fix for resource creation failure</li>\n\t</ul>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>Misc:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>Fix: CLogFont uses ::GetDeviceCaps with wrong default hDC = NULL</li>\n\t\t<li>Fixed CPen::GetExtLogPen</li>\n\t\t<li>Fixed CFrameWindowImpl::OnToolTipText*() handlers not to reset text buffer</li>\n\t\t<li>Added support for chevron menus for multi-line toolbars</li>\n\t\t<li>Fix: CFileDialog(false) fails on Windows Mobile 5 or 6</li>\n\t\t<li>Fix: CFolderDialog::SetOKText should use lParam for string</li>\n\t\t<li>Added CFolderDialog::SetPidlRoot()</li>\n\t\t<li>Fixed CMemDlgTemplate::AddControl</li>\n\t\t<li>Added option to disable item dragging in CTabViewImpl</li>\n\t\t<li>Fixed CTabView::ShowTabControl(false) and UpdateLayout() to hide empty space</li>\n\t\t<li>CTabView: Fixed value of the active page when inserting pages before it</li>\n\t\t<li>PaneContainer: Added support for vertical title bar text</li>\n\t\t<li>atlsplit.h: Added missing support for WM_PRINTCLIENT</li>\n\t\t<li>Fix: CScrollImpl should not scroll horizontally if not needed</li>\n\t\t<li>Fixed CScrollImpl<T>::ScrollToView() to use offset correctly</li>\n\t\t<li>Fixed CPrintDialogExImpl::GetDefaults()</li>\n\t\t<li>atltheme.h: Added CBufferedAnimation::StopAllAnimations()</li>\n\t\t<li>Added support for I64 format to CString::Format()</li>\n\t\t<li>Fix: CStdIndirectDialogImpl - DLGTEMPLATEEX not supported on Mobile devices</li>\n\t\t<li>Fix: Missing CRichInkCtrlT::SetSel(), added CRichInkCtrlT::Undo()</li>\n\t</ul>\n</blockquote>\n<p style=margin:0px><br></p>\n\n\n<h4>Changes Between WTL 8.0 and 7.5</h4>\n<p style=margin:0px>New and improved:</p>\n<blockquote style='margin-top:0px;margin-bottom:0px'>\n  <p style=margin:0px>RunTimeHelper functions for \n\tcorrect struct sizes on different versions of Windows<br>ModuleHelper functions for uniform support of ATL3 and ATL7 module classes<br>SecureHelper functions for support of secure and non-secure run-time \n\tfunctions<br>Support for new Vista features:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>Support for new messages for common controls, dialogs, etc.</li>\n\t\t<li>Support for TaskDialog</li>\n\t\t<li>New Shell file dialogs (IFileOpenDialog and IFileSaveDialog)</li>\n\t\t<li>New Aero Wizard support classes</li>\n\t\t<li>New classes for Buffered Paint and Buffered Animation</li>\n\t</ul>\n\t<p style=margin:0px>\n\t\tNew TabView classes<br>New dialog class that uses in-memory dialog templates<br>New CMultiFileDialogImpl and CMultiFileDialog classes that support \n\t\tmulti-select file dialogs<br>Added message cracker handler prototypes for all handlers<br>Replaced use of _alloca with CTempBuffer everywhere (and added CTempBuffer \n\t\tversion for ATL3)<br>New classes for find/replace support for Edit or RichEdit<br>New class CFileDialogEx that supports GetOpenFileNameEx for Windows Mobile 5<br>\n\t\tNew features for the App Wizard:\n\t</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>New default version values</li>\n\t\t<li>Unicode build option</li>\n\t\t<li>Support for TabView applications</li>\n\t\t<li>Support for Explorer applications</li>\n\t</ul>\n\t<p style=margin:0px>Updates for the desktop App Wizard:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>Added calls to set font for views based on controls that use font</li>\n\t\t<li>Added scroll window as another view type</li>\n\t</ul>\n\t<p style=margin:0px>Support for VC2005 Express:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>Setup for VS2005x</li>\n\t\t<li>Changes in default.js to take into account that VC2005x does not have a resource editor</li>\n\t\t<li>Generated code allows use of ATL3 from the Platform SDK</li>\n\t</ul>\n\t<p style=margin:0px>New AppWizard for Mobile 2003 and 2005 platforms<br>\n\t\tNew samples:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>Aero - demonstrates the Vista Glass UI</li>\n\t\t<li>MiniPie - Windows Mobile 2005 PPC and Smartphone sample</li>\n\t\t<li>TabBrowser - a web browser using TabView class</li>\n\t</ul>\n\t<p style=margin:0px>MTPad sample updated to show usage of CRichEditFindReplaceImpl and CEditCommands/CRichEditCommands</p>\n</blockquote>\n<p style=margin:0px><br></p>\n\n<p style=margin:0px>Fixes and enhancements:</p>\n<blockquote style='margin-top:0px;margin-bottom:0px'>\n  <p style=margin:0px>Command Bar:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>Added support for menu items with bitmaps on Vista</li>\n\t\t<li>Fix: Keyboard cues shown even if the window is disabled</li>\n\t</ul>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>CFolderDialog:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>Added support for PIDLs in addition to the file path</li>\n\t\t<li>Replaced use of SHGetMalloc with CoTaskMemFree</li>\n\t</ul>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>Scroll Windows:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>Fix: CZoomScrollImpl - some methods should be overridable</li>\n\t\t<li>Added support for WM_MOUSEHWHEEL in CScrollImpl</li>\n\t</ul>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>App Wizard:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>Fix: AppWizard fails to add files if C:\\Temp does not exist</li>\n\t\t<li>Fix: App Wizard generates security warning when loaded</li>\n\t\t<li>Fix: App Wizard generates level 4 warning for modal dlg project</li>\n\t\t<li>Fix: App Wizard setupXX.js scripts silently fail on Vista</li>\n\t\t<li>Fix: Added code to unregister message filer and idle processing</li>\n\t\t<li>Fix: Added WS_CLIPSIBLINGS to dialog forms to avoid rebar drawing problems</li>\n\t</ul>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>App Wizard CE:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>Fix: App Wizard CE should not have rich edit as a view option</li>\n\t\t<li>Fix: App Wizard CE generates level 4 warnings for single instance apps</li>\n\t\t<li>Added support for Windows Mobile 6 SDKs</li>\n\t</ul>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>Cracked Handlers:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>Fix: Corrected MSG_WM_TIMER and handler prototype, removed unused argument (breaking change)</li>\n\t\t<li>Fix: atlcrack.h does not support WTL namespace</li>\n\t</ul>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>CDialogResize:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>Added SetIcon(NULL, FALSE) for CDialogResize to remove the generic icon for resizable dialogs</li>\n\t\t<li>Fix: Enabled size/move for both X and Y</li>\n\t\t<li>Added center flags for controls</li>\n\t</ul>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>CFrameWindowImpl:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>Fix: Const issue with title argument of AddSimpleReBarBand</li>\n\t\t<li>Fix: DECLARE_FRAME_WND_CLASS definition missing WTL namespace</li>\n\t</ul>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>Windows CE:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>Fix: Some symbols not defined for CE 4.0</li>\n\t\t<li>Fix: Incorrect WinCE exclusions</li>\n\t\t<li>Fix: Pocket PC - assert after navigating a CHyperLink</li>\n\t\t<li>Fix: Property sheet with listview on WM5.0 causes stack overflow</li>\n\t\t<li>Fix: CFindFile::GetFilePath() fails on diskless root requests</li>\n\t\t<li>Fix: VS 2005 dialog editor bug - DS_FIXEDSYS used but not defined</li>\n\t\t<li>Fix: Windows Mobile 2005 compatibility issues</li>\n\t\t<li>Fix: CFullScreenFrame on Smartphone 20003</li>\n\t\t<li>Fix: SmartPhone back key handling in CAppWindow</li>\n\t\t<li>Added orientation aware support to CAppStdDialogImpl</li>\n\t\t<li>Added CAxDialogImpl base for CStdDialogImpl, CStdDialogResizeImpl and CStdOrientedDialogImpl</li>\n\t\t<li>Added various CStdDialogxxx enhancements</li>\n\t\t<li>Fix: CStdDialogBase does not scale dialog title on VGA</li>\n\t\t<li>Fix: DIBINFO16 triggers code analysis warning</li>\n\t\t<li>Added LPCTSTR AtlLoadString(UINT uID) - CE only overload</li>\n\t\t<li>Added imaging draw support to CZoomScrollImpl</li>\n\t\t<li>Added CBottomTabViewImpl and CBottomTabView classes for PPC</li>\n\t</ul>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>CFindFile:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>Fix: CFindFile class uses CRT functions</li>\n\t\t<li>Fix: FindFile() uses lstrcpy without checking length</li>\n\t</ul>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>General:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>Fix: Adding ReBar bands fails with new Windows SDK</li>\n\t\t<li>Added support for relative include paths</li>\n\t\t<li>Fix: Using std::min and std::max</li>\n\t\t<li>Fix: Problems using WTL with MFC</li>\n\t\t<li>Improved support for Secure CRT</li>\n\t\t<li>Changed implementation of CSize, CPoint, CRect, and CString to be inside class definitions</li>\n\t\t<li>atltheme.h: Corrected method signatures for differences in uxtheme.h versions</li>\n\t\t<li>Replaced malloc/free with new/delete where appropriate</li>\n\t</ul>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>Misc:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>Fix: CString::FormatV can cause GPF with Unicode strings</li>\n\t\t<li>CHyperLink: Added handler for WM_SIZE</li>\n\t\t<li>Fix: CTheme needs constructor from HTHEME handle</li>\n\t\t<li>Added Add* methods to several control classes in atlctrls.h to augment Insert* methods</li>\n\t\t<li>Fix: Incorrect casting in CRichEditCtrl::GetLine()</li>\n\t\t<li>Fix: CTreeViewCtrl::GetItemState changed to return only state-bits as specified by mask</li>\n\t\t<li>Fix: CBitmapButton::DoPaint - wrong button image</li>\n\t\t<li>Added another variant of CDCT::Drawtext with LPTSTR argument that allows text change</li>\n\t\t<li>Fix: CRecentDocumentListBase::AddToList() uses lstrcpy</li>\n\t\t<li>Fix: AtlLoadString(uID, lpBuffer, nBufferMax) has unnecessary code</li>\n\t\t<li>Fix: CCursor::LoadOEMCursor asserts on IDC_HAND</li>\n\t\t<li>Fix: Memory leak when using CRT functions while printing</li>\n\t\t<li>Fix: Undefined CString namespace</li>\n\t\t<li>CPaneContainer: Added border styles</li>\n\t\t<li>CSplitterImpl: Added SetSplitterPosPct, and changed App Wizard code to use it</li>\n\t</ul>\n</blockquote>\n<p style=margin:0px><br></p>\n<p style=margin:0px><br></p>\n\n\n<p style=margin:0px><b>Changes Between WTL 7.5 and 7.1</b></p>\n<p style=margin:0px><br></p>\n<p style=margin:0px>New and improved:</p>\n<blockquote style='margin-top:0px;margin-bottom:0px'>\n\t<p style=margin:0px>\n\t\tVS2005 Compatibility:\n\t\tAdded support for Visual Studio 2005 - both desktop and Windows CE<br>\n\t\tClasses for icons, cursors, accelerator tables<br>\n\t\tCSortListViewImpl, CSortListViewCtrlImpl, and CSortListViewCtrl classes<br>\n\t\tImpl classes for Wizard 97 style wizards: CWizard97Sheet,\n\t\tCWizard97Page, CWizard97ExteriorPage, CWizard97InteriorPage<br>\n\t\tCMemoryDC and CDoubleBufferWindowImpl classes<br>\n\t\tWindows CE specific classes in new header, atlwince.h<br>\n\t\tCScrollContainer class<br>\n\t\tCZoomScrollImpl and CZoomScrollWindowImpl classes<br>\n\t\tCZoomPrintPreviewWindowImpl and CZoomPrintPreviewWindow classes<br>\n\t\tGlobal functions: AtlGetBitmapResourceInfo,\n\t\tAtlGetBitmapResourceBitsPerPixel<br>\n\t\tNew REFLECT_* macros to enable selective reflection of messages<br>\n\t\tApp Wizard: Added App Wizard for VS2005<br>\n\t\tApp Wizard: Added App Wizard for Windows CE for VS2005<br>\n\t\tNew samples: WTLExplorer, ImageView, SPControls<br>\n\t</p>\n</blockquote>\n<p style=margin:0px><br></p>\n\n<p style=margin:0px>Fixes and enhancements:</p>\n<blockquote style='margin-top:0px;margin-bottom:0px'>\n\t<p style=margin:0px>Command Bar:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>DrawBitmapDisabled() doesn't work correctly on Longhorn</li>\n\t\t<li>Submenu size not correct if command bar is off-screen</li>\n\t\t<li>Added handler for WM_SETTINGCHANGE to improve theme color changes</li>\n\t\t<li>Better support for 8/16/24-bit images</li>\n\t\t<li>Command Bar with 2 Levels of submenus remains active</li>\n\t\t<li>Hook procedure fails to call next hook</li>\n\t\t<li>OnDestroy() should not decrement hook use if AttachToWindow() is used</li>\n\t</ul>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>MDI Command Bar:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>Grows bigger if you switch between two maximized MDI child window types</li>\n\t\t<li>Move all hook messages processing to a separate function and use pT</li>\n\t\t<li>MDI icon &amp; buttons should have themed background</li>\n\t\t<li>Should make MDI buttons gray when inactive<br>&nbsp;</li>\n\t</ul>\n\t<p style=margin:0px>CString:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>Helper functions not overloaded properly</li>\n\t\t<li>Some return types are 'const CString&amp;' and could be just 'CString&amp;'</li>\n\t\t<li>FormatV() passes size in characters to _alloca, should be in bytes</li>\n\t\t<li>Fixed stack corruption in FormatV()</li>\n\t\t<li>Improved boundaries checking for integer overflows/underflows<br>&nbsp;</li>\n\t</ul>\n\t<p style=margin:0px>CScrollImpl:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>Scroll bars problem when changing range</li>\n\t\t<li>SetScrollOffset() doesn't move child windows</li>\n\t\t<li>Range and thumb drawing problems</li>\n\t\t<li>Possible overflow in OnMouseWheel()</li>\n\t\t<li>Support for SIF_DISABLENOSCROLL</li>\n\t\t<li>Added ScrollToView methods</li>\n\t</ul>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>CMapScrollImpl:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>SetScrollSize() incorrectly inverts xMin and xMax</li>\n\t\t<li>SetScrollSize() uses bRedraw = NULL</li>\n\t</ul>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>CTheme:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>GetThemeFont() bad parameter ordering</li>\n\t\t<li>Uses LOGFONT and TEXTMETRIC incorrectly (SDK header problem)</li>\n\t</ul>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>CFrameWindowImpl:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>Improved sizing for Windows CE</li>\n\t\t<li>CreateSimpleToolBarCtrl() should handle 24-bit bitmaps</li>\n\t\t<li>Changed WinCE CCECommandBarCtrl typedef and added a PPC CMenuBarCtrl</li>\n\t\t<li>UpdatesBarPosition() doesn't take Windows CE command bar into account</li>\n\t</ul>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>CDialogResize:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>Enabled use for Windows CE</li>\n\t\t<li>Add WS_EX_DLGMODALFRAME to prevent empty icon</li>\n\t</ul>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>CReBarCtrl:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>Background not painted when resized</li>\n\t\t<li>Fixed typo in LockBands()</li>\n\t\t<li>MaximizeBand needs BOOL fIdeal argument</li>\n\t</ul>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>CRichEdit:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>GetSelText() should support UNICODE strings</li>\n\t\t<li>GetSelText() uses lpstr instead of lpstrText</li>\n\t</ul>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>CHyperLink:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>Added _xttoi() helper to avoid CRT in _ATL_MIN_CRT</li>\n\t\t<li>Fixed resource leak by destroying tooltip window<br>&nbsp;</li>\n\t</ul>\n\t<p style=margin:0px>CPropertySheetImpl:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>Improved support for Windows CE</li>\n\t\t<li>Sheet without title generates a memory fault on Windows CE</li>\n\t</ul>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>CFolderDialog:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>Add a way to set an initial folder</li>\n\t\t<li>Uses BFFM_IUNKNOWN which is not always defined</li>\n\t</ul>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>Update UI:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>Add support to dynamically add UpdateUI elements</li>\n\t\t<li>UIUpdateMenuBarElement() should use EnableMenu() instead of SetMenuItemInfo() for Windows CE</li>\n\t</ul>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>CDC:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>FillSolidRect() should restore background color</li>\n\t\t<li>GetClipRgn() method missing</li>\n\t</ul>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>Printing:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>CPrinter::CreatePrinterDC() and CreatePrinterIC() members should be const</li>\n\t\t<li>CDevMode::CopyToHDEVMODE() is missing a call to GlobalUnlock()</li>\n\t</ul>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>AppWizard:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>Use WTL subfolder to create WTL category for VC7.x and VC8</li>\n\t\t<li>Rename files from WTLApp7x to WTLAppWiz, and add VS2005 setup file</li>\n\t\t<li>Fixed setup for x64</li>\n\t</ul>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>General:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>Redefinition of _MAX_FNAME with Dinkumware Standard C++ Library on Windows CE</li>\n\t\t<li>Added ATLVERIFY macro for ATL3</li>\n\t\t<li>Support warning level 4</li>\n\t\t<li>Missing methods CToolBarCtrl::SetButtonInfo, InsertButton, CTabCtrl::SetItem, CComboBoxEx::InsertItem, SetItem</li>\n\t\t<li>Missing support for WM_PRINTCLIENT</li>\n\t\t<li>Removed usage of IsBad* functions</li>\n\t\t<li>Fixed various compiler warnings</li>\n\t\t<li>TCHAR bugs in various files</li>\n\t\t<li>Improved Windows CE support and changes for Visual Studio 2005</li>\n\t</ul>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>Misc:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>CMDIChildWindowImpl: HMENU should be destroyed in OnDestroy()</li>\n\t\t<li>CStatic: Should use STM_SETIMAGE instead of STM_SETICON for SetIcon() on Windows CE</li>\n\t\t<li>CButton: GetButtonStyle() uses wrong mask</li>\n\t\t<li>CImageList: Made Duplicate() method const</li>\n\t\t<li>CListViewCtrl: Made SubItemHitTest() method const</li>\n\t\t<li>CTreeViewCtrl: GetItem() and SetItem() incorrectly restricted to _WIN32_IE &gt;= 0x0500</li>\n\t\t<li>CMonthCalendarCtrl: GetMonthRange() should be GetMaxTodayWidth()</li>\n\t\t<li>CDateTimePickerCtrl: SetFormat() should have const argument</li>\n\t\t<li>CBitmapButtonImpl: Fixed resource leak by destroying tooltip window</li>\n\t\t<li>CMultiPaneStatusBarCtrlImpl: Cannot handle wide panes without resource strings</li>\n\t\t<li>CCheckListViewCtrlImpl: Call CheckSelectedItems() through pT</li>\n\t\t<li>CPaneContainerImpl: SetPaneContainerExtendedStyle() should use pT to call CalcSize()</li>\n\t\t<li>CFindFile: Enabled for Windows CE</li>\n\t\t<li>CPropertyPageImpl: Added handlers for callback messages</li>\n\t\t<li>atlcrack.h: Added return value for MSG_WM_APPCOMMAND</li>\n\t\t<li>CMenu: New method variants: AppendMenu, InsterMenu, ModifyMenu</li>\n\t\t<li>CFont: Added arguments for bold and italic to CreatePointFont()</li>\n\t\t<li>CSize: Added scalar operators for WTL::CSize and ATL::CSize</li>\n\t\t<li>CRecentDocumentList: Allow changing the &quot;DocumentCount&quot; and &quot;Document%i&quot; registry values strings</li>\n\t\t<li>CSplitterWindowImpl: Enabled use for Windows CE</li>\n\t</ul>\n</blockquote>\n<p style=margin:0px><br></p>\n\n\n<h4>Changes Between WTL 7.1 and 7.0</h4>\n<p style=margin:0px>New and improved:</p>\n<blockquote style='margin-top:0px;margin-bottom:0px'>\n\t<p style=margin:0px>VC7 Compatibility: Support for ATL7 Module classes and critical sections and AppWizard setup for VC++ 7.1</p>\n\t<p style=margin:0px>Windows CE Support: Full compatibility with Windows CE platforms and AppWizard for eMbedded Visual C++</p>\n\t<p style=margin:0px>Namespace Support: Automatic &quot;using ATL&quot; (ATL7 only) or &quot;using WTL&quot; can now be turned off</p>\n\t<p style=margin:0px>CHyperLink New Features: not underlined, underlined when hover, command button, link tags</p>\n\t<p style=margin:0px>CCustomWaitCursor class supports custom and animated wait cursors</p>\n\t<p style=margin:0px>AtlCreateBoldFont() for creating bold version of an existing font</p>\n</blockquote>\n<p style=margin:0px><br></p>\n<p style=margin:0px>Fixes and enhancements:</p>\n<blockquote style='margin-top:0px;margin-bottom:0px'>\n\t<p style=margin:0px>CFrameWindowImpl:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>CreateSimpleToolBarCtrl() - remove dead code, improve error checking, add a global function that uses it</li>\n\t\t<li>Fix - PrepareChevronMenu() fails to  get toolbar strings for Unicode</li>\n\t\t<li>CFrameWindowImplBase::Create() - improve ASSERT not to use m_hWnd if creation fails</li>\n\t\t<li>Fix - CFrameWndClassInfo::Register - should use %p formatting only for _WIN32_WINNT &gt;= 0x0500 or for _WIN64</li>\n\t\t<li>Fix - Chevron menus not positioned correctly with RTL</li>\n\t\t<li>Fix - CMDIChildWindowImpl: Problems creating maximized child windows and handling focus</li>\n\t\t<li>Fix - CMDIChildWindowImpl: Should activate on WM_MOUSEACTIVATE</li>\n\t</ul>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>UpdateUI:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>Fix - Incorrectly clears default item from the system menu in MDI apps</li>\n\t\t<li>Added UISetCheck with bool instead of int for the check state</li>\n\t</ul>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>DDX:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>Fix - Doesn't provide a way to change floating point precision</li>\n\t\t<li>Added DDX_CONTROL_HANDLE for non-CWindowImpl objects</li>\n\t\t<li>Added DDX_Check variant with bool instead of int for the check state</li>\n\t</ul>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>Command Bar:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>Fix - OnDrawItem() and OnMeasureItem() don't do a good check for owner-draw menu items</li>\n\t\t<li>Fix - Disabled 32-bit images not painted correctly in 3D menu mode</li>\n\t\t<li>Fix - Popup menus not positioned correctly with RTL</li>\n\t\t<li>Fix - Uses GCL_HICONSM instead of GCLP_HICONSM with GetClassLongPtr()</li>\n\t</ul>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>MDI Command Bar:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>Fix - Doesn't refresh icon if MDI children are different</li>\n\t\t<li>OnAllHookMessages() - improve code to handle MDI child window class icon</li>\n\t\t<li>Fix - OnNcLButtonDown() uses TPM_VERPOSANIMATION without checking Windows version</li>\n\t\t<li>Fix - Maximized MDI buttons in wrong place for RTL</li>\n\t\t<li>Should adjust cxIdeal for rebar bands for IE4</li>\n\t\t<li>Add support for different top-level menu widths by handling ideal size for rebar bands</li>\n\t</ul>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>AppWizard:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>Fix - Doesn't support MSDI application as a COM Server</li>\n\t\t<li>Fix - MDI with Form View - stack overflow closing maximized MDI child windows</li>\n\t\t<li>Fix - Generates VERSION resource name 'test1' regardless of the project name</li>\n\t\t<li>Fix - Dialog project with control hosting doesn't derive a dialog from CAxDialogImpl</li>\n\t\t<li>Fix - COM Server doesn't register type library</li>\n\t\t<li>Fix - COM Server doesn't register AppID properly</li>\n\t</ul>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>CTreeViewCtrl:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>Fix - GetItemData() needs better return value</li>\n\t\t<li>Fix - GetItemState() should use TVM_GETITEMSTATE instead of TVM_GETITEM for IE5</li>\n\t\t<li>GetItem() and SetItem() - added new variants that use TVITEMEX</li>\n\t\t<li>Fix - SortChildren() should add recurse flag argument</li>\n\t\t<li>Fix - CTreeItem doesn't support CTreeViewCtrlExT that has different TBase than CWindow</li>\n\t</ul>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>CThemeImpl:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>Fix - Uses scalar delete instead of the vector one</li>\n\t\t<li>Fix - EnableThemeDialogTexture() argument is BOOL instead of DWORD</li>\n\t</ul>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>CFolderDialog:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>Fix - EnableOK() passes wrong arguments to BFFM_ENABLEOK</li>\n\t\t<li>Fix - Always clears m_hWnd, which causes problem for nested messages</li>\n\t</ul>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>CDialogResize:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>Fix - DlgResize_Init() forces dialog to be visible by using SetRedraw()</li>\n\t\t<li>Forcing WS_THICKFRAME is not enough to make dialog resizable</li>\n\t\t<li>Min track size should be used for child dialogs as well</li>\n\t\t<li>Fix - DlgResize_PositionControl() incorrectly checks return value from MapWindowPoints()</li>\n\t</ul>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>CAppModule:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>Fix - CAppModule methods not thread-safe</li>\n\t\t<li>Fix - AddSettingChangeNotify() unusable in multithreaded apps because of delayed initialization</li>\n\t</ul>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>CString:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>Fix - Delete() doesn't allow deleting more than the length of the string</li>\n\t\t<li>Fix - Append() can cause buffer overrun</li>\n\t\t<li>Fix - MakeReverse() can cause an infinite loop</li>\n\t\t<li>Fix - _cstrstr() unnecessarily inefficient</li>\n\t\t<li>Fix - FindOneOf() is not DBCS-aware</li>\n\t\t<li>Fix - Format() does not recognize %E</li>\n\t\t<li>Fix - TrimLeft() and TrimRight() are only half-way DBCS-aware</li>\n\t\t<li>Fix - May cause assertions or undefined behavior with SBCS</li>\n\t</ul>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>CRecentDocumentList:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>Fix - SetMaxEntries() has an incorrect ASSERT</li>\n\t\t<li>Add CString variant of the GetFromList() method</li>\n\t\t<li>Add a way to replace command IDs used for the MRU list</li>\n\t\t<li>Add a way to replace registry key name</li>\n\t</ul>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>Misc:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>CMessageLoop::Run() - improve the loop by checking bDoIdle before calling PeekMessage()</li>\n\t\t<li>CServerAppModule: Clean-up unused code</li>\n\t\t<li>Fix - CServerAppModule::MonitorProc() - no need to call _endthreadex()</li>\n\t\t<li>Fix - CListBox::GetText() and CComboBox::GetLBText() (CString variants) don't check for LBERR/CB_ERR</li>\n\t\t<li>Fix - CAxPropertyPageImpl doesn't create ActiveX controls with ATL7</li>\n\t\t<li>Fix - CDC::GetTextExtentExPoint() missing</li>\n\t\t<li>CDC::SetWindowExt() should have default value NULL for the lpSizeRet argument</li>\n\t\t<li>Fix - CPropertySheetWindow missing methods for PSM_INSERTPAGE, PSM_SETHEADERTITLE, and PSM_SETHEADERSUBTITLE;\n\t\t\t\tAddPage should return BOOL</li>\n\t\t<li>Fix - CMapScrollImpl::SetScrollSize() uses wrong variable</li>\n\t\t<li>Fix - CHyperLink: WM_UPDATEUISTATE causes repaint without WM_PAINT</li>\n\t\t<li>Fix - CUpDownCtrl::GetPos() returns incorrect value</li>\n\t\t<li>Fix - CUpDownCtrl::GetPos32() doesn't have default arg value</li>\n\t\t<li>Fix - CMultiPaneStatusBarCtrl: Always uses size grip for positioning panes</li>\n\t\t<li>Fix - CTabCtrl::InsertItem() should return int, not BOOL</li>\n\t\t<li>CReBarCtrl: Added LockBands() method</li>\n\t\t<li>Fix - CFont: uninitialized variable passed to DPtoLP</li>\n\t\t<li>Fix - CPrintDialogImpl: Crash when displaying Print Setup dialog</li>\n\t\t<li>Fix - CPageSetupDialogImpl::PaintHookProc() - should use T* and return UINT_PTR instead of UINT</li>\n\t\t<li>Fix - CPrintJob doesn't support printing to a file</li>\n\t\t<li>Fix - CSplitterImpl: Doesn't handle WM_CAPTURECHANGED - can get in an invalid state</li>\n\t\t<li>CRichEditCtrl: Add method for EM_SETTABSTOPS</li>\n\t\t<li>Fix - CFindFile::GetFilePath() checks for a trailing slash, but doesn't use that info</li>\n\t</ul>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>General:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>Fix - Problems compiling with /Zc:forScope ('for' loop scope conformance)</li>\n\t\t<li>Use named constants instead of values for pixel sizes, buffer lengths, etc.</li>\n\t\t<li>Support building with Managed C++ (/CLR)</li>\n\t\t<li>CMenuItemInfo - add run-time support for different versions of Windows</li>\n\t\t<li>CommCtrl.h change - additional fields in IMAGELISTDRAWPARAMS now depend on _WIN32_IE instead of _WIN32_WINNT</li>\n\t\t<li>Fix - Incorrect usage of CRegKey::QueryStringValue()</li>\n\t\t<li>Fix - Operator = for GDI and USER wrappers leaks handle if it's managed variant</li>\n\t\t<li>Fix - GDI and USER wrappers break under self-assignments</li>\n\t\t<li>Fix - Chaining messages with cracked handlers broken with ATL7</li>\n\t\t<li>Initialize all variables and structures prior to use</li>\n\t\t<li>Use new common control struct names</li>\n\t</ul>\n</blockquote>\n<p style=margin:0px><br></p>\n\n\n<h4>Changes Between WTL 7.0 and 3.1</h4>\n<p style=margin:0px>New classes and features:</p>\n<blockquote style='margin-top:0px;margin-bottom:0px'>\n\t<p style=margin:0px>Support for new Common Controls v6 messages</p>\n\t<p style=margin:0px>Support for Visual Studio .NET and ATL 7.0</p>\n\t<p style=margin:0px>WTLApp70 - new AppWizard for Visual Studio .NET</p>\n\t<p style=margin:0px>CThemeImpl - implements support for Windows XP themes</p>\n\t<p style=margin:0px>CMDICommandBarCtrl - implements Command Bar for MDI applications</p>\n</blockquote>\n<p style=margin:0px><br></p>\n<p style=margin:0px>Fixes and enhancements:</p>\n<blockquote style='margin-top:0px;margin-bottom:0px'>\n\t<p style=margin:0px>Command Bar:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>Bogus assert in OnDestroy</li>\n\t\t<li>Check marks can be truncated in large font settings</li>\n\t\t<li>Use pT to access GetSystemSettings, DrawMenuText, DrawBitmapDisabled, Draw3DCheckmark, DoPopupMenu,\n\t\t\tDoTrackPopupMenu, TakeFocus, GiveFocusBack, so they can be overridden</li>\n\t\t<li>No hot-tracking if main window is not active</li>\n\t\t<li>Top level items not painted inactive if app looses activation while drop down menu is displayed</li>\n\t\t<li>Added Windows XP flat menus support</li>\n\t\t<li>Drop-down menu doesn't close if clicked again (Windows XP only)</li>\n\t\t<li>Menu item text and accelerator text too close with some settings</li>\n\t\t<li>Keyboard can still access clipped menu items</li>\n\t\t<li>Added support for hiding keyboard navigation indicators until Alt key is pressed (system setting)</li>\n\t\t<li>Added AddIcon and ReplaceIcon variants for icon resources</li>\n\t\t<li>Image size calculated differently in different places</li>\n\t\t<li>Add support for 32-bit (alpha channel) bitmaps for Windows XP</li>\n\t\t<li>Fixed width calculation for default menu items</li>\n\t</ul>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>CFrameWindowImpl:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>AddSimpleReBarBandCtrl sets toolbar extended styles without preserving old ones</li>\n\t\t<li>PrepareChevronMenu should not create menu items for buttons with TBSTATE_HIDDEN</li>\n\t\t<li>TPM_VERPOSANIMATION will not be defined in atlframe.h if atlctrlw.h is included first</li>\n\t\t<li>CreateSimpleToolBarCtrl - height might be too small if large font is used</li>\n\t\t<li>PrepareChevronMenu uses TB_GETBUTTONTEXT, better use TB_GETBUTTONINFO</li>\n\t\t<li>Chevron menu doesn't close if clicked again (Windows XP only)</li>\n\t\t<li>Should check local classes for superclassing</li>\n\t\t<li>Add support for 32-bit (alpha channel) bitmaps for Windows XP</li>\n\t</ul>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>Update UI:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>UISetText can clear other menu item flags</li>\n\t\t<li>CUpdateUI::UIUpdateState assigns value with |= instead of =</li>\n\t\t<li>Added UISetDefault() and fix default state to work with menus</li>\n\t</ul>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>CString:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>GetBuffer() and GetBufferSetLength() should return NULL in out-of-memory condition</li>\n\t\t<li>Added missing methods: separate c-tors for LPCSTR and LPCWSTR, CollateNoCase, TrimRight and TrimLeft variants, Find\n\t\t\tvariants, moved FormatV to public</li>\n\t\t<li>Fix _IsValidString usage</li>\n\t\t<li>FormatV incorrectly calculates buffer size (too big)</li>\n\t\t<li>Usage of _ttoi causes problems with _ATL_MIN_CRT in VC7</li>\n\t</ul>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>CDC:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>GetTabbedTextExtent() should return DWORD instead of BOOL</li>\n\t\t<li>Add FillRect() that accept color index instead of a brush handle</li>\n\t\t<li>DrawDragRect() leaks regions and a brush</li>\n\t\t<li>Improved DitherBlt() - added brushes as arguments for used colors</li>\n\t\t<li>Added DrawShadowText() (uses LoadLibrary/GetProcAddress to run on older Windows)</li>\n\t</ul>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>CListViewCtrl:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>SetItemState should use LVM_SETITEMSTATE</li>\n\t\t<li>SetItemCount should return a BOOL</li>\n\t</ul>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>CRichEditCtrl:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>Added SetCharFormat() variant that accepts flags (for SCF_ALL)</li>\n\t\t<li>CharFromPos() should pass a pointer to POINTL in lParam</li>\n\t\t<li>GetTextRange() - should add Unicode variant for rich edit version &gt;= 2</li>\n\t\t<li>Added another FormatRange() that can accept a pointer to FORMATRANGE (needed for passing NULL to clear cache)</li>\n\t</ul>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>CHyperLink:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>Allow overriding of Navigate and CalcLabelRect</li>\n\t\t<li>Doesn't handle right or center alignment</li>\n\t</ul>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>CColorDialog:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>Has static variables that were not initialized with _ATL_MIN_CRT</li>\n\t\t<li>Fixed HookProc for ColorOK message - the message is not sent, but the hook proc is called directly</li>\n\t</ul>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>atlcrack.h:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>MSG_WM_TIMER crack macro should cast to TIMERPROC instead of TIMERPROC*</li>\n\t\t<li>Add cracked handlers for all new messages in Common Controls 6</li>\n\t</ul>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>atlapp.h:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>Fixed problems with atlTraceUI with ATL7</li>\n\t\t<li>#ifdefs for ATL7 were in the wrong place</li>\n\t</ul>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>atlctrls.h:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>Add support in control classes for all new messages in Common Controls 6</li>\n\t</ul>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>CRecentDocumentList:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>AtlCompactPath corrupts memory if filename is longer than requested compact size</li>\n\t\t<li>ReadFromRegistry incorrectly checks for error when reading from registry</li>\n\t</ul>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>CSplitterWindow:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>Incorrect calculation of middle position</li>\n\t\t<li>3D border now drawn only if WS_EX_CLIENTEDGE is set</li>\n\t</ul>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>Printing:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>Uses DWORD instead of an int for a job ID</li>\n\t\t<li>CPrintJob::CancelPrintJob shouldn't have a return value</li>\n\t</ul>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>Misc:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>CRegKey::QueryValue and SetValue are deprecated in ATL7</li>\n\t\t<li>Added direct support for ATL7</li>\n\t\t<li>Replace ScreenToClient and ClientToScreen with MapWindowPoints to support RTL layout</li>\n\t\t<li>CFindFile::GetFilePath(LPTSTR...) returns path without the file name</li>\n\t\t<li>MDI: Updating client edge in WM_WINDOWPOSCHANGING causes minimize/maximize/restore animation problems,\n\t\t\tuse WM_WINDOWPOSCHANGED</li>\n\t\t<li>Custom Draw: Added CCustomDraw::OnSubItemPrePaint() overrideable method</li>\n\t\t<li>CFolderDialogImpl uses 'this' for BROWSEINFO.lParam instead of T*</li>\n\t\t<li>CImageList::Destroy shouldn't use Detach()</li>\n\t\t<li>ATL7 has its own AtlLoadString</li>\n\t\t<li>CPropertySheet doesn't close when you press X button</li>\n\t\t<li>Fixed problems for _U_STRINGorID and others that moved from atlbase.h to atlwin.h in ATL7</li>\n\t\t<li>Add AtlMessageBox() that accepts either in-memory or resource strings</li>\n\t\t<li>CScrollImpl: fixed bug with scrolling child windows</li>\n\t\t<li>CPropertyPageImpl: Add new notification handlers to enable direct return values \n\t\t\t(use #ifdef _WTL_NEW_PAGE_NOTIFY_HANDLERS to use them)</li>\n\t\t<li>Add AtlInitCommonControls() to simplify use</li>\n\t\t<li>DDX: Fixed usage of the size of char arrays for DDX</li>\n\t\t<li>CPageSetupDialog: changed usage of CWndProcThunk because of changes in ATL7</li>\n\t\t<li>Fix confusing precedence in expressions</li>\n\t\t<li>Removed forward declarations because default values for template arguments \n\t\t\tshouldn't be specified in two places (we don't need them anyway)</li>\n\t\t<li>Win64: Fix /Wp64 warnings from 32-bit VC7 compiler caused by SDK headers</li>\n\t\t<li>Fix direct usage of English strings (they can be #defined to something else now)</li>\n\t\t<li>AtlGetCommCtrlVersion not defined if _ATL_DLL is in ATL 3.0 (and CmdBar is using it)</li>\n\t</ul>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>AppWizard:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>Added manifest for Common Controls 6</li>\n\t\t<li>Loading Rich Edit DLL should use HMODULE</li>\n\t\t<li>Should not use atlimpl.cpp for ATL7</li>\n\t\t<li>Added message handler prototypes to generated files</li>\n\t\t<li>VERSION resource always has VALUE &quot;OLESelfRegister&quot; (now only for COM servers)</li>\n\t\t<li>Added option for putting implementation in CPP files</li>\n\t\t<li>d-tor for the thread manager class in MSDI project executed after the heap is destroyed</li>\n\t\t<li>Wrong settings when changing to a dialog project and back (AppWizard 6.0 only)</li>\n\t\t<li>Remove cut/copy/paste accelerators for form view and dialogs projects</li>\n\t\t<li>Fix toolbar bitmaps so they are not transparent (problem with Windows XP flat menus only)</li>\n\t\t<li>Used CMDICommandBarCtrl for MDI apps</li>\n\t\t<li>Add symbols required for VC7 Class Wizard to recognize an ATL project</li>\n\t\t<li>Changed default styles for the rebar, so it does look OK without CmdBar and with manifest</li>\n\t\t<li>Added setup programs for both AppWizards</li>\n\t\t<li>Remove ignored resource attributes: MOVEABLE, PURE, etc. (AppWizard 7.0 only)</li>\n\t\t<li>Add call to DefWindowProc to WinMain to resolve possible problems if MSLU is used</li>\n\t</ul>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>Samples:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>Updated toolbar bitmaps, added #ifdefs for ATL7, added manifest file for CommCtrl6, qualified _U_RECT with WTL\n\t\t\tnamespace, updated use of deprecated CRegKey functions, added VC7 projects</li>\n\t\t<li>Added Alpha sample</li>\n\t</ul>\n</blockquote>\n<p style=margin:0px><br></p>\n\n\n<h4>Changes Between WTL 3.1 and 3.0</h4>\n<p style=margin:0px>New classes:</p>\n<blockquote style='margin-top:0px;margin-bottom:0px'>\n\t<p style=margin:0px>\n\t\tCPaneContainer - implements a window that provides a title bar and a close button (like Explorer)\n\t</p>\n\t<p style=margin:0px>\n\t\tCDialogResize - an MI class that allows resizing of dialogs (or any windows with child windows/controls)\n\t</p>\n\t<p style=margin:0px>\n\t\tCAxPropertyPageImpl - implements a property page that can host ActiveX controls\n\t</p>\n</blockquote>\n<p style=margin:0px><br></p>\n<p style=margin:0px>Fixes and enhancements:</p>\n<blockquote style='margin-top:0px;margin-bottom:0px'>\n\t<p style=margin:0px>\n\t\tCServerAppModule now clears m_hEventShutdown to avoid calling CloseHandle twice\n\t</p>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>CString:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>operator += now leaves original string intact if it's out of memory</li>\n\t\t<li>Fixed bad DWORD_PTR usage in TrimRight, TrimLeft, Replace, Remove</li>\n\t\t<li>Removed dependencies on CRT for projects that don't use it</li>\n\t\t<li>Insert - fixed string corruption in release builds</li>\n\t\t<li>Added optional floating point formatting (for projects that use CRT)</li>\n\t</ul>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>\n\t\tCEdit and CRichEditCtrl: SetSelAll and SetSelNone had reversed implementation\n\t</p>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>\n\t\tatlres.h: Changed IDs so that they are compatible with MFC's afxres.h\n\t</p>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>Command Bar:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>Added LoadMappedImages()</li>\n\t\t<li>Changed handling of left and right arrow keys so that they don't close context menus</li>\n\t\t<li>Add code to handle left/right arrow keys correctly on mirrored (RTL) systems</li>\n\t\t<li>Removed handler that eats parent window's WM_SETTINGCHANGE</li>\n\t\t<li>Fixed bitmap resource leak in Draw3DCheckmark</li>\n\t\t<li>Fixed incorrect usage of CharLower in OnMenuChar</li>\n\t\t<li>Fixed wrong color for the disabled items in hi-contrast mode</li>\n\t\t<li>Added code to gray menu items if main window is inactive</li>\n\t\t<li>Fixed keyboard mnemonic handling for IE 4</li>\n\t\t<li>Fixed hook problems with multiple cmdbars in the same thread</li>\n\t\t<li>Added support for radio menu items</li>\n\t\t<li>Added support for disabled top-level menu items (also added in CFrameWindowImpl::PrepareChevronMenu)</li>\n\t\t<li>Added keyboard shortcut (Alt+/) to invoke chevron menu</li>\n\t\t<li>Added support to override menu item length in a derived class</li>\n\t</ul>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>CBitmapButton:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>Bypassed BUTTON DefWindowProc for hover style so that the button doesn't take focus</li>\n\t\t<li>Added BMPBTN_AUTOFIRE extended style</li>\n\t</ul>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>CDC:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>Added _WTL_FORWARD_DECLARE_CSTRING define to allow usage of methods that accept CString</li>\n\t\t<li>Fixed errors in GetTextFace and GetMenuItemString</li>\n\t\t<li>Added GetCharWidth32</li>\n\t\t<li>Added DrawIconEx method</li>\n\t</ul>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>CMenu:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>Implement following missing methods:<br>\n\t\t\t&nbsp;&nbsp;&nbsp; GetMenuDefaultItem<br>\n\t\t\t&nbsp;&nbsp;&nbsp; GetMenuInfo<br>\n\t\t\t&nbsp;&nbsp;&nbsp; GetMenuItemRect<br>\n\t\t\t&nbsp;&nbsp;&nbsp; HiliteMenuItem<br>\n\t\t\t&nbsp;&nbsp;&nbsp; IsMenu<br>\n\t\t\t&nbsp;&nbsp;&nbsp; MenuItemFromPoint<br>\n\t\t\t&nbsp;&nbsp;&nbsp; SetMenuDefaultItem<br>\n\t\t\t&nbsp;&nbsp;&nbsp; SetMenuInfo</li>\n\t\t<li>GetMenuString - fixed to include space for terminating NULL character in returning string</li>\n\t</ul>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>\n\t\tGDI and USER classes should destroy the GDI/USER objects in Attach if GDI/USER resource is managed\n\t</p>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>CFrameWindowImpl:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>OnToolTipText shouldn't save tool tip text if it's not for a menu</li>\n\t\t<li>AddSimpleReBarBandCtrl now adds chevron style only for toolbars with buttons</li>\n\t\t<li>AddSimpleReBarBand(Ctrl) - calc band ID if not specified</li>\n\t</ul>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>CRecentDocumentList:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>Fix - UpdateMenu deletes wrong menu item when the list is empty</li>\n\t\t<li>Added code to allow restricting the number of characters displayed by MRU menu items</li>\n\t</ul>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>Update UI:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>Added support for blocking accelerators for disabled items</li>\n\t\t<li>Improved search code assuming there are no duplicate entries (and added checks for duplicates)</li>\n\t</ul>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>CSplitterWindow:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>CSplitterWindowImpl should derive from CSplitterImpl&lt;T , t_bVertical&gt; to allow overriding of methods</li>\n\t\t<li>Added single pane mode and SetSinglePaneMode/GetSinglePaneMode</li>\n\t\t<li>Added right/bottom aligned resize mode using extended styles SPLIT_RIGHTALIGNED/SPLIT_BOTTOMALIGNED</li>\n\t</ul>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>\n\t\tatlcrack.h: Added handlers for following new\n\t\tmessages:<br>\n\t\t&nbsp;&nbsp;&nbsp; WM_APPCOMMAND<br>\n\t\t&nbsp;&nbsp;&nbsp; WM_NCXBUTTONDOWN<br>\n\t\t&nbsp;&nbsp;&nbsp; WM_NCXBUTTONUP<br>\n\t\t&nbsp;&nbsp;&nbsp; WM_NCXBUTTONDBLCLK<br>\n\t\t&nbsp;&nbsp;&nbsp; WM_XBUTTONDOWN<br>\n\t\t&nbsp;&nbsp;&nbsp; WM_XBUTTONUP<br>\n\t\t&nbsp;&nbsp;&nbsp; WM_XBUTTONDBLCLK\n\t</p>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>Win64:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>Dialog return value should use DWLP_MSGRESULT and SetWindowLongPtr</li>\n\t\t<li>CMenu::InsertMenu, AppendMenu, ModifyMenu should have UINT_PTR for the menu ID</li>\n\t\t<li>Added appropriate type casts</li>\n\t\t<li>CFrameWindowImpl::m_szAutoName - changed the size to fit the pointer value size</li>\n\t\t<li>CListViewCtrl::SortItems should use LPARAM for user data instead of DWORD</li>\n\t</ul>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>Misc:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>Added optional mask argument to all methods for setting extended styles</li>\n\t\t<li>CMDIWindow::MDIRestore - fixed to send WM_MDIRESTORE instead of WM_MDIICONARRANGE</li>\n\t\t<li>CListViewCtrl: Added SortItemsEx method</li>\n\t\t<li>CToolBarCtrl::GetButtonInfo - fixed to return int instead of BOOL</li>\n\t\t<li>Added CToolBarCtrl::SetButtonSize and SetBitmapSize that accept cx and cy instead of SIZE</li>\n\t\t<li>Printing: Changed how GetNewDevModeForPage works (comments in code)</li>\n\t\t<li>CFileDialogImpl::_OnTypeChange incorrectly calls pT-&gt;OnSelChange instead of pT-&gt;OnTypeChange</li>\n\t\t<li>CMultiPaneStatusBarCtrl::GetPaneTipText - fixed to use index instead of and ID internally</li>\n\t\t<li>CWinDataExchange: Added references to arguments of DoDataExchange, so there are no level 4 warning \n\t\t\teven if the map is empty</li>\n\t\t<li>CPropertySheetWindow: Added new, IE 5.0 specific methods</li>\n\t\t<li>CPropertyPageImpl: Added new, IE 5.0 specific methods</li>\n\t</ul>\n\t<p style=margin:0px><br></p>\n\t<p style=margin:0px>AppWizard:</p>\n\t<ul style='margin-top:0px;margin-bottom:0px'>\n\t\t<li>added calls to RemoveMessageFilter and RemoveIdleHandler in CMainFrame::OnDestroy for COM server projects</li>\n\t\t<li>added scroll bars for HTML view</li>\n\t\t<li>CAppServerModule now handles -embedding as well as -automation</li>\n\t\t<li>corrected code in CMainFrame::OnShowToolBar to correctly identify the toolbar in a rebar</li>\n\t\t<li>dialog based app code now derives from CUpdateUI as public</li>\n\t</ul>\n</blockquote>\n<p style=margin:0px><br></p>\n\n<p style=margin:0px>- end of readme.html -</p>\n\n</body>\n\n</html>\n"
  },
  {
    "path": "Examples/WhisperDesktop/Utils/WTL/atlapp.h",
    "content": "// Windows Template Library - WTL version 10.0\n// Copyright (C) Microsoft Corporation, WTL Team. All rights reserved.\n//\n// This file is a part of the Windows Template Library.\n// The use and distribution terms for this software are covered by the\n// Microsoft Public License (http://opensource.org/licenses/MS-PL)\n// which can be found in the file MS-PL.txt at the root folder.\n\n#ifndef __ATLAPP_H__\n#define __ATLAPP_H__\n\n#pragma once\n\n#ifndef __cplusplus\n\t#error WTL requires C++ compilation (use a .cpp suffix)\n#endif\n\n#ifndef __ATLBASE_H__\n\t#error atlapp.h requires atlbase.h to be included first\n#endif\n\n#ifdef _WIN32_WCE\n\t#error WTL10 doesn't support Windows CE\n#endif\n\n#ifdef _ATL_NO_COMMODULE\n\t#error WTL doesn't support _ATL_NO_COMMODULE\n#endif\n\n#ifdef _ATL_NO_WIN_SUPPORT\n\t#error WTL doesn't support _ATL_NO_WIN_SUPPORT\n#endif\n\n#if (_MSC_VER < 1400)\n\t#error WTL10 requires C++ compiler version 14 (Visual C++ 2005) or higher\n#endif\n\n#if (WINVER < 0x0501)\n\t#error WTL requires WINVER >= 0x0501\n#endif\n\n#if (_WIN32_WINNT < 0x0501)\n\t#error WTL requires _WIN32_WINNT >= 0x0501\n#endif\n\n#if (_WIN32_IE < 0x0600)\n\t#error WTL requires _WIN32_IE >= 0x0600\n#endif\n\n#if (_ATL_VER < 0x0800)\n\t#error WTL10 requires ATL version 8 or higher\n#endif\n\n#ifdef _ATL_MIN_CRT\n\t#error WTL10 doesn't support _ATL_MIN_CRT\n#endif\n\n#ifdef _ATL_NO_MSIMG\n\t#error WTL10 doesn't support _ATL_NO_MSIMG\n#endif\n\n#include <limits.h>\n#ifdef _MT\n  #include <process.h>\t// for _beginthreadex\n#endif\n\n#include <commctrl.h>\n#pragma comment(lib, \"comctl32.lib\")\n\n#include <commdlg.h>\n#include <shellapi.h>\n\n// Check for VS2005 without newer WinSDK\n#if (_MSC_VER == 1400) && !defined(RB_GETEXTENDEDSTYLE)\n\t#error WTL10 requires WinSDK 6.0 ot higher\n#endif\n\n#include <uxtheme.h>\n#pragma comment(lib, \"uxtheme.lib\")\n\n#if defined(_SYSINFOAPI_H_) && defined(NOT_BUILD_WINDOWS_DEPRECATE)\n  #include <VersionHelpers.h>\n#endif\n\n#include \"atlres.h\"\n\n\n///////////////////////////////////////////////////////////////////////////////\n// WTL version number\n\n#define _WTL_VER\t0x1000   // version 10.0\n\n\n///////////////////////////////////////////////////////////////////////////////\n// Classes in this file:\n//\n// CMessageFilter\n// CIdleHandler\n// CMessageLoop\n//\n// CAppModule\n// CServerAppModule\n//\n// Global functions:\n//   AtlInitCommonControls()\n//   AtlGetDefaultGuiFont()\n//   AtlCreateControlFont()\n//   AtlCreateBoldFont()\n//   AtlGetStringPtr()\n\n\n///////////////////////////////////////////////////////////////////////////////\n// Miscellaneous global support\n\n// define useful macros from winuser.h\n#ifndef IS_INTRESOURCE\n  #define IS_INTRESOURCE(_r) (((ULONG_PTR)(_r) >> 16) == 0)\n#endif // IS_INTRESOURCE\n\n// protect template members from windowsx.h macros\n#ifdef _INC_WINDOWSX\n  #undef SubclassWindow\n#endif // _INC_WINDOWSX\n\n// define useful macros from windowsx.h\n#ifndef GET_X_LPARAM\n  #define GET_X_LPARAM(lParam)\t((int)(short)LOWORD(lParam))\n#endif\n#ifndef GET_Y_LPARAM\n  #define GET_Y_LPARAM(lParam)\t((int)(short)HIWORD(lParam))\n#endif\n\n// Dummy structs for compiling with /CLR\n#ifdef _MANAGED\n  __if_not_exists(_IMAGELIST::_IMAGELIST) { struct _IMAGELIST { }; }\n  __if_not_exists(_TREEITEM::_TREEITEM) { struct _TREEITEM { }; }\n  __if_not_exists(_PSP::_PSP) { struct _PSP { }; }\n#endif\n\n// Forward declaration for ATL11 fix\n#if (_ATL_VER >= 0x0B00)\n  namespace ATL { HRESULT AtlGetCommCtrlVersion(LPDWORD pdwMajor, LPDWORD pdwMinor); }\n#endif\n\n#ifndef WM_MOUSEHWHEEL\n  #define WM_MOUSEHWHEEL                  0x020E\n#endif\n\n// Used for stack allocations with ATL::CTempBuffer\n#ifndef _WTL_STACK_ALLOC_THRESHOLD\n  #define _WTL_STACK_ALLOC_THRESHOLD   512\n#endif\n\n\nnamespace WTL\n{\n\nDECLARE_TRACE_CATEGORY(atlTraceUI)\n#ifdef _DEBUG\n  __declspec(selectany) ATL::CTraceCategory atlTraceUI(_T(\"atlTraceUI\"));\n#endif // _DEBUG\n\n// Common Controls initialization helper\ninline BOOL AtlInitCommonControls(DWORD dwFlags)\n{\n\tINITCOMMONCONTROLSEX iccx = { sizeof(INITCOMMONCONTROLSEX), dwFlags };\n\tBOOL bRet = ::InitCommonControlsEx(&iccx);\n\tATLASSERT(bRet);\n\treturn bRet;\n}\n\n// Default GUI font helper - \"MS Shell Dlg\" stock font\ninline HFONT AtlGetDefaultGuiFont()\n{\n\treturn (HFONT)::GetStockObject(DEFAULT_GUI_FONT);\n}\n\n// Control font helper - default font for controls not in a dialog\n// (NOTE: Caller owns the font, and should destroy it when it's no longer needed)\ninline HFONT AtlCreateControlFont()\n{\n\tLOGFONT lf = {};\n\tATLVERIFY(::SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(LOGFONT), &lf, 0) != FALSE);\n\tHFONT hFont = ::CreateFontIndirect(&lf);\n\tATLASSERT(hFont != NULL);\n\treturn hFont;\n}\n\n// Bold font helper\n// (NOTE: Caller owns the font, and should destroy it when it's no longer needed)\ninline HFONT AtlCreateBoldFont(HFONT hFont = NULL)\n{\n\tLOGFONT lf = {};\n\tif(hFont == NULL)\n\t\tATLVERIFY(::SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(LOGFONT), &lf, 0) != FALSE);\n\telse\n\t\tATLVERIFY(::GetObject(hFont, sizeof(LOGFONT), &lf) == sizeof(LOGFONT));\n\tlf.lfWeight = FW_BOLD;\n\tHFONT hFontBold =  ::CreateFontIndirect(&lf);\n\tATLASSERT(hFontBold != NULL);\n\treturn hFontBold;\n}\n\n// Resource string pointer\ninline LPCWSTR AtlGetStringPtr(UINT uID, int* pch = NULL)\n{\n\tLPCWSTR lpstr = NULL;\n\tint nRet = ::LoadStringW(ATL::_AtlBaseModule.GetResourceInstance(), uID, (LPWSTR)&lpstr, 0);\n\tif(pch != NULL)\n\t\t*pch = nRet;\n\treturn lpstr;\n}\n\n\n///////////////////////////////////////////////////////////////////////////////\n// RunTimeHelper - helper functions for Windows version and structure sizes\n\n#ifndef _WTL_NO_RUNTIME_STRUCT_SIZE\n\n#ifndef _SIZEOF_STRUCT\n  #define _SIZEOF_STRUCT(structname, member)  (((int)((LPBYTE)(&((structname*)0)->member) - ((LPBYTE)((structname*)0)))) + sizeof(((structname*)0)->member))\n#endif\n\n#if (_WIN32_WINNT >= 0x0600) && !defined(REBARBANDINFO_V6_SIZE)\n  #define REBARBANDINFO_V6_SIZE   _SIZEOF_STRUCT(REBARBANDINFO, cxHeader)\n#endif // (_WIN32_WINNT >= 0x0600) && !defined(REBARBANDINFO_V6_SIZE)\n\n#if (_WIN32_WINNT >= 0x0600) && !defined(LVGROUP_V5_SIZE)\n  #define LVGROUP_V5_SIZE   _SIZEOF_STRUCT(LVGROUP, uAlign)\n#endif // (_WIN32_WINNT >= 0x0600) && !defined(LVGROUP_V5_SIZE)\n\n#if (_WIN32_WINNT >= 0x0600) && !defined(LVTILEINFO_V5_SIZE)\n  #define LVTILEINFO_V5_SIZE   _SIZEOF_STRUCT(LVTILEINFO, puColumns)\n#endif // (_WIN32_WINNT >= 0x0600) && !defined(LVTILEINFO_V5_SIZE)\n\n#if defined(NTDDI_VERSION) && (NTDDI_VERSION >= NTDDI_LONGHORN) && !defined(MCHITTESTINFO_V1_SIZE)\n  #define MCHITTESTINFO_V1_SIZE   _SIZEOF_STRUCT(MCHITTESTINFO, st)\n#endif // defined(NTDDI_VERSION) && (NTDDI_VERSION >= NTDDI_LONGHORN) && !defined(MCHITTESTINFO_V1_SIZE)\n\n#if (WINVER >= 0x0600) && !defined(NONCLIENTMETRICS_V1_SIZE)\n  #define NONCLIENTMETRICS_V1_SIZE   _SIZEOF_STRUCT(NONCLIENTMETRICS, lfMessageFont)\n#endif // (WINVER >= 0x0600) && !defined(NONCLIENTMETRICS_V1_SIZE)\n\n#ifndef TTTOOLINFO_V2_SIZE\n  #define TTTOOLINFO_V2_SIZE   _SIZEOF_STRUCT(TTTOOLINFO, lParam)\n#endif\n\n#endif // !_WTL_NO_RUNTIME_STRUCT_SIZE\n\nnamespace RunTimeHelper\n{\n\tinline bool IsCommCtrl6()\n\t{\n\t\tDWORD dwMajor = 0, dwMinor = 0;\n\t\tHRESULT hRet = ATL::AtlGetCommCtrlVersion(&dwMajor, &dwMinor);\n\t\treturn (SUCCEEDED(hRet) && (dwMajor >= 6));\n\t}\n\n\tinline bool IsVista()\n\t{\n#ifdef _versionhelpers_H_INCLUDED_\n\t\treturn ::IsWindowsVistaOrGreater();\n#else // !_versionhelpers_H_INCLUDED_\n\t\tOSVERSIONINFO ovi = { sizeof(OSVERSIONINFO) };\n\t\tBOOL bRet = ::GetVersionEx(&ovi);\n\t\treturn ((bRet != FALSE) && (ovi.dwMajorVersion >= 6));\n#endif // _versionhelpers_H_INCLUDED_\n\t}\n\n\tinline bool IsThemeAvailable()\n\t{\n\t\treturn IsCommCtrl6() && (::IsThemeActive() != FALSE) && (::IsAppThemed() != FALSE);\n\t}\n\n\tinline bool IsWin7()\n\t{\n#ifdef _versionhelpers_H_INCLUDED_\n\t\treturn ::IsWindows7OrGreater();\n#else // !_versionhelpers_H_INCLUDED_\n\t\tOSVERSIONINFO ovi = { sizeof(OSVERSIONINFO) };\n\t\tBOOL bRet = ::GetVersionEx(&ovi);\n\t\treturn ((bRet != FALSE) && ((ovi.dwMajorVersion > 6) || ((ovi.dwMajorVersion == 6) && (ovi.dwMinorVersion >= 1))));\n#endif // _versionhelpers_H_INCLUDED_\n\t}\n\n\tinline bool IsRibbonUIAvailable()\n\t{\n\t\tstatic INT iRibbonUI = -1;\n\n#if defined(NTDDI_WIN7) && (NTDDI_VERSION >= NTDDI_WIN7)\n\t\tif (iRibbonUI == -1)\n\t\t{\n\t\t\tHMODULE hRibbonDLL = ::LoadLibrary(_T(\"propsys.dll\"));\n\t\t\tif (hRibbonDLL != NULL)\n\t\t\t{\n\t\t\t\tconst GUID CLSID_UIRibbonFramework = { 0x926749fa, 0x2615, 0x4987, { 0x88, 0x45, 0xc3, 0x3e, 0x65, 0xf2, 0xb9, 0x57 } };\n\t\t\t\t// block - create instance\n\t\t\t\t{\n\t\t\t\t\tATL::CComPtr<IUnknown> pIUIFramework;\n\t\t\t\t\tiRibbonUI = SUCCEEDED(pIUIFramework.CoCreateInstance(CLSID_UIRibbonFramework)) ? 1 : 0;\n\t\t\t\t}\n\t\t\t\t::FreeLibrary(hRibbonDLL);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tiRibbonUI = 0;\n\t\t\t}\n\t\t}\n#endif // defined(NTDDI_WIN7) && (NTDDI_VERSION >= NTDDI_WIN7)\n\n\t\treturn (iRibbonUI == 1);\n\t}\n\n\tinline UINT SizeOf_REBARBANDINFO()\n\t{\n\t\tUINT uSize = sizeof(REBARBANDINFO);\n#if !defined(_WTL_NO_RUNTIME_STRUCT_SIZE) && (_WIN32_WINNT >= 0x0600)\n\t\tif(!(IsVista() && IsCommCtrl6()))\n\t\t\tuSize = REBARBANDINFO_V6_SIZE;\n#endif // !defined(_WTL_NO_RUNTIME_STRUCT_SIZE) && (_WIN32_WINNT >= 0x0600)\n\t\treturn uSize;\n\t}\n\n  \tinline UINT SizeOf_LVGROUP()\n\t{\n\t\tUINT uSize = sizeof(LVGROUP);\n#if !defined(_WTL_NO_RUNTIME_STRUCT_SIZE) && (_WIN32_WINNT >= 0x0600)\n\t\tif(!IsVista())\n\t\t\tuSize = LVGROUP_V5_SIZE;\n#endif // !defined(_WTL_NO_RUNTIME_STRUCT_SIZE) && (_WIN32_WINNT >= 0x0600)\n\t\treturn uSize;\n\t}\n\n\tinline UINT SizeOf_LVTILEINFO()\n\t{\n\t\tUINT uSize = sizeof(LVTILEINFO);\n#if !defined(_WTL_NO_RUNTIME_STRUCT_SIZE) && (_WIN32_WINNT >= 0x0600)\n\t\tif(!IsVista())\n\t\t\tuSize = LVTILEINFO_V5_SIZE;\n#endif // !defined(_WTL_NO_RUNTIME_STRUCT_SIZE) && (_WIN32_WINNT >= 0x0600)\n\t\treturn uSize;\n\t}\n\n\tinline UINT SizeOf_MCHITTESTINFO()\n\t{\n\t\tUINT uSize = sizeof(MCHITTESTINFO);\n#if !defined(_WTL_NO_RUNTIME_STRUCT_SIZE) && defined(NTDDI_VERSION) && (NTDDI_VERSION >= NTDDI_LONGHORN)\n\t\tif(!(IsVista() && IsCommCtrl6()))\n\t\t\tuSize = MCHITTESTINFO_V1_SIZE;\n#endif // !defined(_WTL_NO_RUNTIME_STRUCT_SIZE) && defined(NTDDI_VERSION) && (NTDDI_VERSION >= NTDDI_LONGHORN)\n\t\treturn uSize;\n\t}\n\n\tinline UINT SizeOf_NONCLIENTMETRICS()\n\t{\n\t\tUINT uSize = sizeof(NONCLIENTMETRICS);\n#if !defined(_WTL_NO_RUNTIME_STRUCT_SIZE) && (WINVER >= 0x0600)\n\t\tif(!IsVista())\n\t\t\tuSize = NONCLIENTMETRICS_V1_SIZE;\n#endif // !defined(_WTL_NO_RUNTIME_STRUCT_SIZE) && (WINVER >= 0x0600)\n\t\treturn uSize;\n\t}\n\n\tinline UINT SizeOf_TOOLINFO()\n\t{\n\t\tUINT uSize = sizeof(TOOLINFO);\n#ifndef _WTL_NO_RUNTIME_STRUCT_SIZE\n\t\tif(!IsVista())\n\t\t\tuSize = TTTOOLINFO_V2_SIZE;\n#endif\n\t\treturn uSize;\n\t}\n} // namespace RunTimeHelper\n\n\n///////////////////////////////////////////////////////////////////////////////\n// ModuleHelper - helper functions for ATL (deprecated)\n\nnamespace ModuleHelper\n{\n\tinline HINSTANCE GetModuleInstance()\n\t{\n\t\treturn ATL::_AtlBaseModule.GetModuleInstance();\n\t}\n\n\tinline HINSTANCE GetResourceInstance()\n\t{\n\t\treturn ATL::_AtlBaseModule.GetResourceInstance();\n\t}\n\n\tinline void AddCreateWndData(ATL::_AtlCreateWndData* pData, void* pObject)\n\t{\n\t\tATL::_AtlWinModule.AddCreateWndData(pData, pObject);\n\t}\n\n\tinline void* ExtractCreateWndData()\n\t{\n\t\treturn ATL::_AtlWinModule.ExtractCreateWndData();\n\t}\n} // namespace ModuleHelper\n\n\n///////////////////////////////////////////////////////////////////////////////\n// SecureHelper - WTL10 requires use of secure functions\n// these are here only for compatibility with existing projects\n\nnamespace SecureHelper\n{\n\tinline void strcpyA_x(char* lpstrDest, size_t cchDest, const char* lpstrSrc)\n\t{\n\t\tATL::Checked::strcpy_s(lpstrDest, cchDest, lpstrSrc);\n\t}\n\n\tinline void strcpyW_x(wchar_t* lpstrDest, size_t cchDest, const wchar_t* lpstrSrc)\n\t{\n\t\tATL::Checked::wcscpy_s(lpstrDest, cchDest, lpstrSrc);\n\t}\n\n\tinline void strcpy_x(LPTSTR lpstrDest, size_t cchDest, LPCTSTR lpstrSrc)\n\t{\n#ifdef _UNICODE\n\t\tstrcpyW_x(lpstrDest, cchDest, lpstrSrc);\n#else\n\t\tstrcpyA_x(lpstrDest, cchDest, lpstrSrc);\n#endif\n\t}\n\n\tinline errno_t strncpyA_x(char* lpstrDest, size_t cchDest, const char* lpstrSrc, size_t cchCount)\n\t{\n\t\treturn ATL::Checked::strncpy_s(lpstrDest, cchDest, lpstrSrc, cchCount);\n\t}\n\n\tinline errno_t strncpyW_x(wchar_t* lpstrDest, size_t cchDest, const wchar_t* lpstrSrc, size_t cchCount)\n\t{\n\t\treturn ATL::Checked::wcsncpy_s(lpstrDest, cchDest, lpstrSrc, cchCount);\n\t}\n\n\tinline errno_t strncpy_x(LPTSTR lpstrDest, size_t cchDest, LPCTSTR lpstrSrc, size_t cchCount)\n\t{\n#ifdef _UNICODE\n\t\treturn strncpyW_x(lpstrDest, cchDest, lpstrSrc, cchCount);\n#else\n\t\treturn strncpyA_x(lpstrDest, cchDest, lpstrSrc, cchCount);\n#endif\n\t}\n\n\tinline void strcatA_x(char* lpstrDest, size_t cchDest, const char* lpstrSrc)\n\t{\n\t\tATL::Checked::strcat_s(lpstrDest, cchDest, lpstrSrc);\n\t}\n\n\tinline void strcatW_x(wchar_t* lpstrDest, size_t cchDest, const wchar_t* lpstrSrc)\n\t{\n\t\tATL::Checked::wcscat_s(lpstrDest, cchDest, lpstrSrc);\n\t}\n\n\tinline void strcat_x(LPTSTR lpstrDest, size_t cchDest, LPCTSTR lpstrSrc)\n\t{\n#ifdef _UNICODE\n\t\tstrcatW_x(lpstrDest, cchDest, lpstrSrc);\n#else\n\t\tstrcatA_x(lpstrDest, cchDest, lpstrSrc);\n#endif\n\t}\n\n\tinline void memcpy_x(void* pDest, size_t cbDest, const void* pSrc, size_t cbSrc)\n\t{\n\t\tATL::Checked::memcpy_s(pDest, cbDest, pSrc, cbSrc);\n\t}\n\n\tinline void memmove_x(void* pDest, size_t cbDest, const void* pSrc, size_t cbSrc)\n\t{\n\t\tATL::Checked::memmove_s(pDest, cbDest, pSrc, cbSrc);\n\t}\n\n\tinline int vsprintf_x(LPTSTR lpstrBuff, size_t cchBuff, LPCTSTR lpstrFormat, va_list args)\n\t{\n\t\treturn _vstprintf_s(lpstrBuff, cchBuff, lpstrFormat, args);\n\t}\n\n\tinline int wvsprintf_x(LPTSTR lpstrBuff, size_t cchBuff, LPCTSTR lpstrFormat, va_list args)\n\t{\n\t\treturn _vstprintf_s(lpstrBuff, cchBuff, lpstrFormat, args);\n\t}\n\n\tinline int sprintf_x(LPTSTR lpstrBuff, size_t cchBuff, LPCTSTR lpstrFormat, ...)\n\t{\n\t\tva_list args;\n\t\tva_start(args, lpstrFormat);\n\t\tint nRes = vsprintf_x(lpstrBuff, cchBuff, lpstrFormat, args);\n\t\tva_end(args);\n\t\treturn nRes;\n\t}\n\n\tinline int wsprintf_x(LPTSTR lpstrBuff, size_t cchBuff, LPCTSTR lpstrFormat, ...)\n\t{\n\t\tva_list args;\n\t\tva_start(args, lpstrFormat);\n\t\tint nRes = wvsprintf_x(lpstrBuff, cchBuff, lpstrFormat, args);\n\t\tva_end(args);\n\t\treturn nRes;\n\t}\n} // namespace SecureHelper\n\n\n///////////////////////////////////////////////////////////////////////////////\n// MinCrtHelper - WTL10 doesn't support _ATL_MIN_CRT,\n// these are here only for compatibility with existing projects\n\nnamespace MinCrtHelper\n{\n\tinline int _isspace(TCHAR ch)\n\t{\n\t\treturn _istspace(ch);\n\t}\n\n\tinline int _isdigit(TCHAR ch)\n\t{\n\t\treturn _istdigit(ch);\n\t}\n\n\tinline int _atoi(LPCTSTR str)\n\t{\n\t\treturn _ttoi(str);\n\t}\n\n\tinline LPCTSTR _strrchr(LPCTSTR str, TCHAR ch)\n\t{\n\t\treturn _tcsrchr(str, ch);\n\t}\n\n\tinline LPTSTR _strrchr(LPTSTR str, TCHAR ch)\n\t{\n\t\treturn _tcsrchr(str, ch);\n\t}\n} // namespace MinCrtHelper\n\n\n///////////////////////////////////////////////////////////////////////////////\n// GenericWndClass - generic window class usable for subclassing\n\n// Use in dialog templates to specify a placeholder to be subclassed\n// Specify as a custom control with class name WTL_GenericWindow\n// Call Rregister() before creating dialog (for example, in WinMain)\nnamespace GenericWndClass\n{\n\tinline LPCTSTR GetName()\n\t{\n\t\treturn _T(\"WTL_GenericWindow\");\n\t}\n\n\tinline ATOM Register()\n\t{\n\t\tWNDCLASSEX wc = { sizeof(WNDCLASSEX) };\n\t\twc.lpfnWndProc = ::DefWindowProc;\n\t\twc.hInstance = ModuleHelper::GetModuleInstance();\n\t\twc.hCursor = ::LoadCursor(NULL, IDC_ARROW);\n\t\twc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);\n\t\twc.lpszClassName = GetName();\n\t\tATOM atom = ::RegisterClassEx(&wc);\n\t\tATLASSERT(atom != 0);\n\t\treturn atom;\n\t}\n\n\tinline BOOL Unregister()   // only needed for DLLs or tmp use\n\t{\n\t\treturn ::UnregisterClass(GetName(), ModuleHelper::GetModuleInstance());\n\t}\n} // namespace GenericWndClass\n\n\n///////////////////////////////////////////////////////////////////////////////\n// CMessageFilter - Interface for message filter support\n\nclass ATL_NO_VTABLE CMessageFilter\n{\npublic:\n\tvirtual BOOL PreTranslateMessage(MSG* pMsg) = 0;\n};\n\n\n///////////////////////////////////////////////////////////////////////////////\n// CIdleHandler - Interface for idle processing\n\nclass ATL_NO_VTABLE CIdleHandler\n{\npublic:\n\tvirtual BOOL OnIdle() = 0;\n};\n\n\n///////////////////////////////////////////////////////////////////////////////\n// CMessageLoop - message loop implementation\n\nclass CMessageLoop\n{\npublic:\n\tATL::CSimpleArray<CMessageFilter*> m_aMsgFilter;\n\tATL::CSimpleArray<CIdleHandler*> m_aIdleHandler;\n\tMSG m_msg;\n\n\tCMessageLoop()\n\t{\n\t\tmemset(&m_msg, 0, sizeof(m_msg));\n\t}\n\n\tvirtual ~CMessageLoop()\n\t{ }\n\n// Message filter operations\n\tBOOL AddMessageFilter(CMessageFilter* pMessageFilter)\n\t{\n\t\treturn m_aMsgFilter.Add(pMessageFilter);\n\t}\n\n\tBOOL RemoveMessageFilter(CMessageFilter* pMessageFilter)\n\t{\n\t\treturn m_aMsgFilter.Remove(pMessageFilter);\n\t}\n\n// Idle handler operations\n\tBOOL AddIdleHandler(CIdleHandler* pIdleHandler)\n\t{\n\t\treturn m_aIdleHandler.Add(pIdleHandler);\n\t}\n\n\tBOOL RemoveIdleHandler(CIdleHandler* pIdleHandler)\n\t{\n\t\treturn m_aIdleHandler.Remove(pIdleHandler);\n\t}\n\n// message loop\n\tint Run()\n\t{\n\t\tBOOL bDoIdle = TRUE;\n\t\tint nIdleCount = 0;\n\t\tBOOL bRet = FALSE;\n\n\t\tfor(;;)\n\t\t{\n\t\t\twhile(bDoIdle && !::PeekMessage(&m_msg, NULL, 0, 0, PM_NOREMOVE))\n\t\t\t{\n\t\t\t\tif(!OnIdle(nIdleCount++))\n\t\t\t\t\tbDoIdle = FALSE;\n\t\t\t}\n\n\t\t\tbRet = ::GetMessage(&m_msg, NULL, 0, 0);\n\n\t\t\tif(bRet == -1)\n\t\t\t{\n\t\t\t\tATLTRACE2(atlTraceUI, 0, _T(\"::GetMessage returned -1 (error)\\n\"));\n\t\t\t\tcontinue;   // error, don't process\n\t\t\t}\n\t\t\telse if(!bRet)\n\t\t\t{\n\t\t\t\tATLTRACE2(atlTraceUI, 0, _T(\"CMessageLoop::Run - exiting\\n\"));\n\t\t\t\tbreak;   // WM_QUIT, exit message loop\n\t\t\t}\n\n\t\t\tif(!PreTranslateMessage(&m_msg))\n\t\t\t{\n\t\t\t\t::TranslateMessage(&m_msg);\n\t\t\t\t::DispatchMessage(&m_msg);\n\t\t\t}\n\n\t\t\tif(IsIdleMessage(&m_msg))\n\t\t\t{\n\t\t\t\tbDoIdle = TRUE;\n\t\t\t\tnIdleCount = 0;\n\t\t\t}\n\t\t}\n\n\t\treturn (int)m_msg.wParam;\n\t}\n\n// Overrideables\n\t// Override to change message filtering\n\tvirtual BOOL PreTranslateMessage(MSG* pMsg)\n\t{\n\t\t// loop backwards\n\t\tfor(int i = m_aMsgFilter.GetSize() - 1; i >= 0; i--)\n\t\t{\n\t\t\tCMessageFilter* pMessageFilter = m_aMsgFilter[i];\n\t\t\tif((pMessageFilter != NULL) && pMessageFilter->PreTranslateMessage(pMsg))\n\t\t\t\treturn TRUE;\n\t\t}\n\t\treturn FALSE;   // not translated\n\t}\n\n\t// override to change idle processing\n\tvirtual BOOL OnIdle(int /*nIdleCount*/)\n\t{\n\t\tfor(int i = 0; i < m_aIdleHandler.GetSize(); i++)\n\t\t{\n\t\t\tCIdleHandler* pIdleHandler = m_aIdleHandler[i];\n\t\t\tif(pIdleHandler != NULL)\n\t\t\t\tpIdleHandler->OnIdle();\n\t\t}\n\t\treturn FALSE;   // don't continue\n\t}\n\n\t// override to change non-idle messages\n\tvirtual BOOL IsIdleMessage(MSG* pMsg) const\n\t{\n\t\t// These messages should NOT cause idle processing\n\t\tswitch(pMsg->message)\n\t\t{\n\t\tcase WM_MOUSEMOVE:\n\t\tcase WM_NCMOUSEMOVE:\n\t\tcase WM_PAINT:\n\t\tcase 0x0118:\t// WM_SYSTIMER (caret blink)\n\t\t\treturn FALSE;\n\t\t}\n\n\t\treturn TRUE;\n\t}\n};\n\n\n///////////////////////////////////////////////////////////////////////////////\n// CStaticDataInitCriticalSectionLock and CWindowCreateCriticalSectionLock\n// internal classes to manage critical sections for ATL (deprecated)\n\nclass CStaticDataInitCriticalSectionLock\n{\npublic:\n\tATL::CComCritSecLock<ATL::CComCriticalSection> m_cslock;\n\n\tCStaticDataInitCriticalSectionLock() : m_cslock(ATL::_pAtlModule->m_csStaticDataInitAndTypeInfo, false)\n\t{ }\n\n\tHRESULT Lock()\n\t{\n\t\treturn m_cslock.Lock();\n\t}\n\n\tvoid Unlock()\n\t{\n\t\tm_cslock.Unlock();\n\t}\n};\n\n\nclass CWindowCreateCriticalSectionLock\n{\npublic:\n\tATL::CComCritSecLock<ATL::CComCriticalSection> m_cslock;\n\n\tCWindowCreateCriticalSectionLock() : m_cslock(ATL::_AtlWinModule.m_csWindowCreate, false)\n\t{ }\n\n\tHRESULT Lock()\n\t{\n\t\treturn m_cslock.Lock();\n\t}\n\n\tvoid Unlock()\n\t{\n\t\tm_cslock.Unlock();\n\t}\n};\n\n\n///////////////////////////////////////////////////////////////////////////////\n// CAppModule - module class for an application\n\n#if (_MSC_VER == 1400)   // VS2005\n  #pragma warning(push)\n  #pragma warning(disable : 4244)\n  #pragma warning(disable : 4312)\n#endif\n\nclass CAppModule : public ATL::CComModule\n{\npublic:\n\tDWORD m_dwMainThreadID;\n\tATL::CSimpleMap<DWORD, CMessageLoop*>* m_pMsgLoopMap;\n\tATL::CSimpleArray<HWND>* m_pSettingChangeNotify;\n\n\tCAppModule() : m_dwMainThreadID(0), m_pMsgLoopMap(NULL), m_pSettingChangeNotify(NULL)\n\t{ }\n\n// Overrides of CComModule::Init and Term\n\tHRESULT Init(ATL::_ATL_OBJMAP_ENTRY* pObjMap, HINSTANCE hInstance, const GUID* pLibID = NULL)\n\t{\n\t\tHRESULT hRet = CComModule::Init(pObjMap, hInstance, pLibID);\n\t\tif(FAILED(hRet))\n\t\t\treturn hRet;\n\n\t\tm_dwMainThreadID = ::GetCurrentThreadId();\n\t\ttypedef ATL::CSimpleMap<DWORD, CMessageLoop*>   _mapClass;\n\t\tm_pMsgLoopMap = NULL;\n\t\tATLTRY(m_pMsgLoopMap = new _mapClass);\n\t\tif(m_pMsgLoopMap == NULL)\n\t\t\treturn E_OUTOFMEMORY;\n\t\tm_pSettingChangeNotify = NULL;\n\n\t\treturn hRet;\n\t}\n\n\tvoid Term()\n\t{\n\t\tTermSettingChangeNotify();\n\t\tdelete m_pMsgLoopMap;\n\t\tCComModule::Term();\n\t}\n\n// Message loop map methods\n\tBOOL AddMessageLoop(CMessageLoop* pMsgLoop)\n\t{\n\t\tCStaticDataInitCriticalSectionLock lock;\n\t\tif(FAILED(lock.Lock()))\n\t\t{\n\t\t\tATLTRACE2(atlTraceUI, 0, _T(\"ERROR : Unable to lock critical section in CAppModule::AddMessageLoop.\\n\"));\n\t\t\tATLASSERT(FALSE);\n\t\t\treturn FALSE;\n\t\t}\n\n\t\tATLASSERT(pMsgLoop != NULL);\n\t\tATLASSERT(m_pMsgLoopMap->Lookup(::GetCurrentThreadId()) == NULL);   // not in map yet\n\n\t\tBOOL bRet = m_pMsgLoopMap->Add(::GetCurrentThreadId(), pMsgLoop);\n\n\t\tlock.Unlock();\n\n\t\treturn bRet;\n\t}\n\n\tBOOL RemoveMessageLoop()\n\t{\n\t\tCStaticDataInitCriticalSectionLock lock;\n\t\tif(FAILED(lock.Lock()))\n\t\t{\n\t\t\tATLTRACE2(atlTraceUI, 0, _T(\"ERROR : Unable to lock critical section in CAppModule::RemoveMessageLoop.\\n\"));\n\t\t\tATLASSERT(FALSE);\n\t\t\treturn FALSE;\n\t\t}\n\n\t\tBOOL bRet = m_pMsgLoopMap->Remove(::GetCurrentThreadId());\n\n\t\tlock.Unlock();\n\n\t\treturn bRet;\n\t}\n\n\tCMessageLoop* GetMessageLoop(DWORD dwThreadID = ::GetCurrentThreadId()) const\n\t{\n\t\tCStaticDataInitCriticalSectionLock lock;\n\t\tif(FAILED(lock.Lock()))\n\t\t{\n\t\t\tATLTRACE2(atlTraceUI, 0, _T(\"ERROR : Unable to lock critical section in CAppModule::GetMessageLoop.\\n\"));\n\t\t\tATLASSERT(FALSE);\n\t\t\treturn NULL;\n\t\t}\n\n\t\tCMessageLoop* pLoop =  m_pMsgLoopMap->Lookup(dwThreadID);\n\n\t\tlock.Unlock();\n\n\t\treturn pLoop;\n\t}\n\n// Setting change notify methods\n\t// Note: Call this from the main thread for MSDI apps\n\tBOOL InitSettingChangeNotify(DLGPROC pfnDlgProc = _SettingChangeDlgProc)\n\t{\n\t\tCStaticDataInitCriticalSectionLock lock;\n\t\tif(FAILED(lock.Lock()))\n\t\t{\n\t\t\tATLTRACE2(atlTraceUI, 0, _T(\"ERROR : Unable to lock critical section in CAppModule::InitSettingChangeNotify.\\n\"));\n\t\t\tATLASSERT(FALSE);\n\t\t\treturn FALSE;\n\t\t}\n\n\t\tif(m_pSettingChangeNotify == NULL)\n\t\t{\n\t\t\ttypedef ATL::CSimpleArray<HWND>   _notifyClass;\n\t\t\tATLTRY(m_pSettingChangeNotify = new _notifyClass);\n\t\t\tATLASSERT(m_pSettingChangeNotify != NULL);\n\t\t}\n\n\t\tBOOL bRet = (m_pSettingChangeNotify != NULL);\n\t\tif(bRet && (m_pSettingChangeNotify->GetSize() == 0))\n\t\t{\n\t\t\t// init everything\n\t\t\t_ATL_EMPTY_DLGTEMPLATE templ;\n\t\t\tHWND hNtfWnd = ::CreateDialogIndirect(GetModuleInstance(), &templ, NULL, pfnDlgProc);\n\t\t\tATLASSERT(::IsWindow(hNtfWnd));\n\t\t\tif(::IsWindow(hNtfWnd))\n\t\t\t{\n\t\t\t\t::SetWindowLongPtr(hNtfWnd, GWLP_USERDATA, (LONG_PTR)this);\n\t\t\t\tbRet = m_pSettingChangeNotify->Add(hNtfWnd);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tbRet = FALSE;\n\t\t\t}\n\t\t}\n\n\t\tlock.Unlock();\n\n\t\treturn bRet;\n\t}\n\n\tvoid TermSettingChangeNotify()\n\t{\n\t\tCStaticDataInitCriticalSectionLock lock;\n\t\tif(FAILED(lock.Lock()))\n\t\t{\n\t\t\tATLTRACE2(atlTraceUI, 0, _T(\"ERROR : Unable to lock critical section in CAppModule::TermSettingChangeNotify.\\n\"));\n\t\t\tATLASSERT(FALSE);\n\t\t\treturn;\n\t\t}\n\n\t\tif((m_pSettingChangeNotify != NULL) && (m_pSettingChangeNotify->GetSize() > 0))\n\t\t\t::DestroyWindow((*m_pSettingChangeNotify)[0]);\n\t\tdelete m_pSettingChangeNotify;\n\t\tm_pSettingChangeNotify = NULL;\n\n\t\tlock.Unlock();\n\t}\n\n\tBOOL AddSettingChangeNotify(HWND hWnd)\n\t{\n\t\tCStaticDataInitCriticalSectionLock lock;\n\t\tif(FAILED(lock.Lock()))\n\t\t{\n\t\t\tATLTRACE2(atlTraceUI, 0, _T(\"ERROR : Unable to lock critical section in CAppModule::AddSettingChangeNotify.\\n\"));\n\t\t\tATLASSERT(FALSE);\n\t\t\treturn FALSE;\n\t\t}\n\n\t\tATLASSERT(::IsWindow(hWnd));\n\t\tBOOL bRet = FALSE;\n\t\tif(InitSettingChangeNotify() != FALSE)\n\t\t\tbRet = m_pSettingChangeNotify->Add(hWnd);\n\n\t\tlock.Unlock();\n\n\t\treturn bRet;\n\t}\n\n\tBOOL RemoveSettingChangeNotify(HWND hWnd)\n\t{\n\t\tCStaticDataInitCriticalSectionLock lock;\n\t\tif(FAILED(lock.Lock()))\n\t\t{\n\t\t\tATLTRACE2(atlTraceUI, 0, _T(\"ERROR : Unable to lock critical section in CAppModule::RemoveSettingChangeNotify.\\n\"));\n\t\t\tATLASSERT(FALSE);\n\t\t\treturn FALSE;\n\t\t}\n\n\t\tBOOL bRet = FALSE;\n\t\tif(m_pSettingChangeNotify != NULL)\n\t\t\tbRet = m_pSettingChangeNotify->Remove(hWnd);\n\n\t\tlock.Unlock();\n\n\t\treturn bRet;\n\t}\n\n// Implementation - setting change notify dialog template and dialog procedure\n\tstruct _ATL_EMPTY_DLGTEMPLATE : DLGTEMPLATE\n\t{\n\t\t_ATL_EMPTY_DLGTEMPLATE()\n\t\t{\n\t\t\tmemset(this, 0, sizeof(_ATL_EMPTY_DLGTEMPLATE));\n\t\t\tstyle = WS_POPUP;\n\t\t}\n\t\tWORD wMenu, wClass, wTitle;\n\t};\n\n\tstatic INT_PTR CALLBACK _SettingChangeDlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)\n\t{\n\t\tif(uMsg == WM_SETTINGCHANGE)\n\t\t{\n\t\t\tCAppModule* pModule = (CAppModule*)::GetWindowLongPtr(hWnd, GWLP_USERDATA);\n\t\t\tATLASSERT(pModule != NULL);\n\t\t\tATLASSERT(pModule->m_pSettingChangeNotify != NULL);\n\t\t\tconst UINT uTimeout = 1500;   // ms\n\t\t\tfor(int i = 1; i < pModule->m_pSettingChangeNotify->GetSize(); i++)\n\t\t\t\t::SendMessageTimeout((*pModule->m_pSettingChangeNotify)[i], uMsg, wParam, lParam, SMTO_ABORTIFHUNG, uTimeout, NULL);\n\n\t\t\treturn TRUE;\n\t\t}\n\n\t\treturn FALSE;\n\t}\n};\n\n#if (_MSC_VER == 1400)   // VS2005\n  #pragma warning(pop)\n#endif\n\n\n///////////////////////////////////////////////////////////////////////////////\n// CServerAppModule - module class for a COM server application\n\nclass CServerAppModule : public CAppModule\n{\npublic:\n\tHANDLE m_hEventShutdown;\n\tbool m_bActivity;\n\tDWORD m_dwTimeOut;\n\tDWORD m_dwPause;\n\n\tCServerAppModule() : m_hEventShutdown(NULL), m_bActivity(false), m_dwTimeOut(5000), m_dwPause(1000)\n\t{ }\n\n// Override of CAppModule::Init\n\tHRESULT Init(ATL::_ATL_OBJMAP_ENTRY* pObjMap, HINSTANCE hInstance, const GUID* pLibID = NULL)\n\t{\n\t\tm_dwTimeOut = 5000;\n\t\tm_dwPause = 1000;\n\t\treturn CAppModule::Init(pObjMap, hInstance, pLibID);\n\t}\n\n\tvoid Term()\n\t{\n\t\tif((m_hEventShutdown != NULL) && ::CloseHandle(m_hEventShutdown))\n\t\t\tm_hEventShutdown = NULL;\n\t\tCAppModule::Term();\n\t}\n\n// COM Server methods\n\tLONG Unlock() throw()\n\t{\n\t\tLONG lRet = CComModule::Unlock();\n\t\tif(lRet == 0)\n\t\t{\n\t\t\tm_bActivity = true;\n\t\t\t::SetEvent(m_hEventShutdown); // tell monitor that we transitioned to zero\n\t\t}\n\t\treturn lRet;\n\t}\n\n\tvoid MonitorShutdown()\n\t{\n\t\tfor(;;)\n\t\t{\n\t\t\t::WaitForSingleObject(m_hEventShutdown, INFINITE);\n\t\t\tDWORD dwWait = 0;\n\t\t\tdo\n\t\t\t{\n\t\t\t\tm_bActivity = false;\n\t\t\t\tdwWait = ::WaitForSingleObject(m_hEventShutdown, m_dwTimeOut);\n\t\t\t}\n\t\t\twhile(dwWait == WAIT_OBJECT_0);\n\t\t\t// timed out\n\t\t\tif(!m_bActivity && (m_nLockCnt == 0)) // if no activity let's really bail\n\t\t\t{\n#if defined(_WIN32_DCOM) && defined(_ATL_FREE_THREADED)\n\t\t\t\t::CoSuspendClassObjects();\n\t\t\t\tif(!m_bActivity && (m_nLockCnt == 0))\n#endif\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\t// This handle should be valid now. If it isn't, \n\t\t// check if _Module.Term was called first (it shouldn't)\n\t\tif(::CloseHandle(m_hEventShutdown))\n\t\t\tm_hEventShutdown = NULL;\n\t\t::PostThreadMessage(m_dwMainThreadID, WM_QUIT, 0, 0);\n\t}\n\n\tbool StartMonitor()\n\t{\n\t\tm_hEventShutdown = ::CreateEvent(NULL, false, false, NULL);\n\t\tif(m_hEventShutdown == NULL)\n\t\t\treturn false;\n\t\tDWORD dwThreadID = 0;\n#ifdef _MT\n\t\tHANDLE hThread = (HANDLE)_beginthreadex(NULL, 0, (UINT (WINAPI*)(void*))MonitorProc, this, 0, (UINT*)&dwThreadID);\n#else\n\t\tHANDLE hThread = ::CreateThread(NULL, 0, MonitorProc, this, 0, &dwThreadID);\n#endif\n\t\tbool bRet = (hThread != NULL);\n\t\tif(bRet)\n\t\t\t::CloseHandle(hThread);\n\t\treturn bRet;\n\t}\n\n\tstatic DWORD WINAPI MonitorProc(void* pv)\n\t{\n\t\tCServerAppModule* p = (CServerAppModule*)pv;\n\t\tp->MonitorShutdown();\n\t\treturn 0;\n\t}\n};\n\n\n///////////////////////////////////////////////////////////////////////////////\n// CRegKeyEx - not used any more, here only for compatibility with old projects\n\ntypedef ATL::CRegKey CRegKeyEx;\n\n} // namespace WTL\n\n\n///////////////////////////////////////////////////////////////////////////////\n// CString forward reference (enables CString use in atluser.h and atlgdi.h)\n\n#if (defined(_WTL_USE_CSTRING) || defined(_WTL_FORWARD_DECLARE_CSTRING)) && !defined(__ATLSTR_H__)\n  #include <atlstr.h>\n#endif\n\n// CString namespace\n#define _CSTRING_NS\tATL\n\n// Type classes namespace\n#define _WTYPES_NS\n\n\n///////////////////////////////////////////////////////////////////////////////\n// General DLL version helpers (removed in ATL11)\n\n#if (_ATL_VER >= 0x0B00)\n\nnamespace ATL\n{\n\ninline HRESULT AtlGetDllVersion(HINSTANCE hInstDLL, DLLVERSIONINFO* pDllVersionInfo)\n{\n\tATLASSERT(pDllVersionInfo != NULL);\n\tif(pDllVersionInfo == NULL)\n\t\treturn E_INVALIDARG;\n\n\t// We must get this function explicitly because some DLLs don't implement it.\n\tDLLGETVERSIONPROC pfnDllGetVersion = (DLLGETVERSIONPROC)::GetProcAddress(hInstDLL, \"DllGetVersion\");\n\tif(pfnDllGetVersion == NULL)\n\t\treturn E_NOTIMPL;\n\n\treturn (*pfnDllGetVersion)(pDllVersionInfo);\n}\n\ninline HRESULT AtlGetDllVersion(LPCTSTR lpstrDllName, DLLVERSIONINFO* pDllVersionInfo)\n{\n\tHINSTANCE hInstDLL = ::LoadLibrary(lpstrDllName);\n\tif(hInstDLL == NULL)\n\t\treturn E_FAIL;\n\tHRESULT hRet = AtlGetDllVersion(hInstDLL, pDllVersionInfo);\n\t::FreeLibrary(hInstDLL);\n\treturn hRet;\n}\n\n// Common Control Versions:\n//   Win95/WinNT 4.0    maj=4 min=00\n//   IE 3.x     maj=4 min=70\n//   IE 4.0     maj=4 min=71\ninline HRESULT AtlGetCommCtrlVersion(LPDWORD pdwMajor, LPDWORD pdwMinor)\n{\n\tATLASSERT((pdwMajor != NULL) && (pdwMinor != NULL));\n\tif((pdwMajor == NULL) || (pdwMinor == NULL))\n\t\treturn E_INVALIDARG;\n\n\tDLLVERSIONINFO dvi;\n\t::ZeroMemory(&dvi, sizeof(dvi));\n\tdvi.cbSize = sizeof(dvi);\n\tHRESULT hRet = AtlGetDllVersion(_T(\"comctl32.dll\"), &dvi);\n\n\tif(SUCCEEDED(hRet))\n\t{\n\t\t*pdwMajor = dvi.dwMajorVersion;\n\t\t*pdwMinor = dvi.dwMinorVersion;\n\t}\n\telse if(hRet == E_NOTIMPL)\n\t{\n\t\t// If DllGetVersion is not there, then the DLL is a version\n\t\t// previous to the one shipped with IE 3.x\n\t\t*pdwMajor = 4;\n\t\t*pdwMinor = 0;\n\t\thRet = S_OK;\n\t}\n\n\treturn hRet;\n}\n\n// Shell Versions:\n//   Win95/WinNT 4.0                    maj=4 min=00\n//   IE 3.x, IE 4.0 without Web Integrated Desktop  maj=4 min=00\n//   IE 4.0 with Web Integrated Desktop         maj=4 min=71\n//   IE 4.01 with Web Integrated Desktop        maj=4 min=72\ninline HRESULT AtlGetShellVersion(LPDWORD pdwMajor, LPDWORD pdwMinor)\n{\n\tATLASSERT((pdwMajor != NULL) && (pdwMinor != NULL));\n\tif((pdwMajor == NULL) || (pdwMinor == NULL))\n\t\treturn E_INVALIDARG;\n\n\tDLLVERSIONINFO dvi;\n\t::ZeroMemory(&dvi, sizeof(dvi));\n\tdvi.cbSize = sizeof(dvi);\n\tHRESULT hRet = AtlGetDllVersion(_T(\"shell32.dll\"), &dvi);\n\n\tif(SUCCEEDED(hRet))\n\t{\n\t\t*pdwMajor = dvi.dwMajorVersion;\n\t\t*pdwMinor = dvi.dwMinorVersion;\n\t}\n\telse if(hRet == E_NOTIMPL)\n\t{\n\t\t// If DllGetVersion is not there, then the DLL is a version\n\t\t// previous to the one shipped with IE 4.x\n\t\t*pdwMajor = 4;\n\t\t*pdwMinor = 0;\n\t\thRet = S_OK;\n\t}\n\n\treturn hRet;\n}\n\n} // namespace ATL\n\n#endif // (_ATL_VER >= 0x0B00)\n\n\n// These are always included\n#include \"atlwinx.h\"\n#include \"atluser.h\"\n#include \"atlgdi.h\"\n\n#ifndef _WTL_NO_AUTOMATIC_NAMESPACE\nusing namespace WTL;\n#endif // !_WTL_NO_AUTOMATIC_NAMESPACE\n\n#endif // __ATLAPP_H__\n"
  },
  {
    "path": "Examples/WhisperDesktop/Utils/WTL/atlcrack.h",
    "content": "// Windows Template Library - WTL version 10.0\n// Copyright (C) Microsoft Corporation, WTL Team. All rights reserved.\n//\n// This file is a part of the Windows Template Library.\n// The use and distribution terms for this software are covered by the\n// Microsoft Public License (http://opensource.org/licenses/MS-PL)\n// which can be found in the file MS-PL.txt at the root folder.\n\n#ifndef __ATLCRACK_H__\n#define __ATLCRACK_H__\n\n#pragma once\n\n#ifndef __ATLAPP_H__\n\t#error atlcrack.h requires atlapp.h to be included first\n#endif\n\n\n///////////////////////////////////////////////////////////////////////////////\n// Message map macro for cracked handlers\n\n// Note about message maps with cracked handlers:\n//   You can use BEGIN_MSG_MAP for classes that derive from CWindowImpl/CDialogImpl,\n//   but must use BEGIN_MSG_MAP_EX for classes that don't.\n\n#define BEGIN_MSG_MAP_EX(theClass) \\\npublic: \\\n\tBOOL m_bMsgHandled; \\\n\t/* \"handled\" management for cracked handlers */ \\\n\tBOOL IsMsgHandled() const \\\n\t{ \\\n\t\treturn m_bMsgHandled; \\\n\t} \\\n\tvoid SetMsgHandled(BOOL bHandled) \\\n\t{ \\\n\t\tm_bMsgHandled = bHandled; \\\n\t} \\\n\tBOOL ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& lResult, DWORD dwMsgMapID = 0) \\\n\t{ \\\n\t\tBOOL bOldMsgHandled = m_bMsgHandled; \\\n\t\tBOOL bRet = _ProcessWindowMessage(hWnd, uMsg, wParam, lParam, lResult, dwMsgMapID); \\\n\t\tm_bMsgHandled = bOldMsgHandled; \\\n\t\treturn bRet; \\\n\t} \\\n\tBOOL _ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& lResult, DWORD dwMsgMapID) \\\n\t{ \\\n\t\tBOOL bHandled = TRUE; \\\n\t\t(hWnd); \\\n\t\t(uMsg); \\\n\t\t(wParam); \\\n\t\t(lParam); \\\n\t\t(lResult); \\\n\t\t(bHandled); \\\n\t\tswitch(dwMsgMapID) \\\n\t\t{ \\\n\t\tcase 0:\n\n\n///////////////////////////////////////////////////////////////////////////////\n// Standard Windows message macros\n\n// int OnCreate(LPCREATESTRUCT lpCreateStruct)\n#define MSG_WM_CREATE(func) \\\n\tif (uMsg == WM_CREATE) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tlResult = (LRESULT)func((LPCREATESTRUCT)lParam); \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// BOOL OnInitDialog(CWindow wndFocus, LPARAM lInitParam)\n#define MSG_WM_INITDIALOG(func) \\\n\tif (uMsg == WM_INITDIALOG) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tlResult = (LRESULT)func((HWND)wParam, lParam); \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// BOOL OnCopyData(CWindow wnd, PCOPYDATASTRUCT pCopyDataStruct)\n#define MSG_WM_COPYDATA(func) \\\n\tif (uMsg == WM_COPYDATA) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tlResult = (LRESULT)func((HWND)wParam, (PCOPYDATASTRUCT)lParam); \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnDestroy()\n#define MSG_WM_DESTROY(func) \\\n\tif (uMsg == WM_DESTROY) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc(); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnMove(CPoint ptPos)\n#define MSG_WM_MOVE(func) \\\n\tif (uMsg == WM_MOVE) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc(::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnSize(UINT nType, CSize size)\n#define MSG_WM_SIZE(func) \\\n\tif (uMsg == WM_SIZE) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((UINT)wParam, ::CSize(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnActivate(UINT nState, BOOL bMinimized, CWindow wndOther)\n#define MSG_WM_ACTIVATE(func) \\\n\tif (uMsg == WM_ACTIVATE) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((UINT)LOWORD(wParam), (BOOL)HIWORD(wParam), (HWND)lParam); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnSetFocus(CWindow wndOld)\n#define MSG_WM_SETFOCUS(func) \\\n\tif (uMsg == WM_SETFOCUS) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((HWND)wParam); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnKillFocus(CWindow wndFocus)\n#define MSG_WM_KILLFOCUS(func) \\\n\tif (uMsg == WM_KILLFOCUS) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((HWND)wParam); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnEnable(BOOL bEnable)\n#define MSG_WM_ENABLE(func) \\\n\tif (uMsg == WM_ENABLE) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((BOOL)wParam); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnPaint(CDCHandle dc)\n#define MSG_WM_PAINT(func) \\\n\tif (uMsg == WM_PAINT) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((HDC)wParam); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnClose()\n#define MSG_WM_CLOSE(func) \\\n\tif (uMsg == WM_CLOSE) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc(); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// BOOL OnQueryEndSession(UINT nSource, UINT uLogOff)\n#define MSG_WM_QUERYENDSESSION(func) \\\n\tif (uMsg == WM_QUERYENDSESSION) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tlResult = (LRESULT)func((UINT)wParam, (UINT)lParam); \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// BOOL OnQueryOpen()\n#define MSG_WM_QUERYOPEN(func) \\\n\tif (uMsg == WM_QUERYOPEN) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tlResult = (LRESULT)func(); \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// BOOL OnEraseBkgnd(CDCHandle dc)\n#define MSG_WM_ERASEBKGND(func) \\\n\tif (uMsg == WM_ERASEBKGND) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tlResult = (LRESULT)func((HDC)wParam); \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnSysColorChange()\n#define MSG_WM_SYSCOLORCHANGE(func) \\\n\tif (uMsg == WM_SYSCOLORCHANGE) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc(); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnEndSession(BOOL bEnding, UINT uLogOff)\n#define MSG_WM_ENDSESSION(func) \\\n\tif (uMsg == WM_ENDSESSION) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((BOOL)wParam, (UINT)lParam); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnShowWindow(BOOL bShow, UINT nStatus)\n#define MSG_WM_SHOWWINDOW(func) \\\n\tif (uMsg == WM_SHOWWINDOW) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((BOOL)wParam, (int)lParam); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// HBRUSH OnCtlColorEdit(CDCHandle dc, CEdit edit)\n#define MSG_WM_CTLCOLOREDIT(func) \\\n\tif (uMsg == WM_CTLCOLOREDIT) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tlResult = (LRESULT)func((HDC)wParam, (HWND)lParam); \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// HBRUSH OnCtlColorListBox(CDCHandle dc, CListBox listBox)\n#define MSG_WM_CTLCOLORLISTBOX(func) \\\n\tif (uMsg == WM_CTLCOLORLISTBOX) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tlResult = (LRESULT)func((HDC)wParam, (HWND)lParam); \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// HBRUSH OnCtlColorBtn(CDCHandle dc, CButton button)\n#define MSG_WM_CTLCOLORBTN(func) \\\n\tif (uMsg == WM_CTLCOLORBTN) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tlResult = (LRESULT)func((HDC)wParam, (HWND)lParam); \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// HBRUSH OnCtlColorDlg(CDCHandle dc, CWindow wnd)\n#define MSG_WM_CTLCOLORDLG(func) \\\n\tif (uMsg == WM_CTLCOLORDLG) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tlResult = (LRESULT)func((HDC)wParam, (HWND)lParam); \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// HBRUSH OnCtlColorScrollBar(CDCHandle dc, CScrollBar scrollBar)\n#define MSG_WM_CTLCOLORSCROLLBAR(func) \\\n\tif (uMsg == WM_CTLCOLORSCROLLBAR) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tlResult = (LRESULT)func((HDC)wParam, (HWND)lParam); \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// HBRUSH OnCtlColorStatic(CDCHandle dc, CStatic wndStatic)\n#define MSG_WM_CTLCOLORSTATIC(func) \\\n\tif (uMsg == WM_CTLCOLORSTATIC) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tlResult = (LRESULT)func((HDC)wParam, (HWND)lParam); \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnSettingChange(UINT uFlags, LPCTSTR lpszSection)\n#define MSG_WM_SETTINGCHANGE(func) \\\n\tif (uMsg == WM_SETTINGCHANGE) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((UINT)wParam, (LPCTSTR)lParam); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnDevModeChange(LPCTSTR lpDeviceName)\n#define MSG_WM_DEVMODECHANGE(func) \\\n\tif (uMsg == WM_DEVMODECHANGE) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((LPCTSTR)lParam); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnActivateApp(BOOL bActive, DWORD dwThreadID)\n#define MSG_WM_ACTIVATEAPP(func) \\\n\tif (uMsg == WM_ACTIVATEAPP) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((BOOL)wParam, (DWORD)lParam); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnFontChange()\n#define MSG_WM_FONTCHANGE(func) \\\n\tif (uMsg == WM_FONTCHANGE) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc(); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnTimeChange()\n#define MSG_WM_TIMECHANGE(func) \\\n\tif (uMsg == WM_TIMECHANGE) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc(); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnCancelMode()\n#define MSG_WM_CANCELMODE(func) \\\n\tif (uMsg == WM_CANCELMODE) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc(); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// BOOL OnSetCursor(CWindow wnd, UINT nHitTest, UINT message)\n#define MSG_WM_SETCURSOR(func) \\\n\tif (uMsg == WM_SETCURSOR) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tlResult = (LRESULT)func((HWND)wParam, (UINT)LOWORD(lParam), (UINT)HIWORD(lParam)); \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// int OnMouseActivate(CWindow wndTopLevel, UINT nHitTest, UINT message)\n#define MSG_WM_MOUSEACTIVATE(func) \\\n\tif (uMsg == WM_MOUSEACTIVATE) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tlResult = (LRESULT)func((HWND)wParam, (UINT)LOWORD(lParam), (UINT)HIWORD(lParam)); \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnChildActivate()\n#define MSG_WM_CHILDACTIVATE(func) \\\n\tif (uMsg == WM_CHILDACTIVATE) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc(); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnGetMinMaxInfo(LPMINMAXINFO lpMMI)\n#define MSG_WM_GETMINMAXINFO(func) \\\n\tif (uMsg == WM_GETMINMAXINFO) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((LPMINMAXINFO)lParam); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnIconEraseBkgnd(CDCHandle dc)\n#define MSG_WM_ICONERASEBKGND(func) \\\n\tif (uMsg == WM_ICONERASEBKGND) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((HDC)wParam); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnSpoolerStatus(UINT nStatus, UINT nJobs)\n#define MSG_WM_SPOOLERSTATUS(func) \\\n\tif (uMsg == WM_SPOOLERSTATUS) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((UINT)wParam, (UINT)LOWORD(lParam)); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct)\n#define MSG_WM_DRAWITEM(func) \\\n\tif (uMsg == WM_DRAWITEM) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((UINT)wParam, (LPDRAWITEMSTRUCT)lParam); \\\n\t\tlResult = TRUE; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct)\n#define MSG_WM_MEASUREITEM(func) \\\n\tif (uMsg == WM_MEASUREITEM) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((UINT)wParam, (LPMEASUREITEMSTRUCT)lParam); \\\n\t\tlResult = TRUE; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnDeleteItem(int nIDCtl, LPDELETEITEMSTRUCT lpDeleteItemStruct)\n#define MSG_WM_DELETEITEM(func) \\\n\tif (uMsg == WM_DELETEITEM) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((UINT)wParam, (LPDELETEITEMSTRUCT)lParam); \\\n\t\tlResult = TRUE; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n//int OnCharToItem(UINT nChar, UINT nIndex, CListBox listBox)\n#define MSG_WM_CHARTOITEM(func) \\\n\tif (uMsg == WM_CHARTOITEM) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tlResult = (LRESULT)func((UINT)LOWORD(wParam), (UINT)HIWORD(wParam), (HWND)lParam); \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// int OnVKeyToItem(UINT nKey, UINT nIndex, CListBox listBox)\n#define MSG_WM_VKEYTOITEM(func) \\\n\tif (uMsg == WM_VKEYTOITEM) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tlResult = (LRESULT)func((UINT)LOWORD(wParam), (UINT)HIWORD(wParam), (HWND)lParam); \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// HCURSOR OnQueryDragIcon()\n#define MSG_WM_QUERYDRAGICON(func) \\\n\tif (uMsg == WM_QUERYDRAGICON) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tlResult = (LRESULT)func(); \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// int OnCompareItem(int nIDCtl, LPCOMPAREITEMSTRUCT lpCompareItemStruct)\n#define MSG_WM_COMPAREITEM(func) \\\n\tif (uMsg == WM_COMPAREITEM) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tlResult = (LRESULT)func((UINT)wParam, (LPCOMPAREITEMSTRUCT)lParam); \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnCompacting(UINT nCpuTime)\n#define MSG_WM_COMPACTING(func) \\\n\tif (uMsg == WM_COMPACTING) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((UINT)wParam); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// BOOL OnNcCreate(LPCREATESTRUCT lpCreateStruct)\n#define MSG_WM_NCCREATE(func) \\\n\tif (uMsg == WM_NCCREATE) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tlResult = (LRESULT)func((LPCREATESTRUCT)lParam); \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnNcDestroy()\n#define MSG_WM_NCDESTROY(func) \\\n\tif (uMsg == WM_NCDESTROY) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc(); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// LRESULT OnNcCalcSize(BOOL bCalcValidRects, LPARAM lParam)\n#define MSG_WM_NCCALCSIZE(func) \\\n\tif (uMsg == WM_NCCALCSIZE) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tlResult = func((BOOL)wParam, lParam); \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// UINT OnNcHitTest(CPoint point)\n#define MSG_WM_NCHITTEST(func) \\\n\tif (uMsg == WM_NCHITTEST) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tlResult = (LRESULT)func(::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnNcPaint(CRgnHandle rgn)\n#define MSG_WM_NCPAINT(func) \\\n\tif (uMsg == WM_NCPAINT) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((HRGN)wParam); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// BOOL OnNcActivate(BOOL bActive)\n#define MSG_WM_NCACTIVATE(func) \\\n\tif (uMsg == WM_NCACTIVATE) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tlResult = (LRESULT)func((BOOL)wParam); \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// UINT OnGetDlgCode(LPMSG lpMsg)\n#define MSG_WM_GETDLGCODE(func) \\\n\tif (uMsg == WM_GETDLGCODE) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tlResult = (LRESULT)func((LPMSG)lParam); \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnNcMouseMove(UINT nHitTest, CPoint point)\n#define MSG_WM_NCMOUSEMOVE(func) \\\n\tif (uMsg == WM_NCMOUSEMOVE) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((UINT)wParam, ::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnNcLButtonDown(UINT nHitTest, CPoint point)\n#define MSG_WM_NCLBUTTONDOWN(func) \\\n\tif (uMsg == WM_NCLBUTTONDOWN) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((UINT)wParam, ::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnNcLButtonUp(UINT nHitTest, CPoint point)\n#define MSG_WM_NCLBUTTONUP(func) \\\n\tif (uMsg == WM_NCLBUTTONUP) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((UINT)wParam, ::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnNcLButtonDblClk(UINT nHitTest, CPoint point)\n#define MSG_WM_NCLBUTTONDBLCLK(func) \\\n\tif (uMsg == WM_NCLBUTTONDBLCLK) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((UINT)wParam, ::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnNcRButtonDown(UINT nHitTest, CPoint point)\n#define MSG_WM_NCRBUTTONDOWN(func) \\\n\tif (uMsg == WM_NCRBUTTONDOWN) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((UINT)wParam, ::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnNcRButtonUp(UINT nHitTest, CPoint point)\n#define MSG_WM_NCRBUTTONUP(func) \\\n\tif (uMsg == WM_NCRBUTTONUP) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((UINT)wParam, ::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnNcRButtonDblClk(UINT nHitTest, CPoint point)\n#define MSG_WM_NCRBUTTONDBLCLK(func) \\\n\tif (uMsg == WM_NCRBUTTONDBLCLK) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((UINT)wParam, ::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnNcMButtonDown(UINT nHitTest, CPoint point)\n#define MSG_WM_NCMBUTTONDOWN(func) \\\n\tif (uMsg == WM_NCMBUTTONDOWN) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((UINT)wParam, ::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnNcMButtonUp(UINT nHitTest, CPoint point)\n#define MSG_WM_NCMBUTTONUP(func) \\\n\tif (uMsg == WM_NCMBUTTONUP) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((UINT)wParam, ::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnNcMButtonDblClk(UINT nHitTest, CPoint point)\n#define MSG_WM_NCMBUTTONDBLCLK(func) \\\n\tif (uMsg == WM_NCMBUTTONDBLCLK) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((UINT)wParam, ::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)\n#define MSG_WM_KEYDOWN(func) \\\n\tif (uMsg == WM_KEYDOWN) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((UINT)wParam, (UINT)lParam & 0xFFFF, (UINT)((lParam & 0xFFFF0000) >> 16)); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags)\n#define MSG_WM_KEYUP(func) \\\n\tif (uMsg == WM_KEYUP) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((UINT)wParam, (UINT)lParam & 0xFFFF, (UINT)((lParam & 0xFFFF0000) >> 16)); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnChar(TCHAR chChar, UINT nRepCnt, UINT nFlags)\n#define MSG_WM_CHAR(func) \\\n\tif (uMsg == WM_CHAR) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((TCHAR)wParam, (UINT)lParam & 0xFFFF, (UINT)((lParam & 0xFFFF0000) >> 16)); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnDeadChar(TCHAR chChar, UINT nRepCnt, UINT nFlags)\n#define MSG_WM_DEADCHAR(func) \\\n\tif (uMsg == WM_DEADCHAR) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((TCHAR)wParam, (UINT)lParam & 0xFFFF, (UINT)((lParam & 0xFFFF0000) >> 16)); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnSysKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)\n#define MSG_WM_SYSKEYDOWN(func) \\\n\tif (uMsg == WM_SYSKEYDOWN) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((UINT)wParam, (UINT)lParam & 0xFFFF, (UINT)((lParam & 0xFFFF0000) >> 16)); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnSysKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags)\n#define MSG_WM_SYSKEYUP(func) \\\n\tif (uMsg == WM_SYSKEYUP) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((UINT)wParam, (UINT)lParam & 0xFFFF, (UINT)((lParam & 0xFFFF0000) >> 16)); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnSysChar(TCHAR chChar, UINT nRepCnt, UINT nFlags)\n#define MSG_WM_SYSCHAR(func) \\\n\tif (uMsg == WM_SYSCHAR) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((TCHAR)wParam, (UINT)lParam & 0xFFFF, (UINT)((lParam & 0xFFFF0000) >> 16)); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnSysDeadChar(TCHAR chChar, UINT nRepCnt, UINT nFlags)\n#define MSG_WM_SYSDEADCHAR(func) \\\n\tif (uMsg == WM_SYSDEADCHAR) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((TCHAR)wParam, (UINT)lParam & 0xFFFF, (UINT)((lParam & 0xFFFF0000) >> 16)); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnSysCommand(UINT nID, CPoint point)\n#define MSG_WM_SYSCOMMAND(func) \\\n\tif (uMsg == WM_SYSCOMMAND) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((UINT)wParam, ::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnTCard(UINT idAction, DWORD dwActionData)\n#define MSG_WM_TCARD(func) \\\n\tif (uMsg == WM_TCARD) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((UINT)wParam, (DWORD)lParam); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnTimer(UINT_PTR nIDEvent)\n#define MSG_WM_TIMER(func) \\\n\tif (uMsg == WM_TIMER) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((UINT_PTR)wParam); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnHScroll(UINT nSBCode, UINT nPos, CScrollBar pScrollBar)\n#define MSG_WM_HSCROLL(func) \\\n\tif (uMsg == WM_HSCROLL) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((int)LOWORD(wParam), (short)HIWORD(wParam), (HWND)lParam); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnVScroll(UINT nSBCode, UINT nPos, CScrollBar pScrollBar)\n#define MSG_WM_VSCROLL(func) \\\n\tif (uMsg == WM_VSCROLL) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((int)LOWORD(wParam), (short)HIWORD(wParam), (HWND)lParam); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnInitMenu(CMenuHandle menu)\n#define MSG_WM_INITMENU(func) \\\n\tif (uMsg == WM_INITMENU) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((HMENU)wParam); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnInitMenuPopup(CMenuHandle menuPopup, UINT nIndex, BOOL bSysMenu)\n#define MSG_WM_INITMENUPOPUP(func) \\\n\tif (uMsg == WM_INITMENUPOPUP) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((HMENU)wParam, (UINT)LOWORD(lParam), (BOOL)HIWORD(lParam)); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnMenuSelect(UINT nItemID, UINT nFlags, CMenuHandle menu)\n#define MSG_WM_MENUSELECT(func) \\\n\tif (uMsg == WM_MENUSELECT) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((UINT)LOWORD(wParam), (UINT)HIWORD(wParam), (HMENU)lParam); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// LRESULT OnMenuChar(UINT nChar, UINT nFlags, CMenuHandle menu)\n#define MSG_WM_MENUCHAR(func) \\\n\tif (uMsg == WM_MENUCHAR) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tlResult = func((TCHAR)LOWORD(wParam), (UINT)HIWORD(wParam), (HMENU)lParam); \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// LRESULT OnNotify(int idCtrl, LPNMHDR pnmh)\n#define MSG_WM_NOTIFY(func) \\\n\tif (uMsg == WM_NOTIFY) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tlResult = func((int)wParam, (LPNMHDR)lParam); \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnEnterIdle(UINT nWhy, CWindow wndWho)\n#define MSG_WM_ENTERIDLE(func) \\\n\tif (uMsg == WM_ENTERIDLE) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((UINT)wParam, (HWND)lParam); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnMouseMove(UINT nFlags, CPoint point)\n#define MSG_WM_MOUSEMOVE(func) \\\n\tif (uMsg == WM_MOUSEMOVE) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((UINT)wParam, ::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// BOOL OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)\n#define MSG_WM_MOUSEWHEEL(func) \\\n\tif (uMsg == WM_MOUSEWHEEL) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tlResult = (LRESULT)func((UINT)LOWORD(wParam), (short)HIWORD(wParam), ::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnLButtonDown(UINT nFlags, CPoint point)\n#define MSG_WM_LBUTTONDOWN(func) \\\n\tif (uMsg == WM_LBUTTONDOWN) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((UINT)wParam, ::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnLButtonUp(UINT nFlags, CPoint point)\n#define MSG_WM_LBUTTONUP(func) \\\n\tif (uMsg == WM_LBUTTONUP) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((UINT)wParam, ::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnLButtonDblClk(UINT nFlags, CPoint point)\n#define MSG_WM_LBUTTONDBLCLK(func) \\\n\tif (uMsg == WM_LBUTTONDBLCLK) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((UINT)wParam, ::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnRButtonDown(UINT nFlags, CPoint point)\n#define MSG_WM_RBUTTONDOWN(func) \\\n\tif (uMsg == WM_RBUTTONDOWN) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((UINT)wParam, ::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnRButtonUp(UINT nFlags, CPoint point)\n#define MSG_WM_RBUTTONUP(func) \\\n\tif (uMsg == WM_RBUTTONUP) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((UINT)wParam, ::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnRButtonDblClk(UINT nFlags, CPoint point)\n#define MSG_WM_RBUTTONDBLCLK(func) \\\n\tif (uMsg == WM_RBUTTONDBLCLK) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((UINT)wParam, ::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnMButtonDown(UINT nFlags, CPoint point)\n#define MSG_WM_MBUTTONDOWN(func) \\\n\tif (uMsg == WM_MBUTTONDOWN) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((UINT)wParam, ::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnMButtonUp(UINT nFlags, CPoint point)\n#define MSG_WM_MBUTTONUP(func) \\\n\tif (uMsg == WM_MBUTTONUP) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((UINT)wParam, ::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnMButtonDblClk(UINT nFlags, CPoint point)\n#define MSG_WM_MBUTTONDBLCLK(func) \\\n\tif (uMsg == WM_MBUTTONDBLCLK) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((UINT)wParam, ::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnParentNotify(UINT message, UINT nChildID, LPARAM lParam)\n#define MSG_WM_PARENTNOTIFY(func) \\\n\tif (uMsg == WM_PARENTNOTIFY) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((UINT)LOWORD(wParam), (UINT)HIWORD(wParam), lParam); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnMDIActivate(CWindow wndDeactivate, CWindow wndActivate)\n#define MSG_WM_MDIACTIVATE(func) \\\n\tif (uMsg == WM_MDIACTIVATE) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((HWND)wParam, (HWND)lParam); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnRenderFormat(UINT nFormat)\n#define MSG_WM_RENDERFORMAT(func) \\\n\tif (uMsg == WM_RENDERFORMAT) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((UINT)wParam); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnRenderAllFormats()\n#define MSG_WM_RENDERALLFORMATS(func) \\\n\tif (uMsg == WM_RENDERALLFORMATS) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc(); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnDestroyClipboard()\n#define MSG_WM_DESTROYCLIPBOARD(func) \\\n\tif (uMsg == WM_DESTROYCLIPBOARD) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc(); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnDrawClipboard()\n#define MSG_WM_DRAWCLIPBOARD(func) \\\n\tif (uMsg == WM_DRAWCLIPBOARD) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc(); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnPaintClipboard(CWindow wndViewer, const LPPAINTSTRUCT lpPaintStruct)\n#define MSG_WM_PAINTCLIPBOARD(func) \\\n\tif (uMsg == WM_PAINTCLIPBOARD) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((HWND)wParam, (const LPPAINTSTRUCT)::GlobalLock((HGLOBAL)lParam)); \\\n\t\t::GlobalUnlock((HGLOBAL)lParam); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnVScrollClipboard(CWindow wndViewer, UINT nSBCode, UINT nPos)\n#define MSG_WM_VSCROLLCLIPBOARD(func) \\\n\tif (uMsg == WM_VSCROLLCLIPBOARD) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((HWND)wParam, (UINT)LOWORD(lParam), (UINT)HIWORD(lParam)); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnContextMenu(CWindow wnd, CPoint point)\n#define MSG_WM_CONTEXTMENU(func) \\\n\tif (uMsg == WM_CONTEXTMENU) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((HWND)wParam, ::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnSizeClipboard(CWindow wndViewer, const LPRECT lpRect)\n#define MSG_WM_SIZECLIPBOARD(func) \\\n\tif (uMsg == WM_SIZECLIPBOARD) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((HWND)wParam, (const LPRECT)::GlobalLock((HGLOBAL)lParam)); \\\n\t\t::GlobalUnlock((HGLOBAL)lParam); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnAskCbFormatName(UINT nMaxCount, LPTSTR lpszString)\n#define MSG_WM_ASKCBFORMATNAME(func) \\\n\tif (uMsg == WM_ASKCBFORMATNAME) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((UINT)wParam, (LPTSTR)lParam); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnChangeCbChain(CWindow wndRemove, CWindow wndAfter)\n#define MSG_WM_CHANGECBCHAIN(func) \\\n\tif (uMsg == WM_CHANGECBCHAIN) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((HWND)wParam, (HWND)lParam); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnHScrollClipboard(CWindow wndViewer, UINT nSBCode, UINT nPos)\n#define MSG_WM_HSCROLLCLIPBOARD(func) \\\n\tif (uMsg == WM_HSCROLLCLIPBOARD) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((HWND)wParam, (UINT)LOWORD(lParam), (UINT)HIWORD(lParam)); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// BOOL OnQueryNewPalette()\n#define MSG_WM_QUERYNEWPALETTE(func) \\\n\tif (uMsg == WM_QUERYNEWPALETTE) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tlResult = (LRESULT)func(); \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnPaletteChanged(CWindow wndFocus)\n#define MSG_WM_PALETTECHANGED(func) \\\n\tif (uMsg == WM_PALETTECHANGED) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((HWND)wParam); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnPaletteIsChanging(CWindow wndPalChg)\n#define MSG_WM_PALETTEISCHANGING(func) \\\n\tif (uMsg == WM_PALETTEISCHANGING) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((HWND)wParam); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnDropFiles(HDROP hDropInfo)\n#define MSG_WM_DROPFILES(func) \\\n\tif (uMsg == WM_DROPFILES) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((HDROP)wParam); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnWindowPosChanging(LPWINDOWPOS lpWndPos)\n#define MSG_WM_WINDOWPOSCHANGING(func) \\\n\tif (uMsg == WM_WINDOWPOSCHANGING) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((LPWINDOWPOS)lParam); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnWindowPosChanged(LPWINDOWPOS lpWndPos)\n#define MSG_WM_WINDOWPOSCHANGED(func) \\\n\tif (uMsg == WM_WINDOWPOSCHANGED) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((LPWINDOWPOS)lParam); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnExitMenuLoop(BOOL fIsTrackPopupMenu)\n#define MSG_WM_EXITMENULOOP(func) \\\n\tif (uMsg == WM_EXITMENULOOP) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((BOOL)wParam); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnEnterMenuLoop(BOOL fIsTrackPopupMenu)\n#define MSG_WM_ENTERMENULOOP(func) \\\n\tif (uMsg == WM_ENTERMENULOOP) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((BOOL)wParam); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnStyleChanged(int nStyleType, LPSTYLESTRUCT lpStyleStruct)\n#define MSG_WM_STYLECHANGED(func) \\\n\tif (uMsg == WM_STYLECHANGED) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((UINT)wParam, (LPSTYLESTRUCT)lParam); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnStyleChanging(int nStyleType, LPSTYLESTRUCT lpStyleStruct)\n#define MSG_WM_STYLECHANGING(func) \\\n\tif (uMsg == WM_STYLECHANGING) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((UINT)wParam, (LPSTYLESTRUCT)lParam); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnSizing(UINT fwSide, LPRECT pRect)\n#define MSG_WM_SIZING(func) \\\n\tif (uMsg == WM_SIZING) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((UINT)wParam, (LPRECT)lParam); \\\n\t\tlResult = TRUE; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnMoving(UINT fwSide, LPRECT pRect)\n#define MSG_WM_MOVING(func) \\\n\tif (uMsg == WM_MOVING) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((UINT)wParam, (LPRECT)lParam); \\\n\t\tlResult = TRUE; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnCaptureChanged(CWindow wnd)\n#define MSG_WM_CAPTURECHANGED(func) \\\n\tif (uMsg == WM_CAPTURECHANGED) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((HWND)lParam); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// BOOL OnDeviceChange(UINT nEventType, DWORD_PTR dwData)\n#define MSG_WM_DEVICECHANGE(func) \\\n\tif (uMsg == WM_DEVICECHANGE) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tlResult = (LRESULT)func((UINT)wParam, (DWORD_PTR)lParam); \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnCommand(UINT uNotifyCode, int nID, CWindow wndCtl)\n#define MSG_WM_COMMAND(func) \\\n\tif (uMsg == WM_COMMAND) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((UINT)HIWORD(wParam), (int)LOWORD(wParam), (HWND)lParam); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnDisplayChange(UINT uBitsPerPixel, CSize sizeScreen)\n#define MSG_WM_DISPLAYCHANGE(func) \\\n\tif (uMsg == WM_DISPLAYCHANGE) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((UINT)wParam, ::CSize(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnEnterSizeMove()\n#define MSG_WM_ENTERSIZEMOVE(func) \\\n\tif (uMsg == WM_ENTERSIZEMOVE) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc(); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnExitSizeMove()\n#define MSG_WM_EXITSIZEMOVE(func) \\\n\tif (uMsg == WM_EXITSIZEMOVE) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc(); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// HFONT OnGetFont()\n#define MSG_WM_GETFONT(func) \\\n\tif (uMsg == WM_GETFONT) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tlResult = (LRESULT)func(); \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// LRESULT OnGetHotKey()\n#define MSG_WM_GETHOTKEY(func) \\\n\tif (uMsg == WM_GETHOTKEY) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tlResult = func(); \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// HICON OnGetIcon()\n#define MSG_WM_GETICON(func) \\\n\tif (uMsg == WM_GETICON) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tlResult = (LRESULT)func((UINT)wParam); \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// int OnGetText(int cchTextMax, LPTSTR lpszText)\n#define MSG_WM_GETTEXT(func) \\\n\tif (uMsg == WM_GETTEXT) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tlResult = (LRESULT)func((int)wParam, (LPTSTR)lParam); \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// int OnGetTextLength()\n#define MSG_WM_GETTEXTLENGTH(func) \\\n\tif (uMsg == WM_GETTEXTLENGTH) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tlResult = (LRESULT)func(); \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnHelp(LPHELPINFO lpHelpInfo)\n#define MSG_WM_HELP(func) \\\n\tif (uMsg == WM_HELP) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((LPHELPINFO)lParam); \\\n\t\tlResult = TRUE; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnHotKey(int nHotKeyID, UINT uModifiers, UINT uVirtKey)\n#define MSG_WM_HOTKEY(func) \\\n\tif (uMsg == WM_HOTKEY) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((int)wParam, (UINT)LOWORD(lParam), (UINT)HIWORD(lParam)); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnInputLangChange(DWORD dwCharSet, HKL hKbdLayout)\n#define MSG_WM_INPUTLANGCHANGE(func) \\\n\tif (uMsg == WM_INPUTLANGCHANGE) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((DWORD)wParam, (HKL)lParam); \\\n\t\tlResult = TRUE; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnInputLangChangeRequest(BOOL bSysCharSet, HKL hKbdLayout)\n#define MSG_WM_INPUTLANGCHANGEREQUEST(func) \\\n\tif (uMsg == WM_INPUTLANGCHANGEREQUEST) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((BOOL)wParam, (HKL)lParam); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnNextDlgCtl(BOOL bHandle, WPARAM wCtlFocus)\n#define MSG_WM_NEXTDLGCTL(func) \\\n\tif (uMsg == WM_NEXTDLGCTL) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((BOOL)LOWORD(lParam), wParam); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnNextMenu(int nVirtKey, LPMDINEXTMENU lpMdiNextMenu)\n#define MSG_WM_NEXTMENU(func) \\\n\tif (uMsg == WM_NEXTMENU) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((int)wParam, (LPMDINEXTMENU)lParam); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// int OnNotifyFormat(CWindow wndFrom, int nCommand)\n#define MSG_WM_NOTIFYFORMAT(func) \\\n\tif (uMsg == WM_NOTIFYFORMAT) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tlResult = (LRESULT)func((HWND)wParam, (int)lParam); \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// BOOL OnPowerBroadcast(DWORD dwPowerEvent, DWORD_PTR dwData)\n#define MSG_WM_POWERBROADCAST(func) \\\n\tif (uMsg == WM_POWERBROADCAST) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tlResult = (LRESULT)func((DWORD)wParam, (DWORD_PTR)lParam); \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnPrint(CDCHandle dc, UINT uFlags)\n#define MSG_WM_PRINT(func) \\\n\tif (uMsg == WM_PRINT) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((HDC)wParam, (UINT)lParam); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnPrintClient(CDCHandle dc, UINT uFlags)\n#define MSG_WM_PRINTCLIENT(func) \\\n\tif (uMsg == WM_PRINTCLIENT) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((HDC)wParam, (UINT)lParam); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnRasDialEvent(RASCONNSTATE rasconnstate, DWORD dwError)\n#define MSG_WM_RASDIALEVENT(func) \\\n\tif (uMsg == WM_RASDIALEVENT) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((RASCONNSTATE)wParam, (DWORD)lParam); \\\n\t\tlResult = TRUE; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnSetFont(CFontHandle font, BOOL bRedraw)\n#define MSG_WM_SETFONT(func) \\\n\tif (uMsg == WM_SETFONT) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((HFONT)wParam, (BOOL)LOWORD(lParam)); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// int OnSetHotKey(int nVirtKey, UINT uFlags)\n#define MSG_WM_SETHOTKEY(func) \\\n\tif (uMsg == WM_SETHOTKEY) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tlResult = (LRESULT)func((int)LOBYTE(LOWORD(wParam)), (UINT)HIBYTE(LOWORD(wParam))); \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// HICON OnSetIcon(UINT uType, HICON hIcon)\n#define MSG_WM_SETICON(func) \\\n\tif (uMsg == WM_SETICON) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tlResult = (LRESULT)func((UINT)wParam, (HICON)lParam); \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnSetRedraw(BOOL bRedraw)\n#define MSG_WM_SETREDRAW(func) \\\n\tif (uMsg == WM_SETREDRAW) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((BOOL)wParam); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// int OnSetText(LPCTSTR lpstrText)\n#define MSG_WM_SETTEXT(func) \\\n\tif (uMsg == WM_SETTEXT) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tlResult = (LRESULT)func((LPCTSTR)lParam); \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnUserChanged()\n#define MSG_WM_USERCHANGED(func) \\\n\tif (uMsg == WM_USERCHANGED) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc(); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n///////////////////////////////////////////////////////////////////////////////\n// Newer Windows messages\n\n// void OnMouseHover(WPARAM wParam, CPoint ptPos)\n#define MSG_WM_MOUSEHOVER(func) \\\n\tif (uMsg == WM_MOUSEHOVER) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc(wParam, ::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnMouseLeave()\n#define MSG_WM_MOUSELEAVE(func) \\\n\tif (uMsg == WM_MOUSELEAVE) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc(); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnNcMouseHover(UINT nHitTest, CPoint ptPos)\n#define MSG_WM_NCMOUSEHOVER(func) \\\n\tif (uMsg == WM_NCMOUSEHOVER) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((UINT)wParam, ::CPoint(MAKEPOINTS(lParam).x, MAKEPOINTS(lParam).y)); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnNcMouseLeave()\n#define MSG_WM_NCMOUSELEAVE(func) \\\n\tif (uMsg == WM_NCMOUSELEAVE) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc(); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnMenuRButtonUp(WPARAM wParam, CMenuHandle menu)\n#define MSG_WM_MENURBUTTONUP(func) \\\n\tif (uMsg == WM_MENURBUTTONUP) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc(wParam, (HMENU)lParam); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// LRESULT OnMenuDrag(WPARAM wParam, CMenuHandle menu)\n#define MSG_WM_MENUDRAG(func) \\\n\tif (uMsg == WM_MENUDRAG) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tlResult = func(wParam, (HMENU)lParam); \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// LRESULT OnMenuGetObject(PMENUGETOBJECTINFO info)\n#define MSG_WM_MENUGETOBJECT(func) \\\n\tif (uMsg == WM_MENUGETOBJECT) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tlResult = func((PMENUGETOBJECTINFO)lParam); \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnUnInitMenuPopup(UINT nID, CMenuHandle menu)\n#define MSG_WM_UNINITMENUPOPUP(func) \\\n\tif (uMsg == WM_UNINITMENUPOPUP) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((UINT)HIWORD(lParam), (HMENU)wParam); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnMenuCommand(WPARAM nIndex, CMenuHandle menu)\n#define MSG_WM_MENUCOMMAND(func) \\\n\tif (uMsg == WM_MENUCOMMAND) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc(wParam, (HMENU)lParam); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// BOOL OnAppCommand(CWindow wndFocus, short cmd, WORD uDevice, int dwKeys)\n#define MSG_WM_APPCOMMAND(func) \\\n\tif (uMsg == WM_APPCOMMAND) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tlResult = (LRESULT)func((HWND)wParam, GET_APPCOMMAND_LPARAM(lParam), GET_DEVICE_LPARAM(lParam), GET_KEYSTATE_LPARAM(lParam)); \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnNCXButtonDown(int fwButton, short nHittest, CPoint ptPos)\n#define MSG_WM_NCXBUTTONDOWN(func) \\\n\tif (uMsg == WM_NCXBUTTONDOWN) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc(GET_XBUTTON_WPARAM(wParam), GET_NCHITTEST_WPARAM(wParam), ::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \\\n\t\tlResult = (LRESULT)TRUE; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnNCXButtonUp(int fwButton, short nHittest, CPoint ptPos)\n#define MSG_WM_NCXBUTTONUP(func) \\\n\tif (uMsg == WM_NCXBUTTONUP) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc(GET_XBUTTON_WPARAM(wParam), GET_NCHITTEST_WPARAM(wParam), ::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \\\n\t\tlResult = (LRESULT)TRUE; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnNCXButtonDblClk(int fwButton, short nHittest, CPoint ptPos)\n#define MSG_WM_NCXBUTTONDBLCLK(func) \\\n\tif (uMsg == WM_NCXBUTTONDBLCLK) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc(GET_XBUTTON_WPARAM(wParam), GET_NCHITTEST_WPARAM(wParam), ::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \\\n\t\tlResult = (LRESULT)TRUE; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnXButtonDown(int fwButton, int dwKeys, CPoint ptPos)\n#define MSG_WM_XBUTTONDOWN(func) \\\n\tif (uMsg == WM_XBUTTONDOWN) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc(GET_XBUTTON_WPARAM(wParam), GET_KEYSTATE_WPARAM(wParam), ::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \\\n\t\tlResult = (LRESULT)TRUE; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnXButtonUp(int fwButton, int dwKeys, CPoint ptPos)\n#define MSG_WM_XBUTTONUP(func) \\\n\tif (uMsg == WM_XBUTTONUP) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc(GET_XBUTTON_WPARAM(wParam), GET_KEYSTATE_WPARAM(wParam), ::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \\\n\t\tlResult = (LRESULT)TRUE; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnXButtonDblClk(int fwButton, int dwKeys, CPoint ptPos)\n#define MSG_WM_XBUTTONDBLCLK(func) \\\n\tif (uMsg == WM_XBUTTONDBLCLK) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc(GET_XBUTTON_WPARAM(wParam), GET_KEYSTATE_WPARAM(wParam), ::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \\\n\t\tlResult = (LRESULT)TRUE; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnChangeUIState(WORD nAction, WORD nState)\n#define MSG_WM_CHANGEUISTATE(func) \\\n\tif (uMsg == WM_CHANGEUISTATE) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc(LOWORD(wParam), HIWORD(wParam)); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnUpdateUIState(WORD nAction, WORD nState)\n#define MSG_WM_UPDATEUISTATE(func) \\\n\tif (uMsg == WM_UPDATEUISTATE) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc(LOWORD(wParam), HIWORD(wParam)); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// LRESULT OnQueryUIState()\n#define MSG_WM_QUERYUISTATE(func) \\\n\tif (uMsg == WM_QUERYUISTATE) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tlResult = func(); \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnInput(WPARAM RawInputCode, HRAWINPUT hRawInput)\n#define MSG_WM_INPUT(func) \\\n\tif (uMsg == WM_INPUT) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc(GET_RAWINPUT_CODE_WPARAM(wParam), (HRAWINPUT)lParam); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnUniChar(TCHAR nChar, UINT nRepCnt, UINT nFlags)\n#define MSG_WM_UNICHAR(func) \\\n\tif (uMsg == WM_UNICHAR) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((TCHAR)wParam, (UINT)lParam & 0xFFFF, (UINT)((lParam & 0xFFFF0000) >> 16)); \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t{ \\\n\t\t\tlResult = (wParam == UNICODE_NOCHAR) ? TRUE : FALSE; \\\n\t\t\treturn TRUE; \\\n\t\t} \\\n\t}\n\n// void OnWTSSessionChange(WPARAM nStatusCode, DWORD dwSessionID)\n#define MSG_WM_WTSSESSION_CHANGE(func) \\\n\tif (uMsg == WM_WTSSESSION_CHANGE) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc(wParam, (DWORD)lParam); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnThemeChanged()\n#define MSG_WM_THEMECHANGED(func) \\\n\tif (uMsg == WM_THEMECHANGED) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc(); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n#if (_WIN32_WINNT >= 0x0600)\n\n// BOOL OnMouseHWheel(UINT nFlags, short zDelta, CPoint pt)\n#define MSG_WM_MOUSEHWHEEL(func) \\\n\tif (uMsg == WM_MOUSEHWHEEL) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tlResult = (LRESULT)func((UINT)LOWORD(wParam), (short)HIWORD(wParam), ::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n#endif // (_WIN32_WINNT >= 0x0600)\n\n#if (WINVER >= 0x0601)\n\n// void OnGesture(ULONGLONG ullArguments, HGESTUREINFO hGestureInfo)\n#define MSG_WM_GESTURE(func) \\\n\tif (uMsg == WM_GESTURE) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((ULONGLONG)wParam, (HGESTUREINFO)lParam); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnGestureNotify(PGESTURENOTIFYSTRUCT pGestureNotifyStruct)\n#define MSG_WM_GESTURENOTIFY(func) \\\n\tif (uMsg == WM_GESTURENOTIFY) \\\n\t{ \\\n\t\tfunc((PGESTURENOTIFYSTRUCT)lParam); \\\n\t}\n\n// void OnDpiChanged(UINT nDpiX, UINT nDpiY, PRECT pRect)\n#define MSG_WM_DPICHANGED(func) \\\n\tif (uMsg == WM_DPICHANGED) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((UINT)LOWORD(wParam), (UINT)HIWORD(wParam), (PRECT)lParam); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n#endif // (WINVER >= 0x0601)\n\n#if (WINVER >= 0x0605)\n\n// void OnDpiChangedBeforeParent()\n#define MSG_WM_DPICHANGED_BEFOREPARENT(func) \\\n\tif (uMsg == WM_DPICHANGED_BEFOREPARENT) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc(); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnDpiChangedAfterParent()\n#define MSG_WM_DPICHANGED_AFTERPARENT(func) \\\n\tif (uMsg == WM_DPICHANGED_AFTERPARENT) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc(); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// BOOL OnGetDpiScaledSize(UINT uDpi, PSIZE pSize)\n#define MSG_WM_GETDPISCALEDSIZE(func) \\\nif (uMsg == WM_GETDPISCALEDSIZE) \\\n{ \\\n\tthis->SetMsgHandled(TRUE); \\\n\tlResult = (LRESULT)func((UINT)wParam, (PSIZE)lParam); \\\n\tif(this->IsMsgHandled()) \\\n\t\treturn TRUE; \\\n}\n\n#endif // (WINVER >= 0x0605)\n\n///////////////////////////////////////////////////////////////////////////////\n// ATL defined messages\n\n// BOOL OnForwardMsg(LPMSG Msg, DWORD nUserData)\n#define MSG_WM_FORWARDMSG(func) \\\n\tif (uMsg == WM_FORWARDMSG) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tlResult = (LRESULT)func((LPMSG)lParam, (DWORD)wParam); \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n///////////////////////////////////////////////////////////////////////////////\n// Dialog specific messages\n\n// LRESULT OnDMGetDefID()\n#define MSG_DM_GETDEFID(func) \\\n\tif (uMsg == DM_GETDEFID) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tlResult = func(); \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnDMSetDefID(UINT DefID)\n#define MSG_DM_SETDEFID(func) \\\n\tif (uMsg == DM_SETDEFID) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((UINT)wParam); \\\n\t\tlResult = TRUE; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnDMReposition()\n#define MSG_DM_REPOSITION(func) \\\n\tif (uMsg == DM_REPOSITION) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc(); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n///////////////////////////////////////////////////////////////////////////////\n// Reflected messages\n\n// void OnReflectedCommand(UINT uNotifyCode, int nID, CWindow wndCtl)\n#define MSG_OCM_COMMAND(func) \\\n\tif (uMsg == OCM_COMMAND) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((UINT)HIWORD(wParam), (int)LOWORD(wParam), (HWND)lParam); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// LRESULT OnReflectedNotify(int idCtrl, LPNMHDR pnmh)\n#define MSG_OCM_NOTIFY(func) \\\n\tif (uMsg == OCM_NOTIFY) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tlResult = func((int)wParam, (LPNMHDR)lParam); \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnReflectedParentNotify(UINT message, UINT nChildID, LPARAM lParam)\n#define MSG_OCM_PARENTNOTIFY(func) \\\n\tif (uMsg == OCM_PARENTNOTIFY) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((UINT)LOWORD(wParam), (UINT)HIWORD(wParam), lParam); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnReflectedDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct)\n#define MSG_OCM_DRAWITEM(func) \\\n\tif (uMsg == OCM_DRAWITEM) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((UINT)wParam, (LPDRAWITEMSTRUCT)lParam); \\\n\t\tlResult = TRUE; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnReflectedMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct)\n#define MSG_OCM_MEASUREITEM(func) \\\n\tif (uMsg == OCM_MEASUREITEM) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((UINT)wParam, (LPMEASUREITEMSTRUCT)lParam); \\\n\t\tlResult = TRUE; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// int OnReflectedCompareItem(int nIDCtl, LPCOMPAREITEMSTRUCT lpCompareItemStruct)\n#define MSG_OCM_COMPAREITEM(func) \\\n\tif (uMsg == OCM_COMPAREITEM) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tlResult = (LRESULT)func((UINT)wParam, (LPCOMPAREITEMSTRUCT)lParam); \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnReflectedDeleteItem(int nIDCtl, LPDELETEITEMSTRUCT lpDeleteItemStruct)\n#define MSG_OCM_DELETEITEM(func) \\\n\tif (uMsg == OCM_DELETEITEM) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((UINT)wParam, (LPDELETEITEMSTRUCT)lParam); \\\n\t\tlResult = TRUE; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// int OnReflectedVKeyToItem(UINT nKey, UINT nIndex, CListBox listBox)\n#define MSG_OCM_VKEYTOITEM(func) \\\n\tif (uMsg == OCM_VKEYTOITEM) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tlResult = (LRESULT)func((UINT)LOWORD(wParam), (UINT)HIWORD(wParam), (HWND)lParam); \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n//int OnReflectedCharToItem(UINT nChar, UINT nIndex, CListBox listBox)\n#define MSG_OCM_CHARTOITEM(func) \\\n\tif (uMsg == OCM_CHARTOITEM) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tlResult = (LRESULT)func((UINT)LOWORD(wParam), (UINT)HIWORD(wParam), (HWND)lParam); \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnReflectedHScroll(UINT nSBCode, UINT nPos, CScrollBar pScrollBar)\n#define MSG_OCM_HSCROLL(func) \\\n\tif (uMsg == OCM_HSCROLL) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((int)LOWORD(wParam), (short)HIWORD(wParam), (HWND)lParam); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnReflectedVScroll(UINT nSBCode, UINT nPos, CScrollBar pScrollBar)\n#define MSG_OCM_VSCROLL(func) \\\n\tif (uMsg == OCM_VSCROLL) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((int)LOWORD(wParam), (short)HIWORD(wParam), (HWND)lParam); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// HBRUSH OnReflectedCtlColorEdit(CDCHandle dc, CEdit edit)\n#define MSG_OCM_CTLCOLOREDIT(func) \\\n\tif (uMsg == OCM_CTLCOLOREDIT) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tlResult = (LRESULT)func((HDC)wParam, (HWND)lParam); \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// HBRUSH OnReflectedCtlColorListBox(CDCHandle dc, CListBox listBox)\n#define MSG_OCM_CTLCOLORLISTBOX(func) \\\n\tif (uMsg == OCM_CTLCOLORLISTBOX) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tlResult = (LRESULT)func((HDC)wParam, (HWND)lParam); \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// HBRUSH OnReflectedCtlColorBtn(CDCHandle dc, CButton button)\n#define MSG_OCM_CTLCOLORBTN(func) \\\n\tif (uMsg == OCM_CTLCOLORBTN) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tlResult = (LRESULT)func((HDC)wParam, (HWND)lParam); \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// HBRUSH OnReflectedCtlColorDlg(CDCHandle dc, CWindow wnd)\n#define MSG_OCM_CTLCOLORDLG(func) \\\n\tif (uMsg == OCM_CTLCOLORDLG) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tlResult = (LRESULT)func((HDC)wParam, (HWND)lParam); \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// HBRUSH OnReflectedCtlColorScrollBar(CDCHandle dc, CScrollBar scrollBar)\n#define MSG_OCM_CTLCOLORSCROLLBAR(func) \\\n\tif (uMsg == OCM_CTLCOLORSCROLLBAR) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tlResult = (LRESULT)func((HDC)wParam, (HWND)lParam); \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// HBRUSH OnReflectedCtlColorStatic(CDCHandle dc, CStatic wndStatic)\n#define MSG_OCM_CTLCOLORSTATIC(func) \\\n\tif (uMsg == OCM_CTLCOLORSTATIC) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tlResult = (LRESULT)func((HDC)wParam, (HWND)lParam); \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n///////////////////////////////////////////////////////////////////////////////\n// Edit specific messages\n\n// void OnClear()\n#define MSG_WM_CLEAR(func) \\\n\tif (uMsg == WM_CLEAR) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc(); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnCopy()\n#define MSG_WM_COPY(func) \\\n\tif (uMsg == WM_COPY) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc(); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnCut()\n#define MSG_WM_CUT(func) \\\n\tif (uMsg == WM_CUT) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc(); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnPaste()\n#define MSG_WM_PASTE(func) \\\n\tif (uMsg == WM_PASTE) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc(); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnUndo()\n#define MSG_WM_UNDO(func) \\\n\tif (uMsg == WM_UNDO) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc(); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n///////////////////////////////////////////////////////////////////////////////\n// Generic message handlers\n\n// LRESULT OnMessageHandlerEX(UINT uMsg, WPARAM wParam, LPARAM lParam)\n#define MESSAGE_HANDLER_EX(msg, func) \\\n\tif(uMsg == msg) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tlResult = func(uMsg, wParam, lParam); \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// LRESULT OnMessageRangeHandlerEX(UINT uMsg, WPARAM wParam, LPARAM lParam)\n#define MESSAGE_RANGE_HANDLER_EX(msgFirst, msgLast, func) \\\n\tif((uMsg >= msgFirst) && (uMsg <= msgLast)) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tlResult = func(uMsg, wParam, lParam); \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n///////////////////////////////////////////////////////////////////////////////\n// Commands and notifications\n\n// void OnCommandHandlerEX(UINT uNotifyCode, int nID, CWindow wndCtl)\n#define COMMAND_HANDLER_EX(id, code, func) \\\n\tif ((uMsg == WM_COMMAND) && (code == HIWORD(wParam)) && (id == LOWORD(wParam))) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((UINT)HIWORD(wParam), (int)LOWORD(wParam), (HWND)lParam); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnCommandIDHandlerEX(UINT uNotifyCode, int nID, CWindow wndCtl)\n#define COMMAND_ID_HANDLER_EX(id, func) \\\n\tif ((uMsg == WM_COMMAND) && (id == LOWORD(wParam))) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((UINT)HIWORD(wParam), (int)LOWORD(wParam), (HWND)lParam); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnCommandCodeHandlerEX(UINT uNotifyCode, int nID, CWindow wndCtl)\n#define COMMAND_CODE_HANDLER_EX(code, func) \\\n\tif ((uMsg == WM_COMMAND) && (code == HIWORD(wParam))) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((UINT)HIWORD(wParam), (int)LOWORD(wParam), (HWND)lParam); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// LRESULT OnNotifyHandlerEX(LPNMHDR pnmh)\n#define NOTIFY_HANDLER_EX(id, cd, func) \\\n\tif ((uMsg == WM_NOTIFY) && (cd == ((LPNMHDR)lParam)->code) && (id == ((LPNMHDR)lParam)->idFrom)) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tlResult = func((LPNMHDR)lParam); \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// LRESULT OnNotifyIDHandlerEX(LPNMHDR pnmh)\n#define NOTIFY_ID_HANDLER_EX(id, func) \\\n\tif ((uMsg == WM_NOTIFY) && (id == ((LPNMHDR)lParam)->idFrom)) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tlResult = func((LPNMHDR)lParam); \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// LRESULT OnNotifyCodeHandlerEX(LPNMHDR pnmh)\n#define NOTIFY_CODE_HANDLER_EX(cd, func) \\\n\tif ((uMsg == WM_NOTIFY) && (cd == ((LPNMHDR)lParam)->code)) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tlResult = func((LPNMHDR)lParam); \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnCommandRangeHandlerEX(UINT uNotifyCode, int nID, CWindow wndCtl)\n#define COMMAND_RANGE_HANDLER_EX(idFirst, idLast, func) \\\n\tif((uMsg == WM_COMMAND) && (LOWORD(wParam) >= idFirst) && (LOWORD(wParam) <= idLast)) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((UINT)HIWORD(wParam), (int)LOWORD(wParam), (HWND)lParam); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnCommandRangeCodeHandlerEX(UINT uNotifyCode, int nID, CWindow wndCtl)\n#define COMMAND_RANGE_CODE_HANDLER_EX(idFirst, idLast, code, func) \\\n\tif((uMsg == WM_COMMAND) && (code == HIWORD(wParam)) && (LOWORD(wParam) >= idFirst) && (LOWORD(wParam) <= idLast)) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((UINT)HIWORD(wParam), (int)LOWORD(wParam), (HWND)lParam); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// LRESULT OnNotifyRangeHandlerEX(LPNMHDR pnmh)\n#define NOTIFY_RANGE_HANDLER_EX(idFirst, idLast, func) \\\n\tif((uMsg == WM_NOTIFY) && (((LPNMHDR)lParam)->idFrom >= idFirst) && (((LPNMHDR)lParam)->idFrom <= idLast)) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tlResult = func((LPNMHDR)lParam); \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// LRESULT OnNotifyRangeCodeHandlerEX(LPNMHDR pnmh)\n#define NOTIFY_RANGE_CODE_HANDLER_EX(idFirst, idLast, cd, func) \\\n\tif((uMsg == WM_NOTIFY) && (cd == ((LPNMHDR)lParam)->code) && (((LPNMHDR)lParam)->idFrom >= idFirst) && (((LPNMHDR)lParam)->idFrom <= idLast)) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tlResult = func((LPNMHDR)lParam); \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// LRESULT OnReflectedCommandHandlerEX(UINT uNotifyCode, int nID, CWindow wndCtl)\n#define REFLECTED_COMMAND_HANDLER_EX(id, code, func) \\\n\tif ((uMsg == OCM_COMMAND) && (code == HIWORD(wParam)) && (id == LOWORD(wParam))) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((UINT)HIWORD(wParam), (int)LOWORD(wParam), (HWND)lParam); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// LRESULT OnReflectedCommandIDHandlerEX(UINT uNotifyCode, int nID, CWindow wndCtl)\n#define REFLECTED_COMMAND_ID_HANDLER_EX(id, func) \\\n\tif ((uMsg == OCM_COMMAND) && (id == LOWORD(wParam))) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((UINT)HIWORD(wParam), (int)LOWORD(wParam), (HWND)lParam); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// LRESULT OnReflectedCommandCodeHandlerEX(UINT uNotifyCode, int nID, CWindow wndCtl)\n#define REFLECTED_COMMAND_CODE_HANDLER_EX(code, func) \\\n\tif ((uMsg == OCM_COMMAND) && (code == HIWORD(wParam))) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((UINT)HIWORD(wParam), (int)LOWORD(wParam), (HWND)lParam); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// LRESULT OnReflectedNotifyHandlerEX(LPNMHDR pnmh)\n#define REFLECTED_NOTIFY_HANDLER_EX(id, cd, func) \\\n\tif ((uMsg == OCM_NOTIFY) && (cd == ((LPNMHDR)lParam)->code) && (id == ((LPNMHDR)lParam)->idFrom)) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tlResult = func((LPNMHDR)lParam); \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// LRESULT OnReflectedNotifyIDHandlerEX(LPNMHDR pnmh)\n#define REFLECTED_NOTIFY_ID_HANDLER_EX(id, func) \\\n\tif ((uMsg == OCM_NOTIFY) && (id == ((LPNMHDR)lParam)->idFrom)) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tlResult = func((LPNMHDR)lParam); \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// LRESULT OnReflectedNotifyCodeHandlerEX(LPNMHDR pnmh)\n#define REFLECTED_NOTIFY_CODE_HANDLER_EX(cd, func) \\\n\tif ((uMsg == OCM_NOTIFY) && (cd == ((LPNMHDR)lParam)->code)) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tlResult = func((LPNMHDR)lParam); \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnReflectedCommandRangeHandlerEX(UINT uNotifyCode, int nID, CWindow wndCtl)\n#define REFLECTED_COMMAND_RANGE_HANDLER_EX(idFirst, idLast, func) \\\n\tif((uMsg == OCM_COMMAND) && (LOWORD(wParam) >= idFirst) && (LOWORD(wParam) <= idLast)) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((UINT)HIWORD(wParam), (int)LOWORD(wParam), (HWND)lParam); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnReflectedCommandRangeCodeHandlerEX(UINT uNotifyCode, int nID, CWindow wndCtl)\n#define REFLECTED_COMMAND_RANGE_CODE_HANDLER_EX(idFirst, idLast, code, func) \\\n\tif((uMsg == OCM_COMMAND) && (code == HIWORD(wParam)) && (LOWORD(wParam) >= idFirst) && (LOWORD(wParam) <= idLast)) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc((UINT)HIWORD(wParam), (int)LOWORD(wParam), (HWND)lParam); \\\n\t\tlResult = 0; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// LRESULT OnReflectedNotifyRangeHandlerEX(LPNMHDR pnmh)\n#define REFLECTED_NOTIFY_RANGE_HANDLER_EX(idFirst, idLast, func) \\\n\tif((uMsg == OCM_NOTIFY) && (((LPNMHDR)lParam)->idFrom >= idFirst) && (((LPNMHDR)lParam)->idFrom <= idLast)) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tlResult = func((LPNMHDR)lParam); \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// LRESULT OnReflectedNotifyRangeCodeHandlerEX(LPNMHDR pnmh)\n#define REFLECTED_NOTIFY_RANGE_CODE_HANDLER_EX(idFirst, idLast, cd, func) \\\n\tif((uMsg == OCM_NOTIFY) && (cd == ((LPNMHDR)lParam)->code) && (((LPNMHDR)lParam)->idFrom >= idFirst) && (((LPNMHDR)lParam)->idFrom <= idLast)) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tlResult = func((LPNMHDR)lParam); \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n// void OnAppCommandHandler(UINT uDevice, DWORD dwKeys, CWindow wndFocus)\n#define APPCOMMAND_HANDLER_EX(cmd, func) \\\n\tif((uMsg == WM_APPCOMMAND) && (cmd == GET_APPCOMMAND_LPARAM(lParam))) \\\n\t{ \\\n\t\tthis->SetMsgHandled(TRUE); \\\n\t\tfunc(GET_DEVICE_LPARAM(lParam), GET_KEYSTATE_LPARAM(lParam), (HWND)wParam); \\\n\t\tlResult = TRUE; \\\n\t\tif(this->IsMsgHandled()) \\\n\t\t\treturn TRUE; \\\n\t}\n\n#endif // __ATLCRACK_H__\n"
  },
  {
    "path": "Examples/WhisperDesktop/Utils/WTL/atlctrls.h",
    "content": "// Windows Template Library - WTL version 10.0\n// Copyright (C) Microsoft Corporation, WTL Team. All rights reserved.\n//\n// This file is a part of the Windows Template Library.\n// The use and distribution terms for this software are covered by the\n// Microsoft Public License (http://opensource.org/licenses/MS-PL)\n// which can be found in the file MS-PL.txt at the root folder.\n\n#ifndef __ATLCTRLS_H__\n#define __ATLCTRLS_H__\n\n#pragma once\n\n#ifndef __ATLAPP_H__\n\t#error atlctrls.h requires atlapp.h to be included first\n#endif\n\n#ifndef __ATLWIN_H__\n\t#error atlctrls.h requires atlwin.h to be included first\n#endif\n\n#include <richedit.h>\n#include <richole.h>\n\n#if (_RICHEDIT_VER < 0x0300)\n\t#error WTL10 requires _RICHEDIT_VER >= 0x0300\n#endif\n\n// protect template members from windowsx.h macros\n#ifdef _INC_WINDOWSX\n  #undef GetNextSibling\n  #undef GetPrevSibling\n#endif // _INC_WINDOWSX\n\n\n///////////////////////////////////////////////////////////////////////////////\n// Classes in this file:\n//\n// CStaticT<TBase> - CStatic\n// CButtonT<TBase> - CButton\n// CListBoxT<TBase> - CListBox\n// CComboBoxT<TBase> - CComboBox\n// CEditT<TBase> - CEdit\n// CEditCommands<T>\n// CScrollBarT<TBase> - CScrollBar\n//\n// CImageListT<t_bManaged> - CImageList, CImageListManaged\n// CListViewCtrlT<TBase> - CListViewCtrl\n// CTreeViewCtrlT<TBase> - CTreeViewCtrl\n// CTreeItemT<TBase> - CTreeItem\n// CTreeViewCtrlExT<TBase> - CTreeViewCtrlEx\n// CHeaderCtrlT<TBase> - CHeaderCtrl\n// CToolBarCtrlT<TBase> - CToolBarCtrl\n// CStatusBarCtrlT<TBase> - CStatusBarCtrl\n// CTabCtrlT<TBase> - CTabCtrl\n// CToolInfo\n// CToolTipCtrlT<TBase> - CToolTipCtrl\n// CTrackBarCtrlT<TBase> - CTrackBarCtrl\n// CUpDownCtrlT<TBase> - CUpDownCtrl\n// CProgressBarCtrlT<TBase> - CProgressBarCtrl\n// CHotKeyCtrlT<TBase> - CHotKeyCtrl\n// CAnimateCtrlT<TBase> - CAnimateCtrl\n// CRichEditCtrlT<TBase> - CRichEditCtrl\n// CRichEditCommands<T>\n// CDragListBoxT<TBase> - CDragListBox\n// CDragListNotifyImpl<T>\n// CReBarCtrlT<TBase> - CReBarCtrl\n// CComboBoxExT<TBase> - CComboBoxEx\n// CDateTimePickerCtrlT<TBase> - CDateTimePickerCtrl\n// CMonthCalendarCtrlT<TBase> - CMonthCalendarCtrl\n// CFlatScrollBarImpl<T>\n// CFlatScrollBarT<TBase> - CFlatScrollBar\n// CIPAddressCtrlT<TBase> - CIPAddressCtrl\n// CPagerCtrlT<TBase> - CPagerCtrl\n// CLinkCtrlT<TBase> - CLinkCtrl\n//\n// CCustomDraw<T>\n\n\nnamespace WTL\n{\n\n// These are wrapper classes for Windows standard and common controls.\n// To implement a window based on a control, use following:\n// Example: Implementing a window based on a list box\n//\n// class CMyListBox : CWindowImpl<CMyListBox, CListBox>\n// {\n// public:\n//      BEGIN_MSG_MAP(CMyListBox)\n//          // put your message handler entries here\n//      END_MSG_MAP()\n// };\n\n\n\n// --- Standard Windows controls ---\n\n///////////////////////////////////////////////////////////////////////////////\n// CStatic - client side for a Windows STATIC control\n\ntemplate <class TBase>\nclass CStaticT : public TBase\n{\npublic:\n// Constructors\n\tCStaticT(HWND hWnd = NULL) : TBase(hWnd)\n\t{ }\n\n\tCStaticT< TBase >& operator =(HWND hWnd)\n\t{\n\t\tthis->m_hWnd = hWnd;\n\t\treturn *this;\n\t}\n\n\tHWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL,\n\t\t\tDWORD dwStyle = 0, DWORD dwExStyle = 0,\n\t\t\tATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL)\n\t{\n\t\treturn TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam);\n\t}\n\n// Attributes\n\tstatic LPCTSTR GetWndClassName()\n\t{\n\t\treturn _T(\"STATIC\");\n\t}\n\n\tHICON GetIcon() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (HICON)::SendMessage(this->m_hWnd, STM_GETICON, 0, 0L);\n\t}\n\n\tHICON SetIcon(HICON hIcon)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (HICON)::SendMessage(this->m_hWnd, STM_SETICON, (WPARAM)hIcon, 0L);\n\t}\n\n\tHENHMETAFILE GetEnhMetaFile() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (HENHMETAFILE)::SendMessage(this->m_hWnd, STM_GETIMAGE, IMAGE_ENHMETAFILE, 0L);\n\t}\n\n\tHENHMETAFILE SetEnhMetaFile(HENHMETAFILE hMetaFile)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (HENHMETAFILE)::SendMessage(this->m_hWnd, STM_SETIMAGE, IMAGE_ENHMETAFILE, (LPARAM)hMetaFile);\n\t}\n\n\tCBitmapHandle GetBitmap() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn CBitmapHandle((HBITMAP)::SendMessage(this->m_hWnd, STM_GETIMAGE, IMAGE_BITMAP, 0L));\n\t}\n\n\tCBitmapHandle SetBitmap(HBITMAP hBitmap)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn CBitmapHandle((HBITMAP)::SendMessage(this->m_hWnd, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hBitmap));\n\t}\n\n\tHCURSOR GetCursor() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (HCURSOR)::SendMessage(this->m_hWnd, STM_GETIMAGE, IMAGE_CURSOR, 0L);\n\t}\n\n\tHCURSOR SetCursor(HCURSOR hCursor)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (HCURSOR)::SendMessage(this->m_hWnd, STM_SETIMAGE, IMAGE_CURSOR, (LPARAM)hCursor);\n\t}\n};\n\ntypedef CStaticT<ATL::CWindow>   CStatic;\n\n\n///////////////////////////////////////////////////////////////////////////////\n// CButton - client side for a Windows BUTTON control\n\ntemplate <class TBase>\nclass CButtonT : public TBase\n{\npublic:\n// Constructors\n\tCButtonT(HWND hWnd = NULL) : TBase(hWnd)\n\t{ }\n\n\tCButtonT< TBase >& operator =(HWND hWnd)\n\t{\n\t\tthis->m_hWnd = hWnd;\n\t\treturn *this;\n\t}\n\n\tHWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL,\n\t\t\tDWORD dwStyle = 0, DWORD dwExStyle = 0,\n\t\t\tATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL)\n\t{\n\t\treturn TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam);\n\t}\n\n// Attributes\n\tstatic LPCTSTR GetWndClassName()\n\t{\n\t\treturn _T(\"BUTTON\");\n\t}\n\n\tUINT GetState() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (UINT)::SendMessage(this->m_hWnd, BM_GETSTATE, 0, 0L);\n\t}\n\n\tvoid SetState(BOOL bHighlight)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, BM_SETSTATE, bHighlight, 0L);\n\t}\n\n\tint GetCheck() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, BM_GETCHECK, 0, 0L);\n\t}\n\n\tvoid SetCheck(int nCheck)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, BM_SETCHECK, nCheck, 0L);\n\t}\n\n\tUINT GetButtonStyle() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (UINT)::GetWindowLong(this->m_hWnd, GWL_STYLE) & 0xFFFF;\n\t}\n\n\tvoid SetButtonStyle(UINT nStyle, BOOL bRedraw = TRUE)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, BM_SETSTYLE, nStyle, (LPARAM)bRedraw);\n\t}\n\n\tHICON GetIcon() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (HICON)::SendMessage(this->m_hWnd, BM_GETIMAGE, IMAGE_ICON, 0L);\n\t}\n\n\tHICON SetIcon(HICON hIcon)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (HICON)::SendMessage(this->m_hWnd, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon);\n\t}\n\n\tCBitmapHandle GetBitmap() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn CBitmapHandle((HBITMAP)::SendMessage(this->m_hWnd, BM_GETIMAGE, IMAGE_BITMAP, 0L));\n\t}\n\n\tCBitmapHandle SetBitmap(HBITMAP hBitmap)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn CBitmapHandle((HBITMAP)::SendMessage(this->m_hWnd, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hBitmap));\n\t}\n\n\tBOOL GetIdealSize(LPSIZE lpSize) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, BCM_GETIDEALSIZE, 0, (LPARAM)lpSize);\n\t}\n\n\tBOOL GetImageList(PBUTTON_IMAGELIST pButtonImagelist) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, BCM_GETIMAGELIST, 0, (LPARAM)pButtonImagelist);\n\t}\n\n\tBOOL SetImageList(PBUTTON_IMAGELIST pButtonImagelist)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, BCM_SETIMAGELIST, 0, (LPARAM)pButtonImagelist);\n\t}\n\n\tBOOL GetTextMargin(LPRECT lpRect) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, BCM_GETTEXTMARGIN, 0, (LPARAM)lpRect);\n\t}\n\n\tBOOL SetTextMargin(LPRECT lpRect)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, BCM_SETTEXTMARGIN, 0, (LPARAM)lpRect);\n\t}\n\n#if (WINVER >= 0x0600)\n\tvoid SetDontClick(BOOL bDontClick)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, BM_SETDONTCLICK, (WPARAM)bDontClick, 0L);\n\t}\n#endif // (WINVER >= 0x0600)\n\n#if (_WIN32_WINNT >= 0x0600)\n\tBOOL SetDropDownState(BOOL bDropDown)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT((this->GetStyle() & (BS_SPLITBUTTON | BS_DEFSPLITBUTTON)) != 0);\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, BCM_SETDROPDOWNSTATE, (WPARAM)bDropDown, 0L);\n\t}\n\n\tBOOL GetSplitInfo(PBUTTON_SPLITINFO pSplitInfo) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT((this->GetStyle() & (BS_SPLITBUTTON | BS_DEFSPLITBUTTON)) != 0);\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, BCM_GETSPLITINFO, 0, (LPARAM)pSplitInfo);\n\t}\n\n\tBOOL SetSplitInfo(PBUTTON_SPLITINFO pSplitInfo)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT((this->GetStyle() & (BS_SPLITBUTTON | BS_DEFSPLITBUTTON)) != 0);\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, BCM_SETSPLITINFO, 0, (LPARAM)pSplitInfo);\n\t}\n\n\tint GetNoteLength() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT((this->GetStyle() & (BS_COMMANDLINK | BS_DEFCOMMANDLINK)) != 0);\n\t\treturn (int)::SendMessage(this->m_hWnd, BCM_GETNOTELENGTH, 0, 0L);\n\t}\n\n\tBOOL GetNote(LPWSTR lpstrNoteText, int cchNoteText) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT((this->GetStyle() & (BS_COMMANDLINK | BS_DEFCOMMANDLINK)) != 0);\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, BCM_GETNOTE, cchNoteText, (LPARAM)lpstrNoteText);\n\t}\n\n\tBOOL SetNote(LPCWSTR lpstrNoteText)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT((this->GetStyle() & (BS_COMMANDLINK | BS_DEFCOMMANDLINK)) != 0);\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, BCM_SETNOTE, 0, (LPARAM)lpstrNoteText);\n\t}\n\n\tLRESULT SetElevationRequiredState(BOOL bSet)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn ::SendMessage(this->m_hWnd, BCM_SETSHIELD, 0, (LPARAM)bSet);\n\t}\n#endif // (_WIN32_WINNT >= 0x0600)\n\n// Operations\n\tvoid Click()\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, BM_CLICK, 0, 0L);\n\t}\n};\n\ntypedef CButtonT<ATL::CWindow>   CButton;\n\n\n///////////////////////////////////////////////////////////////////////////////\n// CListBox - client side for a Windows LISTBOX control\n\ntemplate <class TBase>\nclass CListBoxT : public TBase\n{\npublic:\n// Constructors\n\tCListBoxT(HWND hWnd = NULL) : TBase(hWnd)\n\t{ }\n\n\tCListBoxT< TBase >& operator =(HWND hWnd)\n\t{\n\t\tthis->m_hWnd = hWnd;\n\t\treturn *this;\n\t}\n\n\tHWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL,\n\t\t\tDWORD dwStyle = 0, DWORD dwExStyle = 0,\n\t\t\tATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL)\n\t{\n\t\treturn TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam);\n\t}\n\n// Attributes\n\tstatic LPCTSTR GetWndClassName()\n\t{\n\t\treturn _T(\"LISTBOX\");\n\t}\n\n\t// for entire listbox\n\tint GetCount() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, LB_GETCOUNT, 0, 0L);\n\t}\n\n\tint SetCount(int cItems)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT(((this->GetStyle() & LBS_NODATA) != 0) && ((this->GetStyle() & LBS_HASSTRINGS) == 0));\n\t\treturn (int)::SendMessage(this->m_hWnd, LB_SETCOUNT, cItems, 0L);\n\t}\n\n\tint GetHorizontalExtent() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, LB_GETHORIZONTALEXTENT, 0, 0L);\n\t}\n\n\tvoid SetHorizontalExtent(int cxExtent)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, LB_SETHORIZONTALEXTENT, cxExtent, 0L);\n\t}\n\n\tint GetTopIndex() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, LB_GETTOPINDEX, 0, 0L);\n\t}\n\n\tint SetTopIndex(int nIndex)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, LB_SETTOPINDEX, nIndex, 0L);\n\t}\n\n\tLCID GetLocale() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (LCID)::SendMessage(this->m_hWnd, LB_GETLOCALE, 0, 0L);\n\t}\n\n\tLCID SetLocale(LCID nNewLocale)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (LCID)::SendMessage(this->m_hWnd, LB_SETLOCALE, (WPARAM)nNewLocale, 0L);\n\t}\n\n\tDWORD GetListBoxInfo() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (DWORD)::SendMessage(this->m_hWnd, LB_GETLISTBOXINFO, 0, 0L);\n\t}\n\n\t// for single-selection listboxes\n\tint GetCurSel() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT((this->GetStyle() & (LBS_MULTIPLESEL | LBS_EXTENDEDSEL)) == 0);\n\t\treturn (int)::SendMessage(this->m_hWnd, LB_GETCURSEL, 0, 0L);\n\t}\n\n\tint SetCurSel(int nSelect)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT((this->GetStyle() & (LBS_MULTIPLESEL | LBS_EXTENDEDSEL)) == 0);\n\t\treturn (int)::SendMessage(this->m_hWnd, LB_SETCURSEL, nSelect, 0L);\n\t}\n\n\t// for multiple-selection listboxes\n\tint GetSel(int nIndex) const           // also works for single-selection\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, LB_GETSEL, nIndex, 0L);\n\t}\n\n\tint SetSel(int nIndex, BOOL bSelect = TRUE)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT((this->GetStyle() & (LBS_MULTIPLESEL | LBS_EXTENDEDSEL)) != 0);\n\t\treturn (int)::SendMessage(this->m_hWnd, LB_SETSEL, bSelect, nIndex);\n\t}\n\n\tint GetSelCount() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT((this->GetStyle() & (LBS_MULTIPLESEL | LBS_EXTENDEDSEL)) != 0);\n\t\treturn (int)::SendMessage(this->m_hWnd, LB_GETSELCOUNT, 0, 0L);\n\t}\n\n\tint GetSelItems(int nMaxItems, LPINT rgIndex) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT((this->GetStyle() & (LBS_MULTIPLESEL | LBS_EXTENDEDSEL)) != 0);\n\t\treturn (int)::SendMessage(this->m_hWnd, LB_GETSELITEMS, nMaxItems, (LPARAM)rgIndex);\n\t}\n\n\tint GetAnchorIndex() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT((this->GetStyle() & (LBS_MULTIPLESEL | LBS_EXTENDEDSEL)) != 0);\n\t\treturn (int)::SendMessage(this->m_hWnd, LB_GETANCHORINDEX, 0, 0L);\n\t}\n\n\tvoid SetAnchorIndex(int nIndex)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT((this->GetStyle() & (LBS_MULTIPLESEL | LBS_EXTENDEDSEL)) != 0);\n\t\t::SendMessage(this->m_hWnd, LB_SETANCHORINDEX, nIndex, 0L);\n\t}\n\n\tint GetCaretIndex() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, LB_GETCARETINDEX, 0, 0);\n\t}\n\n\tint SetCaretIndex(int nIndex, BOOL bScroll = TRUE)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, LB_SETCARETINDEX, nIndex, MAKELONG(bScroll, 0));\n\t}\n\n\t// for listbox items\n\tDWORD_PTR GetItemData(int nIndex) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (DWORD_PTR)::SendMessage(this->m_hWnd, LB_GETITEMDATA, nIndex, 0L);\n\t}\n\n\tint SetItemData(int nIndex, DWORD_PTR dwItemData)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, LB_SETITEMDATA, nIndex, (LPARAM)dwItemData);\n\t}\n\n\tvoid* GetItemDataPtr(int nIndex) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (void*)::SendMessage(this->m_hWnd, LB_GETITEMDATA, nIndex, 0L);\n\t}\n\n\tint SetItemDataPtr(int nIndex, void* pData)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn SetItemData(nIndex, (DWORD_PTR)pData);\n\t}\n\n\tint GetItemRect(int nIndex, LPRECT lpRect) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, LB_GETITEMRECT, nIndex, (LPARAM)lpRect);\n\t}\n\n\tint GetText(int nIndex, LPTSTR lpszBuffer) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, LB_GETTEXT, nIndex, (LPARAM)lpszBuffer);\n\t}\n\n#ifdef _OLEAUTO_H_\n\tBOOL GetTextBSTR(int nIndex, BSTR& bstrText) const\n\t{\n\t\tUSES_CONVERSION;\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT(bstrText == NULL);\n\n\t\tint nLen = GetTextLen(nIndex);\n\t\tif(nLen == LB_ERR)\n\t\t\treturn FALSE;\n\n\t\tATL::CTempBuffer<TCHAR, _WTL_STACK_ALLOC_THRESHOLD> buff;\n\t\tLPTSTR lpstrText = buff.Allocate(nLen + 1);\n\t\tif(lpstrText == NULL)\n\t\t\treturn FALSE;\n\n\t\tif(GetText(nIndex, lpstrText) == LB_ERR)\n\t\t\treturn FALSE;\n\n\t\tbstrText = ::SysAllocString(T2OLE(lpstrText));\n\t\treturn (bstrText != NULL) ? TRUE : FALSE;\n\t}\n#endif // _OLEAUTO_H_\n\n#ifdef __ATLSTR_H__\n\tint GetText(int nIndex, ATL::CString& strText) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tint cchLen = GetTextLen(nIndex);\n\t\tif(cchLen == LB_ERR)\n\t\t\treturn LB_ERR;\n\t\tint nRet = LB_ERR;\n\t\tLPTSTR lpstr = strText.GetBufferSetLength(cchLen);\n\t\tif(lpstr != NULL)\n\t\t{\n\t\t\tnRet = GetText(nIndex, lpstr);\n\t\t\tstrText.ReleaseBuffer();\n\t\t}\n\t\treturn nRet;\n\t}\n#endif // __ATLSTR_H__\n\n\tint GetTextLen(int nIndex) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, LB_GETTEXTLEN, nIndex, 0L);\n\t}\n\n\tint GetItemHeight(int nIndex) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, LB_GETITEMHEIGHT, nIndex, 0L);\n\t}\n\n\tint SetItemHeight(int nIndex, UINT cyItemHeight)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, LB_SETITEMHEIGHT, nIndex, MAKELONG(cyItemHeight, 0));\n\t}\n\n\t// Settable only attributes\n\tvoid SetColumnWidth(int cxWidth)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, LB_SETCOLUMNWIDTH, cxWidth, 0L);\n\t}\n\n\tBOOL SetTabStops(int nTabStops, LPINT rgTabStops)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT((this->GetStyle() & LBS_USETABSTOPS) != 0);\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, LB_SETTABSTOPS, nTabStops, (LPARAM)rgTabStops);\n\t}\n\n\tBOOL SetTabStops()\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT((this->GetStyle() & LBS_USETABSTOPS) != 0);\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, LB_SETTABSTOPS, 0, 0L);\n\t}\n\n\tBOOL SetTabStops(const int& cxEachStop)    // takes an 'int'\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT((this->GetStyle() & LBS_USETABSTOPS) != 0);\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, LB_SETTABSTOPS, 1, (LPARAM)(LPINT)&cxEachStop);\n\t}\n\n// Operations\n\tint InitStorage(int nItems, UINT nBytes)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, LB_INITSTORAGE, (WPARAM)nItems, nBytes);\n\t}\n\n\tvoid ResetContent()\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, LB_RESETCONTENT, 0, 0L);\n\t}\n\n\tUINT ItemFromPoint(POINT pt, BOOL& bOutside) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tDWORD dw = (DWORD)::SendMessage(this->m_hWnd, LB_ITEMFROMPOINT, 0, MAKELPARAM(pt.x, pt.y));\n\t\tbOutside = (BOOL)HIWORD(dw);\n\t\treturn (UINT)LOWORD(dw);\n\t}\n\n\t// manipulating listbox items\n\tint AddString(LPCTSTR lpszItem)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, LB_ADDSTRING, 0, (LPARAM)lpszItem);\n\t}\n\n\tint DeleteString(UINT nIndex)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, LB_DELETESTRING, nIndex, 0L);\n\t}\n\n\tint InsertString(int nIndex, LPCTSTR lpszItem)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, LB_INSERTSTRING, nIndex, (LPARAM)lpszItem);\n\t}\n\n\tint Dir(UINT attr, LPCTSTR lpszWildCard)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, LB_DIR, attr, (LPARAM)lpszWildCard);\n\t}\n\n\tint AddFile(LPCTSTR lpstrFileName)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, LB_ADDFILE, 0, (LPARAM)lpstrFileName);\n\t}\n\n\t// selection helpers\n\tint FindString(int nStartAfter, LPCTSTR lpszItem) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, LB_FINDSTRING, nStartAfter, (LPARAM)lpszItem);\n\t}\n\n\tint FindStringExact(int nIndexStart, LPCTSTR lpszFind) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, LB_FINDSTRINGEXACT, nIndexStart, (LPARAM)lpszFind);\n\t}\n\n\tint SelectString(int nStartAfter, LPCTSTR lpszItem)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, LB_SELECTSTRING, nStartAfter, (LPARAM)lpszItem);\n\t}\n\n\tint SelItemRange(BOOL bSelect, int nFirstItem, int nLastItem)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT((this->GetStyle() & (LBS_MULTIPLESEL | LBS_EXTENDEDSEL)) != 0);\n\t\tATLASSERT(nFirstItem <= nLastItem);\n\t\treturn bSelect ? (int)::SendMessage(this->m_hWnd, LB_SELITEMRANGEEX, nFirstItem, nLastItem) : (int)::SendMessage(this->m_hWnd, LB_SELITEMRANGEEX, nLastItem, nFirstItem);\n\t}\n};\n\ntypedef CListBoxT<ATL::CWindow>   CListBox;\n\n\n///////////////////////////////////////////////////////////////////////////////\n// CComboBox - client side for a Windows COMBOBOX control\n\ntemplate <class TBase>\nclass CComboBoxT : public TBase\n{\npublic:\n// Constructors\n\tCComboBoxT(HWND hWnd = NULL) : TBase(hWnd)\n\t{ }\n\n\tCComboBoxT< TBase >& operator =(HWND hWnd)\n\t{\n\t\tthis->m_hWnd = hWnd;\n\t\treturn *this;\n\t}\n\n\tHWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL,\n\t\t\tDWORD dwStyle = 0, DWORD dwExStyle = 0,\n\t\t\tATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL)\n\t{\n\t\treturn TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam);\n\t}\n\n// Attributes\n\tstatic LPCTSTR GetWndClassName()\n\t{\n\t\treturn _T(\"COMBOBOX\");\n\t}\n\n\t// for entire combo box\n\tint GetCount() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, CB_GETCOUNT, 0, 0L);\n\t}\n\n\tint GetCurSel() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, CB_GETCURSEL, 0, 0L);\n\t}\n\n\tint SetCurSel(int nSelect)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, CB_SETCURSEL, nSelect, 0L);\n\t}\n\n\tLCID GetLocale() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (LCID)::SendMessage(this->m_hWnd, CB_GETLOCALE, 0, 0L);\n\t}\n\n\tLCID SetLocale(LCID nNewLocale)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (LCID)::SendMessage(this->m_hWnd, CB_SETLOCALE, (WPARAM)nNewLocale, 0L);\n\t}\n\n\tint GetTopIndex() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, CB_GETTOPINDEX, 0, 0L);\n\t}\n\n\tint SetTopIndex(int nIndex)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, CB_SETTOPINDEX, nIndex, 0L);\n\t}\n\n\tUINT GetHorizontalExtent() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (UINT)::SendMessage(this->m_hWnd, CB_GETHORIZONTALEXTENT, 0, 0L);\n\t}\n\n\tvoid SetHorizontalExtent(UINT nExtent)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, CB_SETHORIZONTALEXTENT, nExtent, 0L);\n\t}\n\n\tint GetDroppedWidth() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, CB_GETDROPPEDWIDTH, 0, 0L);\n\t}\n\n\tint SetDroppedWidth(UINT nWidth)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, CB_SETDROPPEDWIDTH, nWidth, 0L);\n\t}\n\n\tBOOL GetComboBoxInfo(PCOMBOBOXINFO pComboBoxInfo) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, CB_GETCOMBOBOXINFO, 0, (LPARAM)pComboBoxInfo);\n\t}\n\n\t// for edit control\n\tDWORD GetEditSel() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (DWORD)::SendMessage(this->m_hWnd, CB_GETEDITSEL, 0, 0L);\n\t}\n\n\tBOOL SetEditSel(int nStartChar, int nEndChar)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, CB_SETEDITSEL, 0, MAKELONG(nStartChar, nEndChar));\n\t}\n\n\t// for combobox item\n\tDWORD_PTR GetItemData(int nIndex) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (DWORD_PTR)::SendMessage(this->m_hWnd, CB_GETITEMDATA, nIndex, 0L);\n\t}\n\n\tint SetItemData(int nIndex, DWORD_PTR dwItemData)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, CB_SETITEMDATA, nIndex, (LPARAM)dwItemData);\n\t}\n\n\tvoid* GetItemDataPtr(int nIndex) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (void*)GetItemData(nIndex);\n\t}\n\n\tint SetItemDataPtr(int nIndex, void* pData)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn SetItemData(nIndex, (DWORD_PTR)pData);\n\t}\n\n\tint GetLBText(int nIndex, LPTSTR lpszText) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, CB_GETLBTEXT, nIndex, (LPARAM)lpszText);\n\t}\n\n\tBOOL GetLBTextBSTR(int nIndex, BSTR& bstrText) const\n\t{\n\t\tUSES_CONVERSION;\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT(bstrText == NULL);\n\n\t\tint nLen = GetLBTextLen(nIndex);\n\t\tif(nLen == CB_ERR)\n\t\t\treturn FALSE;\n\n\t\tATL::CTempBuffer<TCHAR, _WTL_STACK_ALLOC_THRESHOLD> buff;\n\t\tLPTSTR lpstrText = buff.Allocate(nLen + 1);\n\t\tif(lpstrText == NULL)\n\t\t\treturn FALSE;\n\n\t\tif(GetLBText(nIndex, lpstrText) == CB_ERR)\n\t\t\treturn FALSE;\n\n\t\tbstrText = ::SysAllocString(T2OLE(lpstrText));\n\t\treturn (bstrText != NULL) ? TRUE : FALSE;\n\t}\n\n#ifdef __ATLSTR_H__\n\tint GetLBText(int nIndex, ATL::CString& strText) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tint cchLen = GetLBTextLen(nIndex);\n\t\tif(cchLen == CB_ERR)\n\t\t\treturn CB_ERR;\n\t\tint nRet = CB_ERR;\n\t\tLPTSTR lpstr = strText.GetBufferSetLength(cchLen);\n\t\tif(lpstr != NULL)\n\t\t{\n\t\t\tnRet = GetLBText(nIndex, lpstr);\n\t\t\tstrText.ReleaseBuffer();\n\t\t}\n\t\treturn nRet;\n\t}\n#endif // __ATLSTR_H__\n\n\tint GetLBTextLen(int nIndex) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, CB_GETLBTEXTLEN, nIndex, 0L);\n\t}\n\n\tint GetItemHeight(int nIndex) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, CB_GETITEMHEIGHT, nIndex, 0L);\n\t}\n\n\tint SetItemHeight(int nIndex, UINT cyItemHeight)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, CB_SETITEMHEIGHT, nIndex, MAKELONG(cyItemHeight, 0));\n\t}\n\n\tBOOL GetExtendedUI() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, CB_GETEXTENDEDUI, 0, 0L);\n\t}\n\n\tint SetExtendedUI(BOOL bExtended = TRUE)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, CB_SETEXTENDEDUI, bExtended, 0L);\n\t}\n\n\tvoid GetDroppedControlRect(LPRECT lprect) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)lprect);\n\t}\n\n\tBOOL GetDroppedState() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, CB_GETDROPPEDSTATE, 0, 0L);\n\t}\n\n\tint GetMinVisible() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, CB_GETMINVISIBLE, 0, 0L);\n\t}\n\n\tBOOL SetMinVisible(int nMinVisible)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, CB_SETMINVISIBLE, nMinVisible, 0L);\n\t}\n\n\t// Vista only\n\tBOOL GetCueBannerText(LPWSTR lpwText, int cchText) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, CB_GETCUEBANNER, (WPARAM)lpwText, cchText);\n\t}\n\n\t// Vista only\n\tBOOL SetCueBannerText(LPCWSTR lpcwText)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, CB_SETCUEBANNER, 0, (LPARAM)lpcwText);\n\t}\n\n// Operations\n\tint InitStorage(int nItems, UINT nBytes)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, CB_INITSTORAGE, (WPARAM)nItems, nBytes);\n\t}\n\n\tvoid ResetContent()\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, CB_RESETCONTENT, 0, 0L);\n\t}\n\n\t// for edit control\n\tBOOL LimitText(int nMaxChars)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, CB_LIMITTEXT, nMaxChars, 0L);\n\t}\n\n\t// for drop-down combo boxes\n\tvoid ShowDropDown(BOOL bShowIt = TRUE)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, CB_SHOWDROPDOWN, bShowIt, 0L);\n\t}\n\n\t// manipulating listbox items\n\tint AddString(LPCTSTR lpszString)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, CB_ADDSTRING, 0, (LPARAM)lpszString);\n\t}\n\n\tint DeleteString(UINT nIndex)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, CB_DELETESTRING, nIndex, 0L);\n\t}\n\n\tint InsertString(int nIndex, LPCTSTR lpszString)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, CB_INSERTSTRING, nIndex, (LPARAM)lpszString);\n\t}\n\n\tint Dir(UINT attr, LPCTSTR lpszWildCard)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, CB_DIR, attr, (LPARAM)lpszWildCard);\n\t}\n\n\t// selection helpers\n\tint FindString(int nStartAfter, LPCTSTR lpszString) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, CB_FINDSTRING, nStartAfter, (LPARAM)lpszString);\n\t}\n\n\tint FindStringExact(int nIndexStart, LPCTSTR lpszFind) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, CB_FINDSTRINGEXACT, nIndexStart, (LPARAM)lpszFind);\n\t}\n\n\tint SelectString(int nStartAfter, LPCTSTR lpszString)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, CB_SELECTSTRING, nStartAfter, (LPARAM)lpszString);\n\t}\n\n\t// Clipboard operations\n\tvoid Clear()\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, WM_CLEAR, 0, 0L);\n\t}\n\n\tvoid Copy()\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, WM_COPY, 0, 0L);\n\t}\n\n\tvoid Cut()\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, WM_CUT, 0, 0L);\n\t}\n\n\tvoid Paste()\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, WM_PASTE, 0, 0L);\n\t}\n};\n\ntypedef CComboBoxT<ATL::CWindow>   CComboBox;\n\n\n///////////////////////////////////////////////////////////////////////////////\n// CEdit - client side for a Windows EDIT control\n\ntemplate <class TBase>\nclass CEditT : public TBase\n{\npublic:\n// Constructors\n\tCEditT(HWND hWnd = NULL) : TBase(hWnd)\n\t{ }\n\n\tCEditT< TBase >& operator =(HWND hWnd)\n\t{\n\t\tthis->m_hWnd = hWnd;\n\t\treturn *this;\n\t}\n\n\tHWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL,\n\t\t\tDWORD dwStyle = 0, DWORD dwExStyle = 0,\n\t\t\tATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL)\n\t{\n\t\treturn TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam);\n\t}\n\n// Attributes\n\tstatic LPCTSTR GetWndClassName()\n\t{\n\t\treturn _T(\"EDIT\");\n\t}\n\n\tBOOL CanUndo() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, EM_CANUNDO, 0, 0L);\n\t}\n\n\tint GetLineCount() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, EM_GETLINECOUNT, 0, 0L);\n\t}\n\n\tBOOL GetModify() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, EM_GETMODIFY, 0, 0L);\n\t}\n\n\tvoid SetModify(BOOL bModified = TRUE)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, EM_SETMODIFY, bModified, 0L);\n\t}\n\n\tvoid GetRect(LPRECT lpRect) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, EM_GETRECT, 0, (LPARAM)lpRect);\n\t}\n\n\tDWORD GetSel() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (DWORD)::SendMessage(this->m_hWnd, EM_GETSEL, 0, 0L);\n\t}\n\n\tvoid GetSel(int& nStartChar, int& nEndChar) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, EM_GETSEL, (WPARAM)&nStartChar, (LPARAM)&nEndChar);\n\t}\n\n\tHLOCAL GetHandle() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (HLOCAL)::SendMessage(this->m_hWnd, EM_GETHANDLE, 0, 0L);\n\t}\n\n\tvoid SetHandle(HLOCAL hBuffer)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, EM_SETHANDLE, (WPARAM)hBuffer, 0L);\n\t}\n\n\tDWORD GetMargins() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (DWORD)::SendMessage(this->m_hWnd, EM_GETMARGINS, 0, 0L);\n\t}\n\n\tvoid GetMargins(UINT& nLeft, UINT& nRight) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tDWORD dwRet = (DWORD)::SendMessage(this->m_hWnd, EM_GETMARGINS, 0, 0L);\n\t\tnLeft = LOWORD(dwRet);\n\t\tnRight = HIWORD(dwRet);\n\t}\n\n\tvoid SetMargins(UINT nLeft, UINT nRight, WORD wFlags = EC_LEFTMARGIN | EC_RIGHTMARGIN)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, EM_SETMARGINS, wFlags, MAKELONG(nLeft, nRight));\n\t}\n\n\tUINT GetLimitText() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (UINT)::SendMessage(this->m_hWnd, EM_GETLIMITTEXT, 0, 0L);\n\t}\n\n\tvoid SetLimitText(UINT nMax)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, EM_SETLIMITTEXT, nMax, 0L);\n\t}\n\n\tPOINT PosFromChar(UINT nChar) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tDWORD dwRet = (DWORD)::SendMessage(this->m_hWnd, EM_POSFROMCHAR, nChar, 0);\n\t\tPOINT point = { GET_X_LPARAM(dwRet), GET_Y_LPARAM(dwRet) };\n\t\treturn point;\n\t}\n\n\tint CharFromPos(POINT pt, int* pLine = NULL) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tDWORD dwRet = (DWORD)::SendMessage(this->m_hWnd, EM_CHARFROMPOS, 0, MAKELPARAM(pt.x, pt.y));\n\t\tif(pLine != NULL)\n\t\t\t*pLine = (int)(short)HIWORD(dwRet);\n\t\treturn (int)(short)LOWORD(dwRet);\n\t}\n\n\t// NOTE: first word in lpszBuffer must contain the size of the buffer!\n\tint GetLine(int nIndex, LPTSTR lpszBuffer) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, EM_GETLINE, nIndex, (LPARAM)lpszBuffer);\n\t}\n\n\tint GetLine(int nIndex, LPTSTR lpszBuffer, int nMaxLength) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t*(LPWORD)lpszBuffer = (WORD)nMaxLength;\n\t\treturn (int)::SendMessage(this->m_hWnd, EM_GETLINE, nIndex, (LPARAM)lpszBuffer);\n\t}\n\n\tTCHAR GetPasswordChar() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (TCHAR)::SendMessage(this->m_hWnd, EM_GETPASSWORDCHAR, 0, 0L);\n\t}\n\n\tvoid SetPasswordChar(TCHAR ch)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, EM_SETPASSWORDCHAR, ch, 0L);\n\t}\n\n\tEDITWORDBREAKPROC GetWordBreakProc() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (EDITWORDBREAKPROC)::SendMessage(this->m_hWnd, EM_GETWORDBREAKPROC, 0, 0L);\n\t}\n\n\tvoid SetWordBreakProc(EDITWORDBREAKPROC ewbprc)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, EM_SETWORDBREAKPROC, 0, (LPARAM)ewbprc);\n\t}\n\n\tint GetFirstVisibleLine() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, EM_GETFIRSTVISIBLELINE, 0, 0L);\n\t}\n\n\tint GetThumb() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT((this->GetStyle() & ES_MULTILINE) != 0);\n\t\treturn (int)::SendMessage(this->m_hWnd, EM_GETTHUMB, 0, 0L);\n\t}\n\n\tBOOL SetReadOnly(BOOL bReadOnly = TRUE)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, EM_SETREADONLY, bReadOnly, 0L);\n\t}\n\n\tUINT GetImeStatus(UINT uStatus) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (UINT)::SendMessage(this->m_hWnd, EM_GETIMESTATUS, uStatus, 0L);\n\t}\n\n\tUINT SetImeStatus(UINT uStatus, UINT uData)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (UINT)::SendMessage(this->m_hWnd, EM_SETIMESTATUS, uStatus, uData);\n\t}\n\n\tBOOL GetCueBannerText(LPCWSTR lpstrText, int cchText) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, EM_GETCUEBANNER, (WPARAM)lpstrText, cchText);\n\t}\n\n\t// bKeepWithFocus - Vista only\n\tBOOL SetCueBannerText(LPCWSTR lpstrText, BOOL bKeepWithFocus = FALSE)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, EM_SETCUEBANNER, (WPARAM)bKeepWithFocus, (LPARAM)(lpstrText));\n\t}\n\n// Operations\n\tvoid EmptyUndoBuffer()\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, EM_EMPTYUNDOBUFFER, 0, 0L);\n\t}\n\n\tBOOL FmtLines(BOOL bAddEOL)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, EM_FMTLINES, bAddEOL, 0L);\n\t}\n\n\tvoid LimitText(int nChars = 0)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, EM_LIMITTEXT, nChars, 0L);\n\t}\n\n\tint LineFromChar(int nIndex = -1) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, EM_LINEFROMCHAR, nIndex, 0L);\n\t}\n\n\tint LineIndex(int nLine = -1) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, EM_LINEINDEX, nLine, 0L);\n\t}\n\n\tint LineLength(int nLine = -1) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, EM_LINELENGTH, nLine, 0L);\n\t}\n\n\tvoid LineScroll(int nLines, int nChars = 0)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, EM_LINESCROLL, nChars, nLines);\n\t}\n\n\tvoid ReplaceSel(LPCTSTR lpszNewText, BOOL bCanUndo = FALSE)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, EM_REPLACESEL, (WPARAM) bCanUndo, (LPARAM)lpszNewText);\n\t}\n\n\tvoid SetRect(LPCRECT lpRect)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, EM_SETRECT, 0, (LPARAM)lpRect);\n\t}\n\n\tvoid SetRectNP(LPCRECT lpRect)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, EM_SETRECTNP, 0, (LPARAM)lpRect);\n\t}\n\n\tvoid SetSel(DWORD dwSelection, BOOL bNoScroll = FALSE)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, EM_SETSEL, LOWORD(dwSelection), HIWORD(dwSelection));\n\t\tif(!bNoScroll)\n\t\t\t::SendMessage(this->m_hWnd, EM_SCROLLCARET, 0, 0L);\n\t}\n\n\tvoid SetSel(int nStartChar, int nEndChar, BOOL bNoScroll = FALSE)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, EM_SETSEL, nStartChar, nEndChar);\n\t\tif(!bNoScroll)\n\t\t\t::SendMessage(this->m_hWnd, EM_SCROLLCARET, 0, 0L);\n\t}\n\n\tvoid SetSelAll(BOOL bNoScroll = FALSE)\n\t{\n\t\tSetSel(0, -1, bNoScroll);\n\t}\n\n\tvoid SetSelNone(BOOL bNoScroll = FALSE)\n\t{\n\t\tSetSel(-1, 0, bNoScroll);\n\t}\n\n\tBOOL SetTabStops(int nTabStops, LPINT rgTabStops)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, EM_SETTABSTOPS, nTabStops, (LPARAM)rgTabStops);\n\t}\n\n\tBOOL SetTabStops()\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, EM_SETTABSTOPS, 0, 0L);\n\t}\n\n\tBOOL SetTabStops(const int& cxEachStop)    // takes an 'int'\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, EM_SETTABSTOPS, 1, (LPARAM)(LPINT)&cxEachStop);\n\t}\n\n\tvoid ScrollCaret()\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, EM_SCROLLCARET, 0, 0L);\n\t}\n\n\tint Scroll(int nScrollAction)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT((this->GetStyle() & ES_MULTILINE) != 0);\n\t\tLRESULT lRet = ::SendMessage(this->m_hWnd, EM_SCROLL, nScrollAction, 0L);\n\t\tif(!(BOOL)HIWORD(lRet))\n\t\t\treturn -1;   // failed\n\t\treturn (int)(short)LOWORD(lRet);\n\t\t\n\t}\n\n\tvoid InsertText(int nInsertAfterChar, LPCTSTR lpstrText, BOOL bNoScroll = FALSE, BOOL bCanUndo = FALSE)\n\t{\n\t\tSetSel(nInsertAfterChar, nInsertAfterChar, bNoScroll);\n\t\tReplaceSel(lpstrText, bCanUndo);\n\t}\n\n\tvoid AppendText(LPCTSTR lpstrText, BOOL bNoScroll = FALSE, BOOL bCanUndo = FALSE)\n\t{\n\t\tInsertText(this->GetWindowTextLength(), lpstrText, bNoScroll, bCanUndo);\n\t}\n\n\tBOOL ShowBalloonTip(PEDITBALLOONTIP pEditBaloonTip)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, EM_SHOWBALLOONTIP, 0, (LPARAM)pEditBaloonTip);\n\t}\n\n\tBOOL HideBalloonTip()\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, EM_HIDEBALLOONTIP, 0, 0L);\n\t}\n\n#if (_WIN32_WINNT >= 0x0600)\n\tDWORD GetHilite() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (DWORD)::SendMessage(this->m_hWnd, EM_GETHILITE, 0, 0L);\n\t}\n\n\tvoid GetHilite(int& nStartChar, int& nEndChar) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tDWORD dwRet = (DWORD)::SendMessage(this->m_hWnd, EM_GETHILITE, 0, 0L);\n\t\tnStartChar = (int)(short)LOWORD(dwRet);\n\t\tnEndChar = (int)(short)HIWORD(dwRet);\n\t}\n\n\tvoid SetHilite(int nStartChar, int nEndChar)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, EM_SETHILITE, nStartChar, nEndChar);\n\t}\n#endif // (_WIN32_WINNT >= 0x0600)\n\n\t// Clipboard operations\n\tBOOL Undo()\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, EM_UNDO, 0, 0L);\n\t}\n\n\tvoid Clear()\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, WM_CLEAR, 0, 0L);\n\t}\n\n\tvoid Copy()\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, WM_COPY, 0, 0L);\n\t}\n\n\tvoid Cut()\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, WM_CUT, 0, 0L);\n\t}\n\n\tvoid Paste()\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, WM_PASTE, 0, 0L);\n\t}\n\n\t// New messages added in Windows 10.0.17763\n#if defined(NTDDI_VERSION) && defined(NTDDI_WIN10_RS5) && (NTDDI_VERSION >= NTDDI_WIN10_RS5)\n\tDWORD SetExtendedStyle(DWORD dwStyle, DWORD dwMask)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn ::SendMessage(this->m_hWnd, EM_SETEXTENDEDSTYLE, dwMask, dwStyle);\n\t}\n\n\tDWORD GetExtendedStyle() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn ::SendMessage(this->m_hWnd, EM_GETEXTENDEDSTYLE, 0, 0L);\n\t}\n\n\tBOOL SetEndOfLine(EC_ENDOFLINE eolType)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, EM_SETENDOFLINE, eolType, 0L);\n\t}\n\n\tEC_ENDOFLINE GetEndOfLine() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (EC_ENDOFLINE)::SendMessage(this->m_hWnd, EM_GETENDOFLINE, 0, 0L);\n\t}\n\n\tBOOL EnableSearchWeb(BOOL bEnable)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, EM_ENABLESEARCHWEB, (WPARAM)bEnable, 0L);\n\t}\n\n\tvoid SearchWeb()\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, EM_SEARCHWEB, 0, 0L);\n\t}\n\n\tBOOL SetCaretIndex(DWORD dwCaretIndex)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, EM_SETCARETINDEX, dwCaretIndex, 0L);\n\t}\n\n\tDWORD GetCaretIndex() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn ::SendMessage(this->m_hWnd, EM_GETCARETINDEX, 0, 0L);\n\t}\n\n\tBOOL GetZoom(int& nNum, int& nDen) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, EM_GETZOOM, (WPARAM)&nNum, (LPARAM)&nDen);\n\t}\n\n\tBOOL SetZoom(int nNum, int nDen)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT((nNum >= 0) && (nNum <= 64));\n\t\tATLASSERT((nDen >= 0) && (nDen <= 64));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, EM_SETZOOM, nNum, nDen);\n\t}\n\n\tDWORD GetFileLineFromChar(DWORD dwCharIndex) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn ::SendMessage(this->m_hWnd, EM_FILELINEFROMCHAR, dwCharIndex, 0L);\n\t}\n\n\tDWORD GetFileLineIndex(DWORD dwLineNum) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn ::SendMessage(this->m_hWnd, EM_FILELINEINDEX, dwLineNum, 0L);\n\t}\n\n\tDWORD GetFileLineLength(DWORD dwCharIndex) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn ::SendMessage(this->m_hWnd, EM_FILELINELENGTH, dwCharIndex, 0L);\n\t}\n\n\tDWORD GetFileLine(DWORD dwLineNum, LPTSTR lpstrLine, WORD wLen) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tWORD* pw = (WORD*)lpstrLine;\n\t\t*pw = wLen;\n\t\treturn ::SendMessage(this->m_hWnd, EM_GETFILELINE, dwLineNum, (LPARAM)lpstrLine);\n\t}\n\n#ifdef __ATLSTR_H__\n\tATL::CString GetFileLine(DWORD dwLineNum) const\n\t{\n\t\tATL::CString strLine;\n\t\tDWORD dwCharIndex = GetFileLineIndex(dwLineNum);\n\t\tif(dwCharIndex != (DWORD)-1)\n\t\t{\n\t\t\tDWORD dwLen = GetFileLineLength(dwCharIndex);\n\t\t\tif(dwLen > 0)\n\t\t\t{\n\t\t\t\tLPTSTR lpstrLine = strLine.GetBufferSetLength(dwLen);\n\t\t\t\tATLVERIFY(GetFileLine(dwLineNum, lpstrLine, (WORD)dwLen) == dwLen);\n\t\t\t\tstrLine.ReleaseBuffer();\n\t\t\t}\n\t\t}\n\n\t\treturn strLine;\n\t}\n#endif // __ATLSTR_H__\n\n\tDWORD GetFileLineCount() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn ::SendMessage(this->m_hWnd, EM_GETFILELINECOUNT, 0, 0L);\n\t}\n#endif // defined(NTDDI_VERSION) && defined(NTDDI_WIN10_RS5) && (NTDDI_VERSION >= NTDDI_WIN10_RS5)\n};\n\ntypedef CEditT<ATL::CWindow>   CEdit;\n\n\n///////////////////////////////////////////////////////////////////////////////\n// CEditCommands - message handlers for standard EDIT commands\n\n// Chain to CEditCommands message map. Your class must also derive from CEdit.\n// Example:\n// class CMyEdit : public CWindowImpl<CMyEdit, CEdit>,\n//                 public CEditCommands<CMyEdit>\n// {\n// public:\n//      BEGIN_MSG_MAP(CMyEdit)\n//              // your handlers...\n//              CHAIN_MSG_MAP_ALT(CEditCommands<CMyEdit>, 1)\n//      END_MSG_MAP()\n//      // other stuff...\n// };\n\ntemplate <class T>\nclass CEditCommands\n{\npublic:\n\tBEGIN_MSG_MAP(CEditCommands< T >)\n\tALT_MSG_MAP(1)\n\t\tCOMMAND_ID_HANDLER(ID_EDIT_CLEAR, OnEditClear)\n\t\tCOMMAND_ID_HANDLER(ID_EDIT_CLEAR_ALL, OnEditClearAll)\n\t\tCOMMAND_ID_HANDLER(ID_EDIT_COPY, OnEditCopy)\n\t\tCOMMAND_ID_HANDLER(ID_EDIT_CUT, OnEditCut)\n\t\tCOMMAND_ID_HANDLER(ID_EDIT_PASTE, OnEditPaste)\n\t\tCOMMAND_ID_HANDLER(ID_EDIT_SELECT_ALL, OnEditSelectAll)\n\t\tCOMMAND_ID_HANDLER(ID_EDIT_UNDO, OnEditUndo)\n\tEND_MSG_MAP()\n\n\tLRESULT OnEditClear(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)\n\t{\n\t\tT* pT = static_cast<T*>(this);\n\t\tpT->Clear();\n\t\treturn 0;\n\t}\n\n\tLRESULT OnEditClearAll(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)\n\t{\n\t\tT* pT = static_cast<T*>(this);\n\t\tpT->SetSel(0, -1);\n\t\tpT->Clear();\n\t\treturn 0;\n\t}\n\n\tLRESULT OnEditCopy(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)\n\t{\n\t\tT* pT = static_cast<T*>(this);\n\t\tpT->Copy();\n\t\treturn 0;\n\t}\n\n\tLRESULT OnEditCut(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)\n\t{\n\t\tT* pT = static_cast<T*>(this);\n\t\tpT->Cut();\n\t\treturn 0;\n\t}\n\n\tLRESULT OnEditPaste(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)\n\t{\n\t\tT* pT = static_cast<T*>(this);\n\t\tpT->Paste();\n\t\treturn 0;\n\t}\n\n\tLRESULT OnEditSelectAll(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)\n\t{\n\t\tT* pT = static_cast<T*>(this);\n\t\tpT->SetSel(0, -1);\n\t\treturn 0;\n\t}\n\n\tLRESULT OnEditUndo(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)\n\t{\n\t\tT* pT = static_cast<T*>(this);\n\t\tpT->Undo();\n\t\treturn 0;\n\t}\n\n// State (update UI) helpers\n\tBOOL CanCut() const\n\t{ return HasSelection(); }\n\n\tBOOL CanCopy() const\n\t{ return HasSelection(); }\n\n\tBOOL CanClear() const\n\t{ return HasSelection(); }\n\n\tBOOL CanSelectAll() const\n\t{ return HasText(); }\n\n\tBOOL CanFind() const\n\t{ return HasText(); }\n\n\tBOOL CanRepeat() const\n\t{ return HasText(); }\n\n\tBOOL CanReplace() const\n\t{ return HasText(); }\n\n\tBOOL CanClearAll() const\n\t{ return HasText(); }\n\n// Implementation\n\tBOOL HasSelection() const\n\t{\n\t\tconst T* pT = static_cast<const T*>(this);\n\t\tint nMin = 0, nMax = 0;\n\t\t::SendMessage(pT->m_hWnd, EM_GETSEL, (WPARAM)&nMin, (LPARAM)&nMax);\n\t\treturn (nMin != nMax);\n\t}\n\n\tBOOL HasText() const\n\t{\n\t\tconst T* pT = static_cast<const T*>(this);\n\t\treturn (pT->GetWindowTextLength() > 0);\n\t}\n};\n\n\n///////////////////////////////////////////////////////////////////////////////\n// CScrollBar - client side for a Windows SCROLLBAR control\n\ntemplate <class TBase>\nclass CScrollBarT : public TBase\n{\npublic:\n// Constructors\n\tCScrollBarT(HWND hWnd = NULL) : TBase(hWnd)\n\t{ }\n\n\tCScrollBarT< TBase >& operator =(HWND hWnd)\n\t{\n\t\tthis->m_hWnd = hWnd;\n\t\treturn *this;\n\t}\n\n\tHWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL,\n\t\t\tDWORD dwStyle = 0, DWORD dwExStyle = 0,\n\t\t\tATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL)\n\t{\n\t\treturn TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam);\n\t}\n\n// Attributes\n\tstatic LPCTSTR GetWndClassName()\n\t{\n\t\treturn _T(\"SCROLLBAR\");\n\t}\n\n\tint GetScrollPos() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn ::GetScrollPos(this->m_hWnd, SB_CTL);\n\t}\n\n\tint SetScrollPos(int nPos, BOOL bRedraw = TRUE)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn ::SetScrollPos(this->m_hWnd, SB_CTL, nPos, bRedraw);\n\t}\n\n\tvoid GetScrollRange(LPINT lpMinPos, LPINT lpMaxPos) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::GetScrollRange(this->m_hWnd, SB_CTL, lpMinPos, lpMaxPos);\n\t}\n\n\tvoid SetScrollRange(int nMinPos, int nMaxPos, BOOL bRedraw = TRUE)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SetScrollRange(this->m_hWnd, SB_CTL, nMinPos, nMaxPos, bRedraw);\n\t}\n\n\tBOOL GetScrollInfo(LPSCROLLINFO lpScrollInfo) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn ::GetScrollInfo(this->m_hWnd, SB_CTL, lpScrollInfo);\n\t}\n\n\tint SetScrollInfo(LPSCROLLINFO lpScrollInfo, BOOL bRedraw = TRUE)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn ::SetScrollInfo(this->m_hWnd, SB_CTL, lpScrollInfo, bRedraw);\n\t}\n\n\tint GetScrollLimit() const\n\t{\n\t\tSCROLLINFO info = { sizeof(SCROLLINFO), SIF_RANGE | SIF_PAGE };\n\t\t::GetScrollInfo(this->m_hWnd, SB_CTL, &info);\n\t\tif(info.nPage > 1)\n\t\t\tinfo.nMax -= info.nPage - 1;\n\n\t\treturn info.nMax;\n\t}\n\n\tBOOL GetScrollBarInfo(PSCROLLBARINFO pScrollBarInfo) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, SBM_GETSCROLLBARINFO, 0, (LPARAM)pScrollBarInfo);\n\t}\n\n// Operations\n\tvoid ShowScrollBar(BOOL bShow = TRUE)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::ShowScrollBar(this->m_hWnd, SB_CTL, bShow);\n\t}\n\n\tBOOL EnableScrollBar(UINT nArrowFlags = ESB_ENABLE_BOTH)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn ::EnableScrollBar(this->m_hWnd, SB_CTL, nArrowFlags);\n\t}\n};\n\ntypedef CScrollBarT<ATL::CWindow>   CScrollBar;\n\n\n// --- Windows Common Controls ---\n\n///////////////////////////////////////////////////////////////////////////////\n// CImageList\n\n// forward declarations\ntemplate <bool t_bManaged> class CImageListT;\ntypedef CImageListT<false>   CImageList;\ntypedef CImageListT<true>    CImageListManaged;\n\n\ntemplate <bool t_bManaged>\nclass CImageListT\n{\npublic:\n// Data members\n\tHIMAGELIST m_hImageList;\n\n// Constructor/destructor/operators\n\tCImageListT(HIMAGELIST hImageList = NULL) : m_hImageList(hImageList)\n\t{ }\n\n\t~CImageListT()\n\t{\n\t\tif(t_bManaged && (m_hImageList != NULL))\n\t\t\tDestroy();\n\t}\n\n\tCImageListT<t_bManaged>& operator =(HIMAGELIST hImageList)\n\t{\n\t\tAttach(hImageList);\n\t\treturn *this;\n\t}\n\n\tvoid Attach(HIMAGELIST hImageList)\n\t{\n\t\tif(t_bManaged && (m_hImageList != NULL) && (m_hImageList != hImageList))\n\t\t\tImageList_Destroy(m_hImageList);\n\t\tm_hImageList = hImageList;\n\t}\n\n\tHIMAGELIST Detach()\n\t{\n\t\tHIMAGELIST hImageList = m_hImageList;\n\t\tm_hImageList = NULL;\n\t\treturn hImageList;\n\t}\n\n\toperator HIMAGELIST() const { return m_hImageList; }\n\n\tbool IsNull() const { return (m_hImageList == NULL); }\n\n// Attributes\n\tint GetImageCount() const\n\t{\n\t\tATLASSERT(m_hImageList != NULL);\n\t\treturn ImageList_GetImageCount(m_hImageList);\n\t}\n\n\tCOLORREF GetBkColor() const\n\t{\n\t\tATLASSERT(m_hImageList != NULL);\n\t\treturn ImageList_GetBkColor(m_hImageList);\n\t}\n\n\tCOLORREF SetBkColor(COLORREF cr)\n\t{\n\t\tATLASSERT(m_hImageList != NULL);\n\t\treturn ImageList_SetBkColor(m_hImageList, cr);\n\t}\n\n\tBOOL GetImageInfo(int nImage, IMAGEINFO* pImageInfo) const\n\t{\n\t\tATLASSERT(m_hImageList != NULL);\n\t\treturn ImageList_GetImageInfo(m_hImageList, nImage, pImageInfo);\n\t}\n\n\tHICON GetIcon(int nIndex, UINT uFlags = ILD_NORMAL) const\n\t{\n\t\tATLASSERT(m_hImageList != NULL);\n\t\treturn ImageList_GetIcon(m_hImageList, nIndex, uFlags);\n\t}\n\n\tBOOL GetIconSize(int& cx, int& cy) const\n\t{\n\t\tATLASSERT(m_hImageList != NULL);\n\t\treturn ImageList_GetIconSize(m_hImageList, &cx, &cy);\n\t}\n\n\tBOOL GetIconSize(SIZE& size) const\n\t{\n\t\tATLASSERT(m_hImageList != NULL);\n\t\treturn ImageList_GetIconSize(m_hImageList, (int*)&size.cx, (int*)&size.cy);\n\t}\n\n\tBOOL SetIconSize(int cx, int cy)\n\t{\n\t\tATLASSERT(m_hImageList != NULL);\n\t\treturn ImageList_SetIconSize(m_hImageList, cx, cy);\n\t}\n\n\tBOOL SetIconSize(SIZE size)\n\t{\n\t\tATLASSERT(m_hImageList != NULL);\n\t\treturn ImageList_SetIconSize(m_hImageList, size.cx, size.cy);\n\t}\n\n\tBOOL SetImageCount(UINT uNewCount)\n\t{\n\t\tATLASSERT(m_hImageList != NULL);\n\t\treturn ImageList_SetImageCount(m_hImageList, uNewCount);\n\t}\n\n\tBOOL SetOverlayImage(int nImage, int nOverlay)\n\t{\n\t\tATLASSERT(m_hImageList != NULL);\n\t\treturn ImageList_SetOverlayImage(m_hImageList, nImage, nOverlay);\n\t}\n\n// Operations\n\tBOOL Create(int cx, int cy, UINT nFlags, int nInitial, int nGrow)\n\t{\n\t\tATLASSERT(m_hImageList == NULL);\n\t\tm_hImageList = ImageList_Create(cx, cy, nFlags, nInitial, nGrow);\n\t\treturn (m_hImageList != NULL) ? TRUE : FALSE;\n\t}\n\n\tBOOL Create(ATL::_U_STRINGorID bitmap, int cx, int nGrow, COLORREF crMask)\n\t{\n\t\tATLASSERT(m_hImageList == NULL);\n\t\tm_hImageList = ImageList_LoadBitmap(ModuleHelper::GetResourceInstance(), bitmap.m_lpstr, cx, nGrow, crMask);\n\t\treturn (m_hImageList != NULL) ? TRUE : FALSE;\n\t}\n\n\tBOOL CreateFromImage(ATL::_U_STRINGorID image, int cx, int nGrow, COLORREF crMask, UINT uType, UINT uFlags = LR_DEFAULTCOLOR | LR_DEFAULTSIZE)\n\t{\n\t\tATLASSERT(m_hImageList == NULL);\n\t\tm_hImageList = ImageList_LoadImage(ModuleHelper::GetResourceInstance(), image.m_lpstr, cx, nGrow, crMask, uType, uFlags);\n\t\treturn (m_hImageList != NULL) ? TRUE : FALSE;\n\t}\n\n\tBOOL Merge(HIMAGELIST hImageList1, int nImage1, HIMAGELIST hImageList2, int nImage2, int dx, int dy)\n\t{\n\t\tATLASSERT(m_hImageList == NULL);\n\t\tm_hImageList = ImageList_Merge(hImageList1, nImage1, hImageList2, nImage2, dx, dy);\n\t\treturn (m_hImageList != NULL) ? TRUE : FALSE;\n\t}\n\n#ifdef __IStream_INTERFACE_DEFINED__\n\tBOOL CreateFromStream(LPSTREAM lpStream)\n\t{\n\t\tATLASSERT(m_hImageList == NULL);\n\t\tm_hImageList = ImageList_Read(lpStream);\n\t\treturn (m_hImageList != NULL) ? TRUE : FALSE;\n\t}\n#endif // __IStream_INTERFACE_DEFINED__\n\n\tBOOL Destroy()\n\t{\n\t\tif (m_hImageList == NULL)\n\t\t\treturn FALSE;\n\t\tBOOL bRet = ImageList_Destroy(m_hImageList);\n\t\tif(bRet)\n\t\t\tm_hImageList = NULL;\n\t\treturn bRet;\n\t}\n\n\tint Add(HBITMAP hBitmap, HBITMAP hBitmapMask = NULL)\n\t{\n\t\tATLASSERT(m_hImageList != NULL);\n\t\treturn ImageList_Add(m_hImageList, hBitmap, hBitmapMask);\n\t}\n\n\tint Add(HBITMAP hBitmap, COLORREF crMask)\n\t{\n\t\tATLASSERT(m_hImageList != NULL);\n\t\treturn ImageList_AddMasked(m_hImageList, hBitmap, crMask);\n\t}\n\n\tBOOL Remove(int nImage)\n\t{\n\t\tATLASSERT(m_hImageList != NULL);\n\t\treturn ImageList_Remove(m_hImageList, nImage);\n\t}\n\n\tBOOL RemoveAll()\n\t{\n\t\tATLASSERT(m_hImageList != NULL);\n\t\treturn ImageList_RemoveAll(m_hImageList);\n\t}\n\n\tBOOL Replace(int nImage, HBITMAP hBitmap, HBITMAP hBitmapMask)\n\t{\n\t\tATLASSERT(m_hImageList != NULL);\n\t\treturn ImageList_Replace(m_hImageList, nImage, hBitmap, hBitmapMask);\n\t}\n\n\tint AddIcon(HICON hIcon)\n\t{\n\t\tATLASSERT(m_hImageList != NULL);\n\t\treturn ImageList_AddIcon(m_hImageList, hIcon);\n\t}\n\n\tint ReplaceIcon(int nImage, HICON hIcon)\n\t{\n\t\tATLASSERT(m_hImageList != NULL);\n\t\treturn ImageList_ReplaceIcon(m_hImageList, nImage, hIcon);\n\t}\n\n\tHICON ExtractIcon(int nImage)\n\t{\n\t\tATLASSERT(m_hImageList != NULL);\n\t\treturn ImageList_ExtractIcon(NULL, m_hImageList, nImage);\n\t}\n\n\tBOOL Draw(HDC hDC, int nImage, int x, int y, UINT nStyle)\n\t{\n\t\tATLASSERT(m_hImageList != NULL);\n\t\tATLASSERT(hDC != NULL);\n\t\treturn ImageList_Draw(m_hImageList, nImage, hDC, x, y, nStyle);\n\t}\n\n\tBOOL Draw(HDC hDC, int nImage, POINT pt, UINT nStyle)\n\t{\n\t\tATLASSERT(m_hImageList != NULL);\n\t\tATLASSERT(hDC != NULL);\n\t\treturn ImageList_Draw(m_hImageList, nImage, hDC, pt.x, pt.y, nStyle);\n\t}\n\n\tBOOL DrawEx(int nImage, HDC hDC, int x, int y, int dx, int dy, COLORREF rgbBk, COLORREF rgbFg, UINT fStyle)\n\t{\n\t\tATLASSERT(m_hImageList != NULL);\n\t\tATLASSERT(hDC != NULL);\n\t\treturn ImageList_DrawEx(m_hImageList, nImage, hDC, x, y, dx, dy, rgbBk, rgbFg, fStyle);\n\t}\n\n\tBOOL DrawEx(int nImage, HDC hDC, RECT& rect, COLORREF rgbBk, COLORREF rgbFg, UINT fStyle)\n\t{\n\t\tATLASSERT(m_hImageList != NULL);\n\t\tATLASSERT(hDC != NULL);\n\t\treturn ImageList_DrawEx(m_hImageList, nImage, hDC, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, rgbBk, rgbFg, fStyle);\n\t}\n\n\tstatic BOOL DrawIndirect(IMAGELISTDRAWPARAMS* pimldp)\n\t{\n\t\treturn ImageList_DrawIndirect(pimldp);\n\t}\n\n\tBOOL Copy(int nSrc, int nDst, UINT uFlags = ILCF_MOVE)\n\t{\n\t\tATLASSERT(m_hImageList != NULL);\n\t\treturn ImageList_Copy(m_hImageList, nDst, m_hImageList, nSrc, uFlags);\n\t}\n\n#ifdef __IStream_INTERFACE_DEFINED__\n\tstatic HIMAGELIST Read(LPSTREAM lpStream)\n\t{\n\t\treturn ImageList_Read(lpStream);\n\t}\n\n\tBOOL Write(LPSTREAM lpStream)\n\t{\n\t\tATLASSERT(m_hImageList != NULL);\n\t\treturn ImageList_Write(m_hImageList, lpStream);\n\t}\n\n\tstatic HRESULT ReadEx(DWORD dwFlags, LPSTREAM lpStream, REFIID riid, PVOID* ppv)\n\t{\n\t\treturn ImageList_ReadEx(dwFlags, lpStream, riid, ppv);\n\t}\n\n\tHRESULT WriteEx(DWORD dwFlags, LPSTREAM lpStream)\n\t{\n\t\tATLASSERT(m_hImageList != NULL);\n\t\treturn ImageList_WriteEx(m_hImageList, dwFlags, lpStream);\n\t}\n#endif // __IStream_INTERFACE_DEFINED__\n\n\t// Drag operations\n\tBOOL BeginDrag(int nImage, POINT ptHotSpot)\n\t{\n\t\tATLASSERT(m_hImageList != NULL);\n\t\treturn ImageList_BeginDrag(m_hImageList, nImage, ptHotSpot.x, ptHotSpot.y);\n\t}\n\n\tBOOL BeginDrag(int nImage, int xHotSpot, int yHotSpot)\n\t{\n\t\tATLASSERT(m_hImageList != NULL);\n\t\treturn ImageList_BeginDrag(m_hImageList, nImage, xHotSpot, yHotSpot);\n\t}\n\n\tstatic void EndDrag()\n\t{\n\t\tImageList_EndDrag();\n\t}\n\n\tstatic BOOL DragMove(POINT pt)\n\t{\n\t\treturn ImageList_DragMove(pt.x, pt.y);\n\t}\n\n\tstatic BOOL DragMove(int x, int y)\n\t{\n\t\treturn ImageList_DragMove(x, y);\n\t}\n\n\tBOOL SetDragCursorImage(int nDrag, POINT ptHotSpot)\n\t{\n\t\tATLASSERT(m_hImageList != NULL);\n\t\treturn ImageList_SetDragCursorImage(m_hImageList, nDrag, ptHotSpot.x, ptHotSpot.y);\n\t}\n\n\tBOOL SetDragCursorImage(int nDrag, int xHotSpot, int yHotSpot)\n\t{\n\t\tATLASSERT(m_hImageList != NULL);\n\t\treturn ImageList_SetDragCursorImage(m_hImageList, nDrag, xHotSpot, yHotSpot);\n\t}\n\n\tstatic BOOL DragShowNolock(BOOL bShow = TRUE)\n\t{\n\t\treturn ImageList_DragShowNolock(bShow);\n\t}\n\n\tstatic CImageList GetDragImage(LPPOINT lpPoint, LPPOINT lpPointHotSpot)\n\t{\n\t\treturn CImageList(ImageList_GetDragImage(lpPoint, lpPointHotSpot));\n\t}\n\n\tstatic BOOL DragEnter(HWND hWnd, POINT point)\n\t{\n\t\treturn ImageList_DragEnter(hWnd, point.x, point.y);\n\t}\n\n\tstatic BOOL DragEnter(HWND hWnd, int x, int y)\n\t{\n\t\treturn ImageList_DragEnter(hWnd, x, y);\n\t}\n\n\tstatic BOOL DragLeave(HWND hWnd)\n\t{\n\t\treturn ImageList_DragLeave(hWnd);\n\t}\n\n\tCImageList Duplicate() const\n\t{\n\t\tATLASSERT(m_hImageList != NULL);\n\t\treturn CImageList(ImageList_Duplicate(m_hImageList));\n\t}\n\n\tstatic CImageList Duplicate(HIMAGELIST hImageList)\n\t{\n\t\tATLASSERT(hImageList != NULL);\n\t\treturn CImageList(ImageList_Duplicate(hImageList));\n\t}\n};\n\n\n///////////////////////////////////////////////////////////////////////////////\n// CToolTipCtrl\n\nclass CToolInfo : public TOOLINFO\n{\npublic:\n\tCToolInfo(UINT nFlags, HWND hWnd, UINT_PTR nIDTool = 0, LPRECT lpRect = NULL, LPTSTR lpstrText = LPSTR_TEXTCALLBACK, LPARAM lUserParam = NULL)\n\t{\n\t\tInit(nFlags, hWnd, nIDTool, lpRect, lpstrText, lUserParam);\n\t}\n\n\toperator LPTOOLINFO() { return this; }\n\n\toperator LPARAM() { return (LPARAM)this; }\n\n\tvoid Init(UINT nFlags, HWND hWnd, UINT_PTR nIDTool = 0, LPRECT lpRect = NULL, LPTSTR lpstrText = LPSTR_TEXTCALLBACK, LPARAM lUserParam = NULL)\n\t{\n\t\tATLASSERT(::IsWindow(hWnd));\n\t\tmemset(this, 0, sizeof(TOOLINFO));\n\t\tcbSize = RunTimeHelper::SizeOf_TOOLINFO();\n\t\tuFlags = nFlags;\n\t\tif(nIDTool == 0)\n\t\t{\n\t\t\thwnd = ::GetParent(hWnd);\n\t\t\tuFlags |= TTF_IDISHWND;\n\t\t\tuId = (UINT_PTR)hWnd;\n\t\t}\n\t\telse\n\t\t{\n\t\t\thwnd = hWnd;\n\t\t\tuId = nIDTool;\n\t\t}\n\t\tif(lpRect != NULL)\n\t\t\trect = *lpRect;\n\t\thinst = ModuleHelper::GetResourceInstance();\n\t\tlpszText = lpstrText;\n\t\tlParam = lUserParam;\n\t}\n};\n\ntemplate <class TBase>\nclass CToolTipCtrlT : public TBase\n{\npublic:\n// Constructors\n\tCToolTipCtrlT(HWND hWnd = NULL) : TBase(hWnd)\n\t{ }\n\n\tCToolTipCtrlT< TBase >& operator =(HWND hWnd)\n\t{\n\t\tthis->m_hWnd = hWnd;\n\t\treturn *this;\n\t}\n\n\tHWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL,\n\t\t\tDWORD dwStyle = 0, DWORD dwExStyle = 0,\n\t\t\tATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL)\n\t{\n\t\treturn TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam);\n\t}\n\n// Attributes\n\tstatic LPCTSTR GetWndClassName()\n\t{\n\t\treturn TOOLTIPS_CLASS;\n\t}\n\n\tvoid GetText(LPTOOLINFO lpToolInfo) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, TTM_GETTEXT, 0, (LPARAM)&lpToolInfo);\n\t}\n\n\tvoid GetText(LPTSTR lpstrText, HWND hWnd, UINT_PTR nIDTool = 0) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT(hWnd != NULL);\n\t\tCToolInfo ti(0, hWnd, nIDTool, NULL, lpstrText);\n\t\t::SendMessage(this->m_hWnd, TTM_GETTEXT, 0, ti);\n\t}\n\n\tBOOL GetToolInfo(LPTOOLINFO lpToolInfo) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TTM_GETTOOLINFO, 0, (LPARAM)lpToolInfo);\n\t}\n\n\tBOOL GetToolInfo(HWND hWnd, UINT_PTR nIDTool, UINT* puFlags, LPRECT lpRect, LPTSTR lpstrText) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT(hWnd != NULL);\n\t\tATLASSERT(puFlags != NULL);\n\t\tATLASSERT(lpRect != NULL);\n\t\tCToolInfo ti(0, hWnd, nIDTool, NULL, lpstrText);\n\t\tBOOL bRet = (BOOL)::SendMessage(this->m_hWnd, TTM_GETTOOLINFO, 0, ti);\n\t\tif(bRet != FALSE)\n\t\t{\n\t\t\t*puFlags = ti.uFlags;\n\t\t\t*lpRect = ti.rect;\n\t\t}\n\t\treturn bRet;\n\t}\n\n\tvoid SetToolInfo(LPTOOLINFO lpToolInfo)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, TTM_SETTOOLINFO, 0, (LPARAM)lpToolInfo);\n\t}\n\n\tvoid SetToolRect(LPTOOLINFO lpToolInfo)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, TTM_NEWTOOLRECT, 0, (LPARAM)lpToolInfo);\n\t}\n\n\tvoid SetToolRect(HWND hWnd, UINT_PTR nIDTool, LPCRECT lpRect)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT(hWnd != NULL);\n\t\tATLASSERT(nIDTool != 0);\n\n\t\tCToolInfo ti(0, hWnd, nIDTool, (LPRECT)lpRect, NULL);\n\t\t::SendMessage(this->m_hWnd, TTM_NEWTOOLRECT, 0, ti);\n\t}\n\n\tint GetToolCount() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, TTM_GETTOOLCOUNT, 0, 0L);\n\t}\n\n\tint GetDelayTime(DWORD dwType) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, TTM_GETDELAYTIME, dwType, 0L);\n\t}\n\n\tvoid SetDelayTime(DWORD dwType, int nTime)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, TTM_SETDELAYTIME, dwType, MAKELPARAM(nTime, 0));\n\t}\n\n\tvoid GetMargin(LPRECT lpRect) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, TTM_GETMARGIN, 0, (LPARAM)lpRect);\n\t}\n\n\tvoid SetMargin(LPRECT lpRect)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, TTM_SETMARGIN, 0, (LPARAM)lpRect);\n\t}\n\n\tint GetMaxTipWidth() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, TTM_GETMAXTIPWIDTH, 0, 0L);\n\t}\n\n\tint SetMaxTipWidth(int nWidth)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, TTM_SETMAXTIPWIDTH, 0, nWidth);\n\t}\n\n\tCOLORREF GetTipBkColor() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (COLORREF)::SendMessage(this->m_hWnd, TTM_GETTIPBKCOLOR, 0, 0L);\n\t}\n\n\tvoid SetTipBkColor(COLORREF clr)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, TTM_SETTIPBKCOLOR, (WPARAM)clr, 0L);\n\t}\n\n\tCOLORREF GetTipTextColor() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (COLORREF)::SendMessage(this->m_hWnd, TTM_GETTIPTEXTCOLOR, 0, 0L);\n\t}\n\n\tvoid SetTipTextColor(COLORREF clr)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, TTM_SETTIPTEXTCOLOR, (WPARAM)clr, 0L);\n\t}\n\n\tBOOL GetCurrentTool(LPTOOLINFO lpToolInfo) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TTM_GETCURRENTTOOL, 0, (LPARAM)lpToolInfo);\n\t}\n\n\tSIZE GetBubbleSize(LPTOOLINFO lpToolInfo) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tDWORD dwRet = (DWORD)::SendMessage(this->m_hWnd, TTM_GETBUBBLESIZE, 0, (LPARAM)lpToolInfo);\n\t\tSIZE size = { GET_X_LPARAM(dwRet), GET_Y_LPARAM(dwRet) };\n\t\treturn size;\n\t}\n\n\tBOOL SetTitle(UINT_PTR uIcon, LPCTSTR lpstrTitle)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TTM_SETTITLE, uIcon, (LPARAM)lpstrTitle);\n\t}\n\n\n\tBOOL SetTitle(HICON hIcon, LPCTSTR lpstrTitle)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TTM_SETTITLE, (WPARAM)hIcon, (LPARAM)lpstrTitle);\n\t}\n\n\tvoid GetTitle(PTTGETTITLE pTTGetTitle) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, TTM_GETTITLE, 0, (LPARAM)pTTGetTitle);\n\t}\n\n\tvoid SetWindowTheme(LPCWSTR lpstrTheme)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, TTM_SETWINDOWTHEME, 0, (LPARAM)lpstrTheme);\n\t}\n\n// Operations\n\tvoid Activate(BOOL bActivate)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, TTM_ACTIVATE, bActivate, 0L);\n\t}\n\n\tBOOL AddTool(LPTOOLINFO lpToolInfo)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TTM_ADDTOOL, 0, (LPARAM)lpToolInfo);\n\t}\n\n\tBOOL AddTool(HWND hWnd, ATL::_U_STRINGorID text = LPSTR_TEXTCALLBACK, LPCRECT lpRectTool = NULL, UINT_PTR nIDTool = 0)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT(hWnd != NULL);\n\t\t// the toolrect and toolid must both be zero or both valid\n\t\tATLASSERT(((lpRectTool != NULL) && (nIDTool != 0)) || ((lpRectTool == NULL) && (nIDTool == 0)));\n\n\t\tCToolInfo ti(0, hWnd, nIDTool, (LPRECT)lpRectTool, (LPTSTR)text.m_lpstr);\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TTM_ADDTOOL, 0, ti);\n\t}\n\n\tvoid DelTool(LPTOOLINFO lpToolInfo)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, TTM_DELTOOL, 0, (LPARAM)lpToolInfo);\n\t}\n\n\tvoid DelTool(HWND hWnd, UINT_PTR nIDTool = 0)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT(hWnd != NULL);\n\n\t\tCToolInfo ti(0, hWnd, nIDTool, NULL, NULL);\n\t\t::SendMessage(this->m_hWnd, TTM_DELTOOL, 0, ti);\n\t}\n\n\tBOOL HitTest(LPTTHITTESTINFO lpHitTestInfo) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TTM_HITTEST, 0, (LPARAM)lpHitTestInfo);\n\t}\n\n\tBOOL HitTest(HWND hWnd, POINT pt, LPTOOLINFO lpToolInfo) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT(hWnd != NULL);\n\t\tATLASSERT(lpToolInfo != NULL);\n\n\t\tTTHITTESTINFO hti = {};\n\t\thti.ti.cbSize = RunTimeHelper::SizeOf_TOOLINFO();\n\t\thti.hwnd = hWnd;\n\t\thti.pt.x = pt.x;\n\t\thti.pt.y = pt.y;\n\t\tif((BOOL)::SendMessage(this->m_hWnd, TTM_HITTEST, 0, (LPARAM)&hti) != FALSE)\n\t\t{\n\t\t\t*lpToolInfo = hti.ti;\n\t\t\treturn TRUE;\n\t\t}\n\t\treturn FALSE;\n\t}\n\n\tvoid RelayEvent(LPMSG lpMsg)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, TTM_RELAYEVENT, 0, (LPARAM)lpMsg);\n\t}\n\n\tvoid UpdateTipText(LPTOOLINFO lpToolInfo)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, TTM_UPDATETIPTEXT, 0, (LPARAM)lpToolInfo);\n\t}\n\n\tvoid UpdateTipText(ATL::_U_STRINGorID text, HWND hWnd, UINT_PTR nIDTool = 0)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT(hWnd != NULL);\n\n\t\tCToolInfo ti(0, hWnd, nIDTool, NULL, (LPTSTR)text.m_lpstr);\n\t\t::SendMessage(this->m_hWnd, TTM_UPDATETIPTEXT, 0, ti);\n\t}\n\n\tBOOL EnumTools(UINT_PTR nTool, LPTOOLINFO lpToolInfo) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TTM_ENUMTOOLS, nTool, (LPARAM)lpToolInfo);\n\t}\n\n\tvoid Pop()\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, TTM_POP, 0, 0L);\n\t}\n\n\tvoid TrackActivate(LPTOOLINFO lpToolInfo, BOOL bActivate)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, TTM_TRACKACTIVATE, bActivate, (LPARAM)lpToolInfo);\n\t}\n\n\tvoid TrackActivate(HWND hWnd, UINT_PTR nIDTool, BOOL bActivate)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT(hWnd != NULL);\n\n\t\tCToolInfo ti(0, hWnd, nIDTool);\n\t\t::SendMessage(this->m_hWnd, TTM_TRACKACTIVATE, bActivate, ti);\n\t}\n\n\tvoid TrackPosition(int xPos, int yPos)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, TTM_TRACKPOSITION, 0, MAKELPARAM(xPos, yPos));\n\t}\n\n\tvoid Update()\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, TTM_UPDATE, 0, 0L);\n\t}\n\n\tBOOL AdjustRect(LPRECT lpRect, BOOL bLarger /*= TRUE*/)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TTM_ADJUSTRECT, bLarger, (LPARAM)lpRect);\n\t}\n\n\tvoid Popup()\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, TTM_POPUP, 0, 0L);\n\t}\n};\n\ntypedef CToolTipCtrlT<ATL::CWindow>   CToolTipCtrl;\n\n\n///////////////////////////////////////////////////////////////////////////////\n// CHeaderCtrl\n\ntemplate <class TBase>\nclass CHeaderCtrlT : public TBase\n{\npublic:\n// Constructors\n\tCHeaderCtrlT(HWND hWnd = NULL) : TBase(hWnd)\n\t{ }\n\n\tCHeaderCtrlT< TBase >& operator =(HWND hWnd)\n\t{\n\t\tthis->m_hWnd = hWnd;\n\t\treturn *this;\n\t}\n\n\tHWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL,\n\t\t\tDWORD dwStyle = 0, DWORD dwExStyle = 0,\n\t\t\tATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL)\n\t{\n\t\treturn TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam);\n\t}\n\n// Attributes\n\tstatic LPCTSTR GetWndClassName()\n\t{\n\t\treturn WC_HEADER;\n\t}\n\n\tint GetItemCount() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, HDM_GETITEMCOUNT, 0, 0L);\n\t}\n\n\tBOOL GetItem(int nIndex, LPHDITEM pHeaderItem) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, HDM_GETITEM, nIndex, (LPARAM)pHeaderItem);\n\t}\n\n\tBOOL SetItem(int nIndex, LPHDITEM pHeaderItem)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, HDM_SETITEM, nIndex, (LPARAM)pHeaderItem);\n\t}\n\n\tCImageList GetImageList() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn CImageList((HIMAGELIST)::SendMessage(this->m_hWnd, HDM_GETIMAGELIST, 0, 0L));\n\t}\n\n\tCImageList SetImageList(HIMAGELIST hImageList)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn CImageList((HIMAGELIST)::SendMessage(this->m_hWnd, HDM_SETIMAGELIST, 0, (LPARAM)hImageList));\n\t}\n\n\tBOOL GetOrderArray(int nSize, int* lpnArray) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, HDM_GETORDERARRAY, nSize, (LPARAM)lpnArray);\n\t}\n\n\tBOOL SetOrderArray(int nSize, int* lpnArray)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, HDM_SETORDERARRAY, nSize, (LPARAM)lpnArray);\n\t}\n\n\tBOOL GetItemRect(int nIndex, LPRECT lpItemRect) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, HDM_GETITEMRECT, nIndex, (LPARAM)lpItemRect);\n\t}\n\n\tint SetHotDivider(BOOL bPos, DWORD dwInputValue)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, HDM_SETHOTDIVIDER, bPos, dwInputValue);\n\t}\n\n\tBOOL GetUnicodeFormat() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, HDM_GETUNICODEFORMAT, 0, 0L);\n\t}\n\n\tBOOL SetUnicodeFormat(BOOL bUnicode = TRUE)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, HDM_SETUNICODEFORMAT, bUnicode, 0L);\n\t}\n\n\tint GetBitmapMargin() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, HDM_GETBITMAPMARGIN, 0, 0L);\n\t}\n\n\tint SetBitmapMargin(int nWidth)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, HDM_SETBITMAPMARGIN, nWidth, 0L);\n\t}\n\n\tint SetFilterChangeTimeout(DWORD dwTimeOut)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, HDM_SETFILTERCHANGETIMEOUT, 0, dwTimeOut);\n\t}\n\n#if (_WIN32_WINNT >= 0x0600)\n\tBOOL GetItemDropDownRect(int nIndex, LPRECT lpRect) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, HDM_GETITEMDROPDOWNRECT, nIndex, (LPARAM)lpRect);\n\t}\n\n\tBOOL GetOverflowRect(LPRECT lpRect) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, HDM_GETOVERFLOWRECT, 0, (LPARAM)lpRect);\n\t}\n\n\tint GetFocusedItem() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, HDM_GETFOCUSEDITEM, 0, 0L);\n\t}\n\n\tBOOL SetFocusedItem(int nIndex)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, HDM_SETFOCUSEDITEM, 0, nIndex);\n\t}\n#endif // (_WIN32_WINNT >= 0x0600)\n\n// Operations\n\tint InsertItem(int nIndex, LPHDITEM phdi)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, HDM_INSERTITEM, nIndex, (LPARAM)phdi);\n\t}\n\n\tint AddItem(LPHDITEM phdi)\n\t{\n\t\treturn InsertItem(GetItemCount(), phdi);\n\t}\n\n\tBOOL DeleteItem(int nIndex)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, HDM_DELETEITEM, nIndex, 0L);\n\t}\n\n\tBOOL Layout(HD_LAYOUT* pHeaderLayout)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, HDM_LAYOUT, 0, (LPARAM)pHeaderLayout);\n\t}\n\n\tint HitTest(LPHDHITTESTINFO lpHitTestInfo) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, HDM_HITTEST, 0, (LPARAM)lpHitTestInfo);\n\t}\n\n\tint OrderToIndex(int nOrder)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, HDM_ORDERTOINDEX, nOrder, 0L);\n\t}\n\n\tCImageList CreateDragImage(int nIndex)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn CImageList((HIMAGELIST)::SendMessage(this->m_hWnd, HDM_CREATEDRAGIMAGE, nIndex, 0L));\n\t}\n\n\tint EditFilter(int nColumn, BOOL bDiscardChanges)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, HDM_EDITFILTER, nColumn, MAKELPARAM(bDiscardChanges, 0));\n\t}\n\n\tint ClearFilter(int nColumn)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, HDM_CLEARFILTER, nColumn, 0L);\n\t}\n\n\tint ClearAllFilters()\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, HDM_CLEARFILTER, (WPARAM)-1, 0L);\n\t}\n};\n\ntypedef CHeaderCtrlT<ATL::CWindow>   CHeaderCtrl;\n\n\n///////////////////////////////////////////////////////////////////////////////\n// CListViewCtrl\n\ntemplate <class TBase>\nclass CListViewCtrlT : public TBase\n{\npublic:\n// Constructors\n\tCListViewCtrlT(HWND hWnd = NULL) : TBase(hWnd)\n\t{ }\n\n\tCListViewCtrlT< TBase >& operator =(HWND hWnd)\n\t{\n\t\tthis->m_hWnd = hWnd;\n\t\treturn *this;\n\t}\n\n\tHWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL,\n\t\t\tDWORD dwStyle = 0, DWORD dwExStyle = 0,\n\t\t\tATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL)\n\t{\n\t\treturn TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam);\n\t}\n\n// Attributes\n\tstatic LPCTSTR GetWndClassName()\n\t{\n\t\treturn WC_LISTVIEW;\n\t}\n\n\tCOLORREF GetBkColor() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (COLORREF)::SendMessage(this->m_hWnd, LVM_GETBKCOLOR, 0, 0L);\n\t}\n\n\tBOOL SetBkColor(COLORREF cr)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, LVM_SETBKCOLOR, 0, cr);\n\t}\n\n\tCImageList GetImageList(int nImageListType) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn CImageList((HIMAGELIST)::SendMessage(this->m_hWnd, LVM_GETIMAGELIST, nImageListType, 0L));\n\t}\n\n\tCImageList SetImageList(HIMAGELIST hImageList, int nImageList)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd)); \n\t\treturn CImageList((HIMAGELIST)::SendMessage(this->m_hWnd, LVM_SETIMAGELIST, nImageList, (LPARAM)hImageList));\n\t}\n\n\tint GetItemCount() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, LVM_GETITEMCOUNT, 0, 0L);\n\t}\n\n\tBOOL SetItemCount(int nItems)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, LVM_SETITEMCOUNT, nItems, 0L);\n\t}\n\n\tBOOL GetItem(LPLVITEM pItem) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, LVM_GETITEM, 0, (LPARAM)pItem);\n\t}\n\n\tBOOL SetItem(const LVITEM* pItem)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, LVM_SETITEM, 0, (LPARAM)pItem);\n\t}\n\n\tBOOL SetItem(int nItem, int nSubItem, UINT nMask, LPCTSTR lpszItem,\n\t\tint nImage, UINT nState, UINT nStateMask, LPARAM lParam)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tLVITEM lvi = {};\n\t\tlvi.mask = nMask;\n\t\tlvi.iItem = nItem;\n\t\tlvi.iSubItem = nSubItem;\n\t\tlvi.stateMask = nStateMask;\n\t\tlvi.state = nState;\n\t\tlvi.pszText = (LPTSTR) lpszItem;\n\t\tlvi.iImage = nImage;\n\t\tlvi.lParam = lParam;\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, LVM_SETITEM, 0, (LPARAM)&lvi);\n\t}\n\n\tUINT GetItemState(int nItem, UINT nMask) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (UINT)::SendMessage(this->m_hWnd, LVM_GETITEMSTATE, nItem, nMask);\n\t}\n\n\tBOOL SetItemState(int nItem, UINT nState, UINT nStateMask)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tLVITEM lvi = {};\n\t\tlvi.state = nState;\n\t\tlvi.stateMask = nStateMask;\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, LVM_SETITEMSTATE, nItem, (LPARAM)&lvi);\n\t}\n\n\tBOOL SetItemState(int nItem, LPLVITEM pItem)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, LVM_SETITEMSTATE, nItem, (LPARAM)pItem);\n\t}\n\n\tBOOL GetItemText(int nItem, int nSubItem, BSTR& bstrText) const\n\t{\n\t\tUSES_CONVERSION;\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT(bstrText == NULL);\n\t\tLVITEM lvi = {};\n\t\tlvi.iSubItem = nSubItem;\n\n\t\tLPTSTR lpstrText = NULL;\n\t\tint nRes = 0;\n\t\tfor(int nLen = 256; ; nLen *= 2)\n\t\t{\n\t\t\tATLTRY(lpstrText = new TCHAR[nLen]);\n\t\t\tif(lpstrText == NULL)\n\t\t\t\tbreak;\n\t\t\tlpstrText[0] = NULL;\n\t\t\tlvi.cchTextMax = nLen;\n\t\t\tlvi.pszText = lpstrText;\n\t\t\tnRes  = (int)::SendMessage(this->m_hWnd, LVM_GETITEMTEXT, (WPARAM)nItem, (LPARAM)&lvi);\n\t\t\tif(nRes < nLen - 1)\n\t\t\t\tbreak;\n\t\t\tdelete [] lpstrText;\n\t\t\tlpstrText = NULL;\n\t\t}\n\n\t\tif(lpstrText != NULL)\n\t\t{\n\t\t\tif(nRes != 0)\n\t\t\t\tbstrText = ::SysAllocString(T2OLE(lpstrText));\n\t\t\tdelete [] lpstrText;\n\t\t}\n\n\t\treturn (bstrText != NULL) ? TRUE : FALSE;\n\t}\n\n#ifdef __ATLSTR_H__\n\tint GetItemText(int nItem, int nSubItem, ATL::CString& strText) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tLVITEM lvi = {};\n\t\tlvi.iSubItem = nSubItem;\n\n\t\tstrText.Empty();\n\t\tint nRes = 0;\n\t\tfor(int nLen = 256; ; nLen *= 2)\n\t\t{\n\t\t\tlvi.cchTextMax = nLen;\n\t\t\tlvi.pszText = strText.GetBufferSetLength(nLen);\n\t\t\tif(lvi.pszText == NULL)\n\t\t\t{\n\t\t\t\tnRes = 0;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tnRes  = (int)::SendMessage(this->m_hWnd, LVM_GETITEMTEXT, (WPARAM)nItem, (LPARAM)&lvi);\n\t\t\tif(nRes < nLen - 1)\n\t\t\t\tbreak;\n\t\t}\n\t\tstrText.ReleaseBuffer();\n\t\treturn nRes;\n\t}\n#endif // __ATLSTR_H__\n\n\tint GetItemText(int nItem, int nSubItem, LPTSTR lpszText, int nLen) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tLVITEM lvi = {};\n\t\tlvi.iSubItem = nSubItem;\n\t\tlvi.cchTextMax = nLen;\n\t\tlvi.pszText = lpszText;\n\t\treturn (int)::SendMessage(this->m_hWnd, LVM_GETITEMTEXT, (WPARAM)nItem, (LPARAM)&lvi);\n\t}\n\n\tBOOL SetItemText(int nItem, int nSubItem, LPCTSTR lpszText)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn SetItem(nItem, nSubItem, LVIF_TEXT, lpszText, 0, 0, 0, 0);\n\t}\n\n\tDWORD_PTR GetItemData(int nItem) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tLVITEM lvi = {};\n\t\tlvi.iItem = nItem;\n\t\tlvi.mask = LVIF_PARAM;\n\t\tBOOL bRet = (BOOL)::SendMessage(this->m_hWnd, LVM_GETITEM, 0, (LPARAM)&lvi);\n\t\treturn (DWORD_PTR)(bRet ? lvi.lParam : NULL);\n\t}\n\n\tBOOL SetItemData(int nItem, DWORD_PTR dwData)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn SetItem(nItem, 0, LVIF_PARAM, NULL, 0, 0, 0, (LPARAM)dwData);\n\t}\n\n\tUINT GetCallbackMask() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (UINT)::SendMessage(this->m_hWnd, LVM_GETCALLBACKMASK, 0, 0L);\n\t}\n\n\tBOOL SetCallbackMask(UINT nMask)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, LVM_SETCALLBACKMASK, nMask, 0L);\n\t}\n\n\tBOOL GetItemPosition(int nItem, LPPOINT lpPoint) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, LVM_GETITEMPOSITION, nItem, (LPARAM)lpPoint);\n\t}\n\n\tBOOL SetItemPosition(int nItem, POINT pt)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT(((this->GetStyle() & LVS_TYPEMASK) == LVS_ICON) || ((this->GetStyle() & LVS_TYPEMASK) == LVS_SMALLICON));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, LVM_SETITEMPOSITION32, nItem, (LPARAM)&pt);\n\t}\n\n\tBOOL SetItemPosition(int nItem, int x, int y)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT(((this->GetStyle() & LVS_TYPEMASK) == LVS_ICON) || ((this->GetStyle() & LVS_TYPEMASK) == LVS_SMALLICON));\n\t\tPOINT pt = { x, y };\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, LVM_SETITEMPOSITION32, nItem, (LPARAM)&pt);\n\t}\n\n\tint GetStringWidth(LPCTSTR lpsz) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, LVM_GETSTRINGWIDTH, 0, (LPARAM)lpsz);\n\t}\n\n\tCEdit GetEditControl() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn CEdit((HWND)::SendMessage(this->m_hWnd, LVM_GETEDITCONTROL, 0, 0L));\n\t}\n\n\tBOOL GetColumn(int nCol, LVCOLUMN* pColumn) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, LVM_GETCOLUMN, nCol, (LPARAM)pColumn);\n\t}\n\n\tBOOL SetColumn(int nCol, const LVCOLUMN* pColumn)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, LVM_SETCOLUMN, nCol, (LPARAM)pColumn);\n\t}\n\n\tint GetColumnWidth(int nCol) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, LVM_GETCOLUMNWIDTH, nCol, 0L);\n\t}\n\n\tBOOL SetColumnWidth(int nCol, int cx)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, LVM_SETCOLUMNWIDTH, nCol, MAKELPARAM(cx, 0));\n\t}\n\n\tBOOL GetViewRect(LPRECT lpRect) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, LVM_GETVIEWRECT, 0, (LPARAM)lpRect);\n\t}\n\n\tCOLORREF GetTextColor() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (COLORREF)::SendMessage(this->m_hWnd, LVM_GETTEXTCOLOR, 0, 0L);\n\t}\n\n\tBOOL SetTextColor(COLORREF cr)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, LVM_SETTEXTCOLOR, 0, cr);\n\t}\n\n\tCOLORREF GetTextBkColor() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (COLORREF)::SendMessage(this->m_hWnd, LVM_GETTEXTBKCOLOR, 0, 0L);\n\t}\n\n\tBOOL SetTextBkColor(COLORREF cr)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, LVM_SETTEXTBKCOLOR, 0, cr);\n\t}\n\n\tint GetTopIndex() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, LVM_GETTOPINDEX, 0, 0L);\n\t}\n\n\tint GetCountPerPage() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, LVM_GETCOUNTPERPAGE, 0, 0L);\n\t}\n\n\tBOOL GetOrigin(LPPOINT lpPoint) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, LVM_GETORIGIN, 0, (LPARAM)lpPoint);\n\t}\n\n\tUINT GetSelectedCount() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (UINT)::SendMessage(this->m_hWnd, LVM_GETSELECTEDCOUNT, 0, 0L);\n\t}\n\n\tBOOL GetItemRect(int nItem, LPRECT lpRect, UINT nCode) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tlpRect->left = nCode;\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, LVM_GETITEMRECT, (WPARAM)nItem, (LPARAM)lpRect);\n\t}\n\n\tHCURSOR GetHotCursor() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (HCURSOR)::SendMessage(this->m_hWnd, LVM_GETHOTCURSOR, 0, 0L);\n\t}\n\n\tHCURSOR SetHotCursor(HCURSOR hHotCursor)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (HCURSOR)::SendMessage(this->m_hWnd, LVM_SETHOTCURSOR, 0, (LPARAM)hHotCursor);\n\t}\n\n\tint GetHotItem() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, LVM_GETHOTITEM, 0, 0L);\n\t}\n\n\tint SetHotItem(int nIndex)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, LVM_SETHOTITEM, nIndex, 0L);\n\t}\n\n\tBOOL GetColumnOrderArray(int nCount, int* lpnArray) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, LVM_GETCOLUMNORDERARRAY, nCount, (LPARAM)lpnArray);\n\t}\n\n\tBOOL SetColumnOrderArray(int nCount, int* lpnArray)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, LVM_SETCOLUMNORDERARRAY, nCount, (LPARAM)lpnArray);\n\t}\n\n\tCHeaderCtrl GetHeader() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn CHeaderCtrl((HWND)::SendMessage(this->m_hWnd, LVM_GETHEADER, 0, 0L));\n\t}\n\n\tBOOL GetSubItemRect(int nItem, int nSubItem, int nFlag, LPRECT lpRect) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT((this->GetStyle() & LVS_TYPEMASK) == LVS_REPORT);\n\t\tATLASSERT(lpRect != NULL);\n\t\tlpRect->top = nSubItem;\n\t\tlpRect->left = nFlag;\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, LVM_GETSUBITEMRECT, nItem, (LPARAM)lpRect);\n\t}\n\n\tDWORD SetIconSpacing(int cx, int cy)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT((this->GetStyle() & LVS_TYPEMASK) == LVS_ICON);\n\t\treturn (DWORD)::SendMessage(this->m_hWnd, LVM_SETICONSPACING, 0, MAKELPARAM(cx, cy));\n\t}\n\n\tint GetISearchString(LPTSTR lpstr) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, LVM_GETISEARCHSTRING, 0, (LPARAM)lpstr);\n\t}\n\n\tvoid GetItemSpacing(SIZE& sizeSpacing, BOOL bSmallIconView = FALSE) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tDWORD dwRet = (DWORD)::SendMessage(this->m_hWnd, LVM_GETITEMSPACING, bSmallIconView, 0L);\n\t\tsizeSpacing.cx = GET_X_LPARAM(dwRet);\n\t\tsizeSpacing.cy = GET_Y_LPARAM(dwRet);\n\t}\n\n\t// single-selection only\n\tint GetSelectedIndex() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT((this->GetStyle() & LVS_SINGLESEL) != 0);\n\t\treturn (int)::SendMessage(this->m_hWnd, LVM_GETNEXTITEM, (WPARAM)-1, MAKELPARAM(LVNI_ALL | LVNI_SELECTED, 0));\n\t}\n\n\tBOOL GetSelectedItem(LPLVITEM pItem) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT((this->GetStyle() & LVS_SINGLESEL) != 0);\n\t\tATLASSERT(pItem != NULL);\n\t\tpItem->iItem = (int)::SendMessage(this->m_hWnd, LVM_GETNEXTITEM, (WPARAM)-1, MAKELPARAM(LVNI_ALL | LVNI_SELECTED, 0));\n\t\tif(pItem->iItem == -1)\n\t\t\treturn FALSE;\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, LVM_GETITEM, 0, (LPARAM)pItem);\n\t}\n\n\t// extended list view styles\n\tDWORD GetExtendedListViewStyle() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (DWORD)::SendMessage(this->m_hWnd, LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0L);\n\t}\n\n\t// dwExMask = 0 means all styles\n\tDWORD SetExtendedListViewStyle(DWORD dwExStyle, DWORD dwExMask = 0)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (DWORD)::SendMessage(this->m_hWnd, LVM_SETEXTENDEDLISTVIEWSTYLE, dwExMask, dwExStyle);\n\t}\n\n\t// checkboxes only\n\tBOOL GetCheckState(int nIndex) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT((GetExtendedListViewStyle() & LVS_EX_CHECKBOXES) != 0);\n\t\tUINT uRet = GetItemState(nIndex, LVIS_STATEIMAGEMASK);\n\t\treturn (uRet >> 12) - 1;\n\t}\n\n\tBOOL SetCheckState(int nItem, BOOL bCheck)\n\t{\n\t\tint nCheck = bCheck ? 2 : 1;   // one based index\n\t\treturn SetItemState(nItem, INDEXTOSTATEIMAGEMASK(nCheck), LVIS_STATEIMAGEMASK);\n\t}\n\n\t// view type\n\tDWORD GetViewType() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (this->GetStyle() & LVS_TYPEMASK);\n\t}\n\n\tDWORD SetViewType(DWORD dwType)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT((dwType == LVS_ICON) || (dwType == LVS_SMALLICON) || (dwType == LVS_LIST) || (dwType == LVS_REPORT));\n\t\tDWORD dwOldType = GetViewType();\n\t\tif(dwType != dwOldType)\n\t\t\tthis->ModifyStyle(LVS_TYPEMASK, (dwType & LVS_TYPEMASK));\n\t\treturn dwOldType;\n\t}\n\n\tBOOL GetBkImage(LPLVBKIMAGE plvbki) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, LVM_GETBKIMAGE, 0, (LPARAM)plvbki);\n\t}\n\n\tBOOL SetBkImage(LPLVBKIMAGE plvbki)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, LVM_SETBKIMAGE, 0, (LPARAM)plvbki);\n\t}\n\n\tint GetSelectionMark() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, LVM_GETSELECTIONMARK, 0, 0L);\n\t}\n\n\tint SetSelectionMark(int nIndex)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, LVM_SETSELECTIONMARK, 0, nIndex);\n\t}\n\n\tBOOL GetWorkAreas(int nWorkAreas, LPRECT lpRect) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, LVM_GETWORKAREAS, nWorkAreas, (LPARAM)lpRect);\n\t}\n\n\tBOOL SetWorkAreas(int nWorkAreas, LPRECT lpRect)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, LVM_SETWORKAREAS, nWorkAreas, (LPARAM)lpRect);\n\t}\n\n\tDWORD GetHoverTime() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT((GetExtendedListViewStyle() & (LVS_EX_TRACKSELECT | LVS_EX_ONECLICKACTIVATE | LVS_EX_TWOCLICKACTIVATE)) != 0);\n\t\treturn (DWORD)::SendMessage(this->m_hWnd, LVM_GETHOVERTIME, 0, 0L);\n\t}\n\n\tDWORD SetHoverTime(DWORD dwHoverTime)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT((GetExtendedListViewStyle() & (LVS_EX_TRACKSELECT | LVS_EX_ONECLICKACTIVATE | LVS_EX_TWOCLICKACTIVATE)) != 0);\n\t\treturn (DWORD)::SendMessage(this->m_hWnd, LVM_SETHOVERTIME, 0, dwHoverTime);\n\t}\n\n\tBOOL GetNumberOfWorkAreas(int* pnWorkAreas) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, LVM_GETNUMBEROFWORKAREAS, 0, (LPARAM)pnWorkAreas);\n\t}\n\n\tBOOL SetItemCountEx(int nItems, DWORD dwFlags)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT(((this->GetStyle() & LVS_OWNERDATA) != 0) && (((this->GetStyle() & LVS_TYPEMASK) == LVS_REPORT) || ((this->GetStyle() & LVS_TYPEMASK) == LVS_LIST)));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, LVM_SETITEMCOUNT, nItems, dwFlags);\n\t}\n\n\tCToolTipCtrl GetToolTips() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn CToolTipCtrl((HWND)::SendMessage(this->m_hWnd, LVM_GETTOOLTIPS, 0, 0L));\n\t}\n\n\tCToolTipCtrl SetToolTips(HWND hWndTT)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn CToolTipCtrl((HWND)::SendMessage(this->m_hWnd, LVM_SETTOOLTIPS, (WPARAM)hWndTT, 0L));\n\t}\n\n\tBOOL GetUnicodeFormat() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, LVM_GETUNICODEFORMAT, 0, 0L);\n\t}\n\n\tBOOL SetUnicodeFormat(BOOL bUnicode = TRUE)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, LVM_SETUNICODEFORMAT, bUnicode, 0L);\n\t}\n\n\tint GetSelectedColumn() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, LVM_GETSELECTEDCOLUMN, 0, 0L);\n\t}\n\n\tvoid SetSelectedColumn(int nColumn)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, LVM_SETSELECTEDCOLUMN, nColumn, 0L);\n\t}\n\n\tDWORD GetView() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (DWORD)::SendMessage(this->m_hWnd, LVM_GETVIEW, 0, 0L);\n\t}\n\n\tint SetView(DWORD dwView)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, LVM_SETVIEW, dwView, 0L);\n\t}\n\n\tBOOL IsGroupViewEnabled() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, LVM_ISGROUPVIEWENABLED, 0, 0L);\n\t}\n\n\tint GetGroupInfo(int nGroupID, PLVGROUP pGroup) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, LVM_GETGROUPINFO, nGroupID, (LPARAM)pGroup);\n\t}\n\n\tint SetGroupInfo(int nGroupID, PLVGROUP pGroup)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, LVM_SETGROUPINFO, nGroupID, (LPARAM)pGroup);\n\t}\n\n\tvoid GetGroupMetrics(PLVGROUPMETRICS pGroupMetrics) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, LVM_GETGROUPMETRICS, 0, (LPARAM)pGroupMetrics);\n\t}\n\n\tvoid SetGroupMetrics(PLVGROUPMETRICS pGroupMetrics)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, LVM_SETGROUPMETRICS, 0, (LPARAM)pGroupMetrics);\n\t}\n\n\tvoid GetTileViewInfo(PLVTILEVIEWINFO pTileViewInfo) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, LVM_GETTILEVIEWINFO, 0, (LPARAM)pTileViewInfo);\n\t}\n\n\tBOOL SetTileViewInfo(PLVTILEVIEWINFO pTileViewInfo)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, LVM_SETTILEVIEWINFO, 0, (LPARAM)pTileViewInfo);\n\t}\n\n\tvoid GetTileInfo(PLVTILEINFO pTileInfo) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, LVM_GETTILEINFO, 0, (LPARAM)pTileInfo);\n\t}\n\n\tBOOL SetTileInfo(PLVTILEINFO pTileInfo)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, LVM_SETTILEINFO, 0, (LPARAM)pTileInfo);\n\t}\n\n\tBOOL GetInsertMark(LPLVINSERTMARK pInsertMark) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, LVM_GETINSERTMARK, 0, (LPARAM)pInsertMark);\n\t}\n\n\tBOOL SetInsertMark(LPLVINSERTMARK pInsertMark)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, LVM_SETINSERTMARK, 0, (LPARAM)pInsertMark);\n\t}\n\n\tint GetInsertMarkRect(LPRECT lpRect) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, LVM_GETINSERTMARKRECT, 0, (LPARAM)lpRect);\n\t}\n\n\tCOLORREF GetInsertMarkColor() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (COLORREF)::SendMessage(this->m_hWnd, LVM_GETINSERTMARKCOLOR, 0, 0L);\n\t}\n\n\tCOLORREF SetInsertMarkColor(COLORREF clr)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (COLORREF)::SendMessage(this->m_hWnd, LVM_SETINSERTMARKCOLOR, 0, clr);\n\t}\n\n\tCOLORREF GetOutlineColor() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (COLORREF)::SendMessage(this->m_hWnd, LVM_GETOUTLINECOLOR, 0, 0L);\n\t}\n\n\tCOLORREF SetOutlineColor(COLORREF clr)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (COLORREF)::SendMessage(this->m_hWnd, LVM_SETOUTLINECOLOR, 0, clr);\n\t}\n\n#if (_WIN32_WINNT >= 0x0600)\n\tint GetGroupCount() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, LVM_GETGROUPCOUNT, 0, 0L);\n\t}\n\n\tBOOL GetGroupInfoByIndex(int nIndex, PLVGROUP pGroup) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, LVM_GETGROUPINFOBYINDEX, nIndex, (LPARAM)pGroup);\n\t}\n\n\tBOOL GetGroupRect(int nGroupID, int nType, LPRECT lpRect) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT(lpRect != NULL);\n\t\tif(lpRect != NULL)\n\t\t\tlpRect->top = nType;\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, LVM_GETGROUPRECT, nGroupID, (LPARAM)lpRect);\n\t}\n\n\tUINT GetGroupState(int nGroupID, UINT uMask) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (UINT)::SendMessage(this->m_hWnd, LVM_GETGROUPSTATE, nGroupID, (LPARAM)uMask);\n\t}\n\n\tint GetFocusedGroup() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, LVM_GETFOCUSEDGROUP, 0, 0L);\n\t}\n\n\tBOOL GetEmptyText(LPWSTR lpstrText, int cchText) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, LVM_GETEMPTYTEXT, cchText, (LPARAM)lpstrText);\n\t}\n\n\tBOOL GetFooterRect(LPRECT lpRect) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, LVM_GETFOOTERRECT, 0, (LPARAM)lpRect);\n\t}\n\n\tBOOL GetFooterInfo(LPLVFOOTERINFO lpFooterInfo) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, LVM_GETFOOTERINFO, 0, (LPARAM)lpFooterInfo);\n\t}\n\n\tBOOL GetFooterItemRect(int nItem, LPRECT lpRect) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, LVM_GETFOOTERITEMRECT, nItem, (LPARAM)lpRect);\n\t}\n\n\tBOOL GetFooterItem(int nItem, LPLVFOOTERITEM lpFooterItem) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, LVM_GETFOOTERITEM, nItem, (LPARAM)lpFooterItem);\n\t}\n\n\tBOOL GetItemIndexRect(PLVITEMINDEX pItemIndex, int nSubItem, int nType, LPRECT lpRect) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT(pItemIndex != NULL);\n\t\tATLASSERT(lpRect != NULL);\n\t\tif(lpRect != NULL)\n\t\t{\n\t\t\tlpRect->top = nSubItem;\n\t\t\tlpRect->left = nType;\n\t\t}\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, LVM_GETITEMINDEXRECT, (WPARAM)pItemIndex, (LPARAM)lpRect);\n\t}\n\n\tBOOL SetItemIndexState(PLVITEMINDEX pItemIndex, UINT uState, UINT dwMask)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tLVITEM lvi = {};\n\t\tlvi.state = uState;\n\t\tlvi.stateMask = dwMask;\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, LVM_SETITEMINDEXSTATE, (WPARAM)pItemIndex, (LPARAM)&lvi);\n\t}\n\n\tBOOL GetNextItemIndex(PLVITEMINDEX pItemIndex, WORD wFlags) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, LVM_GETNEXTITEMINDEX, (WPARAM)pItemIndex, MAKELPARAM(wFlags, 0));\n\t}\n#endif // (_WIN32_WINNT >= 0x0600)\n\n// Operations\n\tint InsertColumn(int nCol, const LVCOLUMN* pColumn)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, LVM_INSERTCOLUMN, nCol, (LPARAM)pColumn);\n\t}\n\n\tint InsertColumn(int nCol, LPCTSTR lpszColumnHeading, int nFormat = LVCFMT_LEFT, \n\t\t\tint nWidth = -1, int nSubItem = -1, int iImage = -1, int iOrder = -1)\n\t{\n\t\tLVCOLUMN column = {};\n\t\tcolumn.mask = LVCF_TEXT | LVCF_FMT;\n\t\tcolumn.pszText = (LPTSTR)lpszColumnHeading;\n\t\tcolumn.fmt = nFormat;\n\t\tif (nWidth != -1)\n\t\t{\n\t\t\tcolumn.mask |= LVCF_WIDTH;\n\t\t\tcolumn.cx = nWidth;\n\t\t}\n\t\tif (nSubItem != -1)\n\t\t{\n\t\t\tcolumn.mask |= LVCF_SUBITEM;\n\t\t\tcolumn.iSubItem = nSubItem;\n\t\t}\n\t\tif (iImage != -1)\n\t\t{\n\t\t\tcolumn.mask |= LVCF_IMAGE;\n\t\t\tcolumn.iImage = iImage;\n\t\t}\n\t\tif (iOrder != -1)\n\t\t{\n\t\t\tcolumn.mask |= LVCF_ORDER;\n\t\t\tcolumn.iOrder = iOrder;\n\t\t}\n\t\treturn InsertColumn(nCol, &column);\n\t}\n\n\tBOOL DeleteColumn(int nCol)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, LVM_DELETECOLUMN, nCol, 0L);\n\t}\n\n\tint InsertItem(UINT nMask, int nItem, LPCTSTR lpszItem, UINT nState, UINT nStateMask, int nImage, LPARAM lParam)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tLVITEM item = {};\n\t\titem.mask = nMask;\n\t\titem.iItem = nItem;\n\t\titem.iSubItem = 0;\n\t\titem.pszText = (LPTSTR)lpszItem;\n\t\titem.state = nState;\n\t\titem.stateMask = nStateMask;\n\t\titem.iImage = nImage;\n\t\titem.lParam = lParam;\n\t\treturn InsertItem(&item);\n\t}\n\n\tint InsertItem(const LVITEM* pItem)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, LVM_INSERTITEM, 0, (LPARAM)pItem);\n\t}\n\n\tint InsertItem(int nItem, LPCTSTR lpszItem)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn InsertItem(LVIF_TEXT, nItem, lpszItem, 0, 0, 0, 0);\n\t}\n\n\tint InsertItem(int nItem, LPCTSTR lpszItem, int nImage)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn InsertItem(LVIF_TEXT|LVIF_IMAGE, nItem, lpszItem, 0, 0, nImage, 0);\n\t}\n\n\tint GetNextItem(int nItem, int nFlags) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, LVM_GETNEXTITEM, nItem, MAKELPARAM(nFlags, 0));\n\t}\n\n\tBOOL DeleteItem(int nItem)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, LVM_DELETEITEM, nItem, 0L);\n\t}\n\n\tBOOL DeleteAllItems()\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, LVM_DELETEALLITEMS, 0, 0L);\n\t}\n\n\tint FindItem(LVFINDINFO* pFindInfo, int nStart = -1) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, LVM_FINDITEM, nStart, (LPARAM)pFindInfo);\n\t}\n\n\tint FindItem(LPCTSTR lpstrFind, bool bPartial = true, bool bWrap = false, int nStart = -1) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tLVFINDINFO lvfi = {};\n\t\tlvfi.flags = LVFI_STRING | (bWrap ? LVFI_WRAP : 0) | (bPartial ? LVFI_PARTIAL : 0);\n\t\tlvfi.psz = lpstrFind;\n\t\treturn (int)::SendMessage(this->m_hWnd, LVM_FINDITEM, nStart, (LPARAM)&lvfi);\n\t}\n\n\tint HitTest(LVHITTESTINFO* pHitTestInfo) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, LVM_HITTEST, 0, (LPARAM)pHitTestInfo);\n\t}\n\n\tint HitTest(POINT pt, UINT* pFlags) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tLVHITTESTINFO hti = {};\n\t\thti.pt = pt;\n\t\tint nRes = (int)::SendMessage(this->m_hWnd, LVM_HITTEST, 0, (LPARAM)&hti);\n\t\tif (pFlags != NULL)\n\t\t\t*pFlags = hti.flags;\n\t\treturn nRes;\n\t}\n\n\tBOOL EnsureVisible(int nItem, BOOL bPartialOK)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, LVM_ENSUREVISIBLE, nItem, MAKELPARAM(bPartialOK, 0));\n\t}\n\n\tBOOL Scroll(int cx, int cy)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, LVM_SCROLL, cx, cy);\n\t}\n\n\tBOOL Scroll(SIZE size)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, LVM_SCROLL, size.cx, size.cy);\n\t}\n\n\tBOOL RedrawItems(int nFirst, int nLast)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, LVM_REDRAWITEMS, nFirst, nLast);\n\t}\n\n\tBOOL Arrange(UINT nCode)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, LVM_ARRANGE, nCode, 0L);\n\t}\n\n\tCEdit EditLabel(int nItem)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn CEdit((HWND)::SendMessage(this->m_hWnd, LVM_EDITLABEL, nItem, 0L));\n\t}\n\n\tBOOL Update(int nItem)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, LVM_UPDATE, nItem, 0L);\n\t}\n\n\tBOOL SortItems(PFNLVCOMPARE pfnCompare, LPARAM lParamSort)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, LVM_SORTITEMS, (WPARAM)lParamSort, (LPARAM)pfnCompare);\n\t}\n\n\tCImageList RemoveImageList(int nImageList)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn CImageList((HIMAGELIST)::SendMessage(this->m_hWnd, LVM_SETIMAGELIST, (WPARAM)nImageList, NULL));\n\t}\n\n\tCImageList CreateDragImage(int nItem, LPPOINT lpPoint)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn CImageList((HIMAGELIST)::SendMessage(this->m_hWnd, LVM_CREATEDRAGIMAGE, nItem, (LPARAM)lpPoint));\n\t}\n\n\tDWORD ApproximateViewRect(int cx = -1, int cy = -1, int nCount = -1)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (DWORD)::SendMessage(this->m_hWnd, LVM_APPROXIMATEVIEWRECT, nCount, MAKELPARAM(cx, cy));\n\t}\n\n\tint SubItemHitTest(LPLVHITTESTINFO lpInfo) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, LVM_SUBITEMHITTEST, 0, (LPARAM)lpInfo);\n\t}\n\n\tint AddColumn(LPCTSTR strColumn, int nItem, int nSubItem = -1,\n\t\t\tint nMask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM,\n\t\t\tint nFmt = LVCFMT_LEFT)\n\t{\n\t\tconst int cxOffset = 15;\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tLVCOLUMN lvc = {};\n\t\tlvc.mask = nMask;\n\t\tlvc.fmt = nFmt;\n\t\tlvc.pszText = (LPTSTR)strColumn;\n\t\tlvc.cx = GetStringWidth(lvc.pszText) + cxOffset;\n\t\tif(nMask & LVCF_SUBITEM)\n\t\t\tlvc.iSubItem = (nSubItem != -1) ? nSubItem : nItem;\n\t\treturn InsertColumn(nItem, &lvc);\n\t}\n\n\tint AddItem(int nItem, int nSubItem, LPCTSTR strItem, int nImageIndex = -3)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tLVITEM lvItem = {};\n\t\tlvItem.mask = LVIF_TEXT;\n\t\tlvItem.iItem = nItem;\n\t\tlvItem.iSubItem = nSubItem;\n\t\tlvItem.pszText = (LPTSTR)strItem;\n\t\tif(nImageIndex != -3)\n\t\t{\n\t\t\tlvItem.mask |= LVIF_IMAGE;\n\t\t\tlvItem.iImage = nImageIndex;\n\t\t}\n\t\tif(nSubItem == 0)\n\t\t\treturn InsertItem(&lvItem);\n\t\treturn SetItem(&lvItem) ? nItem : -1;\n\t}\n\n\tBOOL SortItemsEx(PFNLVCOMPARE pfnCompare, LPARAM lParamSort)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, LVM_SORTITEMSEX, (WPARAM)lParamSort, (LPARAM)pfnCompare);\n\t}\n\n\tint InsertGroup(int nItem, PLVGROUP pGroup)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, LVM_INSERTGROUP, nItem, (LPARAM)pGroup);\n\t}\n\n\tint AddGroup(PLVGROUP pGroup)\n\t{\n\t\treturn InsertGroup(-1, pGroup);\n\t}\n\n\tint RemoveGroup(int nGroupID)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, LVM_REMOVEGROUP, nGroupID, 0L);\n\t}\n\n\tvoid MoveGroup(int nGroupID, int nItem)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, LVM_MOVEGROUP, nGroupID, nItem);\n\t}\n\n\tvoid MoveItemToGroup(int nItem, int nGroupID)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, LVM_MOVEITEMTOGROUP, nItem, nGroupID);\n\t}\n\n\tint EnableGroupView(BOOL bEnable)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, LVM_ENABLEGROUPVIEW, bEnable, 0L);\n\t}\n\n\tint SortGroups(PFNLVGROUPCOMPARE pCompareFunc, LPVOID lpVoid = NULL)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, LVM_SORTGROUPS, (WPARAM)pCompareFunc, (LPARAM)lpVoid);\n\t}\n\n\tvoid InsertGroupSorted(PLVINSERTGROUPSORTED pInsertGroupSorted)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, LVM_INSERTGROUPSORTED, (WPARAM)pInsertGroupSorted, 0L);\n\t}\n\n\tvoid RemoveAllGroups()\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, LVM_REMOVEALLGROUPS, 0, 0L);\n\t}\n\n\tBOOL HasGroup(int nGroupID)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, LVM_HASGROUP, nGroupID, 0L);\n\t}\n\n\tBOOL InsertMarkHitTest(LPPOINT lpPoint, LPLVINSERTMARK pInsertMark) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, LVM_INSERTMARKHITTEST, (WPARAM)lpPoint, (LPARAM)pInsertMark);\n\t}\n\n\tBOOL SetInfoTip(PLVSETINFOTIP pSetInfoTip)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, LVM_SETINFOTIP, 0, (LPARAM)pSetInfoTip);\n\t}\n\n\tvoid CancelEditLabel()\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, LVM_CANCELEDITLABEL, 0, 0L);\n\t}\n\n\tUINT MapIndexToID(int nIndex) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (UINT)::SendMessage(this->m_hWnd, LVM_MAPINDEXTOID, nIndex, 0L);\n\t}\n\n\tint MapIDToIndex(UINT uID) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, LVM_MAPIDTOINDEX, uID, 0L);\n\t}\n\n\tBOOL IsItemVisible(int nItem) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, LVM_ISITEMVISIBLE, nItem, 0L);\n\t}\n\n#if (_WIN32_WINNT >= 0x0600)\n\tint HitTestEx(LPLVHITTESTINFO lpHitTestInfo) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, LVM_HITTEST, (WPARAM)-1, (LPARAM)lpHitTestInfo);\n\t}\n\n\tint HitTestEx(POINT pt, UINT* pFlags) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tLVHITTESTINFO hti = {};\n\t\thti.pt = pt;\n\t\tint nRes = (int)::SendMessage(this->m_hWnd, LVM_HITTEST, (WPARAM)-1, (LPARAM)&hti);\n\t\tif (pFlags != NULL)\n\t\t\t*pFlags = hti.flags;\n\t\treturn nRes;\n\t}\n\n\tint SubItemHitTestEx(LPLVHITTESTINFO lpHitTestInfo) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, LVM_SUBITEMHITTEST, (WPARAM)-1, (LPARAM)lpHitTestInfo);\n\t}\n#endif // (_WIN32_WINNT >= 0x0600)\n\n\t// Note: selects only one item\n\tBOOL SelectItem(int nIndex)   // -1 to select none\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\n\t\tBOOL bRet = FALSE;\n\t\tif(nIndex != -1)\n\t\t{\n\t\t\t// multi-selection only: de-select all items\n\t\t\tif((this->GetStyle() & LVS_SINGLESEL) == 0)\n\t\t\t\tSetItemState(-1, 0, LVIS_SELECTED);\n\n\t\t\tbRet = SetItemState(nIndex, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED);\n\t\t\tif(bRet)\n\t\t\t{\n\t\t\t\tSetSelectionMark(nIndex);\n\t\t\t\tbRet = EnsureVisible(nIndex, FALSE);\n\t\t\t}\n\t\t}\n\t\telse   // no item specified, just de-select\n\t\t{\n\t\t\tbRet = SetItemState(-1, 0, LVIS_SELECTED);\n\t\t}\n\n\t\treturn bRet;\n\t}\n\n\t// multi-selection only\n\tBOOL SelectAllItems(bool bSelect = true)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT((this->GetStyle() & LVS_SINGLESEL) == 0);\n\n\t\treturn SetItemState(-1, bSelect ? LVIS_SELECTED : 0, LVIS_SELECTED);\n\t}\n};\n\ntypedef CListViewCtrlT<ATL::CWindow>   CListViewCtrl;\n\n\n///////////////////////////////////////////////////////////////////////////////\n// CTreeViewCtrl\n\ntemplate <class TBase>\nclass CTreeViewCtrlT : public TBase\n{\npublic:\n// Constructors\n\tCTreeViewCtrlT(HWND hWnd = NULL) : TBase(hWnd)\n\t{ }\n\n\tCTreeViewCtrlT< TBase >& operator =(HWND hWnd)\n\t{\n\t\tthis->m_hWnd = hWnd;\n\t\treturn *this;\n\t}\n\n\tHWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL,\n\t\t\tDWORD dwStyle = 0, DWORD dwExStyle = 0,\n\t\t\tATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL)\n\t{\n\t\treturn TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam);\n\t}\n\n// Attributes\n\tstatic LPCTSTR GetWndClassName()\n\t{\n\t\treturn WC_TREEVIEW;\n\t}\n\n\tUINT GetCount() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (UINT)::SendMessage(this->m_hWnd, TVM_GETCOUNT, 0, 0L);\n\t}\n\n\tUINT GetIndent() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (UINT)::SendMessage(this->m_hWnd, TVM_GETINDENT, 0, 0L);\n\t}\n\n\tvoid SetIndent(UINT nIndent)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, TVM_SETINDENT, nIndent, 0L);\n\t}\n\n\tCImageList GetImageList(int nImageListType = TVSIL_NORMAL) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn CImageList((HIMAGELIST)::SendMessage(this->m_hWnd, TVM_GETIMAGELIST, (WPARAM)nImageListType, 0L));\n\t}\n\n\tCImageList SetImageList(HIMAGELIST hImageList, int nImageListType = TVSIL_NORMAL)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn CImageList((HIMAGELIST)::SendMessage(this->m_hWnd, TVM_SETIMAGELIST, (WPARAM)nImageListType, (LPARAM)hImageList));\n\t}\n\n\tBOOL GetItem(LPTVITEM pItem) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TVM_GETITEM, 0, (LPARAM)pItem);\n\t}\n\n\tBOOL SetItem(LPTVITEM pItem)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TVM_SETITEM, 0, (LPARAM)pItem);\n\t}\n\n\tBOOL SetItem(HTREEITEM hItem, UINT nMask, LPCTSTR lpszItem, int nImage,\n\t\tint nSelectedImage, UINT nState, UINT nStateMask, LPARAM lParam)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tTVITEM item = {};\n\t\titem.hItem = hItem;\n\t\titem.mask = nMask;\n\t\titem.pszText = (LPTSTR) lpszItem;\n\t\titem.iImage = nImage;\n\t\titem.iSelectedImage = nSelectedImage;\n\t\titem.state = nState;\n\t\titem.stateMask = nStateMask;\n\t\titem.lParam = lParam;\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TVM_SETITEM, 0, (LPARAM)&item);\n\t}\n\n\tBOOL GetItemText(HTREEITEM hItem, LPTSTR lpstrText, int nLen) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT(lpstrText != NULL);\n\n\t\tTVITEM item = {};\n\t\titem.hItem = hItem;\n\t\titem.mask = TVIF_TEXT;\n\t\titem.pszText = lpstrText;\n\t\titem.cchTextMax = nLen;\n\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TVM_GETITEM, 0, (LPARAM)&item);\n\t}\n\n\tBOOL GetItemText(HTREEITEM hItem, BSTR& bstrText) const\n\t{\n\t\tUSES_CONVERSION;\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT(bstrText == NULL);\n\t\tTVITEM item = {};\n\t\titem.hItem = hItem;\n\t\titem.mask = TVIF_TEXT;\n\n\t\tLPTSTR lpstrText = NULL;\n\t\tBOOL bRet = FALSE;\n\t\tfor(int nLen = 256; ; nLen *= 2)\n\t\t{\n\t\t\tATLTRY(lpstrText = new TCHAR[nLen]);\n\t\t\tif(lpstrText == NULL)\n\t\t\t\tbreak;\n\t\t\tlpstrText[0] = NULL;\n\t\t\titem.pszText = lpstrText;\n\t\t\titem.cchTextMax = nLen;\n\t\t\tbRet = (BOOL)::SendMessage(this->m_hWnd, TVM_GETITEM, 0, (LPARAM)&item);\n\t\t\tif(!bRet || (lstrlen(item.pszText) < (nLen - 1)))\n\t\t\t\tbreak;\n\t\t\tdelete [] lpstrText;\n\t\t\tlpstrText = NULL;\n\t\t}\n\n\t\tif(lpstrText != NULL)\n\t\t{\n\t\t\tif(bRet)\n\t\t\t\tbstrText = ::SysAllocString(T2OLE(lpstrText));\n\t\t\tdelete [] lpstrText;\n\t\t}\n\n\t\treturn (bstrText != NULL) ? TRUE : FALSE;\n\t}\n\n#ifdef __ATLSTR_H__\n\tBOOL GetItemText(HTREEITEM hItem, ATL::CString& strText) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tTVITEM item = {};\n\t\titem.hItem = hItem;\n\t\titem.mask = TVIF_TEXT;\n\n\t\tstrText.Empty();\n\t\tBOOL bRet = FALSE;\n\t\tfor(int nLen = 256; ; nLen *= 2)\n\t\t{\n\t\t\titem.pszText = strText.GetBufferSetLength(nLen);\n\t\t\tif(item.pszText == NULL)\n\t\t\t{\n\t\t\t\tbRet = FALSE;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\titem.cchTextMax = nLen;\n\t\t\tbRet = (BOOL)::SendMessage(this->m_hWnd, TVM_GETITEM, 0, (LPARAM)&item);\n\t\t\tif(!bRet || (lstrlen(item.pszText) < (nLen - 1)))\n\t\t\t\tbreak;\n\t\t}\n\t\tstrText.ReleaseBuffer();\n\t\treturn bRet;\n\t}\n#endif // __ATLSTR_H__\n\n\tBOOL SetItemText(HTREEITEM hItem, LPCTSTR lpszItem)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn SetItem(hItem, TVIF_TEXT, lpszItem, 0, 0, 0, 0, NULL);\n\t}\n\n\tBOOL GetItemImage(HTREEITEM hItem, int& nImage, int& nSelectedImage) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tTVITEM item = {};\n\t\titem.hItem = hItem;\n\t\titem.mask = TVIF_IMAGE|TVIF_SELECTEDIMAGE;\n\t\tBOOL bRes = (BOOL)::SendMessage(this->m_hWnd, TVM_GETITEM, 0, (LPARAM)&item);\n\t\tif (bRes)\n\t\t{\n\t\t\tnImage = item.iImage;\n\t\t\tnSelectedImage = item.iSelectedImage;\n\t\t}\n\t\treturn bRes;\n\t}\n\n\tBOOL SetItemImage(HTREEITEM hItem, int nImage, int nSelectedImage)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn SetItem(hItem, TVIF_IMAGE|TVIF_SELECTEDIMAGE, NULL, nImage, nSelectedImage, 0, 0, NULL);\n\t}\n\n\tUINT GetItemState(HTREEITEM hItem, UINT nStateMask) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (((UINT)::SendMessage(this->m_hWnd, TVM_GETITEMSTATE, (WPARAM)hItem, (LPARAM)nStateMask)) & nStateMask);\n\t}\n\n\tBOOL SetItemState(HTREEITEM hItem, UINT nState, UINT nStateMask)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn SetItem(hItem, TVIF_STATE, NULL, 0, 0, nState, nStateMask, NULL);\n\t}\n\n\tDWORD_PTR GetItemData(HTREEITEM hItem) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tTVITEM item = {};\n\t\titem.hItem = hItem;\n\t\titem.mask = TVIF_PARAM;\n\t\tBOOL bRet = (BOOL)::SendMessage(this->m_hWnd, TVM_GETITEM, 0, (LPARAM)&item);\n\t\treturn (DWORD_PTR)(bRet ? item.lParam : NULL);\n\t}\n\n\tBOOL SetItemData(HTREEITEM hItem, DWORD_PTR dwData)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn SetItem(hItem, TVIF_PARAM, NULL, 0, 0, 0, 0, (LPARAM)dwData);\n\t}\n\n\tCEdit GetEditControl() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn CEdit((HWND)::SendMessage(this->m_hWnd, TVM_GETEDITCONTROL, 0, 0L));\n\t}\n\n\tUINT GetVisibleCount() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (UINT)::SendMessage(this->m_hWnd, TVM_GETVISIBLECOUNT, 0, 0L);\n\t}\n\n\tBOOL GetItemRect(HTREEITEM hItem, LPRECT lpRect, BOOL bTextOnly) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t*(HTREEITEM*)lpRect = hItem;\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TVM_GETITEMRECT, (WPARAM)bTextOnly, (LPARAM)lpRect);\n\t}\n\n\tBOOL ItemHasChildren(HTREEITEM hItem) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tTVITEM item = {};\n\t\titem.hItem = hItem;\n\t\titem.mask = TVIF_CHILDREN;\n\t\t::SendMessage(this->m_hWnd, TVM_GETITEM, 0, (LPARAM)&item);\n\t\treturn item.cChildren;\n\t}\n\n\tCToolTipCtrl GetToolTips() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn CToolTipCtrl((HWND)::SendMessage(this->m_hWnd, TVM_GETTOOLTIPS, 0, 0L));\n\t}\n\n\tCToolTipCtrl SetToolTips(HWND hWndTT)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn CToolTipCtrl((HWND)::SendMessage(this->m_hWnd, TVM_SETTOOLTIPS, (WPARAM)hWndTT, 0L));\n\t}\n\n\tint GetISearchString(LPTSTR lpstr) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, TVM_GETISEARCHSTRING, 0, (LPARAM)lpstr);\n\t}\n\n\t// checkboxes only\n\tBOOL GetCheckState(HTREEITEM hItem) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT((this->GetStyle() & TVS_CHECKBOXES) != 0);\n\t\tUINT uRet = GetItemState(hItem, TVIS_STATEIMAGEMASK);\n\t\treturn (uRet >> 12) - 1;\n\t}\n\n\tBOOL SetCheckState(HTREEITEM hItem, BOOL bCheck)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT((this->GetStyle() & TVS_CHECKBOXES) != 0);\n\t\tint nCheck = bCheck ? 2 : 1;   // one based index\n\t\treturn SetItemState(hItem, INDEXTOSTATEIMAGEMASK(nCheck), TVIS_STATEIMAGEMASK);\n\t}\n\n\t// for standard and extended checkboxes (0 = no checkbox, 1 = unchecked, 2 = checked, >2 = optional extended check states)\n\tUINT GetCheckStateEx(HTREEITEM hItem) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT(this->GetImageList(TVSIL_STATE) != NULL);\n\t\tUINT uRet = GetItemState(hItem, TVIS_STATEIMAGEMASK);\n\t\treturn (uRet >> 12);\n\t}\n\n\tBOOL SetCheckStateEx(HTREEITEM hItem, UINT uCheckState)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT(this->GetImageList(TVSIL_STATE) != NULL);\n\t\tATLASSERT(uCheckState < (UINT)::ImageList_GetImageCount(this->GetImageList(TVSIL_STATE)));\n\t\treturn SetItemState(hItem, INDEXTOSTATEIMAGEMASK(uCheckState), TVIS_STATEIMAGEMASK);\n\t}\n\n\tCOLORREF GetBkColor() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (COLORREF)::SendMessage(this->m_hWnd, TVM_GETBKCOLOR, 0, 0L);\n\t}\n\n\tCOLORREF SetBkColor(COLORREF clr)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (COLORREF)::SendMessage(this->m_hWnd, TVM_SETBKCOLOR, 0, (LPARAM)clr);\n\t}\n\n\tCOLORREF GetInsertMarkColor() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (COLORREF)::SendMessage(this->m_hWnd, TVM_GETINSERTMARKCOLOR, 0, 0L);\n\t}\n\n\tCOLORREF SetInsertMarkColor(COLORREF clr)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (COLORREF)::SendMessage(this->m_hWnd, TVM_SETINSERTMARKCOLOR, 0, (LPARAM)clr);\n\t}\n\n\tint GetItemHeight() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, TVM_GETITEMHEIGHT, 0, 0L);\n\t}\n\n\tint SetItemHeight(int cyHeight)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, TVM_SETITEMHEIGHT, cyHeight, 0L);\n\t}\n\n\tint GetScrollTime() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, TVM_GETSCROLLTIME, 0, 0L);\n\t}\n\n\tint SetScrollTime(int nScrollTime)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, TVM_SETSCROLLTIME, nScrollTime, 0L);\n\t}\n\n\tCOLORREF GetTextColor() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (COLORREF)::SendMessage(this->m_hWnd, TVM_GETTEXTCOLOR, 0, 0L);\n\t}\n\n\tCOLORREF SetTextColor(COLORREF clr)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (COLORREF)::SendMessage(this->m_hWnd, TVM_SETTEXTCOLOR, 0, (LPARAM)clr);\n\t}\n\n\tBOOL GetUnicodeFormat() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TVM_GETUNICODEFORMAT, 0, 0L);\n\t}\n\n\tBOOL SetUnicodeFormat(BOOL bUnicode = TRUE)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TVM_SETUNICODEFORMAT, bUnicode, 0L);\n\t}\n\n\tCOLORREF GetLineColor() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (COLORREF)::SendMessage(this->m_hWnd, TVM_GETLINECOLOR, 0, 0L);\n\t}\n\n\tCOLORREF SetLineColor(COLORREF clrNew /*= CLR_DEFAULT*/)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (COLORREF)::SendMessage(this->m_hWnd, TVM_SETLINECOLOR, 0, (LPARAM)clrNew);\n\t}\n\n\tBOOL GetItem(LPTVITEMEX pItem) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TVM_GETITEM, 0, (LPARAM)pItem);\n\t}\n\n\tBOOL SetItem(LPTVITEMEX pItem)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TVM_SETITEM, 0, (LPARAM)pItem);\n\t}\n\n\tDWORD GetExtendedStyle() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (DWORD)::SendMessage(this->m_hWnd, TVM_GETEXTENDEDSTYLE, 0, 0L);\n\t}\n\n\tDWORD SetExtendedStyle(DWORD dwStyle, DWORD dwMask)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (DWORD)::SendMessage(this->m_hWnd, TVM_SETEXTENDEDSTYLE, dwMask, dwStyle);\n\t}\n\n#if (_WIN32_WINNT >= 0x0600)\n\tBOOL SetAutoScrollInfo(UINT uPixPerSec, UINT uUpdateTime)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TVM_SETAUTOSCROLLINFO, (WPARAM)uPixPerSec, (LPARAM)uUpdateTime);\n\t}\n\n\tDWORD GetSelectedCount() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (DWORD)::SendMessage(this->m_hWnd, TVM_GETSELECTEDCOUNT, 0, 0L);\n\t}\n\n\tBOOL GetItemPartRect(HTREEITEM hItem, TVITEMPART partID, LPRECT lpRect) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tTVGETITEMPARTRECTINFO gipri = { hItem, lpRect, partID };\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TVM_GETITEMPARTRECT, 0, (LPARAM)&gipri);\n\t}\n#endif // (_WIN32_WINNT >= 0x0600)\n\n// Operations\n\tHTREEITEM InsertItem(LPTVINSERTSTRUCT lpInsertStruct)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (HTREEITEM)::SendMessage(this->m_hWnd, TVM_INSERTITEM, 0, (LPARAM)lpInsertStruct);\n\t}\n\n\tHTREEITEM InsertItem(LPCTSTR lpszItem, int nImage,\n\t\tint nSelectedImage, HTREEITEM hParent, HTREEITEM hInsertAfter)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn InsertItem(TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE, lpszItem, nImage, nSelectedImage, 0, 0, 0, hParent, hInsertAfter); \n\t}\n\n\tHTREEITEM InsertItem(LPCTSTR lpszItem, HTREEITEM hParent, HTREEITEM hInsertAfter)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn InsertItem(TVIF_TEXT, lpszItem, 0, 0, 0, 0, 0, hParent, hInsertAfter);\n\t}\n\n\tHTREEITEM InsertItem(UINT nMask, LPCTSTR lpszItem, int nImage,\n\t\tint nSelectedImage, UINT nState, UINT nStateMask, LPARAM lParam,\n\t\tHTREEITEM hParent, HTREEITEM hInsertAfter)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tTVINSERTSTRUCT tvis = {};\n\t\ttvis.hParent = hParent;\n\t\ttvis.hInsertAfter = hInsertAfter;\n\t\ttvis.item.mask = nMask;\n\t\ttvis.item.pszText = (LPTSTR) lpszItem;\n\t\ttvis.item.iImage = nImage;\n\t\ttvis.item.iSelectedImage = nSelectedImage;\n\t\ttvis.item.state = nState;\n\t\ttvis.item.stateMask = nStateMask;\n\t\ttvis.item.lParam = lParam;\n\t\treturn (HTREEITEM)::SendMessage(this->m_hWnd, TVM_INSERTITEM, 0, (LPARAM)&tvis);\n\t}\n\n\tBOOL DeleteItem(HTREEITEM hItem)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TVM_DELETEITEM, 0, (LPARAM)hItem);\n\t}\n\n\tBOOL DeleteAllItems()\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TVM_DELETEITEM, 0, (LPARAM)TVI_ROOT);\n\t}\n\n\tBOOL Expand(HTREEITEM hItem, UINT nCode = TVE_EXPAND)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TVM_EXPAND, nCode, (LPARAM)hItem);\n\t}\n\n\tHTREEITEM GetNextItem(HTREEITEM hItem, UINT nCode) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd)); \n\t\treturn (HTREEITEM)::SendMessage(this->m_hWnd, TVM_GETNEXTITEM, nCode, (LPARAM)hItem);\n\t}\n\n\tHTREEITEM GetChildItem(HTREEITEM hItem) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd)); \n\t\treturn (HTREEITEM)::SendMessage(this->m_hWnd, TVM_GETNEXTITEM, TVGN_CHILD, (LPARAM)hItem);\n\t}\n\n\tHTREEITEM GetNextSiblingItem(HTREEITEM hItem) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd)); \n\t\treturn (HTREEITEM)::SendMessage(this->m_hWnd, TVM_GETNEXTITEM, TVGN_NEXT, (LPARAM)hItem); \n\t}\n\n\tHTREEITEM GetPrevSiblingItem(HTREEITEM hItem) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd)); \n\t\treturn (HTREEITEM)::SendMessage(this->m_hWnd, TVM_GETNEXTITEM, TVGN_PREVIOUS, (LPARAM)hItem);\n\t}\n\n\tHTREEITEM GetParentItem(HTREEITEM hItem) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd)); \n\t\treturn (HTREEITEM)::SendMessage(this->m_hWnd, TVM_GETNEXTITEM, TVGN_PARENT, (LPARAM)hItem); \n\t}\n\n\tHTREEITEM GetFirstVisibleItem() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd)); \n\t\treturn (HTREEITEM)::SendMessage(this->m_hWnd, TVM_GETNEXTITEM, TVGN_FIRSTVISIBLE, 0L);\n\t}\n\n\tHTREEITEM GetNextVisibleItem(HTREEITEM hItem) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (HTREEITEM)::SendMessage(this->m_hWnd, TVM_GETNEXTITEM, TVGN_NEXTVISIBLE, (LPARAM)hItem);\n\t}\n\n\tHTREEITEM GetPrevVisibleItem(HTREEITEM hItem) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (HTREEITEM)::SendMessage(this->m_hWnd, TVM_GETNEXTITEM, TVGN_PREVIOUSVISIBLE, (LPARAM)hItem);\n\t}\n\n\tHTREEITEM GetSelectedItem() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (HTREEITEM)::SendMessage(this->m_hWnd, TVM_GETNEXTITEM, TVGN_CARET, 0L);\n\t}\n\n\tHTREEITEM GetDropHilightItem() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (HTREEITEM)::SendMessage(this->m_hWnd, TVM_GETNEXTITEM, TVGN_DROPHILITE, 0L);\n\t}\n\n\tHTREEITEM GetRootItem() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (HTREEITEM)::SendMessage(this->m_hWnd, TVM_GETNEXTITEM, TVGN_ROOT, 0L);\n\t}\n\n\tHTREEITEM GetLastVisibleItem() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (HTREEITEM)::SendMessage(this->m_hWnd, TVM_GETNEXTITEM, TVGN_LASTVISIBLE, 0L);\n\t}\n\n\tHTREEITEM GetNextSelectedItem(HTREEITEM hItem) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (HTREEITEM)::SendMessage(this->m_hWnd, TVM_GETNEXTITEM, TVGN_NEXTSELECTED, (LPARAM)hItem);\n\t}\n\n\tBOOL Select(HTREEITEM hItem, UINT nCode)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TVM_SELECTITEM, nCode, (LPARAM)hItem);\n\t}\n\n\tBOOL SelectItem(HTREEITEM hItem)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hItem);\n\t}\n\n\tBOOL SelectDropTarget(HTREEITEM hItem)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TVM_SELECTITEM, TVGN_DROPHILITE, (LPARAM)hItem);\n\t}\n\n\tBOOL SelectSetFirstVisible(HTREEITEM hItem)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TVM_SELECTITEM, TVGN_FIRSTVISIBLE, (LPARAM)hItem);\n\t}\n\n\tCEdit EditLabel(HTREEITEM hItem)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn CEdit((HWND)::SendMessage(this->m_hWnd, TVM_EDITLABEL, 0, (LPARAM)hItem));\n\t}\n\n\tBOOL EndEditLabelNow(BOOL bCancel)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TVM_ENDEDITLABELNOW, bCancel, 0L);\n\t}\n\n\tHTREEITEM HitTest(TVHITTESTINFO* pHitTestInfo) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (HTREEITEM)::SendMessage(this->m_hWnd, TVM_HITTEST, 0, (LPARAM)pHitTestInfo);\n\t}\n\n\tHTREEITEM HitTest(POINT pt, UINT* pFlags) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tTVHITTESTINFO hti = {};\n\t\thti.pt = pt;\n\t\tHTREEITEM hTreeItem = (HTREEITEM)::SendMessage(this->m_hWnd, TVM_HITTEST, 0, (LPARAM)&hti);\n\t\tif (pFlags != NULL)\n\t\t\t*pFlags = hti.flags;\n\t\treturn hTreeItem;\n\t}\n\n\tBOOL SortChildren(HTREEITEM hItem, BOOL bRecurse = FALSE)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TVM_SORTCHILDREN, (WPARAM)bRecurse, (LPARAM)hItem);\n\t}\n\n\tBOOL EnsureVisible(HTREEITEM hItem)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TVM_ENSUREVISIBLE, 0, (LPARAM)hItem);\n\t}\n\n\tBOOL SortChildrenCB(LPTVSORTCB pSort, BOOL bRecurse = FALSE)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TVM_SORTCHILDRENCB, (WPARAM)bRecurse, (LPARAM)pSort);\n\t}\n\n\tCImageList RemoveImageList(int nImageList)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn CImageList((HIMAGELIST)::SendMessage(this->m_hWnd, TVM_SETIMAGELIST, (WPARAM)nImageList, NULL));\n\t}\n\n\tCImageList CreateDragImage(HTREEITEM hItem)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn CImageList((HIMAGELIST)::SendMessage(this->m_hWnd, TVM_CREATEDRAGIMAGE, 0, (LPARAM)hItem));\n\t}\n\n\tBOOL SetInsertMark(HTREEITEM hTreeItem, BOOL bAfter)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TVM_SETINSERTMARK, bAfter, (LPARAM)hTreeItem);\n\t}\n\n\tBOOL RemoveInsertMark()\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TVM_SETINSERTMARK, 0, 0L);\n\t}\n\n\tHTREEITEM MapAccIDToHTREEITEM(UINT uID) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (HTREEITEM)::SendMessage(this->m_hWnd, TVM_MAPACCIDTOHTREEITEM, uID, 0L);\n\t}\n\n\tUINT MapHTREEITEMToAccID(HTREEITEM hTreeItem) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (UINT)::SendMessage(this->m_hWnd, TVM_MAPHTREEITEMTOACCID, (WPARAM)hTreeItem, 0L);\n\t}\n\n#if (_WIN32_WINNT >= 0x0600)\n\tvoid ShowInfoTip(HTREEITEM hItem)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, TVM_SHOWINFOTIP, 0, (LPARAM)hItem);\n\t}\n#endif // (_WIN32_WINNT >= 0x0600)\n};\n\ntypedef CTreeViewCtrlT<ATL::CWindow>   CTreeViewCtrl;\n\n\n///////////////////////////////////////////////////////////////////////////////\n// CTreeViewCtrlEx\n\n// forward declaration\ntemplate <class TBase> class CTreeViewCtrlExT;\n\n// Note: TBase here is for CTreeViewCtrlExT, and not for CTreeItemT itself\ntemplate <class TBase>\nclass CTreeItemT\n{\npublic:\n\tHTREEITEM m_hTreeItem;\n\tCTreeViewCtrlExT<TBase>* m_pTreeView;\n\n// Construction\n\tCTreeItemT(HTREEITEM hTreeItem = NULL, CTreeViewCtrlExT<TBase>* pTreeView = NULL) : m_hTreeItem(hTreeItem), m_pTreeView(pTreeView)\n\t{ }\n \n\tCTreeItemT(const CTreeItemT<TBase>& posSrc)\n\t{\n\t\t*this = posSrc;\n\t}\n\n\toperator HTREEITEM() { return m_hTreeItem; }\n\n\tCTreeItemT<TBase>& operator =(const CTreeItemT<TBase>& itemSrc)\n\t{\n\t\tm_hTreeItem = itemSrc.m_hTreeItem;\n\t\tm_pTreeView = itemSrc.m_pTreeView;\n\t\treturn *this;\n\t}\n\n// Attributes\n\tCTreeViewCtrlExT<TBase>* GetTreeView() const { return m_pTreeView; }\n\n\tBOOL operator !() const { return m_hTreeItem == NULL; }\n\n\tBOOL IsNull() const { return m_hTreeItem == NULL; }\n\t\n\tBOOL GetRect(LPRECT lpRect, BOOL bTextOnly) const;\n\tBOOL GetText(LPTSTR lpstrText, int nLen) const;\n\tBOOL GetText(BSTR& bstrText) const;\n#ifdef __ATLSTR_H__\n\tBOOL GetText(ATL::CString& strText) const;\n#endif // __ATLSTR_H__\n\tBOOL SetText(LPCTSTR lpszItem);\n\tBOOL GetImage(int& nImage, int& nSelectedImage) const;\n\tBOOL SetImage(int nImage, int nSelectedImage);\n\tUINT GetState(UINT nStateMask) const;\n\tBOOL SetState(UINT nState, UINT nStateMask);\n\tDWORD_PTR GetData() const;\n\tBOOL SetData(DWORD_PTR dwData);\n\tBOOL SetItem(UINT nMask, LPCTSTR lpszItem, int nImage, int nSelectedImage, UINT nState, UINT nStateMask, LPARAM lParam);\n\n// Operations\n\tCTreeItemT<TBase> InsertAfter(LPCTSTR lpstrItem, HTREEITEM hItemAfter, int nImageIndex)\n\t{\n\t\treturn _Insert(lpstrItem, nImageIndex, hItemAfter);\n\t}\n\n\tCTreeItemT<TBase> AddHead(LPCTSTR lpstrItem, int nImageIndex)\n\t{\n\t\treturn _Insert(lpstrItem, nImageIndex, TVI_FIRST);\n\t}\n\n\tCTreeItemT<TBase> AddTail(LPCTSTR lpstrItem, int nImageIndex)\n\t{\n\t\treturn _Insert(lpstrItem, nImageIndex, TVI_LAST);\n\t}\n\n\tCTreeItemT<TBase> GetChild() const;\n\tCTreeItemT<TBase> GetNext(UINT nCode) const;\n\tCTreeItemT<TBase> GetNextSibling() const;\n\tCTreeItemT<TBase> GetPrevSibling() const;\n\tCTreeItemT<TBase> GetParent() const;\n\tCTreeItemT<TBase> GetFirstVisible() const;\n\tCTreeItemT<TBase> GetNextVisible() const;\n\tCTreeItemT<TBase> GetPrevVisible() const;\n\tCTreeItemT<TBase> GetSelected() const;\n\tCTreeItemT<TBase> GetDropHilight() const;\n\tCTreeItemT<TBase> GetRoot() const;\n\tCTreeItemT<TBase> GetLastVisible() const;\n\tCTreeItemT<TBase> GetNextSelected() const;\n\tBOOL HasChildren() const;\n\tBOOL Delete();\n\tBOOL Expand(UINT nCode = TVE_EXPAND);\n\tBOOL Select(UINT nCode);\n\tBOOL Select();\n\tBOOL SelectDropTarget();\n\tBOOL SelectSetFirstVisible();\n\tHWND EditLabel();\n\tHIMAGELIST CreateDragImage();\n\tBOOL SortChildren(BOOL bRecurse = FALSE);\n\tBOOL EnsureVisible();\n\tCTreeItemT<TBase> _Insert(LPCTSTR lpstrItem, int nImageIndex, HTREEITEM hItemAfter);\n\tint GetImageIndex() const;\n\tBOOL SetInsertMark(BOOL bAfter);\n\tUINT MapHTREEITEMToAccID() const;\n#if (_WIN32_WINNT >= 0x0600)\n\tvoid ShowInfoTip();\n\tBOOL GetPartRect(TVITEMPART partID, LPRECT lpRect) const;\n#endif // (_WIN32_WINNT >= 0x0600)\n};\n\ntypedef CTreeItemT<ATL::CWindow>   CTreeItem;\n\n\ntemplate <class TBase>\nclass CTreeViewCtrlExT : public CTreeViewCtrlT< TBase >\n{\npublic:\n// Constructors\n\tCTreeViewCtrlExT(HWND hWnd = NULL) : CTreeViewCtrlT< TBase >(hWnd)\n\t{ }\n\n\tCTreeViewCtrlExT< TBase >& operator =(HWND hWnd)\n\t{\n\t\tthis->m_hWnd = hWnd;\n\t\treturn *this;\n\t}\n\n// Operations (overides that return CTreeItem)\n\tCTreeItemT<TBase> InsertItem(LPTVINSERTSTRUCT lpInsertStruct)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tHTREEITEM hTreeItem = (HTREEITEM)::SendMessage(this->m_hWnd, TVM_INSERTITEM, 0, (LPARAM)lpInsertStruct);\n\t\treturn CTreeItemT<TBase>(hTreeItem, this);\n\t}\n\n\tCTreeItemT<TBase> InsertItem(LPCTSTR lpszItem, int nImage,\n\t\tint nSelectedImage, HTREEITEM hParent, HTREEITEM hInsertAfter)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn InsertItem(TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE, lpszItem, nImage, nSelectedImage, 0, 0, 0, hParent, hInsertAfter); \n\t}\n\n\tCTreeItemT<TBase> InsertItem(LPCTSTR lpszItem, HTREEITEM hParent, HTREEITEM hInsertAfter)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn InsertItem(TVIF_TEXT, lpszItem, 0, 0, 0, 0, 0, hParent, hInsertAfter);\n\t}\n\n\tCTreeItemT<TBase> GetNextItem(HTREEITEM hItem, UINT nCode) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd)); \n\t\tHTREEITEM hTreeItem = (HTREEITEM)::SendMessage(this->m_hWnd, TVM_GETNEXTITEM, nCode, (LPARAM)hItem);\n\t\treturn CTreeItemT<TBase>(hTreeItem, (CTreeViewCtrlExT<TBase>*)this);\n\t}\n\n\tCTreeItemT<TBase> GetChildItem(HTREEITEM hItem) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd)); \n\t\tHTREEITEM hTreeItem = (HTREEITEM)::SendMessage(this->m_hWnd, TVM_GETNEXTITEM, TVGN_CHILD, (LPARAM)hItem);\n\t\treturn CTreeItemT<TBase>(hTreeItem, (CTreeViewCtrlExT<TBase>*)this); \n\t}\n\n\tCTreeItemT<TBase> GetNextSiblingItem(HTREEITEM hItem) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd)); \n\t\tHTREEITEM hTreeItem = (HTREEITEM)::SendMessage(this->m_hWnd, TVM_GETNEXTITEM, TVGN_NEXT, (LPARAM)hItem); \n\t\treturn CTreeItemT<TBase>(hTreeItem, (CTreeViewCtrlExT<TBase>*)this);\n\t}\n\n\tCTreeItemT<TBase> GetPrevSiblingItem(HTREEITEM hItem) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd)); \n\t\tHTREEITEM hTreeItem = (HTREEITEM)::SendMessage(this->m_hWnd, TVM_GETNEXTITEM, TVGN_PREVIOUS, (LPARAM)hItem);\n\t\treturn CTreeItemT<TBase>(hTreeItem, (CTreeViewCtrlExT<TBase>*)this);\n\t}\n\n\tCTreeItemT<TBase> GetParentItem(HTREEITEM hItem) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd)); \n\t\tHTREEITEM hTreeItem = (HTREEITEM)::SendMessage(this->m_hWnd, TVM_GETNEXTITEM, TVGN_PARENT, (LPARAM)hItem); \n\t\treturn CTreeItemT<TBase>(hTreeItem, (CTreeViewCtrlExT<TBase>*)this);\n\t}\n\n\tCTreeItemT<TBase> GetFirstVisibleItem() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd)); \n\t\tHTREEITEM hTreeItem = (HTREEITEM)::SendMessage(this->m_hWnd, TVM_GETNEXTITEM, TVGN_FIRSTVISIBLE, 0L);\n\t\treturn CTreeItemT<TBase>(hTreeItem, (CTreeViewCtrlExT<TBase>*)this);\n\t}\n\n\tCTreeItemT<TBase> GetNextVisibleItem(HTREEITEM hItem) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tHTREEITEM hTreeItem = (HTREEITEM)::SendMessage(this->m_hWnd, TVM_GETNEXTITEM, TVGN_NEXTVISIBLE, (LPARAM)hItem);\n\t\treturn CTreeItemT<TBase>(hTreeItem, (CTreeViewCtrlExT<TBase>*)this);\n\t}\n\n\tCTreeItemT<TBase> GetPrevVisibleItem(HTREEITEM hItem) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tHTREEITEM hTreeItem = (HTREEITEM)::SendMessage(this->m_hWnd, TVM_GETNEXTITEM, TVGN_PREVIOUSVISIBLE, (LPARAM)hItem);\n\t\treturn CTreeItemT<TBase>(hTreeItem, (CTreeViewCtrlExT<TBase>*)this);\n\t}\n\n\tCTreeItemT<TBase> GetSelectedItem() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tHTREEITEM hTreeItem = (HTREEITEM)::SendMessage(this->m_hWnd, TVM_GETNEXTITEM, TVGN_CARET, 0L);\n\t\treturn CTreeItemT<TBase>(hTreeItem, (CTreeViewCtrlExT<TBase>*)this);\n\t}\n\n\tCTreeItemT<TBase> GetDropHilightItem() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tHTREEITEM hTreeItem = (HTREEITEM)::SendMessage(this->m_hWnd, TVM_GETNEXTITEM, TVGN_DROPHILITE, 0L);\n\t\treturn CTreeItemT<TBase>(hTreeItem, (CTreeViewCtrlExT<TBase>*)this);\n\t}\n\n\tCTreeItemT<TBase> GetRootItem() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tHTREEITEM hTreeItem = (HTREEITEM)::SendMessage(this->m_hWnd, TVM_GETNEXTITEM, TVGN_ROOT, 0L);\n\t\treturn CTreeItemT<TBase>(hTreeItem, (CTreeViewCtrlExT<TBase>*)this);\n\t}\n\n\tCTreeItemT<TBase> GetLastVisibleItem() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tHTREEITEM hTreeItem = (HTREEITEM)::SendMessage(this->m_hWnd, TVM_GETNEXTITEM, TVGN_LASTVISIBLE, 0L);\n\t\treturn CTreeItemT<TBase>(hTreeItem, (CTreeViewCtrlExT<TBase>*)this);\n\t}\n\n\tCTreeItemT<TBase> GetNextSelectedItem(HTREEITEM hItem) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tHTREEITEM hTreeItem = (HTREEITEM)::SendMessage(this->m_hWnd, TVM_GETNEXTITEM, TVGN_NEXTSELECTED, (LPARAM)hItem);\n\t\treturn CTreeItemT<TBase>(hTreeItem, (CTreeViewCtrlExT<TBase>*)this);\n\t}\n\n\tCTreeItemT<TBase> HitTest(TVHITTESTINFO* pHitTestInfo) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tHTREEITEM hTreeItem = (HTREEITEM)::SendMessage(this->m_hWnd, TVM_HITTEST, 0, (LPARAM)pHitTestInfo);\n\t\treturn CTreeItemT<TBase>(hTreeItem, (CTreeViewCtrlExT<TBase>*)this);\n\t}\n\n\tCTreeItemT<TBase> InsertItem(UINT nMask, LPCTSTR lpszItem, int nImage,\n\t\tint nSelectedImage, UINT nState, UINT nStateMask, LPARAM lParam,\n\t\tHTREEITEM hParent, HTREEITEM hInsertAfter)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tTVINSERTSTRUCT tvis = {};\n\t\ttvis.hParent = hParent;\n\t\ttvis.hInsertAfter = hInsertAfter;\n\t\ttvis.item.mask = nMask;\n\t\ttvis.item.pszText = (LPTSTR) lpszItem;\n\t\ttvis.item.iImage = nImage;\n\t\ttvis.item.iSelectedImage = nSelectedImage;\n\t\ttvis.item.state = nState;\n\t\ttvis.item.stateMask = nStateMask;\n\t\ttvis.item.lParam = lParam;\n\t\tHTREEITEM hTreeItem = (HTREEITEM)::SendMessage(this->m_hWnd, TVM_INSERTITEM, 0, (LPARAM)&tvis);\n\t\treturn CTreeItemT<TBase>(hTreeItem, this);\n\t}\n\n\tCTreeItemT<TBase> HitTest(POINT pt, UINT* pFlags) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tTVHITTESTINFO hti = {};\n\t\thti.pt = pt;\n\t\tHTREEITEM hTreeItem = (HTREEITEM)::SendMessage(this->m_hWnd, TVM_HITTEST, 0, (LPARAM)&hti);\n\t\tif (pFlags != NULL)\n\t\t\t*pFlags = hti.flags;\n\t\treturn CTreeItemT<TBase>(hTreeItem, (CTreeViewCtrlExT<TBase>*)this);\n\t}\n\n\tCTreeItemT<TBase> MapAccIDToHTREEITEM(UINT uID) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tHTREEITEM hTreeItem = (HTREEITEM)::SendMessage(this->m_hWnd, TVM_MAPACCIDTOHTREEITEM, uID, 0L);\n\t\treturn CTreeItemT<TBase>(hTreeItem, (CTreeViewCtrlExT<TBase>*)this);\n\t}\n};\n\ntypedef CTreeViewCtrlExT<ATL::CWindow>   CTreeViewCtrlEx;\n\n\n// CTreeItem inline methods\ntemplate <class TBase>\ninline BOOL CTreeItemT<TBase>::GetRect(LPRECT lpRect, BOOL bTextOnly) const\n{\n\tATLASSERT(m_pTreeView != NULL);\n\treturn m_pTreeView->GetItemRect(m_hTreeItem,lpRect,bTextOnly);\n}\n\ntemplate <class TBase>\ninline CTreeItemT<TBase> CTreeItemT<TBase>::GetNext(UINT nCode) const\n{\n\tATLASSERT(m_pTreeView != NULL);\n\treturn m_pTreeView->GetNextItem(m_hTreeItem,nCode);\n}\n\ntemplate <class TBase>\ninline CTreeItemT<TBase> CTreeItemT<TBase>::GetChild() const\n{\n\tATLASSERT(m_pTreeView != NULL);\n\treturn m_pTreeView->GetChildItem(m_hTreeItem);\n}\n\ntemplate <class TBase>\ninline CTreeItemT<TBase> CTreeItemT<TBase>::GetNextSibling() const\n{\n\tATLASSERT(m_pTreeView != NULL);\n\treturn m_pTreeView->GetNextSiblingItem(m_hTreeItem);\n}\n\ntemplate <class TBase>\ninline CTreeItemT<TBase> CTreeItemT<TBase>::GetPrevSibling() const\n{\n\tATLASSERT(m_pTreeView != NULL);\n\treturn m_pTreeView->GetPrevSiblingItem(m_hTreeItem);\n}\n\ntemplate <class TBase>\ninline CTreeItemT<TBase> CTreeItemT<TBase>::GetParent() const\n{\n\tATLASSERT(m_pTreeView != NULL);\n\treturn m_pTreeView->GetParentItem(m_hTreeItem);\n}\n\ntemplate <class TBase>\ninline CTreeItemT<TBase> CTreeItemT<TBase>::GetFirstVisible() const\n{\n\tATLASSERT(m_pTreeView != NULL);\n\treturn m_pTreeView->GetFirstVisibleItem();\n}\n\ntemplate <class TBase>\ninline CTreeItemT<TBase> CTreeItemT<TBase>::GetNextVisible() const\n{\n\tATLASSERT(m_pTreeView != NULL);\n\treturn m_pTreeView->GetNextVisibleItem(m_hTreeItem);\n}\n\ntemplate <class TBase>\ninline CTreeItemT<TBase> CTreeItemT<TBase>::GetPrevVisible() const\n{\n\tATLASSERT(m_pTreeView != NULL);\n\treturn m_pTreeView->GetPrevVisibleItem(m_hTreeItem);\n}\n\ntemplate <class TBase>\ninline CTreeItemT<TBase> CTreeItemT<TBase>::GetSelected() const\n{\n\tATLASSERT(m_pTreeView != NULL);\n\treturn m_pTreeView->GetSelectedItem();\n}\n\ntemplate <class TBase>\ninline CTreeItemT<TBase> CTreeItemT<TBase>::GetDropHilight() const\n{\n\tATLASSERT(m_pTreeView != NULL);\n\treturn m_pTreeView->GetDropHilightItem();\n}\n\ntemplate <class TBase>\ninline CTreeItemT<TBase> CTreeItemT<TBase>::GetRoot() const\n{\n\tATLASSERT(m_pTreeView != NULL);\n\treturn m_pTreeView->GetRootItem();\n}\n\ntemplate <class TBase>\ninline CTreeItemT<TBase> CTreeItemT<TBase>::GetLastVisible() const\n{\n\tATLASSERT(m_pTreeView != NULL);\n\treturn m_pTreeView->GetLastVisibleItem();\n}\n\ntemplate <class TBase>\ninline CTreeItemT<TBase> CTreeItemT<TBase>::GetNextSelected() const\n{\n\tATLASSERT(m_pTreeView != NULL);\n\treturn m_pTreeView->GetNextSelectedItem(m_hTreeItem);\n}\n\ntemplate <class TBase>\ninline BOOL CTreeItemT<TBase>::GetText(LPTSTR lpstrText, int nLen) const\n{\n\tATLASSERT(m_pTreeView != NULL);\n\treturn m_pTreeView->GetItemText(m_hTreeItem, lpstrText, nLen);\n}\n\n#ifdef _OLEAUTO_H_\ntemplate <class TBase>\ninline BOOL CTreeItemT<TBase>::GetText(BSTR& bstrText) const\n{\n\tATLASSERT(m_pTreeView != NULL);\n\treturn m_pTreeView->GetItemText(m_hTreeItem, bstrText);\n}\n#endif // _OLEAUTO_H_\n\n#ifdef __ATLSTR_H__\ntemplate <class TBase>\ninline BOOL CTreeItemT<TBase>::GetText(ATL::CString& strText) const\n{\n\tATLASSERT(m_pTreeView != NULL);\n\treturn m_pTreeView->GetItemText(m_hTreeItem, strText);\n}\n#endif // __ATLSTR_H__\n\ntemplate <class TBase>\ninline BOOL CTreeItemT<TBase>::GetImage(int& nImage, int& nSelectedImage) const\n{\n\tATLASSERT(m_pTreeView != NULL);\n\treturn m_pTreeView->GetItemImage(m_hTreeItem,nImage,nSelectedImage);\n}\n\ntemplate <class TBase>\ninline UINT CTreeItemT<TBase>::GetState(UINT nStateMask) const\n{\n\tATLASSERT(m_pTreeView != NULL);\n\treturn m_pTreeView->GetItemState(m_hTreeItem,nStateMask);\n}\n\ntemplate <class TBase>\ninline DWORD_PTR CTreeItemT<TBase>::GetData() const\n{\n\tATLASSERT(m_pTreeView != NULL);\n\treturn m_pTreeView->GetItemData(m_hTreeItem);\n}\n\ntemplate <class TBase>\ninline BOOL CTreeItemT<TBase>::SetItem(UINT nMask, LPCTSTR lpszItem, int nImage,\n\t\tint nSelectedImage, UINT nState, UINT nStateMask, LPARAM lParam)\n{\n\tATLASSERT(m_pTreeView != NULL);\n\treturn m_pTreeView->SetItem(m_hTreeItem, nMask, lpszItem, nImage, nSelectedImage, nState, nStateMask, lParam);\n}\n\ntemplate <class TBase>\ninline BOOL CTreeItemT<TBase>::SetText(LPCTSTR lpszItem)\n{\n\tATLASSERT(m_pTreeView != NULL);\n\treturn m_pTreeView->SetItemText(m_hTreeItem,lpszItem);\n}\n\ntemplate <class TBase>\ninline BOOL CTreeItemT<TBase>::SetImage(int nImage, int nSelectedImage)\n{\n\tATLASSERT(m_pTreeView != NULL);\n\treturn m_pTreeView->SetItemImage(m_hTreeItem,nImage,nSelectedImage);\n}\n\ntemplate <class TBase>\ninline BOOL CTreeItemT<TBase>::SetState(UINT nState, UINT nStateMask)\n{\n\tATLASSERT(m_pTreeView != NULL);\n\treturn m_pTreeView->SetItemState(m_hTreeItem,nState,nStateMask);\n}\n\ntemplate <class TBase>\ninline BOOL CTreeItemT<TBase>::SetData(DWORD_PTR dwData)\n{\n\tATLASSERT(m_pTreeView != NULL);\n\treturn m_pTreeView->SetItemData(m_hTreeItem,dwData);\n}\n\ntemplate <class TBase>\ninline BOOL CTreeItemT<TBase>::HasChildren() const\n{\n\tATLASSERT(m_pTreeView != NULL);\n\treturn m_pTreeView->ItemHasChildren(m_hTreeItem);\n}\n\ntemplate <class TBase>\ninline BOOL CTreeItemT<TBase>::Delete()\n{\n\tATLASSERT(m_pTreeView != NULL);\n\treturn m_pTreeView->DeleteItem(m_hTreeItem);\n}\n\ntemplate <class TBase>\ninline BOOL CTreeItemT<TBase>::Expand(UINT nCode /*= TVE_EXPAND*/)\n{\n\tATLASSERT(m_pTreeView != NULL);\n\treturn m_pTreeView->Expand(m_hTreeItem,nCode);\n}\n\ntemplate <class TBase>\ninline BOOL CTreeItemT<TBase>::Select(UINT nCode)\n{\n\tATLASSERT(m_pTreeView != NULL);\n\treturn m_pTreeView->Select(m_hTreeItem,nCode);\n}\n\ntemplate <class TBase>\ninline BOOL CTreeItemT<TBase>::Select()\n{\n\tATLASSERT(m_pTreeView != NULL);\n\treturn m_pTreeView->SelectItem(m_hTreeItem);\n}\n\ntemplate <class TBase>\ninline BOOL CTreeItemT<TBase>::SelectDropTarget()\n{\n\tATLASSERT(m_pTreeView != NULL);\n\treturn m_pTreeView->SelectDropTarget(m_hTreeItem);\n}\n\ntemplate <class TBase>\ninline BOOL CTreeItemT<TBase>::SelectSetFirstVisible()\n{\n\tATLASSERT(m_pTreeView != NULL);\n\treturn m_pTreeView->SelectSetFirstVisible(m_hTreeItem);\n}\n\ntemplate <class TBase>\ninline HWND CTreeItemT<TBase>::EditLabel()\n{\n\tATLASSERT(m_pTreeView != NULL);\n\treturn m_pTreeView->EditLabel(m_hTreeItem);\n}\n\ntemplate <class TBase>\ninline HIMAGELIST CTreeItemT<TBase>::CreateDragImage()\n{\n\tATLASSERT(m_pTreeView != NULL);\n\treturn m_pTreeView->CreateDragImage(m_hTreeItem);\n}\n\ntemplate <class TBase>\ninline BOOL CTreeItemT<TBase>::SortChildren(BOOL bRecurse /*= FALSE*/)\n{\n\tATLASSERT(m_pTreeView != NULL);\n\treturn m_pTreeView->SortChildren(m_hTreeItem, bRecurse);\n}\n\ntemplate <class TBase>\ninline BOOL CTreeItemT<TBase>::EnsureVisible()\n{\n\tATLASSERT(m_pTreeView != NULL);\n\treturn m_pTreeView->EnsureVisible(m_hTreeItem);\n}\n\ntemplate <class TBase>\ninline CTreeItemT<TBase> CTreeItemT<TBase>::_Insert(LPCTSTR lpstrItem, int nImageIndex, HTREEITEM hItemAfter)\n{\n\tATLASSERT(m_pTreeView != NULL);\n\tTVINSERTSTRUCT ins = {};\n\tins.hParent = m_hTreeItem;\n\tins.hInsertAfter = hItemAfter;\n\tins.item.mask = TVIF_TEXT;\n\tins.item.pszText = (LPTSTR)lpstrItem;\n\tif(nImageIndex != -1)\n\t{\n\t\tins.item.mask |= TVIF_IMAGE | TVIF_SELECTEDIMAGE;\n\t\tins.item.iImage = nImageIndex;\n\t\tins.item.iSelectedImage = nImageIndex;\n\t}\n\treturn CTreeItemT<TBase>(m_pTreeView->InsertItem(&ins), m_pTreeView);\n}\n\ntemplate <class TBase>\ninline int CTreeItemT<TBase>::GetImageIndex() const\n{\n\tATLASSERT(m_pTreeView != NULL);\n\tTVITEM item = {};\n\titem.mask = TVIF_HANDLE | TVIF_IMAGE;\n\titem.hItem = m_hTreeItem;\n\tm_pTreeView->GetItem(&item);\n\treturn item.iImage;\n}\n\ntemplate <class TBase>\ninline BOOL CTreeItemT<TBase>::SetInsertMark(BOOL bAfter)\n{\n\tATLASSERT(m_pTreeView != NULL);\n\treturn m_pTreeView->SetInsertMark(m_hTreeItem, bAfter);\n}\n\ntemplate <class TBase>\ninline UINT CTreeItemT<TBase>::MapHTREEITEMToAccID() const\n{\n\tATLASSERT(m_pTreeView != NULL);\n\treturn m_pTreeView->MapHTREEITEMToAccID(m_hTreeItem);\n}\n\n#if (_WIN32_WINNT >= 0x0600)\ntemplate <class TBase>\ninline void CTreeItemT<TBase>::ShowInfoTip()\n{\n\tATLASSERT(m_pTreeView != NULL);\n\tm_pTreeView->ShowInfoTip(m_hTreeItem);\n}\n\ntemplate <class TBase>\ninline BOOL CTreeItemT<TBase>::GetPartRect(TVITEMPART partID, LPRECT lpRect) const\n{\n\tATLASSERT(m_pTreeView != NULL);\n\treturn m_pTreeView->GetItemPartRect(m_hTreeItem, partID, lpRect);\n}\n#endif // (_WIN32_WINNT >= 0x0600)\n\n\n///////////////////////////////////////////////////////////////////////////////\n// CToolBarCtrl\n\ntemplate <class TBase>\nclass CToolBarCtrlT : public TBase\n{\npublic:\n// Construction\n\tCToolBarCtrlT(HWND hWnd = NULL) : TBase(hWnd)\n\t{ }\n\n\tCToolBarCtrlT< TBase >& operator =(HWND hWnd)\n\t{\n\t\tthis->m_hWnd = hWnd;\n\t\treturn *this;\n\t}\n\n\tHWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL,\n\t\t\tDWORD dwStyle = 0, DWORD dwExStyle = 0,\n\t\t\tATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL)\n\t{\n\t\treturn TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam);\n\t}\n\n// Attributes\n\tstatic LPCTSTR GetWndClassName()\n\t{\n\t\treturn TOOLBARCLASSNAME;\n\t}\n\n\tBOOL IsButtonEnabled(int nID) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TB_ISBUTTONENABLED, nID, 0L);\n\t}\n\n\tBOOL IsButtonChecked(int nID) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TB_ISBUTTONCHECKED, nID, 0L);\n\t}\n\n\tBOOL IsButtonPressed(int nID) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TB_ISBUTTONPRESSED, nID, 0L);\n\t}\n\n\tBOOL IsButtonHidden(int nID) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn(BOOL) ::SendMessage(this->m_hWnd, TB_ISBUTTONHIDDEN, nID, 0L);\n\t}\n\n\tBOOL IsButtonIndeterminate(int nID) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TB_ISBUTTONINDETERMINATE, nID, 0L);\n\t}\n\n\tint GetState(int nID) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, TB_GETSTATE, nID, 0L);\n\t}\n\n\tBOOL SetState(int nID, UINT nState)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TB_SETSTATE, nID, MAKELPARAM(nState, 0));\n\t}\n\n\tBOOL GetButton(int nIndex, LPTBBUTTON lpButton) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TB_GETBUTTON, nIndex, (LPARAM)lpButton);\n\t}\n\n\tint GetButtonCount() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, TB_BUTTONCOUNT, 0, 0L);\n\t}\n\n\tBOOL GetItemRect(int nIndex, LPRECT lpRect) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TB_GETITEMRECT, nIndex, (LPARAM)lpRect);\n\t}\n\n\tvoid SetButtonStructSize(int nSize = sizeof(TBBUTTON))\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, TB_BUTTONSTRUCTSIZE, nSize, 0L);\n\t}\n\n\tBOOL SetButtonSize(SIZE size)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TB_SETBUTTONSIZE, 0, MAKELPARAM(size.cx, size.cy));\n\t}\n\n\tBOOL SetButtonSize(int cx, int cy)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TB_SETBUTTONSIZE, 0, MAKELPARAM(cx, cy));\n\t}\n\n\tBOOL SetBitmapSize(SIZE size)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TB_SETBITMAPSIZE, 0, MAKELPARAM(size.cx, size.cy));\n\t}\n\n\tBOOL SetBitmapSize(int cx, int cy)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TB_SETBITMAPSIZE, 0, MAKELPARAM(cx, cy));\n\t}\n\n\tCToolTipCtrl GetToolTips() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn CToolTipCtrl((HWND)::SendMessage(this->m_hWnd, TB_GETTOOLTIPS, 0, 0L));\n\t}\n\n\tvoid SetToolTips(HWND hWndToolTip)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, TB_SETTOOLTIPS, (WPARAM)hWndToolTip, 0L);\n\t}\n\n\tvoid SetNotifyWnd(HWND hWnd)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, TB_SETPARENT, (WPARAM)hWnd, 0L);\n\t}\n\n\tint GetRows() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, TB_GETROWS, 0, 0L);\n\t}\n\n\tvoid SetRows(int nRows, BOOL bLarger, LPRECT lpRect)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, TB_SETROWS, MAKELPARAM(nRows, bLarger), (LPARAM)lpRect);\n\t}\n\n\tBOOL SetCmdID(int nIndex, UINT nID)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TB_SETCMDID, nIndex, nID);\n\t}\n\n\tDWORD GetBitmapFlags() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (DWORD)::SendMessage(this->m_hWnd, TB_GETBITMAPFLAGS, 0, 0L);\n\t}\n\n\tint GetBitmap(int nID) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, TB_GETBITMAP, nID, 0L);\n\t}\n\n\tint GetButtonText(int nID, LPTSTR lpstrText) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, TB_GETBUTTONTEXT, nID, (LPARAM)lpstrText);\n\t}\n\n\t// nIndex - IE5 or higher only\n\tCImageList GetImageList(int nIndex = 0) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn CImageList((HIMAGELIST)::SendMessage(this->m_hWnd, TB_GETIMAGELIST, nIndex, 0L));\n\t}\n\n\t// nIndex - IE5 or higher only\n\tCImageList SetImageList(HIMAGELIST hImageList, int nIndex = 0)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn CImageList((HIMAGELIST)::SendMessage(this->m_hWnd, TB_SETIMAGELIST, nIndex, (LPARAM)hImageList));\n\t}\n\n\t// nIndex - IE5 or higher only\n\tCImageList GetDisabledImageList(int nIndex = 0) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn CImageList((HIMAGELIST)::SendMessage(this->m_hWnd, TB_GETDISABLEDIMAGELIST, nIndex, 0L));\n\t}\n\n\t// nIndex - IE5 or higher only\n\tCImageList SetDisabledImageList(HIMAGELIST hImageList, int nIndex = 0)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn CImageList((HIMAGELIST)::SendMessage(this->m_hWnd, TB_SETDISABLEDIMAGELIST, nIndex, (LPARAM)hImageList));\n\t}\n\n\t// nIndex - IE5 or higher only\n\tCImageList GetHotImageList(int nIndex = 0) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn CImageList((HIMAGELIST)::SendMessage(this->m_hWnd, TB_GETHOTIMAGELIST, nIndex, 0L));\n\t}\n\n\t// nIndex - IE5 or higher only\n\tCImageList SetHotImageList(HIMAGELIST hImageList, int nIndex = 0)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn CImageList((HIMAGELIST)::SendMessage(this->m_hWnd, TB_SETHOTIMAGELIST, nIndex, (LPARAM)hImageList));\n\t}\n\n\tDWORD GetStyle() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (DWORD)::SendMessage(this->m_hWnd, TB_GETSTYLE, 0, 0L);\n\t}\n\n\tvoid SetStyle(DWORD dwStyle)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, TB_SETSTYLE, 0, dwStyle);\n\t}\n\n\tDWORD GetButtonSize() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (DWORD)::SendMessage(this->m_hWnd, TB_GETBUTTONSIZE, 0, 0L);\n\t}\n\n\tvoid GetButtonSize(SIZE& size) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tDWORD dwRet = (DWORD)::SendMessage(this->m_hWnd, TB_GETBUTTONSIZE, 0, 0L);\n\t\tsize.cx = LOWORD(dwRet);\n\t\tsize.cy = HIWORD(dwRet);\n\t}\n\n\tBOOL GetRect(int nID, LPRECT lpRect) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TB_GETRECT, nID, (LPARAM)lpRect);\n\t}\n\n\tint GetTextRows() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, TB_GETTEXTROWS, 0, 0L);\n\t}\n\n\tBOOL SetButtonWidth(int cxMin, int cxMax)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TB_SETBUTTONWIDTH, 0, MAKELPARAM(cxMin, cxMax));\n\t}\n\n\tBOOL SetIndent(int nIndent)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TB_SETINDENT, nIndent, 0L);\n\t}\n\n\tBOOL SetMaxTextRows(int nMaxTextRows)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TB_SETMAXTEXTROWS, nMaxTextRows, 0L);\n\t}\n\n\tBOOL GetAnchorHighlight() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TB_GETANCHORHIGHLIGHT, 0, 0L);\n\t}\n\n\tBOOL SetAnchorHighlight(BOOL bEnable = TRUE)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TB_SETANCHORHIGHLIGHT, bEnable, 0L);\n\t}\n\n\tint GetButtonInfo(int nID, LPTBBUTTONINFO lptbbi) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, TB_GETBUTTONINFO, nID, (LPARAM)lptbbi);\n\t}\n\n\tBOOL SetButtonInfo(int nID, LPTBBUTTONINFO lptbbi)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TB_SETBUTTONINFO, nID, (LPARAM)lptbbi);\n\t}\n\n\tBOOL SetButtonInfo(int nID, DWORD dwMask, BYTE Style, BYTE State, LPCTSTR lpszItem, \n\t                   int iImage, WORD cx, int iCommand, DWORD_PTR lParam)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tTBBUTTONINFO tbbi = {};\n\t\ttbbi.cbSize = sizeof(TBBUTTONINFO);\n\t\ttbbi.dwMask = dwMask;\n\t\ttbbi.idCommand = iCommand;\n\t\ttbbi.iImage = iImage;\n\t\ttbbi.fsState = State;\n\t\ttbbi.fsStyle = Style;\n\t\ttbbi.cx = cx;\n\t\ttbbi.pszText = (LPTSTR) lpszItem;\n\t\ttbbi.lParam = lParam;\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TB_SETBUTTONINFO, nID, (LPARAM)&tbbi);\n\t}\n\n\tint GetHotItem() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, TB_GETHOTITEM, 0, 0L);\n\t}\n\n\tint SetHotItem(int nItem)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, TB_SETHOTITEM, nItem, 0L);\n\t}\n\n\tBOOL IsButtonHighlighted(int nButtonID) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TB_ISBUTTONHIGHLIGHTED, nButtonID, 0L);\n\t}\n\n\tDWORD SetDrawTextFlags(DWORD dwMask, DWORD dwFlags)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (DWORD)::SendMessage(this->m_hWnd, TB_SETDRAWTEXTFLAGS, dwMask, dwFlags);\n\t}\n\n\tBOOL GetColorScheme(LPCOLORSCHEME lpcs) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TB_GETCOLORSCHEME, 0, (LPARAM)lpcs);\n\t}\n\n\tvoid SetColorScheme(LPCOLORSCHEME lpcs)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, TB_SETCOLORSCHEME, 0, (LPARAM)lpcs);\n\t}\n\n\tDWORD GetExtendedStyle() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (DWORD)::SendMessage(this->m_hWnd, TB_GETEXTENDEDSTYLE, 0, 0L);\n\t}\n\n\tDWORD SetExtendedStyle(DWORD dwStyle)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (DWORD)::SendMessage(this->m_hWnd, TB_SETEXTENDEDSTYLE, 0, dwStyle);\n\t}\n\n\tvoid GetInsertMark(LPTBINSERTMARK lptbim) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, TB_GETINSERTMARK, 0, (LPARAM)lptbim);\n\t}\n\n\tvoid SetInsertMark(LPTBINSERTMARK lptbim)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, TB_SETINSERTMARK, 0, (LPARAM)lptbim);\n\t}\n\n\tCOLORREF GetInsertMarkColor() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (COLORREF)::SendMessage(this->m_hWnd, TB_GETINSERTMARKCOLOR, 0, 0L);\n\t}\n\n\tCOLORREF SetInsertMarkColor(COLORREF clr)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (COLORREF)::SendMessage(this->m_hWnd, TB_SETINSERTMARKCOLOR, 0, (LPARAM)clr);\n\t}\n\n\tBOOL GetMaxSize(LPSIZE lpSize) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TB_GETMAXSIZE, 0, (LPARAM)lpSize);\n\t}\n\n\tvoid GetPadding(LPSIZE lpSizePadding) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT(lpSizePadding != NULL);\n\t\tDWORD dwRet = (DWORD)::SendMessage(this->m_hWnd, TB_GETPADDING, 0, 0L);\n\t\tlpSizePadding->cx = GET_X_LPARAM(dwRet);\n\t\tlpSizePadding->cy = GET_Y_LPARAM(dwRet);\n\t}\n\n\tvoid SetPadding(int cx, int cy, LPSIZE lpSizePadding = NULL)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tDWORD dwRet = (DWORD)::SendMessage(this->m_hWnd, TB_SETPADDING, 0, MAKELPARAM(cx, cy));\n\t\tif(lpSizePadding != NULL)\n\t\t{\n\t\t\tlpSizePadding->cx = GET_X_LPARAM(dwRet);\n\t\t\tlpSizePadding->cy = GET_Y_LPARAM(dwRet);\n\t\t}\n\t}\n\n\tBOOL GetUnicodeFormat() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TB_GETUNICODEFORMAT, 0, 0L);\n\t}\n\n\tBOOL SetUnicodeFormat(BOOL bUnicode = TRUE)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TB_SETUNICODEFORMAT, bUnicode, 0L);\n\t}\n\n\tint GetString(int nString, LPTSTR lpstrString, int cchMaxLen) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, TB_GETSTRING, MAKEWPARAM(cchMaxLen, nString), (LPARAM)lpstrString);\n\t}\n\n\tint GetStringBSTR(int nString, BSTR& bstrString) const\n\t{\n\t\tUSES_CONVERSION;\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT(bstrString == NULL);\n\t\tint nLength = (int)(short)LOWORD(::SendMessage(this->m_hWnd, TB_GETSTRING, MAKEWPARAM(0, nString), NULL));\n\t\tif(nLength != -1)\n\t\t{\n\t\t\tATL::CTempBuffer<TCHAR, _WTL_STACK_ALLOC_THRESHOLD> buff;\n\t\t\tLPTSTR lpstrText = buff.Allocate(nLength + 1);\n\t\t\tif(lpstrText != NULL)\n\t\t\t{\n\t\t\t\tnLength = (int)::SendMessage(this->m_hWnd, TB_GETSTRING, MAKEWPARAM(nLength + 1, nString), (LPARAM)lpstrText);\n\t\t\t\tif(nLength != -1)\n\t\t\t\t\tbstrString = ::SysAllocString(T2OLE(lpstrText));\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tnLength = -1;\n\t\t\t}\n\t\t}\n\n\t\treturn nLength;\n\t}\n\n#ifdef __ATLSTR_H__\n\tint GetString(int nString, ATL::CString& str) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tint nLength = (int)(short)LOWORD(::SendMessage(this->m_hWnd, TB_GETSTRING, MAKEWPARAM(0, nString), NULL));\n\t\tif(nLength != -1)\n\t\t{\n\t\t\tLPTSTR lpstr = str.GetBufferSetLength(nLength + 1);\n\t\t\tif(lpstr != NULL)\n\t\t\t\tnLength = (int)::SendMessage(this->m_hWnd, TB_GETSTRING, MAKEWPARAM(nLength + 1, nString), (LPARAM)lpstr);\n\t\t\telse\n\t\t\t\tnLength = -1;\n\t\t\tstr.ReleaseBuffer();\n\t\t}\n\t\treturn nLength;\n\t}\n#endif // __ATLSTR_H__\n\n\tvoid GetMetrics(LPTBMETRICS lptbm) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, TB_GETMETRICS, 0, (LPARAM)lptbm);\n\t}\n\n\tvoid SetMetrics(LPTBMETRICS lptbm)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, TB_SETMETRICS, 0, (LPARAM)lptbm);\n\t}\n\n\tvoid SetWindowTheme(LPCWSTR lpstrTheme)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, TB_SETWINDOWTHEME, 0, (LPARAM)lpstrTheme);\n\t}\n\n#if (_WIN32_WINNT >= 0x0600)\n\tCImageList GetPressedImageList(int nIndex = 0) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn CImageList((HIMAGELIST)::SendMessage(this->m_hWnd, TB_GETPRESSEDIMAGELIST, nIndex, 0L));\n\t}\n\n\tCImageList SetPressedImageList(HIMAGELIST hImageList, int nIndex = 0)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn CImageList((HIMAGELIST)::SendMessage(this->m_hWnd, TB_SETPRESSEDIMAGELIST, nIndex, (LPARAM)hImageList));\n\t}\n\n\tvoid GetItemDropDownRect(int nIndex, LPRECT lpRect) const\n\t{\n#ifndef TB_GETITEMDROPDOWNRECT\n\t\tconst int TB_GETITEMDROPDOWNRECT = WM_USER + 103;\n#endif\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tBOOL bRet = (BOOL)::SendMessage(this->m_hWnd, TB_GETITEMDROPDOWNRECT, nIndex, (LPARAM)lpRect);\n\t\t(void)bRet;   // avoid level 4 warning\n\t\tATLASSERT(bRet != FALSE);\n\t}\n#endif // (_WIN32_WINNT >= 0x0600)\n\n// Operations\n\tBOOL EnableButton(int nID, BOOL bEnable = TRUE)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TB_ENABLEBUTTON, nID, MAKELPARAM(bEnable, 0));\n\t}\n\n\tBOOL CheckButton(int nID, BOOL bCheck = TRUE)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TB_CHECKBUTTON, nID, MAKELPARAM(bCheck, 0));\n\t}\n\n\tBOOL PressButton(int nID, BOOL bPress = TRUE)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TB_PRESSBUTTON, nID, MAKELPARAM(bPress, 0));\n\t}\n\n\tBOOL HideButton(int nID, BOOL bHide = TRUE)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TB_HIDEBUTTON, nID, MAKELPARAM(bHide, 0));\n\t}\n\n\tBOOL Indeterminate(int nID, BOOL bIndeterminate = TRUE)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TB_INDETERMINATE, nID, MAKELPARAM(bIndeterminate, 0));\n\t}\n\n\tint AddBitmap(int nNumButtons, UINT nBitmapID)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tTBADDBITMAP tbab = {};\n\t\ttbab.hInst = ModuleHelper::GetResourceInstance();\n\t\tATLASSERT(tbab.hInst != NULL);\n\t\ttbab.nID = nBitmapID;\n\t\treturn (int)::SendMessage(this->m_hWnd, TB_ADDBITMAP, (WPARAM)nNumButtons, (LPARAM)&tbab);\n\t}\n\n\tint AddBitmap(int nNumButtons, HBITMAP hBitmap)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tTBADDBITMAP tbab = {};\n\t\ttbab.hInst = NULL;\n\t\ttbab.nID = (UINT_PTR)hBitmap;\n\t\treturn (int)::SendMessage(this->m_hWnd, TB_ADDBITMAP, (WPARAM)nNumButtons, (LPARAM)&tbab);\n\t}\n\n\tBOOL AddButtons(int nNumButtons, LPCTBBUTTON lpButtons)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TB_ADDBUTTONS, nNumButtons, (LPARAM)lpButtons);\n\t}\n\n\tBOOL InsertButton(int nIndex, LPCTBBUTTON lpButton)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TB_INSERTBUTTON, nIndex, (LPARAM)lpButton);\n\t}\n\n\tBOOL InsertButton(int nIndex, int iCommand, BYTE Style, BYTE State, int iBitmap, \n\t                  INT_PTR iString, DWORD_PTR lParam)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tTBBUTTON tbb = {};\n\t\ttbb.fsStyle = Style;\n\t\ttbb.fsState = State;\n\t\ttbb.idCommand = iCommand;\n\t\ttbb.iBitmap = iBitmap;\n\t\ttbb.iString = iString;\n\t\ttbb.dwData = lParam;\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TB_INSERTBUTTON, nIndex, (LPARAM)&tbb);\n\t}\n\n\tBOOL InsertButton(int nIndex, int iCommand, BYTE Style, BYTE State, int iBitmap, \n\t                  LPCTSTR lpszItem, DWORD_PTR lParam)\n\t{\n\t\treturn InsertButton(nIndex, iCommand, Style, State, iBitmap, (INT_PTR)lpszItem, lParam);\n\t}\n\n\tBOOL AddButton(LPTBBUTTON lpButton)\n\t{\n\t\treturn InsertButton(-1, lpButton);\n\t}\n\n\tBOOL AddButton(int iCommand, BYTE Style, BYTE State, int iBitmap, INT_PTR iString, DWORD_PTR lParam)\n\t{\n\t\treturn InsertButton(-1, iCommand, Style, State, iBitmap, iString, lParam);\n\t}\n\n\tBOOL AddButton(int iCommand, BYTE Style, BYTE State, int iBitmap, LPCTSTR lpszItem, DWORD_PTR lParam)\n\t{\n\t\treturn InsertButton(-1, iCommand, Style, State, iBitmap, lpszItem, lParam);\n\t}\n\n\tBOOL DeleteButton(int nIndex)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TB_DELETEBUTTON, nIndex, 0L);\n\t}\n\n\tBOOL InsertSeparator(int nIndex, int cxWidth = 8)\n\t{\n\t\treturn InsertButton(nIndex, 0, BTNS_SEP, 0, cxWidth, (INT_PTR)0, 0);\n\t}\n\n\tBOOL AddSeparator(int cxWidth = 8)\n\t{\n\t\treturn AddButton(0, BTNS_SEP, 0, cxWidth, (INT_PTR)0, 0);\n\t}\n\n\tint CommandToIndex(UINT nID) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, TB_COMMANDTOINDEX, nID, 0L);\n\t}\n\n\tvoid SaveState(HKEY hKeyRoot, LPCTSTR lpszSubKey, LPCTSTR lpszValueName)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tTBSAVEPARAMS tbs = {};\n\t\ttbs.hkr = hKeyRoot;\n\t\ttbs.pszSubKey = lpszSubKey;\n\t\ttbs.pszValueName = lpszValueName;\n\t\t::SendMessage(this->m_hWnd, TB_SAVERESTORE, (WPARAM)TRUE, (LPARAM)&tbs);\n\t}\n\n\tvoid RestoreState(HKEY hKeyRoot, LPCTSTR lpszSubKey, LPCTSTR lpszValueName)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tTBSAVEPARAMS tbs = {};\n\t\ttbs.hkr = hKeyRoot;\n\t\ttbs.pszSubKey = lpszSubKey;\n\t\ttbs.pszValueName = lpszValueName;\n\t\t::SendMessage(this->m_hWnd, TB_SAVERESTORE, (WPARAM)FALSE, (LPARAM)&tbs);\n\t}\n\n\tvoid Customize()\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, TB_CUSTOMIZE, 0, 0L);\n\t}\n\n\tint AddString(UINT nStringID)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, TB_ADDSTRING, (WPARAM)ModuleHelper::GetResourceInstance(), (LPARAM)nStringID);\n\t}\n\n\tint AddStrings(LPCTSTR lpszStrings)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, TB_ADDSTRING, 0, (LPARAM)lpszStrings);\n\t}\n\n\tvoid AutoSize()\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, TB_AUTOSIZE, 0, 0L);\n\t}\n\n\tBOOL ChangeBitmap(int nID, int nBitmap)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TB_CHANGEBITMAP, nID, MAKELPARAM(nBitmap, 0));\n\t}\n\n\tint LoadImages(int nBitmapID)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, TB_LOADIMAGES, nBitmapID, (LPARAM)ModuleHelper::GetResourceInstance());\n\t}\n\n\tint LoadStdImages(int nBitmapID)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, TB_LOADIMAGES, nBitmapID, (LPARAM)HINST_COMMCTRL);\n\t}\n\n\tBOOL ReplaceBitmap(LPTBREPLACEBITMAP ptbrb)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TB_REPLACEBITMAP, 0, (LPARAM)ptbrb);\n\t}\n\n\tint HitTest(LPPOINT lpPoint) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, TB_HITTEST, 0, (LPARAM)lpPoint);\n\t}\n\n\tBOOL InsertMarkHitTest(LPPOINT lpPoint, LPTBINSERTMARK lptbim) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TB_INSERTMARKHITTEST, (WPARAM)lpPoint, (LPARAM)lptbim);\n\t}\n\n\tBOOL InsertMarkHitTest(int x, int y, LPTBINSERTMARK lptbim) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tPOINT pt = { x, y };\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TB_INSERTMARKHITTEST, (WPARAM)&pt, (LPARAM)lptbim);\n\t}\n\n\tBOOL MapAccelerator(TCHAR chAccel, int& nID) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TB_MAPACCELERATOR, (WPARAM)chAccel, (LPARAM)&nID);\n\t}\n\n\tBOOL MarkButton(int nID, BOOL bHighlight = TRUE)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TB_MARKBUTTON, nID, MAKELPARAM(bHighlight, 0));\n\t}\n\n\tBOOL MoveButton(int nOldPos, int nNewPos)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TB_MOVEBUTTON, nOldPos, nNewPos);\n\t}\n\n\tHRESULT GetObject(REFIID iid, LPVOID* ppvObject)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (HRESULT)::SendMessage(this->m_hWnd, TB_GETOBJECT, (WPARAM)&iid, (LPARAM)ppvObject);\n\t}\n};\n\ntypedef CToolBarCtrlT<ATL::CWindow>   CToolBarCtrl;\n\n\n///////////////////////////////////////////////////////////////////////////////\n// CStatusBarCtrl\n\ntemplate <class TBase>\nclass CStatusBarCtrlT : public TBase\n{\npublic:\n// Constructors\n\tCStatusBarCtrlT(HWND hWnd = NULL) : TBase(hWnd)\n\t{ }\n\n\tCStatusBarCtrlT< TBase >& operator =(HWND hWnd)\n\t{\n\t\tthis->m_hWnd = hWnd;\n\t\treturn *this;\n\t}\n\n\tHWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL,\n\t\t\tDWORD dwStyle = 0, DWORD dwExStyle = 0,\n\t\t\tATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL)\n\t{\n\t\treturn TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam);\n\t}\n\n// Methods\n\tstatic LPCTSTR GetWndClassName()\n\t{\n\t\treturn STATUSCLASSNAME;\n\t}\n\n\tint GetParts(int nParts, int* pParts) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, SB_GETPARTS, nParts, (LPARAM)pParts);\n\t}\n\n\tBOOL SetParts(int nParts, int* pWidths)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, SB_SETPARTS, nParts, (LPARAM)pWidths);\n\t}\n\n\tint GetTextLength(int nPane, int* pType = NULL) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT(nPane < 256);\n\t\tDWORD dwRet = (DWORD)::SendMessage(this->m_hWnd, SB_GETTEXTLENGTH, (WPARAM)nPane, 0L);\n\t\tif (pType != NULL)\n\t\t\t*pType = (int)(short)HIWORD(dwRet);\n\t\treturn (int)(short)LOWORD(dwRet);\n\t}\n\n\tint GetText(int nPane, LPTSTR lpszText, int* pType = NULL) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT(nPane < 256);\n\t\tDWORD dwRet = (DWORD)::SendMessage(this->m_hWnd, SB_GETTEXT, (WPARAM)nPane, (LPARAM)lpszText);\n\t\tif(pType != NULL)\n\t\t\t*pType = (int)(short)HIWORD(dwRet);\n\t\treturn (int)(short)LOWORD(dwRet);\n\t}\n\n\tBOOL GetTextBSTR(int nPane, BSTR& bstrText, int* pType = NULL) const\n\t{\n\t\tUSES_CONVERSION;\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT(nPane < 256);\n\t\tATLASSERT(bstrText == NULL);\n\t\tint nLength = (int)(short)LOWORD(::SendMessage(this->m_hWnd, SB_GETTEXTLENGTH, (WPARAM)nPane, 0L));\n\t\tif(nLength == 0)\n\t\t\treturn FALSE;\n\n\t\tATL::CTempBuffer<TCHAR, _WTL_STACK_ALLOC_THRESHOLD> buff;\n\t\tLPTSTR lpstrText = buff.Allocate(nLength + 1);\n\t\tif(lpstrText == NULL)\n\t\t\treturn FALSE;\n\n\t\tif(!GetText(nPane, lpstrText, pType))\n\t\t\treturn FALSE;\n\n\t\tbstrText = ::SysAllocString(T2OLE(lpstrText));\n\t\treturn (bstrText != NULL) ? TRUE : FALSE;\n\t}\n\n#ifdef __ATLSTR_H__\n\tint GetText(int nPane, ATL::CString& strText, int* pType = NULL) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT(nPane < 256);\n\t\tint nLength = (int)(short)LOWORD(::SendMessage(this->m_hWnd, SB_GETTEXTLENGTH, (WPARAM)nPane, 0L));\n\t\tif(nLength == 0)\n\t\t\treturn 0;\n\n\t\tLPTSTR lpstr = strText.GetBufferSetLength(nLength);\n\t\tif(lpstr == NULL)\n\t\t\treturn 0;\n\t\treturn GetText(nPane, lpstr, pType);\n\t}\n#endif // __ATLSTR_H__\n\n\tBOOL SetText(int nPane, LPCTSTR lpszText, int nType = 0)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT(nPane < 256);\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, SB_SETTEXT, (nPane | nType), (LPARAM)lpszText);\n\t}\n\n\tBOOL GetRect(int nPane, LPRECT lpRect) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT(nPane < 256);\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, SB_GETRECT, nPane, (LPARAM)lpRect);\n\t}\n\n\tBOOL GetBorders(int* pBorders) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, SB_GETBORDERS, 0, (LPARAM)pBorders);\n\t}\n\n\tBOOL GetBorders(int& nHorz, int& nVert, int& nSpacing) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tint borders[3] = {};\n\t\tBOOL bResult = (BOOL)::SendMessage(this->m_hWnd, SB_GETBORDERS, 0, (LPARAM)&borders);\n\t\tif(bResult)\n\t\t{\n\t\t\tnHorz = borders[0];\n\t\t\tnVert = borders[1];\n\t\t\tnSpacing = borders[2];\n\t\t}\n\t\treturn bResult;\n\t}\n\n\tvoid SetMinHeight(int nMin)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, SB_SETMINHEIGHT, nMin, 0L);\n\t}\n\n\tBOOL SetSimple(BOOL bSimple = TRUE)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, SB_SIMPLE, bSimple, 0L);\n\t}\n\n\tBOOL IsSimple() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, SB_ISSIMPLE, 0, 0L);\n\t}\n\n\tBOOL GetUnicodeFormat() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, SB_GETUNICODEFORMAT, 0, 0L);\n\t}\n\n\tBOOL SetUnicodeFormat(BOOL bUnicode = TRUE)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, SB_SETUNICODEFORMAT, bUnicode, 0L);\n\t}\n\n\tvoid GetTipText(int nPane, LPTSTR lpstrText, int nSize) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT(nPane < 256);\n\t\t::SendMessage(this->m_hWnd, SB_GETTIPTEXT, MAKEWPARAM(nPane, nSize), (LPARAM)lpstrText);\n\t}\n\n\tvoid SetTipText(int nPane, LPCTSTR lpstrText)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT(nPane < 256);\n\t\t::SendMessage(this->m_hWnd, SB_SETTIPTEXT, nPane, (LPARAM)lpstrText);\n\t}\n\n\tCOLORREF SetBkColor(COLORREF clrBk)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (COLORREF)::SendMessage(this->m_hWnd, SB_SETBKCOLOR, 0, (LPARAM)clrBk);\n\t}\n\n\tHICON GetIcon(int nPane) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT(nPane < 256);\n\t\treturn (HICON)::SendMessage(this->m_hWnd, SB_GETICON, nPane, 0L);\n\t}\n\n\tBOOL SetIcon(int nPane, HICON hIcon)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT(nPane < 256);\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, SB_SETICON, nPane, (LPARAM)hIcon);\n\t}\n};\n\ntypedef CStatusBarCtrlT<ATL::CWindow>   CStatusBarCtrl;\n\n\n///////////////////////////////////////////////////////////////////////////////\n// CTabCtrl\n\ntemplate <class TBase>\nclass CTabCtrlT : public TBase\n{\npublic:\n// Constructors\n\tCTabCtrlT(HWND hWnd = NULL) : TBase(hWnd)\n\t{ }\n\n\tCTabCtrlT< TBase >& operator =(HWND hWnd)\n\t{\n\t\tthis->m_hWnd = hWnd;\n\t\treturn *this;\n\t}\n\n\tHWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL,\n\t\t\tDWORD dwStyle = 0, DWORD dwExStyle = 0,\n\t\t\tATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL)\n\t{\n\t\treturn TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam);\n\t}\n\n// Attributes\n\tstatic LPCTSTR GetWndClassName()\n\t{\n\t\treturn WC_TABCONTROL;\n\t}\n\n\tCImageList GetImageList() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn CImageList((HIMAGELIST)::SendMessage(this->m_hWnd, TCM_GETIMAGELIST, 0, 0L));\n\t}\n\n\tCImageList SetImageList(HIMAGELIST hImageList)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn CImageList((HIMAGELIST)::SendMessage(this->m_hWnd, TCM_SETIMAGELIST, 0, (LPARAM)hImageList));\n\t}\n\n\tint GetItemCount() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, TCM_GETITEMCOUNT, 0, 0L);\n\t}\n\n\tBOOL GetItem(int nItem, LPTCITEM pTabCtrlItem) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TCM_GETITEM, nItem, (LPARAM)pTabCtrlItem);\n\t}\n\n\tBOOL SetItem(int nItem, LPTCITEM pTabCtrlItem)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TCM_SETITEM, nItem, (LPARAM)pTabCtrlItem);\n\t}\n\n\tint SetItem(int nItem, UINT mask, LPCTSTR lpszItem, DWORD dwState, DWORD dwStateMask, int iImage, LPARAM lParam)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tTCITEM tci = {};\n\t\ttci.mask = mask;\n\t\ttci.pszText = (LPTSTR) lpszItem;\n\t\ttci.dwState = dwState;\n\t\ttci.dwStateMask = dwStateMask;\n\t\ttci.iImage = iImage;\n\t\ttci.lParam = lParam;\n\t\treturn (int)::SendMessage(this->m_hWnd, TCM_SETITEM, nItem, (LPARAM)&tci);\n\t}\n\n\tBOOL GetItemRect(int nItem, LPRECT lpRect) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TCM_GETITEMRECT, nItem, (LPARAM)lpRect);\n\t}\n\n\tint GetCurSel() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, TCM_GETCURSEL, 0, 0L);\n\t}\n\n\tint SetCurSel(int nItem)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, TCM_SETCURSEL, nItem, 0L);\n\t}\n\n\tSIZE SetItemSize(SIZE size)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tDWORD dwSize = (DWORD)::SendMessage(this->m_hWnd, TCM_SETITEMSIZE, 0, MAKELPARAM(size.cx, size.cy));\n\t\tSIZE sizeRet = { GET_X_LPARAM(dwSize), GET_Y_LPARAM(dwSize) };\n\t\treturn sizeRet;\n\t}\n\n\tvoid SetItemSize(int cx, int cy)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, TCM_SETITEMSIZE, 0, MAKELPARAM(cx, cy));\n\t}\n\n\tvoid SetPadding(SIZE size)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, TCM_SETPADDING, 0, MAKELPARAM(size.cx, size.cy));\n\t}\n\n\tint GetRowCount() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, TCM_GETROWCOUNT, 0, 0L);\n\t}\n\n\tCToolTipCtrl GetToolTips() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn CToolTipCtrl((HWND)::SendMessage(this->m_hWnd, TCM_GETTOOLTIPS, 0, 0L));\n\t}\n\n\t// this method is deprecated, please use GetToolTips\n\tCToolTipCtrl GetTooltips() const { return GetToolTips(); }\n\n\tvoid SetToolTips(HWND hWndToolTip)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, TCM_SETTOOLTIPS, (WPARAM)hWndToolTip, 0L);\n\t}\n\n\t// this method is deprecated, please use SetToolTips\n\tvoid SetTooltips(HWND hWndToolTip) { SetToolTips(hWndToolTip); }\n\n\tint GetCurFocus() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, TCM_GETCURFOCUS, 0, 0L);\n\t}\n\n\tvoid SetCurFocus(int nItem)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, TCM_SETCURFOCUS, nItem, 0L);\n\t}\n\n\tBOOL SetItemExtra(int cbExtra)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT(GetItemCount() == 0);   // must be empty\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TCM_SETITEMEXTRA, cbExtra, 0L);\n\t}\n\n\tint SetMinTabWidth(int nWidth = -1)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, TCM_SETMINTABWIDTH, 0, nWidth);\n\t}\n\n\tDWORD GetExtendedStyle() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (DWORD)::SendMessage(this->m_hWnd, TCM_GETEXTENDEDSTYLE, 0, 0L);\n\t}\n\n\tDWORD SetExtendedStyle(DWORD dwExMask, DWORD dwExStyle)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (DWORD)::SendMessage(this->m_hWnd, TCM_SETEXTENDEDSTYLE, dwExMask, dwExStyle);\n\t}\n\n\tBOOL GetUnicodeFormat() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TCM_GETUNICODEFORMAT, 0, 0L);\n\t}\n\n\tBOOL SetUnicodeFormat(BOOL bUnicode = TRUE)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TCM_SETUNICODEFORMAT, bUnicode, 0L);\n\t}\n\n// Operations\n\tint InsertItem(int nItem, LPTCITEM pTabCtrlItem)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, TCM_INSERTITEM, nItem, (LPARAM)pTabCtrlItem);\n\t}\n\n\tint InsertItem(int nItem, UINT mask, LPCTSTR lpszItem, int iImage, LPARAM lParam)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tTCITEM tci = {};\n\t\ttci.mask = mask;\n\t\ttci.pszText = (LPTSTR) lpszItem;\n\t\ttci.iImage = iImage;\n\t\ttci.lParam = lParam;\n\t\treturn (int)::SendMessage(this->m_hWnd, TCM_INSERTITEM, nItem, (LPARAM)&tci);\n\t}\n\n\tint InsertItem(int nItem, LPCTSTR lpszItem)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tTCITEM tci = {};\n\t\ttci.mask = TCIF_TEXT;\n\t\ttci.pszText = (LPTSTR) lpszItem;\n\t\treturn (int)::SendMessage(this->m_hWnd, TCM_INSERTITEM, nItem, (LPARAM)&tci);\n\t}\n\n\tint AddItem(LPTCITEM pTabCtrlItem)\n\t{\n\t\treturn InsertItem(GetItemCount(), pTabCtrlItem);\n\t}\n\n\tint AddItem(UINT mask, LPCTSTR lpszItem, int iImage, LPARAM lParam)\n\t{\n\t\treturn InsertItem(GetItemCount(), mask, lpszItem, iImage, lParam);\n\t}\n\n\tint AddItem(LPCTSTR lpszItem)\n\t{\n\t\treturn InsertItem(GetItemCount(), lpszItem);\n\t}\n\n\tBOOL DeleteItem(int nItem)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TCM_DELETEITEM, nItem, 0L);\n\t}\n\n\tBOOL DeleteAllItems()\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TCM_DELETEALLITEMS, 0, 0L);\n\t}\n\n\tvoid AdjustRect(BOOL bLarger, LPRECT lpRect)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, TCM_ADJUSTRECT, bLarger, (LPARAM)lpRect);\n\t}\n\n\tvoid RemoveImage(int nImage)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, TCM_REMOVEIMAGE, nImage, 0L);\n\t}\n\n\tint HitTest(TC_HITTESTINFO* pHitTestInfo) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, TCM_HITTEST, 0, (LPARAM)pHitTestInfo);\n\t}\n\n\tvoid DeselectAll(BOOL bExcludeFocus = TRUE)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, TCM_DESELECTALL, bExcludeFocus, 0L);\n\t}\n\n\tBOOL HighlightItem(int nIndex, BOOL bHighlight = TRUE)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TCM_HIGHLIGHTITEM, nIndex, MAKELPARAM(bHighlight, 0));\n\t}\n};\n\ntypedef CTabCtrlT<ATL::CWindow>   CTabCtrl;\n\n\n///////////////////////////////////////////////////////////////////////////////\n// CTrackBarCtrl\n\ntemplate <class TBase>\nclass CTrackBarCtrlT : public TBase\n{\npublic:\n// Constructors\n\tCTrackBarCtrlT(HWND hWnd = NULL) : TBase(hWnd)\n\t{ }\n\n\tCTrackBarCtrlT< TBase >& operator =(HWND hWnd)\n\t{\n\t\tthis->m_hWnd = hWnd;\n\t\treturn *this;\n\t}\n\n\tHWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL,\n\t\t\tDWORD dwStyle = 0, DWORD dwExStyle = 0,\n\t\t\tATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL)\n\t{\n\t\treturn TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam);\n\t}\n\n// Attributes\n\tstatic LPCTSTR GetWndClassName()\n\t{\n\t\treturn TRACKBAR_CLASS;\n\t}\n\n\tint GetLineSize() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, TBM_GETLINESIZE, 0, 0L);\n\t}\n\n\tint SetLineSize(int nSize)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, TBM_SETLINESIZE, 0, nSize);\n\t}\n\n\tint GetPageSize() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, TBM_GETPAGESIZE, 0, 0L);\n\t}\n\n\tint SetPageSize(int nSize)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, TBM_SETPAGESIZE, 0, nSize);\n\t}\n\n\tint GetRangeMin() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, TBM_GETRANGEMIN, 0, 0L);\n\t}\n\n\tvoid SetRangeMin(int nMin, BOOL bRedraw = FALSE)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, TBM_SETRANGEMIN, bRedraw, nMin);\n\t}\n\n\tint GetRangeMax() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, TBM_GETRANGEMAX, 0, 0L);\n\t}\n\n\tvoid SetRangeMax(int nMax, BOOL bRedraw = FALSE)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, TBM_SETRANGEMAX, bRedraw, nMax);\n\t}\n\n\tvoid GetRange(int& nMin, int& nMax) const\n\t{\n\t\tnMin = GetRangeMin();\n\t\tnMax = GetRangeMax();\n\t}\n\n\tvoid SetRange(int nMin, int nMax, BOOL bRedraw = TRUE)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, TBM_SETRANGE, bRedraw, MAKELPARAM(nMin, nMax));\n\t}\n\n\tint GetSelStart() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, TBM_GETSELSTART, 0, 0L);\n\t}\n\n\tvoid SetSelStart(int nMin, BOOL bRedraw = FALSE)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, TBM_SETSELSTART, bRedraw, (LPARAM)nMin);\n\t}\n\n\tint GetSelEnd() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, TBM_GETSELEND, 0, 0L);\n\t}\n\n\tvoid SetSelEnd(int nMax, BOOL bRedraw = FALSE)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, TBM_SETSELEND, bRedraw, (LPARAM)nMax);\n\t}\n\n\tvoid GetSelection(int& nMin, int& nMax) const\n\t{\n\t\tnMin = GetSelStart();\n\t\tnMax = GetSelEnd();\n\t}\n\n\tvoid SetSelection(int nMin, int nMax, BOOL bRedraw = TRUE)\n\t{\n\t\tSetSelStart(nMin, FALSE);\n\t\tSetSelEnd(nMax, bRedraw);\n\t}\n\n\tvoid GetChannelRect(LPRECT lprc) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, TBM_GETCHANNELRECT, 0, (LPARAM)lprc);\n\t}\n\n\tvoid GetThumbRect(LPRECT lprc) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, TBM_GETTHUMBRECT, 0, (LPARAM)lprc);\n\t}\n\n\tint GetPos() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, TBM_GETPOS, 0, 0L);\n\t}\n\n\tvoid SetPos(int nPos)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, TBM_SETPOS, TRUE, nPos);\n\t}\n\n\tUINT GetNumTics() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (UINT)::SendMessage(this->m_hWnd, TBM_GETNUMTICS, 0, 0L);\n\t}\n\n\tDWORD* GetTicArray() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (DWORD*)::SendMessage(this->m_hWnd, TBM_GETPTICS, 0, 0L);\n\t}\n\n\tint GetTic(int nTic) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, TBM_GETTIC, nTic, 0L);\n\t}\n\n\tBOOL SetTic(int nTic)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TBM_SETTIC, 0, nTic);\n\t}\n\n\tint GetTicPos(int nTic) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, TBM_GETTICPOS, nTic, 0L);\n\t}\n\n\tvoid SetTicFreq(int nFreq)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, TBM_SETTICFREQ, nFreq, 0L);\n\t}\n\n\tint GetThumbLength() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, TBM_GETTHUMBLENGTH, 0, 0L);\n\t}\n\n\tvoid SetThumbLength(int nLength)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, TBM_SETTHUMBLENGTH, nLength, 0L);\n\t}\n\n\tvoid SetSel(int nStart, int nEnd, BOOL bRedraw = TRUE)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT((this->GetStyle() & TBS_ENABLESELRANGE) != 0);\n\t\t::SendMessage(this->m_hWnd, TBM_SETSEL, bRedraw, MAKELPARAM(nStart, nEnd));\n\t}\n\n\tATL::CWindow GetBuddy(BOOL bLeft = TRUE) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn ATL::CWindow((HWND)::SendMessage(this->m_hWnd, TBM_GETBUDDY, bLeft, 0L));\n\t}\n\n\tATL::CWindow SetBuddy(HWND hWndBuddy, BOOL bLeft = TRUE)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn ATL::CWindow((HWND)::SendMessage(this->m_hWnd, TBM_SETBUDDY, bLeft, (LPARAM)hWndBuddy));\n\t}\n\n\tCToolTipCtrl GetToolTips() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn CToolTipCtrl((HWND)::SendMessage(this->m_hWnd, TBM_GETTOOLTIPS, 0, 0L));\n\t}\n\n\tvoid SetToolTips(HWND hWndTT)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, TBM_SETTOOLTIPS, (WPARAM)hWndTT, 0L);\n\t}\n\n\tint SetTipSide(int nSide)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, TBM_SETTIPSIDE, nSide, 0L);\n\t}\n\n\tBOOL GetUnicodeFormat() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TBM_GETUNICODEFORMAT, 0, 0L);\n\t}\n\n\tBOOL SetUnicodeFormat(BOOL bUnicode = TRUE)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, TBM_SETUNICODEFORMAT, bUnicode, 0L);\n\t}\n\n// Operations\n\tvoid ClearSel(BOOL bRedraw = FALSE)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, TBM_CLEARSEL, bRedraw, 0L);\n\t}\n\n\tvoid VerifyPos()\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, TBM_SETPOS, FALSE, 0L);\n\t}\n\n\tvoid ClearTics(BOOL bRedraw = FALSE)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, TBM_CLEARTICS, bRedraw, 0L);\n\t}\n};\n\ntypedef CTrackBarCtrlT<ATL::CWindow>   CTrackBarCtrl;\n\n\n///////////////////////////////////////////////////////////////////////////////\n// CUpDownCtrl\n\ntemplate <class TBase>\nclass CUpDownCtrlT : public TBase\n{\npublic:\n// Constructors\n\tCUpDownCtrlT(HWND hWnd = NULL) : TBase(hWnd)\n\t{ }\n\n\tCUpDownCtrlT< TBase >& operator =(HWND hWnd)\n\t{\n\t\tthis->m_hWnd = hWnd;\n\t\treturn *this;\n\t}\n\n\tHWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL,\n\t\t\tDWORD dwStyle = 0, DWORD dwExStyle = 0,\n\t\t\tATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL)\n\t{\n\t\treturn TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam);\n\t}\n\n// Attributes\n\tstatic LPCTSTR GetWndClassName()\n\t{\n\t\treturn UPDOWN_CLASS;\n\t}\n\n\tUINT GetAccel(int nAccel, UDACCEL* pAccel) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (UINT)LOWORD(::SendMessage(this->m_hWnd, UDM_GETACCEL, nAccel, (LPARAM)pAccel));\n\t}\n\n\tBOOL SetAccel(int nAccel, UDACCEL* pAccel)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)LOWORD(::SendMessage(this->m_hWnd, UDM_SETACCEL, nAccel, (LPARAM)pAccel));\n\t}\n\n\tUINT GetBase() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (UINT)LOWORD(::SendMessage(this->m_hWnd, UDM_GETBASE, 0, 0L));\n\t}\n\n\tint SetBase(int nBase)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, UDM_SETBASE, nBase, 0L);\n\t}\n\n\tATL::CWindow GetBuddy() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn ATL::CWindow((HWND)::SendMessage(this->m_hWnd, UDM_GETBUDDY, 0, 0L));\n\t}\n\n\tATL::CWindow SetBuddy(HWND hWndBuddy)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn ATL::CWindow((HWND)::SendMessage(this->m_hWnd, UDM_SETBUDDY, (WPARAM)hWndBuddy, 0L));\n\t}\n\n\tint GetPos(LPBOOL lpbError = NULL) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tDWORD dwRet = (DWORD)::SendMessage(this->m_hWnd, UDM_GETPOS, 0, 0L);\n\t\t// Note: Seems that Windows always sets error to TRUE if\n\t\t// UDS_SETBUDDYINT style is not used\n\t\tif(lpbError != NULL)\n\t\t\t*lpbError = (HIWORD(dwRet) != 0) ? TRUE : FALSE;\n\t\treturn (int)(short)LOWORD(dwRet);\n\t}\n\n\tint SetPos(int nPos)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)(short)LOWORD(::SendMessage(this->m_hWnd, UDM_SETPOS, 0, MAKELPARAM(nPos, 0)));\n\t}\n\n\tDWORD GetRange() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (DWORD)::SendMessage(this->m_hWnd, UDM_GETRANGE, 0, 0L);\n\t}\n\n\tvoid GetRange(int& nLower, int& nUpper) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tDWORD dwRet = (DWORD)::SendMessage(this->m_hWnd, UDM_GETRANGE, 0, 0L);\n\t\tnLower = (int)(short)HIWORD(dwRet);\n\t\tnUpper = (int)(short)LOWORD(dwRet);\n\t}\n\n\tvoid SetRange(int nLower, int nUpper)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, UDM_SETRANGE, 0, MAKELPARAM(nUpper, nLower));\n\t}\n\n\tvoid SetRange32(int nLower, int nUpper)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, UDM_SETRANGE32, nLower, nUpper);\n\t}\n\n\tvoid GetRange32(int& nLower, int& nUpper) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, UDM_GETRANGE32, (WPARAM)&nLower, (LPARAM)&nUpper);\n\t}\n\n\tBOOL GetUnicodeFormat() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, UDM_GETUNICODEFORMAT, 0, 0L);\n\t}\n\n\tBOOL SetUnicodeFormat(BOOL bUnicode = TRUE)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, UDM_SETUNICODEFORMAT, bUnicode, 0L);\n\t}\n\n\tint GetPos32(LPBOOL lpbError = NULL) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t// Note: Seems that Windows always sets error to TRUE if\n\t\t// UDS_SETBUDDYINT style is not used\n\t\treturn (int)::SendMessage(this->m_hWnd, UDM_GETPOS32, 0, (LPARAM)lpbError);\n\t}\n\n\tint SetPos32(int nPos)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, UDM_SETPOS32, 0, (LPARAM)nPos);\n\t}\n};\n\ntypedef CUpDownCtrlT<ATL::CWindow>   CUpDownCtrl;\n\n\n///////////////////////////////////////////////////////////////////////////////\n// CProgressBarCtrl\n\ntemplate <class TBase>\nclass CProgressBarCtrlT : public TBase\n{\npublic:\n// Constructors\n\tCProgressBarCtrlT(HWND hWnd = NULL) : TBase(hWnd)\n\t{ }\n\n\tCProgressBarCtrlT< TBase >& operator =(HWND hWnd)\n\t{\n\t\tthis->m_hWnd = hWnd;\n\t\treturn *this;\n\t}\n\n\tHWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL,\n\t\t\tDWORD dwStyle = 0, DWORD dwExStyle = 0,\n\t\t\tATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL)\n\t{\n\t\treturn TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam);\n\t}\n\n// Attributes\n\tstatic LPCTSTR GetWndClassName()\n\t{\n\t\treturn PROGRESS_CLASS;\n\t}\n\n\tDWORD SetRange(int nLower, int nUpper)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (DWORD)::SendMessage(this->m_hWnd, PBM_SETRANGE, 0, MAKELPARAM(nLower, nUpper));\n\t}\n\n\tint SetPos(int nPos)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)(short)LOWORD(::SendMessage(this->m_hWnd, PBM_SETPOS, nPos, 0L));\n\t}\n\n\tint OffsetPos(int nPos)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)(short)LOWORD(::SendMessage(this->m_hWnd, PBM_DELTAPOS, nPos, 0L));\n\t}\n\n\tint SetStep(int nStep)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)(short)LOWORD(::SendMessage(this->m_hWnd, PBM_SETSTEP, nStep, 0L));\n\t}\n\n\tUINT GetPos() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (UINT)::SendMessage(this->m_hWnd, PBM_GETPOS, 0, 0L);\n\t}\n\n\tvoid GetRange(PPBRANGE pPBRange) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT(pPBRange != NULL);\n\t\t::SendMessage(this->m_hWnd, PBM_GETRANGE, TRUE, (LPARAM)pPBRange);\n\t}\n\n\tvoid GetRange(int& nLower, int& nUpper) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tPBRANGE range = {};\n\t\t::SendMessage(this->m_hWnd, PBM_GETRANGE, TRUE, (LPARAM)&range);\n\t\tnLower = range.iLow;\n\t\tnUpper = range.iHigh;\n\t}\n\n\tint GetRangeLimit(BOOL bLowLimit) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, PBM_GETRANGE, bLowLimit, (LPARAM)NULL);\n\t}\n\n\tDWORD SetRange32(int nMin, int nMax)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (DWORD)::SendMessage(this->m_hWnd, PBM_SETRANGE32, nMin, nMax);\n\t}\n\n\tCOLORREF SetBarColor(COLORREF clr)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (COLORREF)::SendMessage(this->m_hWnd, PBM_SETBARCOLOR, 0, (LPARAM)clr);\n\t}\n\n\tCOLORREF SetBkColor(COLORREF clr)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (COLORREF)::SendMessage(this->m_hWnd, PBM_SETBKCOLOR, 0, (LPARAM)clr);\n\t}\n\n#ifdef PBM_SETMARQUEE\n\tBOOL SetMarquee(BOOL bMarquee, UINT uUpdateTime = 0U)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, PBM_SETMARQUEE, (WPARAM)bMarquee, (LPARAM)uUpdateTime);\n\t}\n#endif\n\n#if (_WIN32_WINNT >= 0x0600)\n\tint GetStep() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, PBM_GETSTEP, 0, 0L);\n\t}\n\n\tCOLORREF GetBkColor() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (COLORREF)::SendMessage(this->m_hWnd, PBM_GETBKCOLOR, 0, 0L);\n\t}\n\n\tCOLORREF GetBarColor() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (COLORREF)::SendMessage(this->m_hWnd, PBM_GETBARCOLOR, 0, 0L);\n\t}\n\n\tint GetState() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, PBM_GETSTATE, 0, 0L);\n\t}\n\n\tint SetState(int nState)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, PBM_SETSTATE, nState, 0L);\n\t}\n#endif // (_WIN32_WINNT >= 0x0600)\n\n// Operations\n\tint StepIt()\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)(short)LOWORD(::SendMessage(this->m_hWnd, PBM_STEPIT, 0, 0L));\n\t}\n};\n\ntypedef CProgressBarCtrlT<ATL::CWindow>   CProgressBarCtrl;\n\n\n///////////////////////////////////////////////////////////////////////////////\n// CHotKeyCtrl\n\ntemplate <class TBase>\nclass CHotKeyCtrlT : public TBase\n{\npublic:\n// Constructors\n\tCHotKeyCtrlT(HWND hWnd = NULL) : TBase(hWnd)\n\t{ }\n\n\tCHotKeyCtrlT< TBase >& operator =(HWND hWnd)\n\t{\n\t\tthis->m_hWnd = hWnd;\n\t\treturn *this;\n\t}\n\n\tHWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL,\n\t\t\tDWORD dwStyle = 0, DWORD dwExStyle = 0,\n\t\t\tATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL)\n\t{\n\t\treturn TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam);\n\t}\n\n// Attributes\n\tstatic LPCTSTR GetWndClassName()\n\t{\n\t\treturn HOTKEY_CLASS;\n\t}\n\n\tDWORD GetHotKey() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (DWORD)::SendMessage(this->m_hWnd, HKM_GETHOTKEY, 0, 0L);\n\t}\n\n\tvoid GetHotKey(WORD &wVirtualKeyCode, WORD &wModifiers) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tDWORD dw = (DWORD)::SendMessage(this->m_hWnd, HKM_GETHOTKEY, 0, 0L);\n\t\twVirtualKeyCode = LOBYTE(LOWORD(dw));\n\t\twModifiers = HIBYTE(LOWORD(dw));\n\t}\n\n\tvoid SetHotKey(WORD wVirtualKeyCode, WORD wModifiers)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, HKM_SETHOTKEY, MAKEWORD(wVirtualKeyCode, wModifiers), 0L);\n\t}\n\n\tvoid SetRules(WORD wInvalidComb, WORD wModifiers)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, HKM_SETRULES, wInvalidComb, MAKELPARAM(wModifiers, 0));\n\t}\n};\n\ntypedef CHotKeyCtrlT<ATL::CWindow>   CHotKeyCtrl;\n\n\n///////////////////////////////////////////////////////////////////////////////\n// CAnimateCtrl\n\ntemplate <class TBase>\nclass CAnimateCtrlT : public TBase\n{\npublic:\n// Constructors\n\tCAnimateCtrlT(HWND hWnd = NULL) : TBase(hWnd)\n\t{ }\n\n\tCAnimateCtrlT< TBase >& operator =(HWND hWnd)\n\t{\n\t\tthis->m_hWnd = hWnd;\n\t\treturn *this;\n\t}\n\n\tHWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL,\n\t\t\tDWORD dwStyle = 0, DWORD dwExStyle = 0,\n\t\t\tATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL)\n\t{\n\t\treturn TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam);\n\t}\n\n// Attributes\n\tstatic LPCTSTR GetWndClassName()\n\t{\n\t\treturn ANIMATE_CLASS;\n\t}\n\n// Operations\n\tBOOL Open(ATL::_U_STRINGorID FileName)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, ACM_OPEN, 0, (LPARAM)FileName.m_lpstr);\n\t}\n\n\tBOOL Play(UINT nFrom, UINT nTo, UINT nRep)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, ACM_PLAY, nRep, MAKELPARAM(nFrom, nTo));\n\t}\n\n\tBOOL Stop()\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, ACM_STOP, 0, 0L);\n\t}\n\n\tBOOL Close()\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, ACM_OPEN, 0, 0L);\n\t}\n\n\tBOOL Seek(UINT nTo)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, ACM_PLAY, 0, MAKELPARAM(nTo, nTo));\n\t}\n\n\t// Vista only\n\tBOOL IsPlaying() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, ACM_ISPLAYING, 0, 0L);\n\t}\n};\n\ntypedef CAnimateCtrlT<ATL::CWindow>   CAnimateCtrl;\n\n\n///////////////////////////////////////////////////////////////////////////////\n// CRichEditCtrl\n\n#if !defined(_UNICODE) && (_RICHEDIT_VER >= 0x0500)\n  #undef MSFTEDIT_CLASS\n  #define MSFTEDIT_CLASS\t\"RICHEDIT50W\"\n#endif\n\ntemplate <class TBase>\nclass CRichEditCtrlT : public TBase\n{\npublic:\n// Constructors\n\tCRichEditCtrlT(HWND hWnd = NULL) : TBase(hWnd)\n\t{ }\n\n\tCRichEditCtrlT< TBase >& operator =(HWND hWnd)\n\t{\n\t\tthis->m_hWnd = hWnd;\n\t\treturn *this;\n\t}\n\n\tHWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL,\n\t\t\tDWORD dwStyle = 0, DWORD dwExStyle = 0,\n\t\t\tATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL)\n\t{\n\t\treturn TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam);\n\t}\n\n// Attributes\n\tstatic LPCTSTR GetWndClassName()\n\t{\n#if (_RICHEDIT_VER >= 0x0500)\n\t\treturn MSFTEDIT_CLASS;\n#else\n\t\treturn RICHEDIT_CLASS;\n#endif\n\t}\n\n\tstatic LPCTSTR GetLibraryName()\n\t{\n#if (_RICHEDIT_VER >= 0x0500)\n\t\treturn _T(\"MSFTEDIT.DLL\");\n#else\n\t\treturn _T(\"RICHED20.DLL\");\n#endif\n\t}\n\n\tint GetLineCount() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, EM_GETLINECOUNT, 0, 0L);\n\t}\n\n\tBOOL GetModify() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, EM_GETMODIFY, 0, 0L);\n\t}\n\n\tvoid SetModify(BOOL bModified = TRUE)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, EM_SETMODIFY, bModified, 0L);\n\t}\n\n\tvoid GetRect(LPRECT lpRect) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, EM_GETRECT, 0, (LPARAM)lpRect);\n\t}\n\n\tDWORD GetOptions() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (DWORD)::SendMessage(this->m_hWnd, EM_GETOPTIONS, 0, 0L);\n\t}\n\n\tDWORD SetOptions(WORD wOperation, DWORD dwOptions)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (DWORD)::SendMessage(this->m_hWnd, EM_SETOPTIONS, wOperation, dwOptions);\n\t}\n\n\t// NOTE: first word in lpszBuffer must contain the size of the buffer!\n\tint GetLine(int nIndex, LPTSTR lpszBuffer) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, EM_GETLINE, nIndex, (LPARAM)lpszBuffer);\n\t}\n\n\tint GetLine(int nIndex, LPTSTR lpszBuffer, int nMaxLength) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t*(LPWORD)lpszBuffer = (WORD)nMaxLength;\n\t\treturn (int)::SendMessage(this->m_hWnd, EM_GETLINE, nIndex, (LPARAM)lpszBuffer);\n\t}\n\n\tBOOL CanUndo() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, EM_CANUNDO, 0, 0L);\n\t}\n\n\tBOOL CanPaste(UINT nFormat = 0) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, EM_CANPASTE, nFormat, 0L);\n\t}\n\n\tvoid GetSel(LONG& nStartChar, LONG& nEndChar) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tCHARRANGE cr = {};\n\t\t::SendMessage(this->m_hWnd, EM_EXGETSEL, 0, (LPARAM)&cr);\n\t\tnStartChar = cr.cpMin;\n\t\tnEndChar = cr.cpMax;\n\t}\n\n\tvoid GetSel(CHARRANGE &cr) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, EM_EXGETSEL, 0, (LPARAM)&cr);\n\t}\n\n\tint SetSel(LONG nStartChar, LONG nEndChar)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tCHARRANGE cr = { nStartChar, nEndChar };\n\t\treturn (int)::SendMessage(this->m_hWnd, EM_EXSETSEL, 0, (LPARAM)&cr);\n\t}\n\n\tint SetSel(CHARRANGE &cr)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, EM_EXSETSEL, 0, (LPARAM)&cr);\n\t}\n\n\tint SetSelAll()\n\t{\n\t\treturn SetSel(0, -1);\n\t}\n\n\tint SetSelNone()\n\t{\n\t\treturn SetSel(-1, 0);\n\t}\n\n\tDWORD GetDefaultCharFormat(CHARFORMAT& cf) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tcf.cbSize = sizeof(CHARFORMAT);\n\t\treturn (DWORD)::SendMessage(this->m_hWnd, EM_GETCHARFORMAT, 0, (LPARAM)&cf);\n\t}\n\n\tDWORD GetSelectionCharFormat(CHARFORMAT& cf) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tcf.cbSize = sizeof(CHARFORMAT);\n\t\treturn (DWORD)::SendMessage(this->m_hWnd, EM_GETCHARFORMAT, 1, (LPARAM)&cf);\n\t}\n\n\tDWORD GetEventMask() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (DWORD)::SendMessage(this->m_hWnd, EM_GETEVENTMASK, 0, 0L);\n\t}\n\n\tLONG GetLimitText() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (LONG)::SendMessage(this->m_hWnd, EM_GETLIMITTEXT, 0, 0L);\n\t}\n\n\tDWORD GetParaFormat(PARAFORMAT& pf) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tpf.cbSize = sizeof(PARAFORMAT);\n\t\treturn (DWORD)::SendMessage(this->m_hWnd, EM_GETPARAFORMAT, 0, (LPARAM)&pf);\n\t}\n\n\tLONG GetSelText(LPTSTR lpstrBuff) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (LONG)::SendMessage(this->m_hWnd, EM_GETSELTEXT, 0, (LPARAM)lpstrBuff);\n\t}\n\n\tBOOL GetSelTextBSTR(BSTR& bstrText) const\n\t{\n\t\tUSES_CONVERSION;\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT(bstrText == NULL);\n\n\t\tCHARRANGE cr = {};\n\t\t::SendMessage(this->m_hWnd, EM_EXGETSEL, 0, (LPARAM)&cr);\n\n\t\tATL::CTempBuffer<TCHAR, _WTL_STACK_ALLOC_THRESHOLD> buff;\n\t\tLPTSTR lpstrText = buff.Allocate(cr.cpMax - cr.cpMin + 1);\n\t\tif(lpstrText == NULL)\n\t\t\treturn FALSE;\n\t\tif(::SendMessage(this->m_hWnd, EM_GETSELTEXT, 0, (LPARAM)lpstrText) == 0)\n\t\t\treturn FALSE;\n\n\t\tbstrText = ::SysAllocString(T2W(lpstrText));\n\n\t\treturn (bstrText != NULL) ? TRUE : FALSE;\n\t}\n\n#ifdef __ATLSTR_H__\n\tLONG GetSelText(ATL::CString& strText) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\n\t\tCHARRANGE cr = {};\n\t\t::SendMessage(this->m_hWnd, EM_EXGETSEL, 0, (LPARAM)&cr);\n\n\t\tLONG lLen = 0;\n\t\tLPTSTR lpstrText = strText.GetBufferSetLength(cr.cpMax - cr.cpMin);\n\t\tif(lpstrText != NULL)\n\t\t{\n\t\t\tlLen = (LONG)::SendMessage(this->m_hWnd, EM_GETSELTEXT, 0, (LPARAM)lpstrText);\n\t\t\tstrText.ReleaseBuffer();\n\t\t}\n\n\t\treturn lLen;\n\t}\n#endif // __ATLSTR_H__\n\n\tWORD GetSelectionType() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (WORD)::SendMessage(this->m_hWnd, EM_SELECTIONTYPE, 0, 0L);\n\t}\n\n\tCOLORREF SetBackgroundColor(COLORREF cr)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (COLORREF)::SendMessage(this->m_hWnd, EM_SETBKGNDCOLOR, 0, cr);\n\t}\n\n\tCOLORREF SetBackgroundColor()   // sets to system background\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (COLORREF)::SendMessage(this->m_hWnd, EM_SETBKGNDCOLOR, 1, 0);\n\t}\n\n\tBOOL SetCharFormat(CHARFORMAT& cf, WORD wFlags)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tcf.cbSize = sizeof(CHARFORMAT);\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, EM_SETCHARFORMAT, (WPARAM)wFlags, (LPARAM)&cf);\n\t}\n\n\tBOOL SetDefaultCharFormat(CHARFORMAT& cf)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tcf.cbSize = sizeof(CHARFORMAT);\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, EM_SETCHARFORMAT, 0, (LPARAM)&cf);\n\t}\n\n\tBOOL SetSelectionCharFormat(CHARFORMAT& cf)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tcf.cbSize = sizeof(CHARFORMAT);\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf);\n\t}\n\n\tBOOL SetWordCharFormat(CHARFORMAT& cf)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tcf.cbSize = sizeof(CHARFORMAT);\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, EM_SETCHARFORMAT, SCF_SELECTION | SCF_WORD, (LPARAM)&cf);\n\t}\n\n\tDWORD SetEventMask(DWORD dwEventMask)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (DWORD)::SendMessage(this->m_hWnd, EM_SETEVENTMASK, 0, dwEventMask);\n\t}\n\n\tBOOL SetParaFormat(PARAFORMAT& pf)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tpf.cbSize = sizeof(PARAFORMAT);\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, EM_SETPARAFORMAT, 0, (LPARAM)&pf);\n\t}\n\n\tBOOL SetTargetDevice(HDC hDC, int cxLineWidth)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, EM_SETTARGETDEVICE, (WPARAM)hDC, cxLineWidth);\n\t}\n\n\tint GetTextLength() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, WM_GETTEXTLENGTH, 0, 0L);\n\t}\n\n\tBOOL SetReadOnly(BOOL bReadOnly = TRUE)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, EM_SETREADONLY, bReadOnly, 0L);\n\t}\n\n\tint GetFirstVisibleLine() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, EM_GETFIRSTVISIBLELINE, 0, 0L);\n\t}\n\n\tint GetTextRange(TEXTRANGE* pTextRange) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, EM_GETTEXTRANGE, 0, (LPARAM)pTextRange);\n\t}\n\n\tint GetTextRange(LONG nStartChar, LONG nEndChar, LPTSTR lpstrText) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tTEXTRANGE tr = {};\n\t\ttr.chrg.cpMin = nStartChar;\n\t\ttr.chrg.cpMax = nEndChar;\n\t\ttr.lpstrText = lpstrText;\n\t\treturn (int)::SendMessage(this->m_hWnd, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\n\t}\n\n\tDWORD GetDefaultCharFormat(CHARFORMAT2& cf) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tcf.cbSize = sizeof(CHARFORMAT2);\n\t\treturn (DWORD)::SendMessage(this->m_hWnd, EM_GETCHARFORMAT, 0, (LPARAM)&cf);\n\t}\n\n\tBOOL SetCharFormat(CHARFORMAT2& cf, WORD wFlags)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tcf.cbSize = sizeof(CHARFORMAT2);\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, EM_SETCHARFORMAT, (WPARAM)wFlags, (LPARAM)&cf);\n\t}\n\n\tBOOL SetDefaultCharFormat(CHARFORMAT2& cf)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tcf.cbSize = sizeof(CHARFORMAT2);\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, EM_SETCHARFORMAT, 0, (LPARAM)&cf);\n\t}\n\n\tDWORD GetSelectionCharFormat(CHARFORMAT2& cf) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tcf.cbSize = sizeof(CHARFORMAT2);\n\t\treturn (DWORD)::SendMessage(this->m_hWnd, EM_GETCHARFORMAT, 1, (LPARAM)&cf);\n\t}\n\n\tBOOL SetSelectionCharFormat(CHARFORMAT2& cf)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tcf.cbSize = sizeof(CHARFORMAT2);\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf);\n\t}\n\n\tBOOL SetWordCharFormat(CHARFORMAT2& cf)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tcf.cbSize = sizeof(CHARFORMAT2);\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, EM_SETCHARFORMAT, SCF_SELECTION | SCF_WORD, (LPARAM)&cf);\n\t}\n\n\tDWORD GetParaFormat(PARAFORMAT2& pf) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tpf.cbSize = sizeof(PARAFORMAT2);\n\t\treturn (DWORD)::SendMessage(this->m_hWnd, EM_GETPARAFORMAT, 0, (LPARAM)&pf);\n\t}\n\n\tBOOL SetParaFormat(PARAFORMAT2& pf)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tpf.cbSize = sizeof(PARAFORMAT2);\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, EM_SETPARAFORMAT, 0, (LPARAM)&pf);\n\t}\n\n\tTEXTMODE GetTextMode() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (TEXTMODE)::SendMessage(this->m_hWnd, EM_GETTEXTMODE, 0, 0L);\n\t}\n\n\tBOOL SetTextMode(TEXTMODE enumTextMode)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn !(BOOL)::SendMessage(this->m_hWnd, EM_SETTEXTMODE, enumTextMode, 0L);\n\t}\n\n\tUNDONAMEID GetUndoName() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (UNDONAMEID)::SendMessage(this->m_hWnd, EM_GETUNDONAME, 0, 0L);\n\t}\n\n\tUNDONAMEID GetRedoName() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (UNDONAMEID)::SendMessage(this->m_hWnd, EM_GETREDONAME, 0, 0L);\n\t}\n\n\tBOOL CanRedo() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, EM_CANREDO, 0, 0L);\n\t}\n\n\tBOOL GetAutoURLDetect() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, EM_GETAUTOURLDETECT, 0, 0L);\n\t}\n\n\tBOOL SetAutoURLDetect(BOOL bAutoDetect = TRUE)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn !(BOOL)::SendMessage(this->m_hWnd, EM_AUTOURLDETECT, bAutoDetect, 0L);\n\t}\n\n\t// this method is deprecated, please use SetAutoURLDetect\n\tBOOL EnableAutoURLDetect(BOOL bEnable = TRUE) { return SetAutoURLDetect(bEnable); }\n\n\tUINT SetUndoLimit(UINT uUndoLimit)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (UINT)::SendMessage(this->m_hWnd, EM_SETUNDOLIMIT, uUndoLimit, 0L);\n\t}\n\n\tvoid SetPalette(HPALETTE hPalette)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, EM_SETPALETTE, (WPARAM)hPalette, 0L);\n\t}\n\n\tint GetTextEx(GETTEXTEX* pGetTextEx, LPTSTR lpstrText) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, EM_GETTEXTEX, (WPARAM)pGetTextEx, (LPARAM)lpstrText);\n\t}\n\n\tint GetTextEx(LPTSTR lpstrText, int nTextLen, DWORD dwFlags = GT_DEFAULT, UINT uCodePage = CP_ACP, LPCSTR lpDefaultChar = NULL, LPBOOL lpUsedDefChar = NULL) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tGETTEXTEX gte = {};\n\t\tgte.cb = nTextLen * sizeof(TCHAR);\n\t\tgte.codepage = uCodePage;\n\t\tgte.flags = dwFlags;\n\t\tgte.lpDefaultChar = lpDefaultChar;\n\t\tgte.lpUsedDefChar = lpUsedDefChar;\n\t\treturn (int)::SendMessage(this->m_hWnd, EM_GETTEXTEX, (WPARAM)&gte, (LPARAM)lpstrText);\n\t}\n\n\tint GetTextLengthEx(GETTEXTLENGTHEX* pGetTextLengthEx) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, EM_GETTEXTLENGTHEX, (WPARAM)pGetTextLengthEx, 0L);\n\t}\n\n\tint GetTextLengthEx(DWORD dwFlags = GTL_DEFAULT, UINT uCodePage = CP_ACP) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tGETTEXTLENGTHEX gtle = {};\n\t\tgtle.codepage = uCodePage;\n\t\tgtle.flags = dwFlags;\n\t\treturn (int)::SendMessage(this->m_hWnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtle, 0L);\n\t}\n\n\tEDITWORDBREAKPROC GetWordBreakProc() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (EDITWORDBREAKPROC)::SendMessage(this->m_hWnd, EM_GETWORDBREAKPROC, 0, 0L);\n\t}\n\n\tvoid SetWordBreakProc(EDITWORDBREAKPROC ewbprc)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, EM_SETWORDBREAKPROC, 0, (LPARAM)ewbprc);\n\t}\n\n\tint SetTextEx(SETTEXTEX* pSetTextEx, LPCTSTR lpstrText)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, EM_SETTEXTEX, (WPARAM)pSetTextEx, (LPARAM)lpstrText);\n\t}\n\n\tint SetTextEx(LPCTSTR lpstrText, DWORD dwFlags = ST_DEFAULT, UINT uCodePage = CP_ACP)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tSETTEXTEX ste = {};\n\t\tste.flags = dwFlags;\n\t\tste.codepage = uCodePage;\n\t\treturn (int)::SendMessage(this->m_hWnd, EM_SETTEXTEX, (WPARAM)&ste, (LPARAM)lpstrText);\n\t}\n\n\tint GetEditStyle() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, EM_GETEDITSTYLE, 0, 0L);\n\t}\n\n\tint SetEditStyle(int nStyle, int nMask = -1)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tif(nMask == -1)\n\t\t\tnMask = nStyle;   // set everything specified\n\t\treturn (int)::SendMessage(this->m_hWnd, EM_SETEDITSTYLE, nStyle, nMask);\n\t}\n\n\tBOOL SetFontSize(int nFontSizeDelta)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT((nFontSizeDelta >= -1637) && (nFontSizeDelta <= 1638));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, EM_SETFONTSIZE, nFontSizeDelta, 0L);\n\t}\n\n\tvoid GetScrollPos(LPPOINT lpPoint) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT(lpPoint != NULL);\n\t\t::SendMessage(this->m_hWnd, EM_GETSCROLLPOS, 0, (LPARAM)lpPoint);\n\t}\n\n\tvoid SetScrollPos(LPPOINT lpPoint)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT(lpPoint != NULL);\n\t\t::SendMessage(this->m_hWnd, EM_SETSCROLLPOS, 0, (LPARAM)lpPoint);\n\t}\n\n\tBOOL GetZoom(int& nNum, int& nDen) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, EM_GETZOOM, (WPARAM)&nNum, (LPARAM)&nDen);\n\t}\n\n\tBOOL SetZoom(int nNum, int nDen)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT((nNum >= 0) && (nNum <= 64));\n\t\tATLASSERT((nDen >= 0) && (nDen <= 64));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, EM_SETZOOM, nNum, nDen);\n\t}\n\n\tBOOL SetZoomOff()\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, EM_SETZOOM, 0, 0L);\n\t}\n\n\tvoid SetMargins(UINT nLeft, UINT nRight, WORD wFlags = EC_LEFTMARGIN | EC_RIGHTMARGIN)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, EM_SETMARGINS, wFlags, MAKELONG(nLeft, nRight));\n\t}\n\n\tWORD GetTypographyOptions() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (WORD)::SendMessage(this->m_hWnd, EM_GETTYPOGRAPHYOPTIONS, 0, 0L);\n\t}\n\n\tBOOL SetTypographyOptions(WORD wOptions, WORD wMask) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, EM_SETTYPOGRAPHYOPTIONS, wOptions, wMask);\n\t}\n\n// Operations\n\tvoid LimitText(LONG nChars = 0)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, EM_EXLIMITTEXT, 0, nChars);\n\t}\n\n\tint LineFromChar(LONG nIndex) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, EM_EXLINEFROMCHAR, 0, nIndex);\n\t}\n\n\tPOINT PosFromChar(LONG nChar) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tPOINT point = {};\n\t\t::SendMessage(this->m_hWnd, EM_POSFROMCHAR, (WPARAM)&point, nChar);\n\t\treturn point;\n\t}\n\n\tint CharFromPos(POINT pt) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tPOINTL ptl = { pt.x, pt.y };\n\t\treturn (int)::SendMessage(this->m_hWnd, EM_CHARFROMPOS, 0, (LPARAM)&ptl);\n\t}\n\n\tvoid EmptyUndoBuffer()\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, EM_EMPTYUNDOBUFFER, 0, 0L);\n\t}\n\n\tint LineIndex(int nLine = -1) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, EM_LINEINDEX, nLine, 0L);\n\t}\n\n\tint LineLength(int nLine = -1) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, EM_LINELENGTH, nLine, 0L);\n\t}\n\n\tBOOL LineScroll(int nLines)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, EM_LINESCROLL, 0, nLines);\n\t}\n\n\tvoid ReplaceSel(LPCTSTR lpszNewText, BOOL bCanUndo = FALSE)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, EM_REPLACESEL, (WPARAM) bCanUndo, (LPARAM)lpszNewText);\n\t}\n\n\tvoid SetRect(LPCRECT lpRect)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, EM_SETRECT, 0, (LPARAM)lpRect);\n\t}\n\n\tBOOL DisplayBand(LPRECT pDisplayRect)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, EM_DISPLAYBAND, 0, (LPARAM)pDisplayRect);\n\t}\n\n\tLONG FindText(DWORD dwFlags, FINDTEXT& ft) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n#ifdef _UNICODE\n\t\treturn (LONG)::SendMessage(this->m_hWnd, EM_FINDTEXTW, dwFlags, (LPARAM)&ft);\n#else\n\t\treturn (LONG)::SendMessage(this->m_hWnd, EM_FINDTEXT, dwFlags, (LPARAM)&ft);\n#endif\n\t}\n\n\tLONG FindText(DWORD dwFlags, FINDTEXTEX& ft) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n#ifdef _UNICODE\n\t\treturn (LONG)::SendMessage(this->m_hWnd, EM_FINDTEXTEXW, dwFlags, (LPARAM)&ft);\n#else\n\t\treturn (LONG)::SendMessage(this->m_hWnd, EM_FINDTEXTEX, dwFlags, (LPARAM)&ft);\n#endif\n\t}\n\n\tLONG FormatRange(FORMATRANGE& fr, BOOL bDisplay = TRUE)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (LONG)::SendMessage(this->m_hWnd, EM_FORMATRANGE, bDisplay, (LPARAM)&fr);\n\t}\n\n\tLONG FormatRange(FORMATRANGE* pFormatRange, BOOL bDisplay = TRUE)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (LONG)::SendMessage(this->m_hWnd, EM_FORMATRANGE, bDisplay, (LPARAM)pFormatRange);\n\t}\n\n\tvoid HideSelection(BOOL bHide = TRUE, BOOL bChangeStyle = FALSE)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, EM_HIDESELECTION, bHide, bChangeStyle);\n\t}\n\n\tvoid PasteSpecial(UINT uClipFormat, DWORD dwAspect = 0, HMETAFILE hMF = 0)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tREPASTESPECIAL reps = { dwAspect, (DWORD_PTR)hMF };\n\t\t::SendMessage(this->m_hWnd, EM_PASTESPECIAL, uClipFormat, (LPARAM)&reps);\n\t}\n\n\tvoid RequestResize()\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, EM_REQUESTRESIZE, 0, 0L);\n\t}\n\n\tLONG StreamIn(UINT uFormat, EDITSTREAM& es)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (LONG)::SendMessage(this->m_hWnd, EM_STREAMIN, uFormat, (LPARAM)&es);\n\t}\n\n\tLONG StreamOut(UINT uFormat, EDITSTREAM& es)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (LONG)::SendMessage(this->m_hWnd, EM_STREAMOUT, uFormat, (LPARAM)&es);\n\t}\n\n\tDWORD FindWordBreak(int nCode, LONG nStartChar)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (DWORD)::SendMessage(this->m_hWnd, EM_FINDWORDBREAK, nCode, nStartChar);\n\t}\n\n\t// Additional operations\n\tvoid ScrollCaret()\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, EM_SCROLLCARET, 0, 0L);\n\t}\n\n\tint InsertText(long nInsertAfterChar, LPCTSTR lpstrText, BOOL bCanUndo = FALSE)\n\t{\n\t\tint nRet = SetSel(nInsertAfterChar, nInsertAfterChar);\n\t\tReplaceSel(lpstrText, bCanUndo);\n\t\treturn nRet;\n\t}\n\n\tint AppendText(LPCTSTR lpstrText, BOOL bCanUndo = FALSE)\n\t{\n\t\treturn InsertText(this->GetWindowTextLength(), lpstrText, bCanUndo);\n\t}\n\n\t// Clipboard operations\n\tBOOL Undo()\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, EM_UNDO, 0, 0L);\n\t}\n\n\tvoid Clear()\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, WM_CLEAR, 0, 0L);\n\t}\n\n\tvoid Copy()\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, WM_COPY, 0, 0L);\n\t}\n\n\tvoid Cut()\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, WM_CUT, 0, 0L);\n\t}\n\n\tvoid Paste()\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, WM_PASTE, 0, 0L);\n\t}\n\n\t// OLE support\n\tIRichEditOle* GetOleInterface() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tIRichEditOle *pRichEditOle = NULL;\n\t\t::SendMessage(this->m_hWnd, EM_GETOLEINTERFACE, 0, (LPARAM)&pRichEditOle);\n\t\treturn pRichEditOle;\n\t}\n\n\tBOOL SetOleCallback(IRichEditOleCallback* pCallback)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, EM_SETOLECALLBACK, 0, (LPARAM)pCallback);\n\t}\n\n\tBOOL Redo()\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, EM_REDO, 0, 0L);\n\t}\n\n\tvoid StopGroupTyping()\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, EM_STOPGROUPTYPING, 0, 0L);\n\t}\n\n\tvoid ShowScrollBar(int nBarType, BOOL bVisible = TRUE)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, EM_SHOWSCROLLBAR, nBarType, bVisible);\n\t}\n\n\tBOOL SetTabStops(int nTabStops, LPINT rgTabStops)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, EM_SETTABSTOPS, nTabStops, (LPARAM)rgTabStops);\n\t}\n\n\tBOOL SetTabStops()\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, EM_SETTABSTOPS, 0, 0L);\n\t}\n\n\tBOOL SetTabStops(const int& cxEachStop)    // takes an 'int'\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, EM_SETTABSTOPS, 1, (LPARAM)(LPINT)&cxEachStop);\n\t}\n\n#if (_RICHEDIT_VER >= 0x0800)\n\tAutoCorrectProc GetAutoCorrectProc() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (AutoCorrectProc)::SendMessage(this->m_hWnd, EM_GETAUTOCORRECTPROC, 0, 0L);\n\t}\n\n\tBOOL SetAutoCorrectProc(AutoCorrectProc pfn)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, EM_SETAUTOCORRECTPROC, (WPARAM)pfn, 0L);\n\t}\n\n\tBOOL CallAutoCorrectProc(WCHAR ch)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, EM_CALLAUTOCORRECTPROC, (WPARAM)ch, 0L);\n\t}\n\n\tDWORD GetEditStyleEx() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (DWORD)::SendMessage(this->m_hWnd, EM_GETEDITSTYLEEX, 0, 0L);\n\t}\n\n\tDWORD SetEditStyleEx(DWORD dwStyleEx, DWORD dwMask)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (DWORD)::SendMessage(this->m_hWnd, EM_SETEDITSTYLEEX, dwStyleEx, dwMask);\n\t}\n\n\tDWORD GetStoryType(int nStoryIndex) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (DWORD)::SendMessage(this->m_hWnd, EM_GETSTORYTYPE, nStoryIndex, 0L);\n\t}\n\n\tDWORD SetStoryType(int nStoryIndex, DWORD dwStoryType)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (DWORD)::SendMessage(this->m_hWnd, EM_SETSTORYTYPE, nStoryIndex, dwStoryType);\n\t}\n\n\tDWORD GetEllipsisMode() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\n\t\tDWORD dwMode = 0;\n\t\tBOOL bRet = (BOOL)::SendMessage(this->m_hWnd, EM_GETELLIPSISMODE, 0, (LPARAM)&dwMode);\n\t\t(void)bRet;   // avoid level 4 warning\n\t\tATLASSERT(bRet != FALSE);\n\n\t\treturn dwMode;\n\t}\n\n\tBOOL SetEllipsisMode(DWORD dwEllipsisMode)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, EM_SETELLIPSISMODE, 0, dwEllipsisMode);\n\t}\n\n\tBOOL GetEllipsisState() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, EM_GETELLIPSISSTATE, 0, 0L);\n\t}\n\n\tBOOL GetTouchOptions(int nTouchOptions) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, EM_GETTOUCHOPTIONS, nTouchOptions, 0L);\n\t}\n\n\tvoid SetTouchOptions(int nTouchOptions, BOOL bEnable)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, EM_SETTOUCHOPTIONS, nTouchOptions, bEnable);\n\t}\n\n\tHRESULT InsertTable(TABLEROWPARMS* pRowParams, TABLECELLPARMS* pCellParams)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (HRESULT)::SendMessage(this->m_hWnd, EM_INSERTTABLE, (WPARAM)pRowParams, (LPARAM)pCellParams);\n\t}\n\n\tHRESULT GetTableParams(TABLEROWPARMS* pRowParams, TABLECELLPARMS* pCellParams) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (HRESULT)::SendMessage(this->m_hWnd, EM_GETTABLEPARMS, (WPARAM)pRowParams, (LPARAM)pCellParams);\n\t}\n\n\tHRESULT SetTableParams(TABLEROWPARMS* pRowParams, TABLECELLPARMS* pCellParams)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (HRESULT)::SendMessage(this->m_hWnd, EM_SETTABLEPARMS, (WPARAM)pRowParams, (LPARAM)pCellParams);\n\t}\n\n\tHRESULT InsertImage(RICHEDIT_IMAGE_PARAMETERS* pParams)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (HRESULT)::SendMessage(this->m_hWnd, EM_INSERTIMAGE, 0, (LPARAM)pParams);\n\t}\n\n\tBOOL SetUiaName(LPCTSTR lpstrName)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, EM_SETUIANAME, 0, (LPARAM)lpstrName);\n\t}\n#endif // (_RICHEDIT_VER >= 0x0800)\n};\n\ntypedef CRichEditCtrlT<ATL::CWindow>   CRichEditCtrl;\n\n\n///////////////////////////////////////////////////////////////////////////////\n// CRichEditCommands - message handlers for standard EDIT commands\n\n// Chain to CRichEditCommands message map. Your class must also derive from CRichEditCtrl.\n// Example:\n// class CMyRichEdit : public CWindowImpl<CMyRichEdit, CRichEditCtrl>,\n//                     public CRichEditCommands<CMyRichEdit>\n// {\n// public:\n//      BEGIN_MSG_MAP(CMyRichEdit)\n//              // your handlers...\n//              CHAIN_MSG_MAP_ALT(CRichEditCommands<CMyRichEdit>, 1)\n//      END_MSG_MAP()\n//      // other stuff...\n// };\n\ntemplate <class T>\nclass CRichEditCommands : public CEditCommands< T >\n{\npublic:\n\tBEGIN_MSG_MAP(CRichEditCommands< T >)\n\tALT_MSG_MAP(1)\n\t\tCOMMAND_ID_HANDLER(ID_EDIT_CLEAR, CEditCommands< T >::OnEditClear)\n\t\tCOMMAND_ID_HANDLER(ID_EDIT_CLEAR_ALL, CEditCommands< T >::OnEditClearAll)\n\t\tCOMMAND_ID_HANDLER(ID_EDIT_COPY, CEditCommands< T >::OnEditCopy)\n\t\tCOMMAND_ID_HANDLER(ID_EDIT_CUT, CEditCommands< T >::OnEditCut)\n\t\tCOMMAND_ID_HANDLER(ID_EDIT_PASTE, CEditCommands< T >::OnEditPaste)\n\t\tCOMMAND_ID_HANDLER(ID_EDIT_SELECT_ALL, CEditCommands< T >::OnEditSelectAll)\n\t\tCOMMAND_ID_HANDLER(ID_EDIT_UNDO, CEditCommands< T >::OnEditUndo)\n\t\tCOMMAND_ID_HANDLER(ID_EDIT_REDO, OnEditRedo)\n\tEND_MSG_MAP()\n\n\tLRESULT OnEditRedo(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)\n\t{\n\t\tT* pT = static_cast<T*>(this);\n\t\tpT->Redo();\n\t\treturn 0;\n\t}\n\n// State (update UI) helpers\n\tBOOL CanCut() const\n\t{ return HasSelection(); }\n\n\tBOOL CanCopy() const\n\t{ return HasSelection(); }\n\n\tBOOL CanClear() const\n\t{ return HasSelection(); }\n\n// Implementation\n\tBOOL HasSelection() const\n\t{\n\t\tconst T* pT = static_cast<const T*>(this);\n\t\treturn (pT->GetSelectionType() != SEL_EMPTY);\n\t}\n};\n\n\n///////////////////////////////////////////////////////////////////////////////\n// CDragListBox\n\ntemplate <class TBase>\nclass CDragListBoxT : public CListBoxT< TBase >\n{\npublic:\n// Constructors\n\tCDragListBoxT(HWND hWnd = NULL) : CListBoxT< TBase >(hWnd)\n\t{ }\n\n\tCDragListBoxT< TBase >& operator =(HWND hWnd)\n\t{\n\t\tthis->m_hWnd = hWnd;\n\t\treturn *this;\n\t}\n\n\tHWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL,\n\t\t\tDWORD dwStyle = 0, DWORD dwExStyle = 0,\n\t\t\tATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL)\n\t{\n\t\tHWND hWnd = TBase::Create(TBase::GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam);\n\t\tif(hWnd != NULL)\n\t\t\tMakeDragList();\n\t\treturn hWnd;\n\t}\n\n// Operations\n\tBOOL MakeDragList()\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT((this->GetStyle() & (LBS_MULTIPLESEL | LBS_EXTENDEDSEL)) == 0);\n\t\treturn ::MakeDragList(this->m_hWnd);\n\t}\n\n\tint LBItemFromPt(POINT pt, BOOL bAutoScroll = TRUE)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn ::LBItemFromPt(this->m_hWnd, pt, bAutoScroll);\n\t}\n\n\tvoid DrawInsert(int nItem)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::DrawInsert(this->GetParent(), this->m_hWnd, nItem);\n\t}\n\n\tstatic UINT GetDragListMessage()\n\t{\n\t\tstatic UINT uDragListMessage = 0;\n\t\tif(uDragListMessage == 0)\n\t\t{\n\t\t\tCStaticDataInitCriticalSectionLock lock;\n\t\t\tif(FAILED(lock.Lock()))\n\t\t\t{\n\t\t\t\tATLTRACE2(atlTraceUI, 0, _T(\"ERROR : Unable to lock critical section in CDragListBox::GetDragListMessage.\\n\"));\n\t\t\t\tATLASSERT(FALSE);\n\t\t\t\treturn 0;\n\t\t\t}\n\n\t\t\tif(uDragListMessage == 0)\n\t\t\t\tuDragListMessage = ::RegisterWindowMessage(DRAGLISTMSGSTRING);\n\n\t\t\tlock.Unlock();\n\t\t}\n\t\tATLASSERT(uDragListMessage != 0);\n\t\treturn uDragListMessage;\n\t}\n};\n\ntypedef CDragListBoxT<ATL::CWindow>   CDragListBox;\n\ntemplate <class T>\nclass CDragListNotifyImpl\n{\npublic:\n\tBEGIN_MSG_MAP(CDragListNotifyImpl< T >)\n\t\tMESSAGE_HANDLER(CDragListBox::GetDragListMessage(), OnDragListNotify)\n\tEND_MSG_MAP()\n\n\tLRESULT OnDragListNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)\n\t{\n\t\t(void)uMsg;   // avoid level 4 warning\n\t\tATLASSERT(uMsg == CDragListBox::GetDragListMessage());\n\t\tT* pT = static_cast<T*>(this);\n\t\tLPDRAGLISTINFO lpDragListInfo = (LPDRAGLISTINFO)lParam;\n\t\tLRESULT lRet = 0;\n\t\tswitch(lpDragListInfo->uNotification)\n\t\t{\n\t\tcase DL_BEGINDRAG:\n\t\t\tlRet = (LPARAM)pT->OnBeginDrag((int)wParam, lpDragListInfo->hWnd, lpDragListInfo->ptCursor);\n\t\t\tbreak;\n\t\tcase DL_CANCELDRAG:\n\t\t\tpT->OnCancelDrag((int)wParam, lpDragListInfo->hWnd, lpDragListInfo->ptCursor);\n\t\t\tbreak;\n\t\tcase DL_DRAGGING:\n\t\t\tlRet = (LPARAM)pT->OnDragging((int)wParam, lpDragListInfo->hWnd, lpDragListInfo->ptCursor);\n\t\t\tbreak;\n\t\tcase DL_DROPPED:\n\t\t\tpT->OnDropped((int)wParam, lpDragListInfo->hWnd, lpDragListInfo->ptCursor);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tATLTRACE2(atlTraceUI, 0, _T(\"Unknown DragListBox notification\\n\"));\n\t\t\tbHandled = FALSE;   // don't handle it\n\t\t\tbreak;\n\t\t}\n\t\treturn lRet;\n\t}\n\n// Overrideables\n\tBOOL OnBeginDrag(int /*nCtlID*/, HWND /*hWndDragList*/, POINT /*ptCursor*/)\n\t{\n\t\treturn TRUE;   // allow dragging\n\t}\n\n\tvoid OnCancelDrag(int /*nCtlID*/, HWND /*hWndDragList*/, POINT /*ptCursor*/)\n\t{\n\t\t// nothing to do\n\t}\n\n\tint OnDragging(int /*nCtlID*/, HWND /*hWndDragList*/, POINT /*ptCursor*/)\n\t{\n\t\treturn 0;   // don't change cursor\n\t}\n\n\tvoid OnDropped(int /*nCtlID*/, HWND /*hWndDragList*/, POINT /*ptCursor*/)\n\t{\n\t\t// nothing to do\n\t}\n};\n\n\n///////////////////////////////////////////////////////////////////////////////\n// CReBarCtrl\n\ntemplate <class TBase>\nclass CReBarCtrlT : public TBase\n{\npublic:\n// Constructors\n\tCReBarCtrlT(HWND hWnd = NULL) : TBase(hWnd)\n\t{ }\n\n\tCReBarCtrlT< TBase >& operator =(HWND hWnd)\n\t{\n\t\tthis->m_hWnd = hWnd;\n\t\treturn *this;\n\t}\n\n\tHWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL,\n\t\t\tDWORD dwStyle = 0, DWORD dwExStyle = 0,\n\t\t\tATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL)\n\t{\n\t\treturn TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam);\n\t}\n\n// Attributes\n\tstatic LPCTSTR GetWndClassName()\n\t{\n\t\treturn REBARCLASSNAME;\n\t}\n\n\tUINT GetBandCount() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (UINT)::SendMessage(this->m_hWnd, RB_GETBANDCOUNT, 0, 0L);\n\t}\n\n\tBOOL GetBandInfo(int nBand, LPREBARBANDINFO lprbbi) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, RB_GETBANDINFO, nBand, (LPARAM)lprbbi);\n\t}\n\n\tBOOL SetBandInfo(int nBand, LPREBARBANDINFO lprbbi)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, RB_SETBANDINFO, nBand, (LPARAM)lprbbi);\n\t}\n\n\tBOOL GetBarInfo(LPREBARINFO lprbi) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, RB_GETBARINFO, 0, (LPARAM)lprbi);\n\t}\n\n\tBOOL SetBarInfo(LPREBARINFO lprbi)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, RB_SETBARINFO, 0, (LPARAM)lprbi);\n\t}\n\n\tCImageList GetImageList() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tREBARINFO rbi = {};\n\t\trbi.cbSize = sizeof(REBARINFO);\n\t\trbi.fMask = RBIM_IMAGELIST;\n\t\tBOOL bRet = (BOOL)::SendMessage(this->m_hWnd, RB_GETBARINFO, 0, (LPARAM)&rbi);\n\t\treturn CImageList((bRet != FALSE) ? rbi.himl : NULL);\n\t}\n\n\tBOOL SetImageList(HIMAGELIST hImageList)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tREBARINFO rbi = {};\n\t\trbi.cbSize = sizeof(REBARINFO);\n\t\trbi.fMask = RBIM_IMAGELIST;\n\t\trbi.himl = hImageList;\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, RB_SETBARINFO, 0, (LPARAM)&rbi);\n\t}\n\n\tUINT GetRowCount() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (UINT)::SendMessage(this->m_hWnd, RB_GETROWCOUNT, 0, 0L);\n\t}\n\n\tUINT GetRowHeight(int nBand) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (UINT)::SendMessage(this->m_hWnd, RB_GETROWHEIGHT, nBand, 0L);\n\t}\n\n\tCOLORREF GetTextColor() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (COLORREF)::SendMessage(this->m_hWnd, RB_GETTEXTCOLOR, 0, 0L);\n\t}\n\n\tCOLORREF SetTextColor(COLORREF clr)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (COLORREF)::SendMessage(this->m_hWnd, RB_SETTEXTCOLOR, 0, (LPARAM)clr);\n\t}\n\n\tCOLORREF GetBkColor() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (COLORREF)::SendMessage(this->m_hWnd, RB_GETBKCOLOR, 0, 0L);\n\t}\n\n\tCOLORREF SetBkColor(COLORREF clr)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (COLORREF)::SendMessage(this->m_hWnd, RB_SETBKCOLOR, 0, (LPARAM)clr);\n\t}\n\n\tUINT GetBarHeight() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (UINT)::SendMessage(this->m_hWnd, RB_GETBARHEIGHT, 0, 0L);\n\t}\n\n\tBOOL GetRect(int nBand, LPRECT lpRect) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, RB_GETRECT, nBand, (LPARAM)lpRect);\n\t}\n\n\tCToolTipCtrl GetToolTips() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn CToolTipCtrl((HWND)::SendMessage(this->m_hWnd, RB_GETTOOLTIPS, 0, 0L));\n\t}\n\n\tvoid SetToolTips(HWND hwndToolTip)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, RB_SETTOOLTIPS, (WPARAM)hwndToolTip, 0L);\n\t}\n\n\tvoid GetBandBorders(int nBand, LPRECT lpRect) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT(lpRect != NULL);\n\t\t::SendMessage(this->m_hWnd, RB_GETBANDBORDERS, nBand, (LPARAM)lpRect);\n\t}\n\n\tBOOL GetColorScheme(LPCOLORSCHEME lpColorScheme) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT(lpColorScheme != NULL);\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, RB_GETCOLORSCHEME, 0, (LPARAM)lpColorScheme);\n\t}\n\n\tvoid SetColorScheme(LPCOLORSCHEME lpColorScheme)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT(lpColorScheme != NULL);\n\t\t::SendMessage(this->m_hWnd, RB_SETCOLORSCHEME, 0, (LPARAM)lpColorScheme);\n\t}\n\n\tHPALETTE GetPalette() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (HPALETTE)::SendMessage(this->m_hWnd, RB_GETPALETTE, 0, 0L);\n\t}\n\n\tHPALETTE SetPalette(HPALETTE hPalette)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (HPALETTE)::SendMessage(this->m_hWnd, RB_SETPALETTE, 0, (LPARAM)hPalette);\n\t}\n\n\tBOOL GetUnicodeFormat() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, RB_GETUNICODEFORMAT, 0, 0L);\n\t}\n\n\tBOOL SetUnicodeFormat(BOOL bUnicode = TRUE)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, RB_SETUNICODEFORMAT, bUnicode, 0L);\n\t}\n\n\t// requires uxtheme.h to be included to use MARGINS struct\n#ifndef _UXTHEME_H_\n\ttypedef struct _MARGINS*   PMARGINS;\n#endif // !_UXTHEME_H_\n\tvoid GetBandMargins(PMARGINS pMargins) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, RB_GETBANDMARGINS, 0, (LPARAM)pMargins);\n\t}\n\n\tvoid SetWindowTheme(LPCWSTR lpstrTheme)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, RB_SETWINDOWTHEME, 0, (LPARAM)lpstrTheme);\n\t}\n\n\tDWORD GetExtendedStyle() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (DWORD)::SendMessage(this->m_hWnd, RB_GETEXTENDEDSTYLE, 0, 0L);\n\t}\n\n\tDWORD SetExtendedStyle(DWORD dwStyle, DWORD dwMask)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (DWORD)::SendMessage(this->m_hWnd, RB_SETEXTENDEDSTYLE, dwMask, dwStyle);\n\t}\n\n// Operations\n\tBOOL InsertBand(int nBand, LPREBARBANDINFO lprbbi)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, RB_INSERTBAND, nBand, (LPARAM)lprbbi);\n\t}\n\n\tBOOL AddBand(LPREBARBANDINFO lprbbi)\n\t{\n\t\treturn InsertBand(-1, lprbbi);\n\t}\n\n\tBOOL DeleteBand(int nBand)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, RB_DELETEBAND, nBand, 0L);\n\t}\n\n\tATL::CWindow SetNotifyWnd(HWND hWnd)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn ATL::CWindow((HWND)::SendMessage(this->m_hWnd, RB_SETPARENT, (WPARAM)hWnd, 0L));\n\t}\n\n\tvoid BeginDrag(int nBand, DWORD dwPos)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, RB_BEGINDRAG, nBand, dwPos);\n\t}\n\n\tvoid BeginDrag(int nBand, int xPos, int yPos)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, RB_BEGINDRAG, nBand, MAKELPARAM(xPos, yPos));\n\t}\n\n\tvoid EndDrag()\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, RB_ENDDRAG, 0, 0L);\n\t}\n\n\tvoid DragMove(DWORD dwPos)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, RB_DRAGMOVE, 0, dwPos);\n\t}\n\n\tvoid DragMove(int xPos, int yPos)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, RB_DRAGMOVE, 0, MAKELPARAM(xPos, yPos));\n\t}\n\n\tvoid GetDropTarget(IDropTarget** ppDropTarget) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, RB_GETDROPTARGET, 0, (LPARAM)ppDropTarget);\n\t}\n\n\tvoid MaximizeBand(int nBand, BOOL bIdeal = FALSE)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, RB_MAXIMIZEBAND, nBand, bIdeal);\n\t}\n\n\tvoid MinimizeBand(int nBand)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, RB_MINIMIZEBAND, nBand, 0L);\n\t}\n\n\tBOOL SizeToRect(LPRECT lpRect)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, RB_SIZETORECT, 0, (LPARAM)lpRect);\n\t}\n\n\tint IdToIndex(UINT uBandID) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, RB_IDTOINDEX, uBandID, 0L);\n\t}\n\n\tint HitTest(LPRBHITTESTINFO lprbht) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, RB_HITTEST, 0, (LPARAM)lprbht);\n\t}\n\n\tBOOL ShowBand(int nBand, BOOL bShow)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, RB_SHOWBAND, nBand, bShow);\n\t}\n\n\tBOOL MoveBand(int nBand, int nNewPos)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT((nNewPos >= 0) && (nNewPos <= ((int)GetBandCount() - 1)));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, RB_MOVEBAND, nBand, nNewPos);\n\t}\n\n\tvoid PushChevron(int nBand, LPARAM lAppValue)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, RB_PUSHCHEVRON, nBand, lAppValue);\n\t}\n\n// Extra operations\n\tvoid LockBands(bool bLock)\n\t{\n\t\tint nBandCount = GetBandCount();\n\t\tfor(int i =0; i < nBandCount; i++)\n\t\t{\n\t\t\tREBARBANDINFO rbbi = { RunTimeHelper::SizeOf_REBARBANDINFO() };\n\t\t\trbbi.fMask = RBBIM_STYLE;\n\t\t\tBOOL bRet = GetBandInfo(i, &rbbi);\n\t\t\tATLASSERT(bRet);\n\n\t\t\tif((rbbi.fStyle & RBBS_GRIPPERALWAYS) == 0)\n\t\t\t{\n\t\t\t\trbbi.fStyle |= RBBS_GRIPPERALWAYS;\n\t\t\t\tbRet = SetBandInfo(i, &rbbi);\n\t\t\t\tATLASSERT(bRet);\n\t\t\t\trbbi.fStyle &= ~RBBS_GRIPPERALWAYS;\n\t\t\t}\n\n\t\t\tif(bLock)\n\t\t\t\trbbi.fStyle |= RBBS_NOGRIPPER;\n\t\t\telse\n\t\t\t\trbbi.fStyle &= ~RBBS_NOGRIPPER;\n\n\t\t\tbRet = SetBandInfo(i, &rbbi);\n\t\t\tATLASSERT(bRet);\n\t\t}\n\t}\n\n#if (_WIN32_WINNT >= 0x0600)\n\tBOOL SetBandWidth(int nBand, int cxWidth)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, RB_SETBANDWIDTH, nBand, cxWidth);\n\t}\n#endif // (_WIN32_WINNT >= 0x0600)\n};\n\ntypedef CReBarCtrlT<ATL::CWindow>   CReBarCtrl;\n\n\n///////////////////////////////////////////////////////////////////////////////\n// CComboBoxEx\n\ntemplate <class TBase>\nclass CComboBoxExT : public CComboBoxT< TBase >\n{\npublic:\n// Constructors\n\tCComboBoxExT(HWND hWnd = NULL) : CComboBoxT< TBase >(hWnd)\n\t{ }\n\n\tCComboBoxExT< TBase >& operator =(HWND hWnd)\n\t{\n\t\tthis->m_hWnd = hWnd;\n\t\treturn *this;\n\t}\n\n\tHWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL,\n\t\t\tDWORD dwStyle = 0, DWORD dwExStyle = 0,\n\t\t\tATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL)\n\t{\n\t\treturn TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam);\n\t}\n\n// Attributes\n\tstatic LPCTSTR GetWndClassName()\n\t{\n\t\treturn WC_COMBOBOXEX;\n\t}\n\n\tCImageList GetImageList() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn CImageList((HIMAGELIST)::SendMessage(this->m_hWnd, CBEM_GETIMAGELIST, 0, 0L));\n\t}\n\n\tCImageList SetImageList(HIMAGELIST hImageList)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn CImageList((HIMAGELIST)::SendMessage(this->m_hWnd, CBEM_SETIMAGELIST, 0, (LPARAM)hImageList));\n\t}\n\n\tDWORD GetExtendedStyle() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (DWORD)::SendMessage(this->m_hWnd, CBEM_GETEXTENDEDSTYLE, 0, 0L);\n\t}\n\n\tDWORD SetExtendedStyle(DWORD dwExMask, DWORD dwExStyle)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (DWORD)::SendMessage(this->m_hWnd, CBEM_SETEXTENDEDSTYLE, dwExMask, dwExStyle);\n\t}\n\n\tBOOL GetUnicodeFormat() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, CBEM_GETUNICODEFORMAT, 0, 0L);\n\t}\n\n\tBOOL SetUnicodeFormat(BOOL bUnicode = TRUE)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, CBEM_SETUNICODEFORMAT, bUnicode, 0L);\n\t}\n\n\tvoid SetWindowTheme(LPCWSTR lpstrTheme)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, CBEM_SETWINDOWTHEME, 0, (LPARAM)lpstrTheme);\n\t}\n\n// Operations\n\tint InsertItem(const COMBOBOXEXITEM* lpcCBItem)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, CBEM_INSERTITEM, 0, (LPARAM)lpcCBItem);\n\t}\n\n\tint InsertItem(UINT nMask, int nIndex, LPCTSTR lpszItem, int nImage, int nSelImage, \n\t               int iIndent, int iOverlay, LPARAM lParam)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tCOMBOBOXEXITEM cbex = {};\n\t\tcbex.mask = nMask;\n\t\tcbex.iItem = nIndex;\n\t\tcbex.pszText = (LPTSTR) lpszItem;\n\t\tcbex.iImage = nImage;\n\t\tcbex.iSelectedImage = nSelImage;\n\t\tcbex.iIndent = iIndent;\n\t\tcbex.iOverlay = iOverlay;\n\t\tcbex.lParam = lParam;\n\t\treturn (int)::SendMessage(this->m_hWnd, CBEM_INSERTITEM, 0, (LPARAM)&cbex);\n\t}\n\n\tint InsertItem(int nIndex, LPCTSTR lpszItem, int nImage, int nSelImage, int iIndent, LPARAM lParam = 0)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tCOMBOBOXEXITEM cbex = {};\n\t\tcbex.mask = CBEIF_TEXT | CBEIF_IMAGE | CBEIF_SELECTEDIMAGE | CBEIF_INDENT | CBEIF_LPARAM;\n\t\tcbex.iItem = nIndex;\n\t\tcbex.pszText = (LPTSTR) lpszItem;\n\t\tcbex.iImage = nImage;\n\t\tcbex.iSelectedImage = nSelImage;\n\t\tcbex.iIndent = iIndent;\n\t\tcbex.lParam = lParam;\n\t\treturn (int)::SendMessage(this->m_hWnd, CBEM_INSERTITEM, 0, (LPARAM)&cbex);\n\t}\n\n\tint AddItem(UINT nMask, LPCTSTR lpszItem, int nImage, int nSelImage, int iIndent, int iOverlay, LPARAM lParam)\n\t{\n\t\treturn InsertItem(nMask, -1, lpszItem, nImage, nSelImage, iIndent, iOverlay, lParam);\n\t}\n\n\tint AddItem(LPCTSTR lpszItem, int nImage, int nSelImage, int iIndent, LPARAM lParam = 0)\n\t{\n\t\treturn InsertItem(-1, lpszItem, nImage, nSelImage, iIndent, lParam);\n\t}\n\n\tint DeleteItem(int nIndex)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, CBEM_DELETEITEM, nIndex, 0L);\n\t}\n\n\tBOOL GetItem(PCOMBOBOXEXITEM pCBItem) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, CBEM_GETITEM, 0, (LPARAM)pCBItem);\n\t}\n\n\tBOOL SetItem(const COMBOBOXEXITEM* lpcCBItem)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, CBEM_SETITEM, 0, (LPARAM)lpcCBItem);\n\t}\n\n\tint SetItem(int nIndex, UINT nMask, LPCTSTR lpszItem, int nImage, int nSelImage, \n\t            int iIndent, int iOverlay, LPARAM lParam)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tCOMBOBOXEXITEM cbex = {};\n\t\tcbex.mask = nMask;\n\t\tcbex.iItem = nIndex;\n\t\tcbex.pszText = (LPTSTR) lpszItem;\n\t\tcbex.iImage = nImage;\n\t\tcbex.iSelectedImage = nSelImage;\n\t\tcbex.iIndent = iIndent;\n\t\tcbex.iOverlay = iOverlay;\n\t\tcbex.lParam = lParam;\n\t\treturn (int)::SendMessage(this->m_hWnd, CBEM_SETITEM, 0, (LPARAM)&cbex);\n\t}\n\n\tBOOL GetItemText(int nIndex, LPTSTR lpszItem, int nLen) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT(lpszItem != NULL);\n\n\t\tCOMBOBOXEXITEM cbex = {};\n\t\tcbex.mask = CBEIF_TEXT;\n\t\tcbex.iItem = nIndex;\n\t\tcbex.pszText = lpszItem;\n\t\tcbex.cchTextMax = nLen;\n\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, CBEM_GETITEM, 0, (LPARAM)&cbex);\n\t}\n\n\tBOOL GetItemText(int nIndex, BSTR& bstrText) const\n\t{\n\t\tUSES_CONVERSION;\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT(bstrText == NULL);\n\n\t\tCOMBOBOXEXITEM cbex = {};\n\t\tcbex.mask = CBEIF_TEXT;\n\t\tcbex.iItem = nIndex;\n\n\t\tLPTSTR lpstrText = NULL;\n\t\tBOOL bRet = FALSE;\n\t\tfor(int nLen = 256; ; nLen *= 2)\n\t\t{\n\t\t\tATLTRY(lpstrText = new TCHAR[nLen]);\n\t\t\tif(lpstrText == NULL)\n\t\t\t\tbreak;\n\t\t\tlpstrText[0] = NULL;\n\t\t\tcbex.pszText = lpstrText;\n\t\t\tcbex.cchTextMax = nLen;\n\t\t\tbRet = (BOOL)::SendMessage(this->m_hWnd, CBEM_GETITEM, 0, (LPARAM)&cbex);\n\t\t\tif(!bRet || (lstrlen(cbex.pszText) < (nLen - 1)))\n\t\t\t\tbreak;\n\t\t\tdelete [] lpstrText;\n\t\t\tlpstrText = NULL;\n\t\t}\n\n\t\tif(lpstrText != NULL)\n\t\t{\n\t\t\tif(bRet)\n\t\t\t\tbstrText = ::SysAllocString(T2OLE(lpstrText));\n\t\t\tdelete [] lpstrText;\n\t\t}\n\n\t\treturn (bstrText != NULL) ? TRUE : FALSE;\n\t}\n\n#ifdef __ATLSTR_H__\n\tBOOL GetItemText(int nIndex, ATL::CString& strText) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\n\t\tCOMBOBOXEXITEM cbex = {};\n\t\tcbex.mask = CBEIF_TEXT;\n\t\tcbex.iItem = nIndex;\n\n\t\tstrText.Empty();\n\t\tBOOL bRet = FALSE;\n\t\tfor(int nLen = 256; ; nLen *= 2)\n\t\t{\n\t\t\tcbex.pszText = strText.GetBufferSetLength(nLen);\n\t\t\tif(cbex.pszText == NULL)\n\t\t\t{\n\t\t\t\tbRet = FALSE;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcbex.cchTextMax = nLen;\n\t\t\tbRet = (BOOL)::SendMessage(this->m_hWnd, CBEM_GETITEM, 0, (LPARAM)&cbex);\n\t\t\tif(!bRet || (lstrlen(cbex.pszText) < (nLen - 1)))\n\t\t\t\tbreak;\n\t\t}\n\t\tstrText.ReleaseBuffer();\n\t\treturn bRet;\n\t}\n#endif // __ATLSTR_H__\n\n\tBOOL SetItemText(int nIndex, LPCTSTR lpszItem)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn SetItem(nIndex, CBEIF_TEXT, lpszItem, 0, 0, 0, 0, 0);\n\t}\n\n\tCComboBox GetComboCtrl() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn CComboBox((HWND)::SendMessage(this->m_hWnd, CBEM_GETCOMBOCONTROL, 0, 0L));\n\t}\n\n\tCEdit GetEditCtrl() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn CEdit((HWND)::SendMessage(this->m_hWnd, CBEM_GETEDITCONTROL, 0, 0L));\n\t}\n\n\tBOOL HasEditChanged() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, CBEM_HASEDITCHANGED, 0, 0L);\n\t}\n\n// Non-functional\n\tint AddString(LPCTSTR /*lpszItem*/)\n\t{\n\t\tATLASSERT(FALSE);  // Not available in CComboBoxEx; use InsertItem\n\t\treturn 0;\n\t}\n\n\tint InsertString(int /*nIndex*/, LPCTSTR /*lpszString*/)\n\t{\n\t\tATLASSERT(FALSE);  // Not available in CComboBoxEx; use InsertItem\n\t\treturn 0;\n\t}\n\n\tint Dir(UINT /*attr*/, LPCTSTR /*lpszWildCard*/)\n\t{\n\t\tATLASSERT(FALSE);  // Not available in CComboBoxEx\n\t\treturn 0;\n\t}\n\n\tint FindString(int /*nStartAfter*/, LPCTSTR /*lpszString*/) const\n\t{\n\t\tATLASSERT(FALSE);  // Not available in CComboBoxEx; try FindStringExact\n\t\treturn 0;\n\t}\n};\n\ntypedef CComboBoxExT<ATL::CWindow>   CComboBoxEx;\n\n\n///////////////////////////////////////////////////////////////////////////////\n// CMonthCalendarCtrl\n\ntemplate <class TBase>\nclass CMonthCalendarCtrlT : public TBase\n{\npublic:\n// Constructors\n\tCMonthCalendarCtrlT(HWND hWnd = NULL) : TBase(hWnd)\n\t{ }\n\n\tCMonthCalendarCtrlT< TBase >& operator =(HWND hWnd)\n\t{\n\t\tthis->m_hWnd = hWnd;\n\t\treturn *this;\n\t}\n\n\tHWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL,\n\t\t\tDWORD dwStyle = 0, DWORD dwExStyle = 0,\n\t\t\tATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL)\n\t{\n\t\treturn TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam);\n\t}\n\n// Attributes\n\tstatic LPCTSTR GetWndClassName()\n\t{\n\t\treturn MONTHCAL_CLASS;\n\t}\n\n\tCOLORREF GetColor(int nColorType) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (COLORREF)::SendMessage(this->m_hWnd, MCM_GETCOLOR, nColorType, 0L);\n\t}\n\n\tCOLORREF SetColor(int nColorType, COLORREF clr)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (COLORREF)::SendMessage(this->m_hWnd, MCM_SETCOLOR, nColorType, clr);\n\t}\n\n\tBOOL GetCurSel(LPSYSTEMTIME lpSysTime) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, MCM_GETCURSEL, 0, (LPARAM)lpSysTime);\n\t}\n\n\tBOOL SetCurSel(LPSYSTEMTIME lpSysTime)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, MCM_SETCURSEL, 0, (LPARAM)lpSysTime);\n\t}\n\n\tint GetFirstDayOfWeek(BOOL* pbLocaleVal = NULL) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tDWORD dwRet = (DWORD)::SendMessage(this->m_hWnd, MCM_GETFIRSTDAYOFWEEK, 0, 0L);\n\t\tif(pbLocaleVal != NULL)\n\t\t\t*pbLocaleVal = (BOOL)HIWORD(dwRet);\n\t\treturn (int)(short)LOWORD(dwRet);\n\t}\n\n\tint SetFirstDayOfWeek(int nDay, BOOL* pbLocaleVal = NULL)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tDWORD dwRet = (DWORD)::SendMessage(this->m_hWnd, MCM_SETFIRSTDAYOFWEEK, 0, nDay);\n\t\tif(pbLocaleVal != NULL)\n\t\t\t*pbLocaleVal = (BOOL)HIWORD(dwRet);\n\t\treturn (int)(short)LOWORD(dwRet);\n\t}\n\n\tint GetMaxSelCount() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, MCM_GETMAXSELCOUNT, 0, 0L);\n\t}\n\n\tBOOL SetMaxSelCount(int nMax)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, MCM_SETMAXSELCOUNT, nMax, 0L);\n\t}\n\n\tint GetMonthDelta() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, MCM_GETMONTHDELTA, 0, 0L);\n\t}\n\n\tint SetMonthDelta(int nDelta)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, MCM_SETMONTHDELTA, nDelta, 0L);\n\t}\n\n\tDWORD GetRange(LPSYSTEMTIME lprgSysTimeArray) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (DWORD)::SendMessage(this->m_hWnd, MCM_GETRANGE, 0, (LPARAM)lprgSysTimeArray);\n\t}\n\n\tBOOL SetRange(DWORD dwFlags, LPSYSTEMTIME lprgSysTimeArray)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, MCM_SETRANGE, dwFlags, (LPARAM)lprgSysTimeArray);\n\t}\n\n\tBOOL GetSelRange(LPSYSTEMTIME lprgSysTimeArray) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, MCM_GETSELRANGE, 0, (LPARAM)lprgSysTimeArray);\n\t}\n\n\tBOOL SetSelRange(LPSYSTEMTIME lprgSysTimeArray)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, MCM_SETSELRANGE, 0, (LPARAM)lprgSysTimeArray);\n\t}\n\n\tBOOL GetToday(LPSYSTEMTIME lpSysTime) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, MCM_GETTODAY, 0, (LPARAM)lpSysTime);\n\t}\n\n\tvoid SetToday(LPSYSTEMTIME lpSysTime)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, MCM_SETTODAY, 0, (LPARAM)lpSysTime);\n\t}\n\n\tBOOL GetMinReqRect(LPRECT lpRectInfo) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, MCM_GETMINREQRECT, 0, (LPARAM)lpRectInfo);\n\t}\n\n\tint GetMaxTodayWidth() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, MCM_GETMAXTODAYWIDTH, 0, 0L);\n\t}\n\n\tBOOL GetUnicodeFormat() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, MCM_GETUNICODEFORMAT, 0, 0L);\n\t}\n\n\tBOOL SetUnicodeFormat(BOOL bUnicode = TRUE)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, MCM_SETUNICODEFORMAT, bUnicode, 0L);\n\t}\n\n#if defined(NTDDI_VERSION) && (NTDDI_VERSION >= NTDDI_LONGHORN)\n\tDWORD GetCurrentView() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (DWORD)::SendMessage(this->m_hWnd, MCM_GETCURRENTVIEW, 0, 0L);\n\t}\n\n\tBOOL SetCurrentView(DWORD dwView)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, MCM_SETCURRENTVIEW, 0, dwView);\n\t}\n\n\tDWORD GetCalendarCount() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (DWORD)::SendMessage(this->m_hWnd, MCM_GETCALENDARCOUNT, 0, 0L);\n\t}\n\n\tBOOL GetCalendarGridInfo(PMCGRIDINFO pGridInfo) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, MCM_GETCALENDARGRIDINFO, 0, (LPARAM)pGridInfo);\n\t}\n\n\tCALID GetCALID() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (CALID)::SendMessage(this->m_hWnd, MCM_GETCALID, 0, 0L);\n\t}\n\n\tvoid SetCALID(CALID calid)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, MCM_SETCALID, (LPARAM)calid, 0L);\n\t}\n\n\tint GetCalendarBorder() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, MCM_GETCALENDARBORDER, 0, 0L);\n\t}\n\n\tvoid SetCalendarBorder(int cxyBorder, BOOL bSet = TRUE)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, MCM_SETCALENDARBORDER, (WPARAM)bSet, (LPARAM)cxyBorder);\n\t}\n#endif // defined(NTDDI_VERSION) && (NTDDI_VERSION >= NTDDI_LONGHORN)\n\n// Operations\n\tint GetMonthRange(DWORD dwFlags, LPSYSTEMTIME lprgSysTimeArray) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, MCM_GETMONTHRANGE, dwFlags, (LPARAM)lprgSysTimeArray);\n\t}\n\n\tBOOL SetDayState(int nMonths, LPMONTHDAYSTATE lpDayStateArray)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, MCM_SETDAYSTATE, nMonths, (LPARAM)lpDayStateArray);\n\t}\n\n\tDWORD HitTest(PMCHITTESTINFO pMCHitTest) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (DWORD)::SendMessage(this->m_hWnd, MCM_HITTEST, 0, (LPARAM)pMCHitTest);\n\t}\n\n#if defined(NTDDI_VERSION) && (NTDDI_VERSION >= NTDDI_LONGHORN)\n\tvoid SizeRectToMin(LPRECT lpRect)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, MCM_SIZERECTTOMIN, 0, (LPARAM)lpRect);\n\t}\n#endif // defined(NTDDI_VERSION) && (NTDDI_VERSION >= NTDDI_LONGHORN)\n};\n\ntypedef CMonthCalendarCtrlT<ATL::CWindow>   CMonthCalendarCtrl;\n\n\n///////////////////////////////////////////////////////////////////////////////\n// CDateTimePickerCtrl\n\ntemplate <class TBase>\nclass CDateTimePickerCtrlT : public TBase\n{\npublic:\n// Constructors\n\tCDateTimePickerCtrlT(HWND hWnd = NULL) : TBase(hWnd)\n\t{ }\n\n\tCDateTimePickerCtrlT< TBase >& operator =(HWND hWnd)\n\t{\n\t\tthis->m_hWnd = hWnd;\n\t\treturn *this;\n\t}\n\n\tHWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL,\n\t\t\tDWORD dwStyle = 0, DWORD dwExStyle = 0,\n\t\t\tATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL)\n\t{\n\t\treturn TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam);\n\t}\n\n// Operations\n\tstatic LPCTSTR GetWndClassName()\n\t{\n\t\treturn DATETIMEPICK_CLASS;\n\t}\n\n\tBOOL SetFormat(LPCTSTR lpszFormat)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, DTM_SETFORMAT, 0, (LPARAM)lpszFormat);\n\t}\n\n\tCOLORREF GetMonthCalColor(int nColorType) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (COLORREF)::SendMessage(this->m_hWnd, DTM_GETMCCOLOR, nColorType, 0L);\n\t}\n\n\tCOLORREF SetMonthCalColor(int nColorType, COLORREF clr)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (COLORREF)::SendMessage(this->m_hWnd, DTM_SETMCCOLOR, nColorType, clr);\n\t}\n\n\tDWORD GetRange(LPSYSTEMTIME lpSysTimeArray) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (DWORD)::SendMessage(this->m_hWnd, DTM_GETRANGE, 0, (LPARAM)lpSysTimeArray);\n\t}\n\n\tBOOL SetRange(DWORD dwFlags, LPSYSTEMTIME lpSysTimeArray)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, DTM_SETRANGE, dwFlags, (LPARAM)lpSysTimeArray);\n\t}\n\n\tDWORD GetSystemTime(LPSYSTEMTIME lpSysTime) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (DWORD)::SendMessage(this->m_hWnd, DTM_GETSYSTEMTIME, 0, (LPARAM)lpSysTime);\n\t}\n\n\tBOOL SetSystemTime(DWORD dwFlags, LPSYSTEMTIME lpSysTime)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, DTM_SETSYSTEMTIME, dwFlags, (LPARAM)lpSysTime);\n\t}\n\n\tCMonthCalendarCtrl GetMonthCal() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn CMonthCalendarCtrl((HWND)::SendMessage(this->m_hWnd, DTM_GETMONTHCAL, 0, 0L));\n\t}\n\n\tCFontHandle GetMonthCalFont() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn CFontHandle((HFONT)::SendMessage(this->m_hWnd, DTM_GETMCFONT, 0, 0L));\n\t}\n\n\tvoid SetMonthCalFont(HFONT hFont, BOOL bRedraw = TRUE)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, DTM_SETMCFONT, (WPARAM)hFont, MAKELPARAM(bRedraw, 0));\n\t}\n\n#if defined(NTDDI_VERSION) && (NTDDI_VERSION >= NTDDI_LONGHORN)\n\tDWORD GetMonthCalStyle() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (DWORD)::SendMessage(this->m_hWnd, DTM_GETMCSTYLE, 0, 0L);\n\t}\n\n\tDWORD SetMonthCalStyle(DWORD dwStyle)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (DWORD)::SendMessage(this->m_hWnd, DTM_SETMCSTYLE, 0, (LPARAM)dwStyle);\n\t}\n\n\tvoid GetDateTimePickerInfo(LPDATETIMEPICKERINFO lpPickerInfo) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, DTM_GETDATETIMEPICKERINFO, 0, (LPARAM)lpPickerInfo);\n\t}\n\n\tBOOL GetIdealSize(LPSIZE lpSize) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, DTM_GETIDEALSIZE, 0, (LPARAM)lpSize);\n\t}\n\n\tvoid CloseMonthCal()\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, DTM_CLOSEMONTHCAL, 0, 0L);\n\t}\n#endif // defined(NTDDI_VERSION) && (NTDDI_VERSION >= NTDDI_LONGHORN)\n};\n\ntypedef CDateTimePickerCtrlT<ATL::CWindow>   CDateTimePickerCtrl;\n\n\n///////////////////////////////////////////////////////////////////////////////\n// CFlatScrollBarImpl - support for flat scroll bars\n\ntemplate <class T>\nclass CFlatScrollBarImpl\n{\npublic:\n// Initialization\n\tBOOL FlatSB_Initialize()\n\t{\n\t\tT* pT = static_cast<T*>(this);\n\t\tATLASSERT(::IsWindow(pT->m_hWnd));\n\t\treturn ::InitializeFlatSB(pT->m_hWnd);\n\t}\n\n\tHRESULT FlatSB_Uninitialize()\n\t{\n\t\tT* pT = static_cast<T*>(this);\n\t\tATLASSERT(::IsWindow(pT->m_hWnd));\n\t\treturn ::UninitializeFlatSB(pT->m_hWnd);\n\t}\n\n// Flat scroll bar properties\n\tBOOL FlatSB_GetScrollProp(UINT uIndex, LPINT lpnValue) const\n\t{\n\t\tconst T* pT = static_cast<const T*>(this);\n\t\tATLASSERT(::IsWindow(pT->m_hWnd));\n\t\treturn ::FlatSB_GetScrollProp(pT->m_hWnd, uIndex, lpnValue);\n\t}\n\n\tBOOL FlatSB_SetScrollProp(UINT uIndex, int nValue, BOOL bRedraw = TRUE)\n\t{\n\t\tT* pT = static_cast<T*>(this);\n\t\tATLASSERT(::IsWindow(pT->m_hWnd));\n\t\treturn ::FlatSB_SetScrollProp(pT->m_hWnd, uIndex, nValue, bRedraw);\n\t}\n\n// Attributes\n\tint FlatSB_GetScrollPos(int nBar) const\n\t{\n\t\tconst T* pT = static_cast<const T*>(this);\n\t\tATLASSERT(::IsWindow(pT->m_hWnd));\n\t\treturn ::FlatSB_GetScrollPos(pT->m_hWnd, nBar);\n\t}\n\n\tint FlatSB_SetScrollPos(int nBar, int nPos, BOOL bRedraw = TRUE)\n\t{\n\t\tT* pT = static_cast<T*>(this);\n\t\tATLASSERT(::IsWindow(pT->m_hWnd));\n\t\treturn ::FlatSB_SetScrollPos(pT->m_hWnd, nBar, nPos, bRedraw);\n\t}\n\n\tBOOL FlatSB_GetScrollRange(int nBar, LPINT lpMinPos, LPINT lpMaxPos) const\n\t{\n\t\tconst T* pT = static_cast<const T*>(this);\n\t\tATLASSERT(::IsWindow(pT->m_hWnd));\n\t\treturn ::FlatSB_GetScrollRange(pT->m_hWnd, nBar, lpMinPos, lpMaxPos);\n\t}\n\n\tBOOL FlatSB_SetScrollRange(int nBar, int nMinPos, int nMaxPos, BOOL bRedraw = TRUE)\n\t{\n\t\tT* pT = static_cast<T*>(this);\n\t\tATLASSERT(::IsWindow(pT->m_hWnd));\n\t\treturn ::FlatSB_SetScrollRange(pT->m_hWnd, nBar, nMinPos, nMaxPos, bRedraw);\n\t}\n\n\tBOOL FlatSB_GetScrollInfo(int nBar, LPSCROLLINFO lpScrollInfo) const\n\t{\n\t\tconst T* pT = static_cast<const T*>(this);\n\t\tATLASSERT(::IsWindow(pT->m_hWnd));\n\t\treturn ::FlatSB_GetScrollInfo(pT->m_hWnd, nBar, lpScrollInfo);\n\t}\n\n\tint FlatSB_SetScrollInfo(int nBar, LPSCROLLINFO lpScrollInfo, BOOL bRedraw = TRUE)\n\t{\n\t\tT* pT = static_cast<T*>(this);\n\t\tATLASSERT(::IsWindow(pT->m_hWnd));\n\t\treturn ::FlatSB_SetScrollInfo(pT->m_hWnd, nBar, lpScrollInfo, bRedraw);\n\t}\n\n// Operations\n\tBOOL FlatSB_ShowScrollBar(UINT nBar, BOOL bShow = TRUE)\n\t{\n\t\tT* pT = static_cast<T*>(this);\n\t\tATLASSERT(::IsWindow(pT->m_hWnd));\n\t\treturn ::FlatSB_ShowScrollBar(pT->m_hWnd, nBar, bShow);\n\t}\n\n\tBOOL FlatSB_EnableScrollBar(UINT uSBFlags, UINT uArrowFlags = ESB_ENABLE_BOTH)\n\t{\n\t\tT* pT = static_cast<T*>(this);\n\t\tATLASSERT(::IsWindow(pT->m_hWnd));\n\t\treturn ::FlatSB_EnableScrollBar(pT->m_hWnd, uSBFlags, uArrowFlags);\n\t}\n};\n\ntemplate <class TBase>\nclass CFlatScrollBarT : public TBase, public CFlatScrollBarImpl<CFlatScrollBarT< TBase > >\n{\npublic:\n\tCFlatScrollBarT(HWND hWnd = NULL) : TBase(hWnd)\n\t{ }\n\n\tCFlatScrollBarT< TBase >& operator =(HWND hWnd)\n\t{\n\t\tthis->m_hWnd = hWnd;\n\t\treturn *this;\n\t}\n};\n\ntypedef CFlatScrollBarT<ATL::CWindow>   CFlatScrollBar;\n\n\n///////////////////////////////////////////////////////////////////////////////\n// CIPAddressCtrl\n\ntemplate <class TBase>\nclass CIPAddressCtrlT : public TBase\n{\npublic:\n// Constructors\n\tCIPAddressCtrlT(HWND hWnd = NULL) : TBase(hWnd)\n\t{ }\n\n\tCIPAddressCtrlT< TBase >& operator =(HWND hWnd)\n\t{\n\t\tthis->m_hWnd = hWnd;\n\t\treturn *this;\n\t}\n\n\tHWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL,\n\t\t\tDWORD dwStyle = 0, DWORD dwExStyle = 0,\n\t\t\tATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL)\n\t{\n\t\treturn TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam);\n\t}\n\n// Atteributes\n\tstatic LPCTSTR GetWndClassName()\n\t{\n\t\treturn WC_IPADDRESS;\n\t}\n\n\tBOOL IsBlank() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, IPM_ISBLANK, 0, 0L);\n\t}\n\n\tint GetAddress(LPDWORD lpdwAddress) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, IPM_GETADDRESS, 0, (LPARAM)lpdwAddress);\n\t}\n\n\tvoid SetAddress(DWORD dwAddress)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, IPM_SETADDRESS, 0, dwAddress);\n\t}\n\n\tvoid ClearAddress()\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, IPM_CLEARADDRESS, 0, 0L);\n\t}\n\n\tvoid SetRange(int nField, WORD wRange)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, IPM_SETRANGE, nField, wRange);\n\t}\n\n\tvoid SetRange(int nField, BYTE nMin, BYTE nMax)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, IPM_SETRANGE, nField, MAKEIPRANGE(nMin, nMax));\n\t}\n\n\tvoid SetFocus(int nField)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, IPM_SETFOCUS, nField, 0L);\n\t}\n};\n\ntypedef CIPAddressCtrlT<ATL::CWindow>   CIPAddressCtrl;\n\n\n///////////////////////////////////////////////////////////////////////////////\n// CPagerCtrl\n\ntemplate <class TBase>\nclass CPagerCtrlT : public TBase\n{\npublic:\n// Constructors\n\tCPagerCtrlT(HWND hWnd = NULL) : TBase(hWnd)\n\t{ }\n\n\tCPagerCtrlT< TBase >& operator =(HWND hWnd)\n\t{\n\t\tthis->m_hWnd = hWnd;\n\t\treturn *this;\n\t}\n\n\tHWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL,\n\t\t\tDWORD dwStyle = 0, DWORD dwExStyle = 0,\n\t\t\tATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL)\n\t{\n\t\treturn TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam);\n\t}\n\n// Attributes\n\tstatic LPCTSTR GetWndClassName()\n\t{\n\t\treturn WC_PAGESCROLLER;\n\t}\n\n\tint GetButtonSize() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, PGM_GETBUTTONSIZE, 0, 0L);\n\t}\n\n\tint SetButtonSize(int nButtonSize)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, PGM_SETBUTTONSIZE, 0, nButtonSize);\n\t}\n\n\tDWORD GetButtonState(int nButton) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT((nButton == PGB_TOPORLEFT) || (nButton == PGB_BOTTOMORRIGHT));\n\t\treturn (DWORD)::SendMessage(this->m_hWnd, PGM_GETBUTTONSTATE, 0, nButton);\n\t}\n\n\tCOLORREF GetBkColor() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (COLORREF)::SendMessage(this->m_hWnd, PGM_GETBKCOLOR, 0, 0L);\n\t}\n\n\tCOLORREF SetBkColor(COLORREF clrBk)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (COLORREF)::SendMessage(this->m_hWnd, PGM_SETBKCOLOR, 0, (LPARAM)clrBk);\n\t}\n\n\tint GetBorder() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, PGM_GETBORDER, 0, 0L);\n\t}\n\n\tint SetBorder(int nBorderSize)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, PGM_SETBORDER, 0, nBorderSize);\n\t}\n\n\tint GetPos() const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, PGM_GETPOS, 0, 0L);\n\t}\n\n\tint SetPos(int nPos)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, PGM_SETPOS, 0, nPos);\n\t}\n\n// Operations\n\tvoid SetChild(HWND hWndChild)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, PGM_SETCHILD, 0, (LPARAM)hWndChild);\n\t}\n\n\tvoid ForwardMouse(BOOL bForward = TRUE)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, PGM_FORWARDMOUSE, bForward, 0L);\n\t}\n\n\tvoid RecalcSize()\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\t::SendMessage(this->m_hWnd, PGM_RECALCSIZE, 0, 0L);\n\t}\n\n\tvoid GetDropTarget(IDropTarget** ppDropTarget)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\tATLASSERT(ppDropTarget != NULL);\n\t\t::SendMessage(this->m_hWnd, PGM_GETDROPTARGET, 0, (LPARAM)ppDropTarget);\n\t}\n};\n\ntypedef CPagerCtrlT<ATL::CWindow>   CPagerCtrl;\n\n\n///////////////////////////////////////////////////////////////////////////////\n// CLinkCtrl - Windows SYSLINK control\n\ntemplate <class TBase>\nclass CLinkCtrlT : public TBase\n{\npublic:\n// Constructors\n\tCLinkCtrlT(HWND hWnd = NULL) : TBase(hWnd)\n\t{ }\n\n\tCLinkCtrlT< TBase >& operator =(HWND hWnd)\n\t{\n\t\tthis->m_hWnd = hWnd;\n\t\treturn *this;\n\t}\n\n\tHWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL,\n\t\t\tDWORD dwStyle = 0, DWORD dwExStyle = 0,\n\t\t\tATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL)\n\t{\n\t\treturn TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam);\n\t}\n\n// Attributes\n\tstatic LPCTSTR GetWndClassName()\n\t{\n#ifdef _UNICODE\n\t\treturn WC_LINK;\n#else // !_UNICODE\n\t\treturn \"SysLink\";\n#endif // !_UNICODE\n\t}\n\n\tint GetIdealHeight(int cxMaxWidth = 0) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, LM_GETIDEALHEIGHT, cxMaxWidth, 0L);\n\t}\n\n\tBOOL GetItem(PLITEM pLItem) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, LM_GETITEM, 0, (LPARAM)pLItem);\n\t}\n\n\tBOOL SetItem(PLITEM pLItem)\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, LM_SETITEM, 0, (LPARAM)pLItem);\n\t}\n\n\t// Vista only\n\tint GetIdealSize(SIZE& size, int cxMaxWidth = 0) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (int)::SendMessage(this->m_hWnd, LM_GETIDEALSIZE, cxMaxWidth, (LPARAM)&size);\n\t}\n\n// Operations\n\tBOOL HitTest(PLHITTESTINFO pLHitTestInfo) const\n\t{\n\t\tATLASSERT(::IsWindow(this->m_hWnd));\n\t\treturn (BOOL)::SendMessage(this->m_hWnd, LM_HITTEST, 0, (LPARAM)pLHitTestInfo);\n\t}\n};\n\ntypedef CLinkCtrlT<ATL::CWindow>   CLinkCtrl;\n\n\n///////////////////////////////////////////////////////////////////////////////\n// CCustomDraw - MI class for custom-draw support\n\ntemplate <class T>\nclass CCustomDraw\n{\npublic:\n// Message map and handlers\n\tBEGIN_MSG_MAP(CCustomDraw< T >)\n\t\tNOTIFY_CODE_HANDLER(NM_CUSTOMDRAW, OnCustomDraw)\n\tALT_MSG_MAP(1)\n\t\tREFLECTED_NOTIFY_CODE_HANDLER(NM_CUSTOMDRAW, OnCustomDraw)\n\tEND_MSG_MAP()\n\n// message handler\n\tLRESULT OnCustomDraw(int idCtrl, LPNMHDR pnmh, BOOL& bHandled)\n\t{\n\t\tT* pT = static_cast<T*>(this);\n\t\tpT->SetMsgHandled(TRUE);\n\t\tLPNMCUSTOMDRAW lpNMCustomDraw = (LPNMCUSTOMDRAW)pnmh;\n\t\tDWORD dwRet = 0;\n\t\tswitch(lpNMCustomDraw->dwDrawStage)\n\t\t{\n\t\tcase CDDS_PREPAINT:\n\t\t\tdwRet = pT->OnPrePaint(idCtrl, lpNMCustomDraw);\n\t\t\tbreak;\n\t\tcase CDDS_POSTPAINT:\n\t\t\tdwRet = pT->OnPostPaint(idCtrl, lpNMCustomDraw);\n\t\t\tbreak;\n\t\tcase CDDS_PREERASE:\n\t\t\tdwRet = pT->OnPreErase(idCtrl, lpNMCustomDraw);\n\t\t\tbreak;\n\t\tcase CDDS_POSTERASE:\n\t\t\tdwRet = pT->OnPostErase(idCtrl, lpNMCustomDraw);\n\t\t\tbreak;\n\t\tcase CDDS_ITEMPREPAINT:\n\t\t\tdwRet = pT->OnItemPrePaint(idCtrl, lpNMCustomDraw);\n\t\t\tbreak;\n\t\tcase CDDS_ITEMPOSTPAINT:\n\t\t\tdwRet = pT->OnItemPostPaint(idCtrl, lpNMCustomDraw);\n\t\t\tbreak;\n\t\tcase CDDS_ITEMPREERASE:\n\t\t\tdwRet = pT->OnItemPreErase(idCtrl, lpNMCustomDraw);\n\t\t\tbreak;\n\t\tcase CDDS_ITEMPOSTERASE:\n\t\t\tdwRet = pT->OnItemPostErase(idCtrl, lpNMCustomDraw);\n\t\t\tbreak;\n\t\tcase (CDDS_ITEMPREPAINT | CDDS_SUBITEM):\n\t\t\tdwRet = pT->OnSubItemPrePaint(idCtrl, lpNMCustomDraw);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tpT->SetMsgHandled(FALSE);\n\t\t\tbreak;\n\t\t}\n\t\tbHandled = pT->IsMsgHandled();\n\t\treturn dwRet;\n\t}\n\n// Overrideables\n\tDWORD OnPrePaint(int /*idCtrl*/, LPNMCUSTOMDRAW /*lpNMCustomDraw*/)\n\t{\n\t\treturn CDRF_DODEFAULT;\n\t}\n\n\tDWORD OnPostPaint(int /*idCtrl*/, LPNMCUSTOMDRAW /*lpNMCustomDraw*/)\n\t{\n\t\treturn CDRF_DODEFAULT;\n\t}\n\n\tDWORD OnPreErase(int /*idCtrl*/, LPNMCUSTOMDRAW /*lpNMCustomDraw*/)\n\t{\n\t\treturn CDRF_DODEFAULT;\n\t}\n\n\tDWORD OnPostErase(int /*idCtrl*/, LPNMCUSTOMDRAW /*lpNMCustomDraw*/)\n\t{\n\t\treturn CDRF_DODEFAULT;\n\t}\n\n\tDWORD OnItemPrePaint(int /*idCtrl*/, LPNMCUSTOMDRAW /*lpNMCustomDraw*/)\n\t{\n\t\treturn CDRF_DODEFAULT;\n\t}\n\n\tDWORD OnItemPostPaint(int /*idCtrl*/, LPNMCUSTOMDRAW /*lpNMCustomDraw*/)\n\t{\n\t\treturn CDRF_DODEFAULT;\n\t}\n\n\tDWORD OnItemPreErase(int /*idCtrl*/, LPNMCUSTOMDRAW /*lpNMCustomDraw*/)\n\t{\n\t\treturn CDRF_DODEFAULT;\n\t}\n\n\tDWORD OnItemPostErase(int /*idCtrl*/, LPNMCUSTOMDRAW /*lpNMCustomDraw*/)\n\t{\n\t\treturn CDRF_DODEFAULT;\n\t}\n\n\tDWORD OnSubItemPrePaint(int /*idCtrl*/, LPNMCUSTOMDRAW /*lpNMCustomDraw*/)\n\t{\n\t\treturn CDRF_DODEFAULT;\n\t}\n};\n\n} // namespace WTL\n\n#endif // __ATLCTRLS_H__\n"
  },
  {
    "path": "Examples/WhisperDesktop/Utils/WTL/atlddx.h",
    "content": "// Windows Template Library - WTL version 10.0\n// Copyright (C) Microsoft Corporation, WTL Team. All rights reserved.\n//\n// This file is a part of the Windows Template Library.\n// The use and distribution terms for this software are covered by the\n// Microsoft Public License (http://opensource.org/licenses/MS-PL)\n// which can be found in the file MS-PL.txt at the root folder.\n\n#ifndef __ATLDDX_H__\n#define __ATLDDX_H__\n\n#pragma once\n\n#ifndef __ATLAPP_H__\n\t#error atlddx.h requires atlapp.h to be included first\n#endif\n\n#include <float.h>\n\n\n///////////////////////////////////////////////////////////////////////////////\n// Classes in this file:\n//\n// CWinDataExchange<T>\n\n\nnamespace WTL\n{\n\n// Constants\n#define DDX_LOAD\tFALSE\n#define DDX_SAVE\tTRUE\n\n// DDX map macros\n#define BEGIN_DDX_MAP(thisClass) \\\n\tBOOL DoDataExchange(BOOL bSaveAndValidate = FALSE, UINT nCtlID = (UINT)-1) \\\n\t{ \\\n\t\t(bSaveAndValidate); \\\n\t\t(nCtlID);\n\n#define DDX_TEXT(nID, var) \\\n\t\tif((nCtlID == (UINT)-1) || (nCtlID == nID)) \\\n\t\t{ \\\n\t\t\tif(!DDX_Text(nID, var, sizeof(var), bSaveAndValidate)) \\\n\t\t\t\treturn FALSE; \\\n\t\t}\n\n#define DDX_TEXT_LEN(nID, var, len) \\\n\t\tif((nCtlID == (UINT)-1) || (nCtlID == nID)) \\\n\t\t{ \\\n\t\t\tif(!DDX_Text(nID, var, sizeof(var), bSaveAndValidate, TRUE, len)) \\\n\t\t\t\treturn FALSE; \\\n\t\t}\n\n#define DDX_INT(nID, var) \\\n\t\tif((nCtlID == (UINT)-1) || (nCtlID == nID)) \\\n\t\t{ \\\n\t\t\tif(!DDX_Int(nID, var, TRUE, bSaveAndValidate)) \\\n\t\t\t\treturn FALSE; \\\n\t\t}\n\n#define DDX_INT_RANGE(nID, var, min, max) \\\n\t\tif((nCtlID == (UINT)-1) || (nCtlID == nID)) \\\n\t\t{ \\\n\t\t\tif(!DDX_Int(nID, var, TRUE, bSaveAndValidate, TRUE, min, max)) \\\n\t\t\t\treturn FALSE; \\\n\t\t}\n\n#define DDX_UINT(nID, var) \\\n\t\tif((nCtlID == (UINT)-1) || (nCtlID == nID)) \\\n\t\t{ \\\n\t\t\tif(!DDX_Int(nID, var, FALSE, bSaveAndValidate)) \\\n\t\t\t\treturn FALSE; \\\n\t\t}\n\n#define DDX_UINT_RANGE(nID, var, min, max) \\\n\t\tif((nCtlID == (UINT)-1) || (nCtlID == nID)) \\\n\t\t{ \\\n\t\t\tif(!DDX_Int(nID, var, FALSE, bSaveAndValidate, TRUE, min, max)) \\\n\t\t\t\treturn FALSE; \\\n\t\t}\n\n#define DDX_FLOAT(nID, var) \\\n\t\tif((nCtlID == (UINT)-1) || (nCtlID == nID)) \\\n\t\t{ \\\n\t\t\tif(!DDX_Float(nID, var, bSaveAndValidate)) \\\n\t\t\t\treturn FALSE; \\\n\t\t}\n\n#define DDX_FLOAT_RANGE(nID, var, min, max) \\\n\t\tif((nCtlID == (UINT)-1) || (nCtlID == nID)) \\\n\t\t{ \\\n\t\t\tif(!DDX_Float(nID, var, bSaveAndValidate, TRUE, min, max)) \\\n\t\t\t\treturn FALSE; \\\n\t\t}\n#define DDX_FLOAT_P(nID, var, precision) \\\n\t\tif((nCtlID == (UINT)-1) || (nCtlID == nID)) \\\n\t\t{ \\\n\t\t\tif(!DDX_Float(nID, var, bSaveAndValidate, FALSE, 0, 0, precision)) \\\n\t\t\t\treturn FALSE; \\\n\t\t}\n\n#define DDX_FLOAT_P_RANGE(nID, var, min, max, precision) \\\n\t\tif((nCtlID == (UINT)-1) || (nCtlID == nID)) \\\n\t\t{ \\\n\t\t\tif(!DDX_Float(nID, var, bSaveAndValidate, TRUE, min, max, precision)) \\\n\t\t\t\treturn FALSE; \\\n\t\t}\n\n#define DDX_CONTROL(nID, obj) \\\n\t\tif((nCtlID == (UINT)-1) || (nCtlID == nID)) \\\n\t\t\tDDX_Control(nID, obj, bSaveAndValidate);\n\n#define DDX_CONTROL_HANDLE(nID, obj) \\\n\t\tif((nCtlID == (UINT)-1) || (nCtlID == nID)) \\\n\t\t\tDDX_Control_Handle(nID, obj, bSaveAndValidate);\n\n#define DDX_CHECK(nID, var) \\\n\t\tif((nCtlID == (UINT)-1) || (nCtlID == nID)) \\\n\t\t\tDDX_Check(nID, var, bSaveAndValidate);\n\n#define DDX_RADIO(nID, var) \\\n\t\tif((nCtlID == (UINT)-1) || (nCtlID == nID)) \\\n\t\t\tDDX_Radio(nID, var, bSaveAndValidate);\n\n#define END_DDX_MAP() \\\n\t\treturn TRUE; \\\n\t}\n\n// DDX support for Tab, Combo, ListBox and ListView selection index\n// Note: Specialized versions require atlctrls.h to be included first\n\n#define DDX_INDEX(CtrlClass, nID, var) \\\n\tif((nCtlID == (UINT)-1) || (nCtlID == nID)) \\\n\t\tDDX_Index<CtrlClass>(nID, var, bSaveAndValidate);\n\n#ifdef __ATLCTRLS_H__\n  #define DDX_TAB_INDEX(nID, var)      DDX_INDEX(WTL::CTabCtrl, nID, var)\n  #define DDX_COMBO_INDEX(nID, var)    DDX_INDEX(WTL::CComboBox, nID, var)\n  #define DDX_LISTBOX_INDEX(nID, var)  DDX_INDEX(WTL::CListBox, nID, var)\n  #define DDX_LISTVIEW_INDEX(nID, var) DDX_INDEX(WTL::CListViewCtrl, nID, var)\n#endif // __ATLCTRLS_H__\n\n\n///////////////////////////////////////////////////////////////////////////////\n// CWinDataExchange - provides support for DDX\n\ntemplate <class T>\nclass CWinDataExchange\n{\npublic:\n// Data exchange method - override in your derived class\n\tBOOL DoDataExchange(BOOL /*bSaveAndValidate*/ = FALSE, UINT /*nCtlID*/ = (UINT)-1)\n\t{\n\t\t// this one should never be called, override it in\n\t\t// your derived class by implementing DDX map\n\t\tATLASSERT(FALSE);\n\t\treturn FALSE;\n\t}\n\n// Helpers for validation error reporting\n\tenum _XDataType\n\t{\n\t\tddxDataNull = 0,\n\t\tddxDataText = 1,\n\t\tddxDataInt = 2,\n\t\tddxDataFloat = 3,\n\t\tddxDataDouble = 4\n\t};\n\n\tstruct _XTextData\n\t{\n\t\tint nLength;\n\t\tint nMaxLength;\n\t};\n\n\tstruct _XIntData\n\t{\n\t\tlong nVal;\n\t\tlong nMin;\n\t\tlong nMax;\n\t};\n\n\tstruct _XFloatData\n\t{\n\t\tdouble nVal;\n\t\tdouble nMin;\n\t\tdouble nMax;\n\t};\n\n\tstruct _XData\n\t{\n\t\t_XDataType nDataType;\n\t\tunion\n\t\t{\n\t\t\t_XTextData textData;\n\t\t\t_XIntData intData;\n\t\t\t_XFloatData floatData;\n\t\t};\n\t};\n\n// Text exchange\n\tBOOL DDX_Text(UINT nID, LPTSTR lpstrText, int cbSize, BOOL bSave, BOOL bValidate = FALSE, int nLength = 0)\n\t{\n\t\tT* pT = static_cast<T*>(this);\n\t\tBOOL bSuccess = TRUE;\n\n\t\tif(bSave)\n\t\t{\n\t\t\tHWND hWndCtrl = pT->GetDlgItem(nID);\n\t\t\tint nRetLen = ::GetWindowText(hWndCtrl, lpstrText, cbSize / sizeof(TCHAR));\n\t\t\tif(nRetLen < ::GetWindowTextLength(hWndCtrl))\n\t\t\t\tbSuccess = FALSE;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tATLASSERT(!bValidate || (lstrlen(lpstrText) <= nLength));\n\t\t\tbSuccess = pT->SetDlgItemText(nID, lpstrText);\n\t\t}\n\n\t\tif(!bSuccess)\n\t\t{\n\t\t\tpT->OnDataExchangeError(nID, bSave);\n\t\t}\n\t\telse if(bSave && bValidate)   // validation\n\t\t{\n\t\t\tATLASSERT(nLength > 0);\n\t\t\tif(lstrlen(lpstrText) > nLength)\n\t\t\t{\n\t\t\t\t_XData data = { ddxDataText };\n\t\t\t\tdata.textData.nLength = lstrlen(lpstrText);\n\t\t\t\tdata.textData.nMaxLength = nLength;\n\t\t\t\tpT->OnDataValidateError(nID, bSave, data);\n\t\t\t\tbSuccess = FALSE;\n\t\t\t}\n\t\t}\n\t\treturn bSuccess;\n\t}\n\n\tBOOL DDX_Text(UINT nID, BSTR& bstrText, int /*cbSize*/, BOOL bSave, BOOL bValidate = FALSE, int nLength = 0)\n\t{\n\t\tT* pT = static_cast<T*>(this);\n\t\tBOOL bSuccess = TRUE;\n\n\t\tif(bSave)\n\t\t{\n\t\t\tbSuccess = pT->GetDlgItemText(nID, bstrText);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tUSES_CONVERSION;\n\t\t\tLPTSTR lpstrText = OLE2T(bstrText);\n\t\t\tATLASSERT(!bValidate || (lstrlen(lpstrText) <= nLength));\n\t\t\tbSuccess = pT->SetDlgItemText(nID, lpstrText);\n\t\t}\n\n\t\tif(!bSuccess)\n\t\t{\n\t\t\tpT->OnDataExchangeError(nID, bSave);\n\t\t}\n\t\telse if(bSave && bValidate)   // validation\n\t\t{\n\t\t\tATLASSERT(nLength > 0);\n\t\t\tif((int)::SysStringLen(bstrText) > nLength)\n\t\t\t{\n\t\t\t\t_XData data = { ddxDataText };\n\t\t\t\tdata.textData.nLength = (int)::SysStringLen(bstrText);\n\t\t\t\tdata.textData.nMaxLength = nLength;\n\t\t\t\tpT->OnDataValidateError(nID, bSave, data);\n\t\t\t\tbSuccess = FALSE;\n\t\t\t}\n\t\t}\n\t\treturn bSuccess;\n\t}\n\n\tBOOL DDX_Text(UINT nID, ATL::CComBSTR& bstrText, int /*cbSize*/, BOOL bSave, BOOL bValidate = FALSE, int nLength = 0)\n\t{\n\t\tT* pT = static_cast<T*>(this);\n\t\tBOOL bSuccess = TRUE;\n\n\t\tif(bSave)\n\t\t{\n\t\t\tbSuccess = pT->GetDlgItemText(nID, (BSTR&)bstrText);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tUSES_CONVERSION;\n\t\t\tLPTSTR lpstrText = OLE2T(bstrText);\n\t\t\tATLASSERT(!bValidate || (lstrlen(lpstrText) <= nLength));\n\t\t\tbSuccess = pT->SetDlgItemText(nID, lpstrText);\n\t\t}\n\n\t\tif(!bSuccess)\n\t\t{\n\t\t\tpT->OnDataExchangeError(nID, bSave);\n\t\t}\n\t\telse if(bSave && bValidate)   // validation\n\t\t{\n\t\t\tATLASSERT(nLength > 0);\n\t\t\tif((int)bstrText.Length() > nLength)\n\t\t\t{\n\t\t\t\t_XData data = { ddxDataText };\n\t\t\t\tdata.textData.nLength = (int)bstrText.Length();\n\t\t\t\tdata.textData.nMaxLength = nLength;\n\t\t\t\tpT->OnDataValidateError(nID, bSave, data);\n\t\t\t\tbSuccess = FALSE;\n\t\t\t}\n\t\t}\n\t\treturn bSuccess;\n\t}\n\n#ifdef __ATLSTR_H__\n\tBOOL DDX_Text(UINT nID, ATL::CString& strText, int /*cbSize*/, BOOL bSave, BOOL bValidate = FALSE, int nLength = 0)\n\t{\n\t\tT* pT = static_cast<T*>(this);\n\t\tBOOL bSuccess = TRUE;\n\n\t\tif(bSave)\n\t\t{\n\t\t\tHWND hWndCtrl = pT->GetDlgItem(nID);\n\t\t\tint nLen = ::GetWindowTextLength(hWndCtrl);\n\t\t\tint nRetLen = -1;\n\t\t\tLPTSTR lpstr = strText.GetBufferSetLength(nLen);\n\t\t\tif(lpstr != NULL)\n\t\t\t{\n\t\t\t\tnRetLen = ::GetWindowText(hWndCtrl, lpstr, nLen + 1);\n\t\t\t\tstrText.ReleaseBuffer();\n\t\t\t}\n\t\t\tif(nRetLen < nLen)\n\t\t\t\tbSuccess = FALSE;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tbSuccess = pT->SetDlgItemText(nID, strText);\n\t\t}\n\n\t\tif(!bSuccess)\n\t\t{\n\t\t\tpT->OnDataExchangeError(nID, bSave);\n\t\t}\n\t\telse if(bSave && bValidate)   // validation\n\t\t{\n\t\t\tATLASSERT(nLength > 0);\n\t\t\tif(strText.GetLength() > nLength)\n\t\t\t{\n\t\t\t\t_XData data = { ddxDataText };\n\t\t\t\tdata.textData.nLength = strText.GetLength();\n\t\t\t\tdata.textData.nMaxLength = nLength;\n\t\t\t\tpT->OnDataValidateError(nID, bSave, data);\n\t\t\t\tbSuccess = FALSE;\n\t\t\t}\n\t\t}\n\t\treturn bSuccess;\n\t}\n#endif // __ATLSTR_H__\n\n// Numeric exchange\n\ttemplate <class Type>\n\tBOOL DDX_Int(UINT nID, Type& nVal, BOOL bSigned, BOOL bSave, BOOL bValidate = FALSE, Type nMin = 0, Type nMax = 0)\n\t{\n\t\tT* pT = static_cast<T*>(this);\n\t\tBOOL bSuccess = TRUE;\n\n\t\tif(bSave)\n\t\t{\n\t\t\tnVal = (Type)pT->GetDlgItemInt(nID, &bSuccess, bSigned);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tATLASSERT(!bValidate || ((nVal >= nMin) && (nVal <= nMax)));\n\t\t\tbSuccess = pT->SetDlgItemInt(nID, nVal, bSigned);\n\t\t}\n\n\t\tif(!bSuccess)\n\t\t{\n\t\t\tpT->OnDataExchangeError(nID, bSave);\n\t\t}\n\t\telse if(bSave && bValidate)   // validation\n\t\t{\n\t\t\tATLASSERT(nMin != nMax);\n\t\t\tif((nVal < nMin) || (nVal > nMax))\n\t\t\t{\n\t\t\t\t_XData data = { ddxDataInt };\n\t\t\t\tdata.intData.nVal = (long)nVal;\n\t\t\t\tdata.intData.nMin = (long)nMin;\n\t\t\t\tdata.intData.nMax = (long)nMax;\n\t\t\t\tpT->OnDataValidateError(nID, bSave, data);\n\t\t\t\tbSuccess = FALSE;\n\t\t\t}\n\t\t}\n\t\treturn bSuccess;\n\t}\n\n// Float exchange\n\tstatic BOOL _AtlSimpleFloatParse(LPCTSTR lpszText, double& d)\n\t{\n\t\tATLASSERT(lpszText != NULL);\n\t\twhile ((*lpszText == _T(' ')) || (*lpszText == _T('\\t')))\n\t\t\tlpszText++;\n\n\t\tTCHAR chFirst = lpszText[0];\n\t\td = _tcstod(lpszText, (LPTSTR*)&lpszText);\n\t\tif ((d == 0.0) && (chFirst != _T('0')))\n\t\t\treturn FALSE;   // could not convert\n\t\twhile ((*lpszText == _T(' ')) || (*lpszText == _T('\\t')))\n\t\t\tlpszText++;\n\n\t\tif (*lpszText != _T('\\0'))\n\t\t\treturn FALSE;   // not terminated properly\n\n\t\treturn TRUE;\n\t}\n\n\tBOOL DDX_Float(UINT nID, float& nVal, BOOL bSave, BOOL bValidate = FALSE, float nMin = 0.F, float nMax = 0.F, int nPrecision = FLT_DIG)\n\t{\n\t\tT* pT = static_cast<T*>(this);\n\t\tBOOL bSuccess = TRUE;\n\t\tconst int cchBuff = 32;\n\t\tTCHAR szBuff[cchBuff] = {};\n\n\t\tif(bSave)\n\t\t{\n\t\t\tpT->GetDlgItemText(nID, szBuff, cchBuff);\n\t\t\tdouble d = 0;\n\t\t\tif(_AtlSimpleFloatParse(szBuff, d))\n\t\t\t\tnVal = (float)d;\n\t\t\telse\n\t\t\t\tbSuccess = FALSE;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tATLASSERT(!bValidate || ((nVal >= nMin) && (nVal <= nMax)));\n\t\t\t_stprintf_s(szBuff, cchBuff, _T(\"%.*g\"), nPrecision, nVal);\n\t\t\tbSuccess = pT->SetDlgItemText(nID, szBuff);\n\t\t}\n\n\t\tif(!bSuccess)\n\t\t{\n\t\t\tpT->OnDataExchangeError(nID, bSave);\n\t\t}\n\t\telse if(bSave && bValidate)   // validation\n\t\t{\n\t\t\tATLASSERT(nMin != nMax);\n\t\t\tif((nVal < nMin) || (nVal > nMax))\n\t\t\t{\n\t\t\t\t_XData data = { ddxDataFloat };\n\t\t\t\tdata.floatData.nVal = (double)nVal;\n\t\t\t\tdata.floatData.nMin = (double)nMin;\n\t\t\t\tdata.floatData.nMax = (double)nMax;\n\t\t\t\tpT->OnDataValidateError(nID, bSave, data);\n\t\t\t\tbSuccess = FALSE;\n\t\t\t}\n\t\t}\n\t\treturn bSuccess;\n\t}\n\n\tBOOL DDX_Float(UINT nID, double& nVal, BOOL bSave, BOOL bValidate = FALSE, double nMin = 0., double nMax = 0., int nPrecision = DBL_DIG)\n\t{\n\t\tT* pT = static_cast<T*>(this);\n\t\tBOOL bSuccess = TRUE;\n\t\tconst int cchBuff = 32;\n\t\tTCHAR szBuff[cchBuff] = {};\n\n\t\tif(bSave)\n\t\t{\n\t\t\tpT->GetDlgItemText(nID, szBuff, cchBuff);\n\t\t\tdouble d = 0;\n\t\t\tif(_AtlSimpleFloatParse(szBuff, d))\n\t\t\t\tnVal = d;\n\t\t\telse\n\t\t\t\tbSuccess = FALSE;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tATLASSERT(!bValidate || ((nVal >= nMin) && (nVal <= nMax)));\n\t\t\t_stprintf_s(szBuff, cchBuff, _T(\"%.*g\"), nPrecision, nVal);\n\t\t\tbSuccess = pT->SetDlgItemText(nID, szBuff);\n\t\t}\n\n\t\tif(!bSuccess)\n\t\t{\n\t\t\tpT->OnDataExchangeError(nID, bSave);\n\t\t}\n\t\telse if(bSave && bValidate)   // validation\n\t\t{\n\t\t\tATLASSERT(nMin != nMax);\n\t\t\tif((nVal < nMin) || (nVal > nMax))\n\t\t\t{\n\t\t\t\t_XData data = { ddxDataFloat };\n\t\t\t\tdata.floatData.nVal = nVal;\n\t\t\t\tdata.floatData.nMin = nMin;\n\t\t\t\tdata.floatData.nMax = nMax;\n\t\t\t\tpT->OnDataValidateError(nID, bSave, data);\n\t\t\t\tbSuccess = FALSE;\n\t\t\t}\n\t\t}\n\t\treturn bSuccess;\n\t}\n\n// Full control subclassing (for CWindowImpl derived controls)\n\ttemplate <class TControl>\n\tvoid DDX_Control(UINT nID, TControl& ctrl, BOOL bSave)\n\t{\n\t\tif(!bSave && (ctrl.m_hWnd == NULL))\n\t\t{\n\t\t\tT* pT = static_cast<T*>(this);\n\t\t\tctrl.SubclassWindow(pT->GetDlgItem(nID));\n\t\t}\n\t}\n\n// Simple control attaching (for HWND wrapper controls)\n\ttemplate <class TControl>\n\tvoid DDX_Control_Handle(UINT nID, TControl& ctrl, BOOL bSave)\n\t{\n\t\tif(!bSave && (ctrl.m_hWnd == NULL))\n\t\t{\n\t\t\tT* pT = static_cast<T*>(this);\n\t\t\tctrl = pT->GetDlgItem(nID);\n\t\t}\n\t}\n\n// Control state\n\tvoid DDX_Check(UINT nID, int& nValue, BOOL bSave)\n\t{\n\t\tT* pT = static_cast<T*>(this);\n\t\tHWND hWndCtrl = pT->GetDlgItem(nID);\n\t\tif(bSave)\n\t\t{\n\t\t\tnValue = (int)::SendMessage(hWndCtrl, BM_GETCHECK, 0, 0L);\n\t\t\tATLASSERT((nValue >= 0) && (nValue <= 2));\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif((nValue < 0) || (nValue > 2))\n\t\t\t{\n\t\t\t\tATLTRACE2(atlTraceUI, 0, _T(\"ATL: Warning - dialog data checkbox value (%d) out of range.\\n\"), nValue);\n\t\t\t\tnValue = 0;  // default to off\n\t\t\t}\n\t\t\t::SendMessage(hWndCtrl, BM_SETCHECK, nValue, 0L);\n\t\t}\n\t}\n\n\t// variant that supports bool (checked/not-checked, no intermediate state)\n\tvoid DDX_Check(UINT nID, bool& bCheck, BOOL bSave)\n\t{\n\t\tint nValue = bCheck ? 1 : 0;\n\t\tDDX_Check(nID, nValue, bSave);\n\n\t\tif(bSave)\n\t\t{\n\t\t\tif(nValue == 2)\n\t\t\t\tATLTRACE2(atlTraceUI, 0, _T(\"ATL: Warning - checkbox state (%d) out of supported range.\\n\"), nValue);\n\t\t\tbCheck = (nValue == 1);\n\t\t}\n\t}\n\n\tvoid DDX_Radio(UINT nID, int& nValue, BOOL bSave)\n\t{\n\t\tT* pT = static_cast<T*>(this);\n\t\tHWND hWndCtrl = pT->GetDlgItem(nID);\n\t\tATLASSERT(hWndCtrl != NULL);\n\n\t\t// must be first in a group of auto radio buttons\n\t\tATLASSERT(::GetWindowLong(hWndCtrl, GWL_STYLE) & WS_GROUP);\n\t\tATLASSERT(::SendMessage(hWndCtrl, WM_GETDLGCODE, 0, 0L) & DLGC_RADIOBUTTON);\n\n\t\tif(bSave)\n\t\t\tnValue = -1;     // value if none found\n\n\t\t// walk all children in group\n\t\tint nButton = 0;\n\t\tdo\n\t\t{\n\t\t\tif(::SendMessage(hWndCtrl, WM_GETDLGCODE, 0, 0L) & DLGC_RADIOBUTTON)\n\t\t\t{\n\t\t\t\t// control in group is a radio button\n\t\t\t\tif(bSave)\n\t\t\t\t{\n\t\t\t\t\tif(::SendMessage(hWndCtrl, BM_GETCHECK, 0, 0L) != 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tATLASSERT(nValue == -1);    // only set once\n\t\t\t\t\t\tnValue = nButton;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t// select button\n\t\t\t\t\t::SendMessage(hWndCtrl, BM_SETCHECK, (nButton == nValue), 0L);\n\t\t\t\t}\n\t\t\t\tnButton++;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tATLTRACE2(atlTraceUI, 0, _T(\"ATL: Warning - skipping non-radio button in group.\\n\"));\n\t\t\t}\n\t\t\thWndCtrl = ::GetWindow(hWndCtrl, GW_HWNDNEXT);\n\t\t}\n\t\twhile ((hWndCtrl != NULL) && !(GetWindowLong(hWndCtrl, GWL_STYLE) & WS_GROUP));\n\t}\n\n// DDX support for Tab, Combo, ListBox and ListView selection index\n\ttemplate <class TCtrl>\n\tINT _getSel(TCtrl& tCtrl)\n\t{\n\t\treturn tCtrl.GetCurSel();\n\t}\n\n\ttemplate <class TCtrl>\n\tvoid _setSel(TCtrl& tCtrl, INT iSel)\n\t{\n\t\tif(iSel < 0)\n\t\t\ttCtrl.SetCurSel(-1);\n\t\telse\n\t\t\ttCtrl.SetCurSel(iSel);\n\t}\n\n#ifdef __ATLCTRLS_H__\n\t// ListViewCtrl specialization\n\ttemplate <>\n\tINT _getSel(WTL::CListViewCtrl& tCtrl)\n\t{\n\t\treturn tCtrl.GetSelectedIndex();\n\t}\n\n\ttemplate <>\n\tvoid _setSel(WTL::CListViewCtrl& tCtrl, INT iSel)\n\t{\n\t\tif(iSel < 0)\n\t\t\ttCtrl.SelectItem(-1);\n\t\telse\n\t\t\ttCtrl.SelectItem(iSel);\n\t}\n#endif // __ATLCTRLS_H__\n\n\ttemplate <class TCtrl>\n\tvoid DDX_Index(UINT nID, INT& nVal, BOOL bSave)\n\t{\n\t\tT* pT = static_cast<T*>(this);\n\t\tTCtrl ctrl(pT->GetDlgItem(nID));\n\n\t\tif(bSave)\n\t\t\tnVal = _getSel(ctrl);\n\t\telse\n\t\t\t_setSel(ctrl, nVal);\n\t}\n\n// Overrideables\n\tvoid OnDataExchangeError(UINT nCtrlID, BOOL /*bSave*/)\n\t{\n\t\t// Override to display an error message\n\t\t::MessageBeep((UINT)-1);\n\t\tT* pT = static_cast<T*>(this);\n\t\t::SetFocus(pT->GetDlgItem(nCtrlID));\n\t}\n\n\tvoid OnDataValidateError(UINT nCtrlID, BOOL /*bSave*/, _XData& /*data*/)\n\t{\n\t\t// Override to display an error message\n\t\t::MessageBeep((UINT)-1);\n\t\tT* pT = static_cast<T*>(this);\n\t\t::SetFocus(pT->GetDlgItem(nCtrlID));\n\t}\n};\n\n} // namespace WTL\n\n#endif // __ATLDDX_H__\n"
  },
  {
    "path": "Examples/WhisperDesktop/Utils/WTL/atlgdi.h",
    "content": "// Windows Template Library - WTL version 10.0\n// Copyright (C) Microsoft Corporation, WTL Team. All rights reserved.\n//\n// This file is a part of the Windows Template Library.\n// The use and distribution terms for this software are covered by the\n// Microsoft Public License (http://opensource.org/licenses/MS-PL)\n// which can be found in the file MS-PL.txt at the root folder.\n\n#ifndef __ATLGDI_H__\n#define __ATLGDI_H__\n\n#pragma once\n\n#ifndef __ATLAPP_H__\n\t#error atlgdi.h requires atlapp.h to be included first\n#endif\n\n\n// protect template members from windowsx.h macros\n#ifdef _INC_WINDOWSX\n  #undef CopyRgn\n  #undef CreateBrush\n  #undef CreatePen\n  #undef SelectBrush\n  #undef SelectPen\n  #undef SelectFont\n  #undef SelectBitmap\n#endif // _INC_WINDOWSX\n\n// required libraries\n#pragma comment(lib, \"msimg32.lib\")\n#if !defined(_ATL_NO_OPENGL)\n  #pragma comment(lib, \"opengl32.lib\")\n#endif\n\n\n///////////////////////////////////////////////////////////////////////////////\n// Classes in this file:\n//\n// CPenT<t_bManaged>\n// CBrushT<t_bManaged>\n// CLogFont\n// CFontT<t_bManaged>\n// CBitmapT<t_bManaged>\n// CPaletteT<t_bManaged>\n// CRgnT<t_bManaged>\n// CDCT<t_bManaged>\n// CPaintDC\n// CClientDC\n// CWindowDC\n// CMemoryDC\n// CEnhMetaFileInfo\n// CEnhMetaFileT<t_bManaged>\n// CEnhMetaFileDC\n\n\nnamespace WTL\n{\n\n///////////////////////////////////////////////////////////////////////////////\n// Bitmap resource helpers to extract bitmap information for a bitmap resource\n\ninline LPBITMAPINFOHEADER AtlGetBitmapResourceInfo(HMODULE hModule, ATL::_U_STRINGorID image)\n{\n\tHRSRC hResource = ::FindResource(hModule, image.m_lpstr, RT_BITMAP);\n\tATLASSERT(hResource != NULL);\n\tHGLOBAL hGlobal = ::LoadResource(hModule, hResource);\n\tATLASSERT(hGlobal != NULL);\n\tLPBITMAPINFOHEADER pBitmapInfoHeader = (LPBITMAPINFOHEADER)::LockResource(hGlobal);\n\tATLASSERT(pBitmapInfoHeader != NULL);\n\treturn pBitmapInfoHeader;\n}\n\ninline WORD AtlGetBitmapResourceBitsPerPixel(HMODULE hModule, ATL::_U_STRINGorID image)\n{\n\tLPBITMAPINFOHEADER pBitmapInfoHeader = AtlGetBitmapResourceInfo(hModule, image);\n\tATLASSERT(pBitmapInfoHeader != NULL);\n\treturn pBitmapInfoHeader->biBitCount;\n}\n\ninline WORD AtlGetBitmapResourceBitsPerPixel(ATL::_U_STRINGorID image)\n{\n\treturn AtlGetBitmapResourceBitsPerPixel(ModuleHelper::GetResourceInstance(), image);\n}\n\n///////////////////////////////////////////////////////////////////////////////\n// 32-bit (alpha channel) bitmap resource helper\n\n// Note: 32-bit (alpha channel) images work only on Windows XP with Common Controls version 6.\n// If you want your app to work on older version of Windows, load non-alpha images if Common\n// Controls version is less than 6.\n\ninline bool AtlIsAlphaBitmapResource(ATL::_U_STRINGorID image)\n{\n\treturn (AtlGetBitmapResourceBitsPerPixel(image) == 32);\n}\n\n\n///////////////////////////////////////////////////////////////////////////////\n// CPen\n\ntemplate <bool t_bManaged>\nclass CPenT\n{\npublic:\n// Data members\n\tHPEN m_hPen;\n\n// Constructor/destructor/operators\n\tCPenT(HPEN hPen = NULL) : m_hPen(hPen)\n\t{ }\n\n\t~CPenT()\n\t{\n\t\tif(t_bManaged && (m_hPen != NULL))\n\t\t\tDeleteObject();\n\t}\n\n\tCPenT<t_bManaged>& operator =(HPEN hPen)\n\t{\n\t\tAttach(hPen);\n\t\treturn *this;\n\t}\n\n\tvoid Attach(HPEN hPen)\n\t{\n\t\tif(t_bManaged && (m_hPen != NULL) && (m_hPen != hPen))\n\t\t\t::DeleteObject(m_hPen);\n\t\tm_hPen = hPen;\n\t}\n\n\tHPEN Detach()\n\t{\n\t\tHPEN hPen = m_hPen;\n\t\tm_hPen = NULL;\n\t\treturn hPen;\n\t}\n\n\toperator HPEN() const { return m_hPen; }\n\n\tbool IsNull() const { return (m_hPen == NULL); }\n\n// Create methods\n\tHPEN CreatePen(int nPenStyle, int nWidth, COLORREF crColor)\n\t{\n\t\tATLASSERT(m_hPen == NULL);\n\t\tm_hPen = ::CreatePen(nPenStyle, nWidth, crColor);\n\t\treturn m_hPen;\n\t}\n\n\tHPEN CreatePen(int nPenStyle, int nWidth, const LOGBRUSH* pLogBrush, int nStyleCount = 0, const DWORD* lpStyle = NULL)\n\t{\n\t\tATLASSERT(m_hPen == NULL);\n\t\tm_hPen = ::ExtCreatePen(nPenStyle, nWidth, pLogBrush, nStyleCount, lpStyle);\n\t\treturn m_hPen;\n\t}\n\n\tHPEN CreatePenIndirect(LPLOGPEN lpLogPen)\n\t{\n\t\tATLASSERT(m_hPen == NULL);\n\t\tm_hPen = ::CreatePenIndirect(lpLogPen);\n\t\treturn m_hPen;\n\t}\n\n\tBOOL DeleteObject()\n\t{\n\t\tATLASSERT(m_hPen != NULL);\n\t\tBOOL bRet = ::DeleteObject(m_hPen);\n\t\tif(bRet)\n\t\t\tm_hPen = NULL;\n\t\treturn bRet;\n\t}\n\n// Attributes\n\tint GetLogPen(LOGPEN* pLogPen) const\n\t{\n\t\tATLASSERT(m_hPen != NULL);\n\t\treturn ::GetObject(m_hPen, sizeof(LOGPEN), pLogPen);\n\t}\n\n\tbool GetLogPen(LOGPEN& LogPen) const\n\t{\n\t\tATLASSERT(m_hPen != NULL);\n\t\treturn (::GetObject(m_hPen, sizeof(LOGPEN), &LogPen) == sizeof(LOGPEN));\n\t}\n\n\tint GetExtLogPen(EXTLOGPEN* pLogPen, int nSize = sizeof(EXTLOGPEN)) const\n\t{\n\t\tATLASSERT(m_hPen != NULL);\n\t\treturn ::GetObject(m_hPen, nSize, pLogPen);\n\t}\n\n\tbool GetExtLogPen(EXTLOGPEN& ExtLogPen, int nSize = sizeof(EXTLOGPEN)) const\n\t{\n\t\tATLASSERT(m_hPen != NULL);\n\t\tint nRet = ::GetObject(m_hPen, nSize, &ExtLogPen);\n\t\treturn ((nRet > 0) && (nRet <= nSize));\n\t}\n};\n\ntypedef CPenT<false>   CPenHandle;\ntypedef CPenT<true>    CPen;\n\n\n///////////////////////////////////////////////////////////////////////////////\n// CBrush\n\ntemplate <bool t_bManaged>\nclass CBrushT\n{\npublic:\n// Data members\n\tHBRUSH m_hBrush;\n\n// Constructor/destructor/operators\n\tCBrushT(HBRUSH hBrush = NULL) : m_hBrush(hBrush)\n\t{ }\n\n\t~CBrushT()\n\t{\n\t\tif(t_bManaged && (m_hBrush != NULL))\n\t\t\tDeleteObject();\n\t}\n\n\tCBrushT<t_bManaged>& operator =(HBRUSH hBrush)\n\t{\n\t\tAttach(hBrush);\n\t\treturn *this;\n\t}\n\n\tvoid Attach(HBRUSH hBrush)\n\t{\n\t\tif(t_bManaged && (m_hBrush != NULL) && (m_hBrush != hBrush))\n\t\t\t::DeleteObject(m_hBrush);\n\t\tm_hBrush = hBrush;\n\t}\n\n\tHBRUSH Detach()\n\t{\n\t\tHBRUSH hBrush = m_hBrush;\n\t\tm_hBrush = NULL;\n\t\treturn hBrush;\n\t}\n\n\toperator HBRUSH() const { return m_hBrush; }\n\n\tbool IsNull() const { return (m_hBrush == NULL); }\n\n// Create methods\n\tHBRUSH CreateSolidBrush(COLORREF crColor)\n\t{\n\t\tATLASSERT(m_hBrush == NULL);\n\t\tm_hBrush = ::CreateSolidBrush(crColor);\n\t\treturn m_hBrush;\n\t}\n\n\tHBRUSH CreateHatchBrush(int nIndex, COLORREF crColor)\n\t{\n\t\tATLASSERT(m_hBrush == NULL);\n\t\tm_hBrush = ::CreateHatchBrush(nIndex, crColor);\n\t\treturn m_hBrush;\n\t}\n\n\tHBRUSH CreateBrushIndirect(const LOGBRUSH* lpLogBrush)\n\t{\n\t\tATLASSERT(m_hBrush == NULL);\n\t\tm_hBrush = ::CreateBrushIndirect(lpLogBrush);\n\t\treturn m_hBrush;\n\t}\n\n\tHBRUSH CreatePatternBrush(HBITMAP hBitmap)\n\t{\n\t\tATLASSERT(m_hBrush == NULL);\n\t\tm_hBrush = ::CreatePatternBrush(hBitmap);\n\t\treturn m_hBrush;\n\t}\n\n\tHBRUSH CreateDIBPatternBrush(HGLOBAL hPackedDIB, UINT nUsage)\n\t{\n\t\tATLASSERT(hPackedDIB != NULL);\n\t\tconst void* lpPackedDIB = GlobalLock(hPackedDIB);\n\t\tATLASSERT(lpPackedDIB != NULL);\n\t\tm_hBrush = ::CreateDIBPatternBrushPt(lpPackedDIB, nUsage);\n\t\tGlobalUnlock(hPackedDIB);\n\t\treturn m_hBrush;\n\t}\n\n\tHBRUSH CreateDIBPatternBrush(const void* lpPackedDIB, UINT nUsage)\n\t{\n\t\tATLASSERT(m_hBrush == NULL);\n\t\tm_hBrush = ::CreateDIBPatternBrushPt(lpPackedDIB, nUsage);\n\t\treturn m_hBrush;\n\t}\n\n\tHBRUSH CreateSysColorBrush(int nIndex)\n\t{\n\t\tATLASSERT(m_hBrush == NULL);\n\t\tm_hBrush = ::GetSysColorBrush(nIndex);\n\t\treturn m_hBrush;\n\t}\n\n\tBOOL DeleteObject()\n\t{\n\t\tATLASSERT(m_hBrush != NULL);\n\t\tBOOL bRet = ::DeleteObject(m_hBrush);\n\t\tif(bRet)\n\t\t\tm_hBrush = NULL;\n\t\treturn bRet;\n\t}\n\n// Attributes\n\tint GetLogBrush(LOGBRUSH* pLogBrush) const\n\t{\n\t\tATLASSERT(m_hBrush != NULL);\n\t\treturn ::GetObject(m_hBrush, sizeof(LOGBRUSH), pLogBrush);\n\t}\n\n\tbool GetLogBrush(LOGBRUSH& LogBrush) const\n\t{\n\t\tATLASSERT(m_hBrush != NULL);\n\t\treturn (::GetObject(m_hBrush, sizeof(LOGBRUSH), &LogBrush) == sizeof(LOGBRUSH));\n\t}\n};\n\ntypedef CBrushT<false>   CBrushHandle;\ntypedef CBrushT<true>    CBrush;\n\n\n///////////////////////////////////////////////////////////////////////////////\n// CFont\n\nclass CLogFont : public LOGFONT\n{\npublic:\n\tCLogFont()\n\t{\n\t\tmemset(this, 0, sizeof(LOGFONT));\n\t}\n\n\tCLogFont(const LOGFONT& lf)\n\t{\n\t\tCopy(&lf);\n\t}\n\n\tCLogFont(HFONT hFont)\n\t{\n\t\tATLASSERT(::GetObjectType(hFont) == OBJ_FONT);\n\t\t::GetObject(hFont, sizeof(LOGFONT), (LOGFONT*)this);\n\t}\n\n\tHFONT CreateFontIndirect()\n\t{\n\t\treturn ::CreateFontIndirect(this);\n\t}\n\n\tvoid SetBold()\n\t{\n\t\tlfWeight = FW_BOLD;\n\t}\n\n\tbool IsBold() const\n\t{\n\t\treturn (lfWeight >= FW_BOLD);\n\t}\n\n\tvoid MakeBolder(int iScale = 1)\n\t{\n\t\tlfWeight += FW_BOLD * iScale;\n\t}\n\n\tvoid MakeLarger(int iScale)\n\t{\n\t\tif(lfHeight > 0)\n\t\t\tlfHeight += iScale;\n\t\telse\n\t\t\tlfHeight -= iScale;\n\t}\n\n\tvoid SetHeight(LONG nPointSize, HDC hDC = NULL)\n\t{\n\t\tHDC hDC1 = (hDC != NULL) ? hDC : ::GetDC(NULL);\n\t\t// For MM_TEXT mapping mode\n\t\tlfHeight = -::MulDiv(nPointSize, ::GetDeviceCaps(hDC1, LOGPIXELSY), 72);\n\t\tif(hDC == NULL)\n\t\t\t::ReleaseDC(NULL, hDC1);\n\t}\n\n\tLONG GetHeight(HDC hDC = NULL) const\n\t{\n\t\tHDC hDC1 = (hDC != NULL) ? hDC : ::GetDC(NULL);\n\t\t// For MM_TEXT mapping mode\n\t\tLONG nPointSize = ::MulDiv(-lfHeight, 72, ::GetDeviceCaps(hDC1, LOGPIXELSY));\n\t\tif(hDC == NULL)\n\t\t\t::ReleaseDC(NULL, hDC1);\n\n\t\treturn nPointSize;\n\t}\n\n\tLONG GetDeciPointHeight(HDC hDC = NULL) const\n\t{\n\t\tHDC hDC1 = (hDC != NULL) ? hDC : ::GetDC(NULL);\n\t\tPOINT ptOrg = { 0, 0 };\n\t\t::DPtoLP(hDC1, &ptOrg, 1);\n\t\tPOINT pt = { 0, 0 };\n\t\tpt.y = abs(lfHeight) + ptOrg.y;\n\t\t::LPtoDP(hDC1, &pt, 1);\n\t\tLONG nDeciPoint = ::MulDiv(pt.y, 720, ::GetDeviceCaps(hDC1, LOGPIXELSY));   // 72 points/inch, 10 decipoints/point\n\t\tif(hDC == NULL)\n\t\t\t::ReleaseDC(NULL, hDC1);\n\n\t\treturn nDeciPoint;\n\t}\n\n\tvoid SetHeightFromDeciPoint(LONG nDeciPtHeight, HDC hDC = NULL)\n\t{\n\t\tHDC hDC1 = (hDC != NULL) ? hDC : ::GetDC(NULL);\n\t\tPOINT pt = { 0, 0 };\n\t\tpt.y = ::MulDiv(::GetDeviceCaps(hDC1, LOGPIXELSY), nDeciPtHeight, 720);   // 72 points/inch, 10 decipoints/point\n\t\t::DPtoLP(hDC1, &pt, 1);\n\t\tPOINT ptOrg = { 0, 0 };\n\t\t::DPtoLP(hDC1, &ptOrg, 1);\n\t\tlfHeight = -abs(pt.y - ptOrg.y);\n\t\tif(hDC == NULL)\n\t\t\t::ReleaseDC(NULL, hDC1);\n\t}\n\n\tvoid SetCaptionFont()\n\t{\n\t\tNONCLIENTMETRICS ncm = { RunTimeHelper::SizeOf_NONCLIENTMETRICS() };\n\t\tATLVERIFY(::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0));\n\t\tCopy(&ncm.lfCaptionFont);\n\t}\n\n\tvoid SetMenuFont()\n\t{\n\t\tNONCLIENTMETRICS ncm = { RunTimeHelper::SizeOf_NONCLIENTMETRICS() };\n\t\tATLVERIFY(::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0));\n\t\tCopy(&ncm.lfMenuFont);\n\t}\n\n\tvoid SetStatusFont()\n\t{\n\t\tNONCLIENTMETRICS ncm = { RunTimeHelper::SizeOf_NONCLIENTMETRICS() };\n\t\tATLVERIFY(::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0));\n\t\tCopy(&ncm.lfStatusFont);\n\t}\n\n\tvoid SetMessageBoxFont()\n\t{\n\t\tNONCLIENTMETRICS ncm = { RunTimeHelper::SizeOf_NONCLIENTMETRICS() };\n\t\tATLVERIFY(::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0));\n\t\tCopy(&ncm.lfMessageFont);\n\t}\n\n\tvoid Copy(const LOGFONT* pLogFont)\n\t{\n\t\tATLASSERT(pLogFont != NULL);\n\t\t*(LOGFONT*)this = *pLogFont;\n\t}\n\n\tCLogFont& operator =(const CLogFont& src)\n\t{\n\t\tCopy(&src);\n\t\treturn *this;\n\t}\n\n\tCLogFont& operator =(const LOGFONT& src)\n\t{\n\t\tCopy(&src);\n\t\treturn *this;\n\t}\n\n\tCLogFont& operator =(HFONT hFont)\n\t{\n\t\tATLASSERT(::GetObjectType(hFont) == OBJ_FONT);\n\t\t::GetObject(hFont, sizeof(LOGFONT), (LOGFONT*)this);\n\t\treturn *this;\n\t}\n\n\tbool operator ==(const LOGFONT& logfont) const\n\t{\n\t\treturn((logfont.lfHeight == lfHeight) &&\n\t\t       (logfont.lfWidth == lfWidth) &&\n\t\t       (logfont.lfEscapement == lfEscapement) &&\n\t\t       (logfont.lfOrientation == lfOrientation) &&\n\t\t       (logfont.lfWeight == lfWeight) &&\n\t\t       (logfont.lfItalic == lfItalic) &&\n\t\t       (logfont.lfUnderline == lfUnderline) &&\n\t\t       (logfont.lfStrikeOut == lfStrikeOut) &&\n\t\t       (logfont.lfCharSet == lfCharSet) &&\n\t\t       (logfont.lfOutPrecision == lfOutPrecision) &&\n\t\t       (logfont.lfClipPrecision == lfClipPrecision) &&\n\t\t       (logfont.lfQuality == lfQuality) &&\n\t\t       (logfont.lfPitchAndFamily == lfPitchAndFamily) &&\n\t\t       (lstrcmp(logfont.lfFaceName, lfFaceName) == 0));\n\t}\n};\n\n\ntemplate <bool t_bManaged>\nclass CFontT\n{\npublic:\n// Data members\n\tHFONT m_hFont;\n\n// Constructor/destructor/operators\n\tCFontT(HFONT hFont = NULL) : m_hFont(hFont)\n\t{ }\n\n\t~CFontT()\n\t{\n\t\tif(t_bManaged && (m_hFont != NULL))\n\t\t\tDeleteObject();\n\t}\n\n\tCFontT<t_bManaged>& operator =(HFONT hFont)\n\t{\n\t\tAttach(hFont);\n\t\treturn *this;\n\t}\n\n\tvoid Attach(HFONT hFont)\n\t{\n\t\tif(t_bManaged && (m_hFont != NULL) && (m_hFont != hFont))\n\t\t\t::DeleteObject(m_hFont);\n\t\tm_hFont = hFont;\n\t}\n\n\tHFONT Detach()\n\t{\n\t\tHFONT hFont = m_hFont;\n\t\tm_hFont = NULL;\n\t\treturn hFont;\n\t}\n\n\toperator HFONT() const { return m_hFont; }\n\n\tbool IsNull() const { return (m_hFont == NULL); }\n\n// Create methods\n\tHFONT CreateFontIndirect(const LOGFONT* lpLogFont)\n\t{\n\t\tATLASSERT(m_hFont == NULL);\n\t\tm_hFont = ::CreateFontIndirect(lpLogFont);\n\t\treturn m_hFont;\n\t}\n\n\tHFONT CreateFontIndirectEx(CONST ENUMLOGFONTEXDV* penumlfex)\n\t{\n\t\tATLASSERT(m_hFont == NULL);\n\t\tm_hFont = ::CreateFontIndirectEx(penumlfex);\n\t\treturn m_hFont;\n\t}\n\n\tHFONT CreateFont(int nHeight, int nWidth, int nEscapement,\n\t\t\tint nOrientation, int nWeight, BYTE bItalic, BYTE bUnderline,\n\t\t\tBYTE cStrikeOut, BYTE nCharSet, BYTE nOutPrecision,\n\t\t\tBYTE nClipPrecision, BYTE nQuality, BYTE nPitchAndFamily,\n\t\t\tLPCTSTR lpszFacename)\n\t{\n\t\tATLASSERT(m_hFont == NULL);\n\t\tm_hFont = ::CreateFont(nHeight, nWidth, nEscapement,\n\t\t\tnOrientation, nWeight, bItalic, bUnderline, cStrikeOut,\n\t\t\tnCharSet, nOutPrecision, nClipPrecision, nQuality,\n\t\t\tnPitchAndFamily, lpszFacename);\n\t\treturn m_hFont;\n\t}\n\n\tHFONT CreatePointFont(int nPointSize, LPCTSTR lpszFaceName, HDC hDC = NULL, bool bBold = false, bool bItalic = false)\n\t{\n\t\tLOGFONT logFont = {};\n\t\tlogFont.lfCharSet = DEFAULT_CHARSET;\n\t\tlogFont.lfHeight = nPointSize;\n\t\tATL::Checked::tcsncpy_s(logFont.lfFaceName, _countof(logFont.lfFaceName), lpszFaceName, _TRUNCATE);\n\n\t\tif(bBold)\n\t\t\tlogFont.lfWeight = FW_BOLD;\n\t\tif(bItalic)\n\t\t\tlogFont.lfItalic = (BYTE)TRUE;\n\n\t\treturn CreatePointFontIndirect(&logFont, hDC);\n\t}\n\n\tHFONT CreatePointFontIndirect(const LOGFONT* lpLogFont, HDC hDC = NULL)\n\t{\n\t\tHDC hDC1 = (hDC != NULL) ? hDC : ::GetDC(NULL);\n\n\t\t// convert nPointSize to logical units based on hDC\n\t\tLOGFONT logFont = *lpLogFont;\n\t\tPOINT pt = { 0, 0 };\n\t\tpt.y = ::MulDiv(::GetDeviceCaps(hDC1, LOGPIXELSY), logFont.lfHeight, 720);   // 72 points/inch, 10 decipoints/point\n\t\t::DPtoLP(hDC1, &pt, 1);\n\t\tPOINT ptOrg = { 0, 0 };\n\t\t::DPtoLP(hDC1, &ptOrg, 1);\n\t\tlogFont.lfHeight = -abs(pt.y - ptOrg.y);\n\n\t\tif(hDC == NULL)\n\t\t\t::ReleaseDC(NULL, hDC1);\n\n\t\treturn CreateFontIndirect(&logFont);\n\t}\n\n\tBOOL DeleteObject()\n\t{\n\t\tATLASSERT(m_hFont != NULL);\n\t\tBOOL bRet = ::DeleteObject(m_hFont);\n\t\tif(bRet)\n\t\t\tm_hFont = NULL;\n\t\treturn bRet;\n\t}\n\n// Attributes\n\tint GetLogFont(LOGFONT* pLogFont) const\n\t{\n\t\tATLASSERT(m_hFont != NULL);\n\t\treturn ::GetObject(m_hFont, sizeof(LOGFONT), pLogFont);\n\t}\n\n\tbool GetLogFont(LOGFONT& LogFont) const\n\t{\n\t\tATLASSERT(m_hFont != NULL);\n\t\treturn (::GetObject(m_hFont, sizeof(LOGFONT), &LogFont) == sizeof(LOGFONT));\n\t}\n};\n\ntypedef CFontT<false>   CFontHandle;\ntypedef CFontT<true>    CFont;\n\n\n///////////////////////////////////////////////////////////////////////////////\n// CBitmap\n\ntemplate <bool t_bManaged>\nclass CBitmapT\n{\npublic:\n// Data members\n\tHBITMAP m_hBitmap;\n\n// Constructor/destructor/operators\n\tCBitmapT(HBITMAP hBitmap = NULL) : m_hBitmap(hBitmap)\n\t{ }\n\n\t~CBitmapT()\n\t{\n\t\tif(t_bManaged && (m_hBitmap != NULL))\n\t\t\tDeleteObject();\n\t}\n\n\tCBitmapT<t_bManaged>& operator =(HBITMAP hBitmap)\n\t{\n\t\tAttach(hBitmap);\n\t\treturn *this;\n\t}\n\n\tvoid Attach(HBITMAP hBitmap)\n\t{\n\t\tif(t_bManaged && (m_hBitmap != NULL) && (m_hBitmap != hBitmap))\n\t\t\t::DeleteObject(m_hBitmap);\n\t\tm_hBitmap = hBitmap;\n\t}\n\n\tHBITMAP Detach()\n\t{\n\t\tHBITMAP hBitmap = m_hBitmap;\n\t\tm_hBitmap = NULL;\n\t\treturn hBitmap;\n\t}\n\n\toperator HBITMAP() const { return m_hBitmap; }\n\n\tbool IsNull() const { return (m_hBitmap == NULL); }\n\n// Create and load methods\n\tHBITMAP LoadBitmap(ATL::_U_STRINGorID bitmap)\n\t{\n\t\tATLASSERT(m_hBitmap == NULL);\n\t\tm_hBitmap = ::LoadBitmap(ModuleHelper::GetResourceInstance(), bitmap.m_lpstr);\n\t\treturn m_hBitmap;\n\t}\n\n\tHBITMAP LoadOEMBitmap(UINT nIDBitmap) // for OBM_/OCR_/OIC_\n\t{\n\t\tATLASSERT(m_hBitmap == NULL);\n\t\tm_hBitmap = ::LoadBitmap(NULL, MAKEINTRESOURCE(nIDBitmap));\n\t\treturn m_hBitmap;\n\t}\n\n\tHBITMAP LoadMappedBitmap(UINT nIDBitmap, UINT nFlags = 0, LPCOLORMAP lpColorMap = NULL, int nMapSize = 0)\n\t{\n\t\tATLASSERT(m_hBitmap == NULL);\n\t\tm_hBitmap = ::CreateMappedBitmap(ModuleHelper::GetResourceInstance(), nIDBitmap, (WORD)nFlags, lpColorMap, nMapSize);\n\t\treturn m_hBitmap;\n\t}\n\n\tHBITMAP CreateBitmap(int nWidth, int nHeight, UINT nPlanes, UINT nBitsPerPixel, const void* lpBits)\n\t{\n\t\tATLASSERT(m_hBitmap == NULL);\n\t\tm_hBitmap = ::CreateBitmap(nWidth, nHeight, nPlanes, nBitsPerPixel, lpBits);\n\t\treturn m_hBitmap;\n\t}\n\n\tHBITMAP CreateBitmapIndirect(LPBITMAP lpBitmap)\n\t{\n\t\tATLASSERT(m_hBitmap == NULL);\n\t\tm_hBitmap = ::CreateBitmapIndirect(lpBitmap);\n\t\treturn m_hBitmap;\n\t}\n\n\tHBITMAP CreateCompatibleBitmap(HDC hDC, int nWidth, int nHeight)\n\t{\n\t\tATLASSERT(m_hBitmap == NULL);\n\t\tm_hBitmap = ::CreateCompatibleBitmap(hDC, nWidth, nHeight);\n\t\treturn m_hBitmap;\n\t}\n\n\tHBITMAP CreateDiscardableBitmap(HDC hDC, int nWidth, int nHeight)\n\t{\n\t\tATLASSERT(m_hBitmap == NULL);\n\t\tm_hBitmap = ::CreateDiscardableBitmap(hDC, nWidth, nHeight);\n\t\treturn m_hBitmap;\n\t}\n\n\tBOOL DeleteObject()\n\t{\n\t\tATLASSERT(m_hBitmap != NULL);\n\t\tBOOL bRet = ::DeleteObject(m_hBitmap);\n\t\tif(bRet)\n\t\t\tm_hBitmap = NULL;\n\t\treturn bRet;\n\t}\n\n// Attributes\n\tint GetBitmap(BITMAP* pBitMap) const\n\t{\n\t\tATLASSERT(m_hBitmap != NULL);\n\t\treturn ::GetObject(m_hBitmap, sizeof(BITMAP), pBitMap);\n\t}\n\n\tbool GetBitmap(BITMAP& bm) const\n\t{\n\t\tATLASSERT(m_hBitmap != NULL);\n\t\treturn (::GetObject(m_hBitmap, sizeof(BITMAP), &bm) == sizeof(BITMAP));\n\t}\n\n\tbool GetSize(SIZE& size) const\n\t{\n\t\tATLASSERT(m_hBitmap != NULL);\n\t\tBITMAP bm = {};\n\t\tif(!GetBitmap(&bm))\n\t\t\treturn false;\n\t\tsize.cx = bm.bmWidth;\n\t\tsize.cy = bm.bmHeight;\n\t\treturn true;\n\t}\n\n\tDWORD GetBitmapBits(DWORD dwCount, LPVOID lpBits) const\n\t{\n\t\tATLASSERT(m_hBitmap != NULL);\n\t\treturn ::GetBitmapBits(m_hBitmap, dwCount, lpBits);\n\t}\n\n\tDWORD SetBitmapBits(DWORD dwCount, const void* lpBits)\n\t{\n\t\tATLASSERT(m_hBitmap != NULL);\n\t\treturn ::SetBitmapBits(m_hBitmap, dwCount, lpBits);\n\t}\n\n\tBOOL GetBitmapDimension(LPSIZE lpSize) const\n\t{\n\t\tATLASSERT(m_hBitmap != NULL);\n\t\treturn ::GetBitmapDimensionEx(m_hBitmap, lpSize);\n\t}\n\n\tBOOL SetBitmapDimension(int nWidth, int nHeight, LPSIZE lpSize = NULL)\n\t{\n\t\tATLASSERT(m_hBitmap != NULL);\n\t\treturn ::SetBitmapDimensionEx(m_hBitmap, nWidth, nHeight, lpSize);\n\t}\n\n// DIB support\n\tHBITMAP CreateDIBitmap(HDC hDC, CONST BITMAPINFOHEADER* lpbmih, DWORD dwInit, CONST VOID* lpbInit, CONST BITMAPINFO* lpbmi, UINT uColorUse)\n\t{\n\t\tATLASSERT(m_hBitmap == NULL);\n\t\tm_hBitmap = ::CreateDIBitmap(hDC, lpbmih, dwInit, lpbInit, lpbmi, uColorUse);\n\t\treturn m_hBitmap;\n\t}\n\n\tHBITMAP CreateDIBSection(HDC hDC, CONST BITMAPINFO* lpbmi, UINT uColorUse, VOID** ppvBits, HANDLE hSection, DWORD dwOffset)\n\t{\n\t\tATLASSERT(m_hBitmap == NULL);\n\t\tm_hBitmap = ::CreateDIBSection(hDC, lpbmi, uColorUse, ppvBits, hSection, dwOffset);\n\t\treturn m_hBitmap;\n\t}\n\n\tint GetDIBits(HDC hDC, UINT uStartScan, UINT cScanLines,  LPVOID lpvBits, LPBITMAPINFO lpbmi, UINT uColorUse) const\n\t{\n\t\tATLASSERT(m_hBitmap != NULL);\n\t\treturn ::GetDIBits(hDC, m_hBitmap, uStartScan, cScanLines,  lpvBits, lpbmi, uColorUse);\n\t}\n\n\tint SetDIBits(HDC hDC, UINT uStartScan, UINT cScanLines, CONST VOID* lpvBits, CONST BITMAPINFO* lpbmi, UINT uColorUse)\n\t{\n\t\tATLASSERT(m_hBitmap != NULL);\n\t\treturn ::SetDIBits(hDC, m_hBitmap, uStartScan, cScanLines, lpvBits, lpbmi, uColorUse);\n\t}\n};\n\ntypedef CBitmapT<false>   CBitmapHandle;\ntypedef CBitmapT<true>    CBitmap;\n\n\n///////////////////////////////////////////////////////////////////////////////\n// CPalette\n\ntemplate <bool t_bManaged>\nclass CPaletteT\n{\npublic:\n// Data members\n\tHPALETTE m_hPalette;\n\n// Constructor/destructor/operators\n\tCPaletteT(HPALETTE hPalette = NULL) : m_hPalette(hPalette)\n\t{ }\n\n\t~CPaletteT()\n\t{\n\t\tif(t_bManaged && (m_hPalette != NULL))\n\t\t\tDeleteObject();\n\t}\n\n\tCPaletteT<t_bManaged>& operator =(HPALETTE hPalette)\n\t{\n\t\tAttach(hPalette);\n\t\treturn *this;\n\t}\n\n\tvoid Attach(HPALETTE hPalette)\n\t{\n\t\tif(t_bManaged && (m_hPalette != NULL) && (m_hPalette != hPalette))\n\t\t\t::DeleteObject(m_hPalette);\n\t\tm_hPalette = hPalette;\n\t}\n\n\tHPALETTE Detach()\n\t{\n\t\tHPALETTE hPalette = m_hPalette;\n\t\tm_hPalette = NULL;\n\t\treturn hPalette;\n\t}\n\n\toperator HPALETTE() const { return m_hPalette; }\n\n\tbool IsNull() const { return (m_hPalette == NULL); }\n\n// Create methods\n\tHPALETTE CreatePalette(LPLOGPALETTE lpLogPalette)\n\t{\n\t\tATLASSERT(m_hPalette == NULL);\n\t\tm_hPalette = ::CreatePalette(lpLogPalette);\n\t\treturn m_hPalette;\n\t}\n\n\tHPALETTE CreateHalftonePalette(HDC hDC)\n\t{\n\t\tATLASSERT(m_hPalette == NULL);\n\t\tATLASSERT(hDC != NULL);\n\t\tm_hPalette = ::CreateHalftonePalette(hDC);\n\t\treturn m_hPalette;\n\t}\n\n\tBOOL DeleteObject()\n\t{\n\t\tATLASSERT(m_hPalette != NULL);\n\t\tBOOL bRet = ::DeleteObject(m_hPalette);\n\t\tif(bRet)\n\t\t\tm_hPalette = NULL;\n\t\treturn bRet;\n\t}\n\n// Attributes\n\tint GetEntryCount() const\n\t{\n\t\tATLASSERT(m_hPalette != NULL);\n\t\tWORD nEntries = 0;\n\t\t::GetObject(m_hPalette, sizeof(WORD), &nEntries);\n\t\treturn (int)nEntries;\n\t}\n\n\tUINT GetPaletteEntries(UINT nStartIndex, UINT nNumEntries, LPPALETTEENTRY lpPaletteColors) const\n\t{\n\t\tATLASSERT(m_hPalette != NULL);\n\t\treturn ::GetPaletteEntries(m_hPalette, nStartIndex, nNumEntries, lpPaletteColors);\n\t}\n\n\tUINT SetPaletteEntries(UINT nStartIndex, UINT nNumEntries, LPPALETTEENTRY lpPaletteColors)\n\t{\n\t\tATLASSERT(m_hPalette != NULL);\n\t\treturn ::SetPaletteEntries(m_hPalette, nStartIndex, nNumEntries, lpPaletteColors);\n\t}\n\n// Operations\n\tvoid AnimatePalette(UINT nStartIndex, UINT nNumEntries, LPPALETTEENTRY lpPaletteColors)\n\t{\n\t\tATLASSERT(m_hPalette != NULL);\n\t\t::AnimatePalette(m_hPalette, nStartIndex, nNumEntries, lpPaletteColors);\n\t}\n\n\tBOOL ResizePalette(UINT nNumEntries)\n\t{\n\t\tATLASSERT(m_hPalette != NULL);\n\t\treturn ::ResizePalette(m_hPalette, nNumEntries);\n\t}\n\n\tUINT GetNearestPaletteIndex(COLORREF crColor) const\n\t{\n\t\tATLASSERT(m_hPalette != NULL);\n\t\treturn ::GetNearestPaletteIndex(m_hPalette, crColor);\n\t}\n};\n\ntypedef CPaletteT<false>   CPaletteHandle;\ntypedef CPaletteT<true>    CPalette;\n\n\n///////////////////////////////////////////////////////////////////////////////\n// CRgn\n\ntemplate <bool t_bManaged>\nclass CRgnT\n{\npublic:\n// Data members\n\tHRGN m_hRgn;\n\n// Constructor/destructor/operators\n\tCRgnT(HRGN hRgn = NULL) : m_hRgn(hRgn)\n\t{ }\n\n\t~CRgnT()\n\t{\n\t\tif(t_bManaged && (m_hRgn != NULL))\n\t\t\tDeleteObject();\n\t}\n\n\tCRgnT<t_bManaged>& operator =(HRGN hRgn)\n\t{\n\t\tAttach(hRgn);\n\t\treturn *this;\n\t}\n\n\tvoid Attach(HRGN hRgn)\n\t{\n\t\tif(t_bManaged && (m_hRgn != NULL) && (m_hRgn != hRgn))\n\t\t\t::DeleteObject(m_hRgn);\n\t\tm_hRgn = hRgn;\n\t}\n\n\tHRGN Detach()\n\t{\n\t\tHRGN hRgn = m_hRgn;\n\t\tm_hRgn = NULL;\n\t\treturn hRgn;\n\t}\n\n\toperator HRGN() const { return m_hRgn; }\n\n\tbool IsNull() const { return (m_hRgn == NULL); }\n\n// Create methods\n\tHRGN CreateRectRgn(int x1, int y1, int x2, int y2)\n\t{\n\t\tATLASSERT(m_hRgn == NULL);\n\t\tm_hRgn = ::CreateRectRgn(x1, y1, x2, y2);\n\t\treturn m_hRgn;\n\t}\n\n\tHRGN CreateRectRgnIndirect(LPCRECT lpRect)\n\t{\n\t\tATLASSERT(m_hRgn == NULL);\n\t\tm_hRgn = ::CreateRectRgnIndirect(lpRect);\n\t\treturn m_hRgn;\n\t}\n\n\tHRGN CreateEllipticRgn(int x1, int y1, int x2, int y2)\n\t{\n\t\tATLASSERT(m_hRgn == NULL);\n\t\tm_hRgn = ::CreateEllipticRgn(x1, y1, x2, y2);\n\t\treturn m_hRgn;\n\t}\n\n\tHRGN CreateEllipticRgnIndirect(LPCRECT lpRect)\n\t{\n\t\tATLASSERT(m_hRgn == NULL);\n\t\tm_hRgn = ::CreateEllipticRgnIndirect(lpRect);\n\t\treturn m_hRgn;\n\t}\n\n\tHRGN CreatePolygonRgn(const POINT* lpPoints, int nCount, int nMode)\n\t{\n\t\tATLASSERT(m_hRgn == NULL);\n\t\tm_hRgn = ::CreatePolygonRgn(lpPoints, nCount, nMode);\n\t\treturn m_hRgn;\n\t}\n\n\tHRGN CreatePolyPolygonRgn(const POINT* lpPoints, const INT* lpPolyCounts, int nCount, int nPolyFillMode)\n\t{\n\t\tATLASSERT(m_hRgn == NULL);\n\t\tm_hRgn = ::CreatePolyPolygonRgn(lpPoints, lpPolyCounts, nCount, nPolyFillMode);\n\t\treturn m_hRgn;\n\t}\n\n\tHRGN CreateRoundRectRgn(int x1, int y1, int x2, int y2, int x3, int y3)\n\t{\n\t\tATLASSERT(m_hRgn == NULL);\n\t\tm_hRgn = ::CreateRoundRectRgn(x1, y1, x2, y2, x3, y3);\n\t\treturn m_hRgn;\n\t}\n\n\tHRGN CreateFromPath(HDC hDC)\n\t{\n\t\tATLASSERT(m_hRgn == NULL);\n\t\tATLASSERT(hDC != NULL);\n\t\tm_hRgn = ::PathToRegion(hDC);\n\t\treturn m_hRgn;\n\t}\n\n\tHRGN CreateFromData(const XFORM* lpXForm, int nCount, const RGNDATA* pRgnData)\n\t{\n\t\tATLASSERT(m_hRgn == NULL);\n\t\tm_hRgn = ::ExtCreateRegion(lpXForm, nCount, pRgnData);\n\t\treturn m_hRgn;\n\t}\n\n\tBOOL DeleteObject()\n\t{\n\t\tATLASSERT(m_hRgn != NULL);\n\t\tBOOL bRet = ::DeleteObject(m_hRgn);\n\t\tif(bRet)\n\t\t\tm_hRgn = NULL;\n\t\treturn bRet;\n\t}\n\n// Operations\n\tvoid SetRectRgn(int x1, int y1, int x2, int y2)\n\t{\n\t\tATLASSERT(m_hRgn != NULL);\n\t\t::SetRectRgn(m_hRgn, x1, y1, x2, y2);\n\t}\n\n\tvoid SetRectRgn(LPCRECT lpRect)\n\t{\n\t\tATLASSERT(m_hRgn != NULL);\n\t\t::SetRectRgn(m_hRgn, lpRect->left, lpRect->top, lpRect->right, lpRect->bottom);\n\t}\n\n\tint CombineRgn(HRGN hRgnSrc1, HRGN hRgnSrc2, int nCombineMode)\n\t{\n\t\tATLASSERT(m_hRgn != NULL);\n\t\treturn ::CombineRgn(m_hRgn, hRgnSrc1, hRgnSrc2, nCombineMode);\n\t}\n\n\tint CombineRgn(HRGN hRgnSrc, int nCombineMode)\n\t{\n\t\tATLASSERT(m_hRgn != NULL);\n\t\treturn ::CombineRgn(m_hRgn, m_hRgn, hRgnSrc, nCombineMode);\n\t}\n\n\tint CopyRgn(HRGN hRgnSrc)\n\t{\n\t\tATLASSERT(m_hRgn != NULL);\n\t\treturn ::CombineRgn(m_hRgn, hRgnSrc, NULL, RGN_COPY);\n\t}\n\n\tBOOL EqualRgn(HRGN hRgn) const\n\t{\n\t\tATLASSERT(m_hRgn != NULL);\n\t\treturn ::EqualRgn(m_hRgn, hRgn);\n\t}\n\n\tint OffsetRgn(int x, int y)\n\t{\n\t\tATLASSERT(m_hRgn != NULL);\n\t\treturn ::OffsetRgn(m_hRgn, x, y);\n\t}\n\n\tint OffsetRgn(POINT point)\n\t{\n\t\tATLASSERT(m_hRgn != NULL);\n\t\treturn ::OffsetRgn(m_hRgn, point.x, point.y);\n\t}\n\n\tint GetRgnBox(LPRECT lpRect) const\n\t{\n\t\tATLASSERT(m_hRgn != NULL);\n\t\treturn ::GetRgnBox(m_hRgn, lpRect);\n\t}\n\n\tBOOL PtInRegion(int x, int y) const\n\t{\n\t\tATLASSERT(m_hRgn != NULL);\n\t\treturn ::PtInRegion(m_hRgn, x, y);\n\t}\n\n\tBOOL PtInRegion(POINT point) const\n\t{\n\t\tATLASSERT(m_hRgn != NULL);\n\t\treturn ::PtInRegion(m_hRgn, point.x, point.y);\n\t}\n\n\tBOOL RectInRegion(LPCRECT lpRect) const\n\t{\n\t\tATLASSERT(m_hRgn != NULL);\n\t\treturn ::RectInRegion(m_hRgn, lpRect);\n\t}\n\n\tint GetRegionData(LPRGNDATA lpRgnData, int nDataSize) const\n\t{\n\t\tATLASSERT(m_hRgn != NULL);\n\t\treturn (int)::GetRegionData(m_hRgn, nDataSize, lpRgnData);\n\t}\n};\n\ntypedef CRgnT<false>   CRgnHandle;\ntypedef CRgnT<true>    CRgn;\n\n\n///////////////////////////////////////////////////////////////////////////////\n// CDC - The device context class\n\ntemplate <bool t_bManaged>\nclass CDCT;\ntypedef CDCT<false>   CDCHandle;\ntypedef CDCT<true>    CDC;\n\ntemplate <bool t_bManaged>\nclass CDCT\n{\npublic:\n// Data members\n\tHDC m_hDC;\n\n// Constructor/destructor/operators\n\tCDCT(HDC hDC = NULL) : m_hDC(hDC)\n\t{\n\t}\n\n\t~CDCT()\n\t{\n\t\tif(t_bManaged && (m_hDC != NULL))\n\t\t\t::DeleteDC(Detach());\n\t}\n\n\tCDCT<t_bManaged>& operator =(HDC hDC)\n\t{\n\t\tAttach(hDC);\n\t\treturn *this;\n\t}\n\n\tvoid Attach(HDC hDC)\n\t{\n\t\tif(t_bManaged && (m_hDC != NULL) && (m_hDC != hDC))\n\t\t\t::DeleteDC(m_hDC);\n\t\tm_hDC = hDC;\n\t}\n\n\tHDC Detach()\n\t{\n\t\tHDC hDC = m_hDC;\n\t\tm_hDC = NULL;\n\t\treturn hDC;\n\t}\n\n\toperator HDC() const { return m_hDC; }\n\n\tbool IsNull() const { return (m_hDC == NULL); }\n\n// Operations\n\tHWND WindowFromDC() const\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::WindowFromDC(m_hDC);\n\t}\n\n\tCPenHandle GetCurrentPen() const\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn CPenHandle((HPEN)::GetCurrentObject(m_hDC, OBJ_PEN));\n\t}\n\n\tCBrushHandle GetCurrentBrush() const\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn CBrushHandle((HBRUSH)::GetCurrentObject(m_hDC, OBJ_BRUSH));\n\t}\n\n\tCPaletteHandle GetCurrentPalette() const\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn CPaletteHandle((HPALETTE)::GetCurrentObject(m_hDC, OBJ_PAL));\n\t}\n\n\tCFontHandle GetCurrentFont() const\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn CFontHandle((HFONT)::GetCurrentObject(m_hDC, OBJ_FONT));\n\t}\n\n\tCBitmapHandle GetCurrentBitmap() const\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn CBitmapHandle((HBITMAP)::GetCurrentObject(m_hDC, OBJ_BITMAP));\n\t}\n\n\tHDC CreateDC(LPCTSTR lpszDriverName, LPCTSTR lpszDeviceName, LPCTSTR lpszOutput, const DEVMODE* lpInitData)\n\t{\n\t\tATLASSERT(m_hDC == NULL);\n\t\tm_hDC = ::CreateDC(lpszDriverName, lpszDeviceName, lpszOutput, lpInitData);\n\t\treturn m_hDC;\n\t}\n\n\tHDC CreateCompatibleDC(HDC hDC = NULL)\n\t{\n\t\tATLASSERT(m_hDC == NULL);\n\t\tm_hDC = ::CreateCompatibleDC(hDC);\n\t\treturn m_hDC;\n\t}\n\n\tBOOL DeleteDC()\n\t{\n\t\tif(m_hDC == NULL)\n\t\t\treturn FALSE;\n\t\tBOOL bRet = ::DeleteDC(m_hDC);\n\t\tif(bRet)\n\t\t\tm_hDC = NULL;\n\t\treturn bRet;\n\t}\n\n// Device-Context Functions\n\tint SaveDC()\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::SaveDC(m_hDC);\n\t}\n\n\tBOOL RestoreDC(int nSavedDC)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::RestoreDC(m_hDC, nSavedDC);\n\t}\n\n\tint GetDeviceCaps(int nIndex) const\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::GetDeviceCaps(m_hDC, nIndex);\n\t}\n\n\tUINT SetBoundsRect(LPCRECT lpRectBounds, UINT flags)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::SetBoundsRect(m_hDC, lpRectBounds, flags);\n\t}\n\n\tUINT GetBoundsRect(LPRECT lpRectBounds, UINT flags) const\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::GetBoundsRect(m_hDC, lpRectBounds, flags);\n\t}\n\n\tBOOL ResetDC(const DEVMODE* lpDevMode)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::ResetDC(m_hDC, lpDevMode) != NULL;\n\t}\n\n// Drawing-Tool Functions\n\tBOOL GetBrushOrg(LPPOINT lpPoint) const\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::GetBrushOrgEx(m_hDC, lpPoint);\n\t}\n\n\tBOOL SetBrushOrg(int x, int y, LPPOINT lpPoint = NULL)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::SetBrushOrgEx(m_hDC, x, y, lpPoint);\n\t}\n\n\tBOOL SetBrushOrg(POINT point, LPPOINT lpPointRet = NULL)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::SetBrushOrgEx(m_hDC, point.x, point.y, lpPointRet);\n\t}\n\n\tint EnumObjects(int nObjectType, int (CALLBACK* lpfn)(LPVOID, LPARAM), LPARAM lpData)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n#ifdef STRICT\n\t\treturn ::EnumObjects(m_hDC, nObjectType, (GOBJENUMPROC)lpfn, lpData);\n#else\n\t\treturn ::EnumObjects(m_hDC, nObjectType, (GOBJENUMPROC)lpfn, (LPVOID)lpData);\n#endif\n\t}\n\n// Type-safe selection helpers\n\tHPEN SelectPen(HPEN hPen)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\tATLASSERT((hPen == NULL) || (::GetObjectType(hPen) == OBJ_PEN) || (::GetObjectType(hPen) == OBJ_EXTPEN));\n\t\treturn (HPEN)::SelectObject(m_hDC, hPen);\n\t}\n\n\tHBRUSH SelectBrush(HBRUSH hBrush)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\tATLASSERT((hBrush == NULL) || (::GetObjectType(hBrush) == OBJ_BRUSH));\n\t\treturn (HBRUSH)::SelectObject(m_hDC, hBrush);\n\t}\n\n\tHFONT SelectFont(HFONT hFont)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\tATLASSERT((hFont == NULL) || (::GetObjectType(hFont) == OBJ_FONT));\n\t\treturn (HFONT)::SelectObject(m_hDC, hFont);\n\t}\n\n\tHBITMAP SelectBitmap(HBITMAP hBitmap)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\tATLASSERT((hBitmap == NULL) || (::GetObjectType(hBitmap) == OBJ_BITMAP));\n\t\treturn (HBITMAP)::SelectObject(m_hDC, hBitmap);\n\t}\n\n\tint SelectRgn(HRGN hRgn)       // special return for regions\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\tATLASSERT((hRgn == NULL) || (::GetObjectType(hRgn) == OBJ_REGION));\n\t\treturn PtrToInt(::SelectObject(m_hDC, hRgn));\n\t}\n\n// Type-safe selection helpers for stock objects\n\tHPEN SelectStockPen(int nPen)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\tATLASSERT((nPen == WHITE_PEN) || (nPen == BLACK_PEN) || (nPen == NULL_PEN) || (nPen == DC_PEN));\n\t\treturn SelectPen((HPEN)::GetStockObject(nPen));\n\t}\n\n\tHBRUSH SelectStockBrush(int nBrush)\n\t{\n\t\tATLASSERT(((nBrush >= WHITE_BRUSH) && (nBrush <= HOLLOW_BRUSH)) || (nBrush == DC_BRUSH));\n\t\treturn SelectBrush((HBRUSH)::GetStockObject(nBrush));\n\t}\n\n\tHFONT SelectStockFont(int nFont)\n\t{\n\t\tATLASSERT(((nFont >= OEM_FIXED_FONT) && (nFont <= SYSTEM_FIXED_FONT)) || (nFont == DEFAULT_GUI_FONT));\n\t\treturn SelectFont((HFONT)::GetStockObject(nFont));\n\t}\n\n\tHPALETTE SelectStockPalette(int nPalette, BOOL bForceBackground)\n\t{\n\t\tATLASSERT(nPalette == DEFAULT_PALETTE); // the only one supported\n\t\treturn SelectPalette((HPALETTE)::GetStockObject(nPalette), bForceBackground);\n\t}\n\n// Color and Color Palette Functions\n\tCOLORREF GetNearestColor(COLORREF crColor) const\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::GetNearestColor(m_hDC, crColor);\n\t}\n\n\tHPALETTE SelectPalette(HPALETTE hPalette, BOOL bForceBackground)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\n\t\treturn ::SelectPalette(m_hDC, hPalette, bForceBackground);\n\t}\n\n\tUINT RealizePalette()\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::RealizePalette(m_hDC);\n\t}\n\n\tvoid UpdateColors()\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\t::UpdateColors(m_hDC);\n\t}\n\n// Drawing-Attribute Functions\n\tCOLORREF GetBkColor() const\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::GetBkColor(m_hDC);\n\t}\n\n\tint GetBkMode() const\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::GetBkMode(m_hDC);\n\t}\n\n\tint GetPolyFillMode() const\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::GetPolyFillMode(m_hDC);\n\t}\n\n\tint GetROP2() const\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::GetROP2(m_hDC);\n\t}\n\n\tint GetStretchBltMode() const\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::GetStretchBltMode(m_hDC);\n\t}\n\n\tCOLORREF GetTextColor() const\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::GetTextColor(m_hDC);\n\t}\n\n\tCOLORREF SetBkColor(COLORREF crColor)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::SetBkColor(m_hDC, crColor);\n\t}\n\n\tint SetBkMode(int nBkMode)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::SetBkMode(m_hDC, nBkMode);\n\t}\n\n\tint SetPolyFillMode(int nPolyFillMode)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::SetPolyFillMode(m_hDC, nPolyFillMode);\n\t}\n\n\tint SetROP2(int nDrawMode)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::SetROP2(m_hDC, nDrawMode);\n\t}\n\n\tint SetStretchBltMode(int nStretchMode)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::SetStretchBltMode(m_hDC, nStretchMode);\n\t}\n\n\tCOLORREF SetTextColor(COLORREF crColor)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::SetTextColor(m_hDC, crColor);\n\t}\n\n\tBOOL GetColorAdjustment(LPCOLORADJUSTMENT lpColorAdjust) const\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::GetColorAdjustment(m_hDC, lpColorAdjust);\n\t}\n\n\tBOOL SetColorAdjustment(const COLORADJUSTMENT* lpColorAdjust)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::SetColorAdjustment(m_hDC, lpColorAdjust);\n\t}\n\n// Mapping Functions\n\tint GetMapMode() const\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::GetMapMode(m_hDC);\n\t}\n\n\tBOOL GetViewportOrg(LPPOINT lpPoint) const\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::GetViewportOrgEx(m_hDC, lpPoint);\n\t}\n\n\tint SetMapMode(int nMapMode)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::SetMapMode(m_hDC, nMapMode);\n\t}\n\n\t// Viewport Origin\n\tBOOL SetViewportOrg(int x, int y, LPPOINT lpPoint = NULL)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::SetViewportOrgEx(m_hDC, x, y, lpPoint);\n\t}\n\n\tBOOL SetViewportOrg(POINT point, LPPOINT lpPointRet = NULL)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn SetViewportOrg(point.x, point.y, lpPointRet);\n\t}\n\n\tBOOL OffsetViewportOrg(int nWidth, int nHeight, LPPOINT lpPoint = NULL)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::OffsetViewportOrgEx(m_hDC, nWidth, nHeight, lpPoint);\n\t}\n\n\t// Viewport Extent\n\tBOOL GetViewportExt(LPSIZE lpSize) const\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::GetViewportExtEx(m_hDC, lpSize);\n\t}\n\n\tBOOL SetViewportExt(int x, int y, LPSIZE lpSize = NULL)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::SetViewportExtEx(m_hDC, x, y, lpSize);\n\t}\n\n\tBOOL SetViewportExt(SIZE size, LPSIZE lpSizeRet = NULL)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn SetViewportExt(size.cx, size.cy, lpSizeRet);\n\t}\n\n\tBOOL ScaleViewportExt(int xNum, int xDenom, int yNum, int yDenom, LPSIZE lpSize = NULL)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::ScaleViewportExtEx(m_hDC, xNum, xDenom, yNum, yDenom, lpSize);\n\t}\n\n\t// Window Origin\n\tBOOL GetWindowOrg(LPPOINT lpPoint) const\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::GetWindowOrgEx(m_hDC, lpPoint);\n\t}\n\n\tBOOL SetWindowOrg(int x, int y, LPPOINT lpPoint = NULL)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::SetWindowOrgEx(m_hDC, x, y, lpPoint);\n\t}\n\n\tBOOL SetWindowOrg(POINT point, LPPOINT lpPointRet = NULL)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn SetWindowOrg(point.x, point.y, lpPointRet);\n\t}\n\n\tBOOL OffsetWindowOrg(int nWidth, int nHeight, LPPOINT lpPoint = NULL)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::OffsetWindowOrgEx(m_hDC, nWidth, nHeight, lpPoint);\n\t}\n\n\t// Window extent\n\tBOOL GetWindowExt(LPSIZE lpSize) const\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::GetWindowExtEx(m_hDC, lpSize);\n\t}\n\n\tBOOL SetWindowExt(int x, int y, LPSIZE lpSize = NULL)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::SetWindowExtEx(m_hDC, x, y, lpSize);\n\t}\n\n\tBOOL SetWindowExt(SIZE size, LPSIZE lpSizeRet = NULL)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn SetWindowExt(size.cx, size.cy, lpSizeRet);\n\t}\n\n\tBOOL ScaleWindowExt(int xNum, int xDenom, int yNum, int yDenom, LPSIZE lpSize = NULL)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::ScaleWindowExtEx(m_hDC, xNum, xDenom, yNum, yDenom, lpSize);\n\t}\n\n// Coordinate Functions\n\tBOOL DPtoLP(LPPOINT lpPoints, int nCount = 1) const\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::DPtoLP(m_hDC, lpPoints, nCount);\n\t}\n\n\tBOOL DPtoLP(LPRECT lpRect) const\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::DPtoLP(m_hDC, (LPPOINT)lpRect, 2);\n\t}\n\n\tBOOL DPtoLP(LPSIZE lpSize) const\n\t{\n\t\tSIZE sizeWinExt = {};\n\t\tif(!GetWindowExt(&sizeWinExt))\n\t\t\treturn FALSE;\n\t\tSIZE sizeVpExt = {};\n\t\tif(!GetViewportExt(&sizeVpExt))\n\t\t\treturn FALSE;\n\t\tlpSize->cx = ::MulDiv(lpSize->cx, abs(sizeWinExt.cx), abs(sizeVpExt.cx));\n\t\tlpSize->cy = ::MulDiv(lpSize->cy, abs(sizeWinExt.cy), abs(sizeVpExt.cy));\n\t\treturn TRUE;\n\t}\n\n\tBOOL LPtoDP(LPPOINT lpPoints, int nCount = 1) const\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::LPtoDP(m_hDC, lpPoints, nCount);\n\t}\n\n\tBOOL LPtoDP(LPRECT lpRect) const\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::LPtoDP(m_hDC, (LPPOINT)lpRect, 2);\n\t}\n\n\tBOOL LPtoDP(LPSIZE lpSize) const\n\t{\n\t\tSIZE sizeWinExt = {};\n\t\tif(!GetWindowExt(&sizeWinExt))\n\t\t\treturn FALSE;\n\t\tSIZE sizeVpExt = {};\n\t\tif(!GetViewportExt(&sizeVpExt))\n\t\t\treturn FALSE;\n\t\tlpSize->cx = ::MulDiv(lpSize->cx, abs(sizeVpExt.cx), abs(sizeWinExt.cx));\n\t\tlpSize->cy = ::MulDiv(lpSize->cy, abs(sizeVpExt.cy), abs(sizeWinExt.cy));\n\t\treturn TRUE;\n\t}\n\n// Special Coordinate Functions (useful for dealing with metafiles and OLE)\n\t#define HIMETRIC_INCH   2540    // HIMETRIC units per inch\n\n\tvoid DPtoHIMETRIC(LPSIZE lpSize)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\tint nMapMode = GetMapMode();\n\t\tif((nMapMode < MM_ISOTROPIC) && (nMapMode != MM_TEXT))\n\t\t{\n\t\t\t// when using a constrained map mode, map against physical inch\n\t\t\tSetMapMode(MM_HIMETRIC);\n\t\t\tDPtoLP(lpSize);\n\t\t\tSetMapMode(nMapMode);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// map against logical inch for non-constrained mapping modes\n\t\t\tint cxPerInch = GetDeviceCaps(LOGPIXELSX);\n\t\t\tint cyPerInch = GetDeviceCaps(LOGPIXELSY);\n\t\t\tATLASSERT((cxPerInch != 0) && (cyPerInch != 0));\n\t\t\tlpSize->cx = ::MulDiv(lpSize->cx, HIMETRIC_INCH, cxPerInch);\n\t\t\tlpSize->cy = ::MulDiv(lpSize->cy, HIMETRIC_INCH, cyPerInch);\n\t\t}\n\t}\n\n\tvoid HIMETRICtoDP(LPSIZE lpSize)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\tint nMapMode = GetMapMode();\n\t\tif((nMapMode < MM_ISOTROPIC) && (nMapMode != MM_TEXT))\n\t\t{\n\t\t\t// when using a constrained map mode, map against physical inch\n\t\t\tSetMapMode(MM_HIMETRIC);\n\t\t\tLPtoDP(lpSize);\n\t\t\tSetMapMode(nMapMode);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// map against logical inch for non-constrained mapping modes\n\t\t\tint cxPerInch = GetDeviceCaps(LOGPIXELSX);\n\t\t\tint cyPerInch = GetDeviceCaps(LOGPIXELSY);\n\t\t\tATLASSERT((cxPerInch != 0) && (cyPerInch != 0));\n\t\t\tlpSize->cx = ::MulDiv(lpSize->cx, cxPerInch, HIMETRIC_INCH);\n\t\t\tlpSize->cy = ::MulDiv(lpSize->cy, cyPerInch, HIMETRIC_INCH);\n\t\t}\n\t}\n\n\tvoid LPtoHIMETRIC(LPSIZE lpSize)\n\t{\n\t\tLPtoDP(lpSize);\n\t\tDPtoHIMETRIC(lpSize);\n\t}\n\n\tvoid HIMETRICtoLP(LPSIZE lpSize)\n\t{\n\t\tHIMETRICtoDP(lpSize);\n\t\tDPtoLP(lpSize);\n\t}\n\n// Region Functions\n\tBOOL FillRgn(HRGN hRgn, HBRUSH hBrush)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::FillRgn(m_hDC, hRgn, hBrush);\n\t}\n\n\tBOOL FrameRgn(HRGN hRgn, HBRUSH hBrush, int nWidth, int nHeight)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::FrameRgn(m_hDC, hRgn, hBrush, nWidth, nHeight);\n\t}\n\n\tBOOL InvertRgn(HRGN hRgn)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::InvertRgn(m_hDC, hRgn);\n\t}\n\n\tBOOL PaintRgn(HRGN hRgn)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::PaintRgn(m_hDC, hRgn);\n\t}\n\n// Clipping Functions\n\tint GetClipBox(LPRECT lpRect) const\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::GetClipBox(m_hDC, lpRect);\n\t}\n\n\tint GetClipRgn(CRgn& region) const\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\tif(region.IsNull())\n\t\t\tregion.CreateRectRgn(0, 0, 0, 0);\n\n\t\tint nRet = ::GetClipRgn(m_hDC, region);\n\t\tif(nRet != 1)\n\t\t\tregion.DeleteObject();\n\n\t\treturn nRet;\n\t}\n\n\tBOOL PtVisible(int x, int y) const\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::PtVisible(m_hDC, x, y);\n\t}\n\n\tBOOL PtVisible(POINT point) const\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::PtVisible(m_hDC, point.x, point.y);\n\t}\n\n\tBOOL RectVisible(LPCRECT lpRect) const\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::RectVisible(m_hDC, lpRect);\n\t}\n\n\tint SelectClipRgn(HRGN hRgn)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::SelectClipRgn(m_hDC, (HRGN)hRgn);\n\t}\n\n\tint ExcludeClipRect(int x1, int y1, int x2, int y2)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::ExcludeClipRect(m_hDC, x1, y1, x2, y2);\n\t}\n\n\tint ExcludeClipRect(LPCRECT lpRect)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::ExcludeClipRect(m_hDC, lpRect->left, lpRect->top, lpRect->right, lpRect->bottom);\n\t}\n\n\tint ExcludeUpdateRgn(HWND hWnd)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::ExcludeUpdateRgn(m_hDC, hWnd);\n\t}\n\n\tint IntersectClipRect(int x1, int y1, int x2, int y2)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::IntersectClipRect(m_hDC, x1, y1, x2, y2);\n\t}\n\n\tint IntersectClipRect(LPCRECT lpRect)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::IntersectClipRect(m_hDC, lpRect->left, lpRect->top, lpRect->right, lpRect->bottom);\n\t}\n\n\tint OffsetClipRgn(int x, int y)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::OffsetClipRgn(m_hDC, x, y);\n\t}\n\n\tint OffsetClipRgn(SIZE size)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::OffsetClipRgn(m_hDC, size.cx, size.cy);\n\t}\n\n\tint SelectClipRgn(HRGN hRgn, int nMode)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::ExtSelectClipRgn(m_hDC, hRgn, nMode);\n\t}\n\n// Line-Output Functions\n\tBOOL GetCurrentPosition(LPPOINT lpPoint) const\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::GetCurrentPositionEx(m_hDC, lpPoint);\n\t}\n\n\tBOOL MoveTo(int x, int y, LPPOINT lpPoint = NULL)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::MoveToEx(m_hDC, x, y, lpPoint);\n\t}\n\n\tBOOL MoveTo(POINT point, LPPOINT lpPointRet = NULL)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn MoveTo(point.x, point.y, lpPointRet);\n\t}\n\n\tBOOL LineTo(int x, int y)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::LineTo(m_hDC, x, y);\n\t}\n\n\tBOOL LineTo(POINT point)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn LineTo(point.x, point.y);\n\t}\n\n\tBOOL Arc(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::Arc(m_hDC, x1, y1, x2, y2, x3, y3, x4, y4);\n\t}\n\n\tBOOL Arc(LPCRECT lpRect, POINT ptStart, POINT ptEnd)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::Arc(m_hDC, lpRect->left, lpRect->top,\n\t\t\tlpRect->right, lpRect->bottom, ptStart.x, ptStart.y,\n\t\t\tptEnd.x, ptEnd.y);\n\t}\n\n\tBOOL Polyline(const POINT* lpPoints, int nCount)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::Polyline(m_hDC, lpPoints, nCount);\n\t}\n\n\tBOOL AngleArc(int x, int y, int nRadius, float fStartAngle, float fSweepAngle)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::AngleArc(m_hDC, x, y, nRadius, fStartAngle, fSweepAngle);\n\t}\n\n\tBOOL ArcTo(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::ArcTo(m_hDC, x1, y1, x2, y2, x3, y3, x4, y4);\n\t}\n\n\tBOOL ArcTo(LPCRECT lpRect, POINT ptStart, POINT ptEnd)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ArcTo(lpRect->left, lpRect->top, lpRect->right,\n\t\tlpRect->bottom, ptStart.x, ptStart.y, ptEnd.x, ptEnd.y);\n\t}\n\n\tint GetArcDirection() const\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::GetArcDirection(m_hDC);\n\t}\n\n\tint SetArcDirection(int nArcDirection)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::SetArcDirection(m_hDC, nArcDirection);\n\t}\n\n\tBOOL PolyDraw(const POINT* lpPoints, const BYTE* lpTypes, int nCount)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::PolyDraw(m_hDC, lpPoints, lpTypes, nCount);\n\t}\n\n\tBOOL PolylineTo(const POINT* lpPoints, int nCount)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::PolylineTo(m_hDC, lpPoints, nCount);\n\t}\n\n\tBOOL PolyPolyline(const POINT* lpPoints,\n\t\tconst DWORD* lpPolyPoints, int nCount)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::PolyPolyline(m_hDC, lpPoints, lpPolyPoints, nCount);\n\t}\n\n\tBOOL PolyBezier(const POINT* lpPoints, int nCount)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::PolyBezier(m_hDC, lpPoints, nCount);\n\t}\n\n\tBOOL PolyBezierTo(const POINT* lpPoints, int nCount)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::PolyBezierTo(m_hDC, lpPoints, nCount);\n\t}\n\n// Simple Drawing Functions\n\tBOOL FillRect(LPCRECT lpRect, HBRUSH hBrush)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::FillRect(m_hDC, lpRect, hBrush);\n\t}\n\n\tBOOL FillRect(LPCRECT lpRect, int nColorIndex)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::FillRect(m_hDC, lpRect, (HBRUSH)LongToPtr(nColorIndex + 1));\n\t}\n\n\tBOOL FrameRect(LPCRECT lpRect, HBRUSH hBrush)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::FrameRect(m_hDC, lpRect, hBrush);\n\t}\n\n\tBOOL InvertRect(LPCRECT lpRect)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::InvertRect(m_hDC, lpRect);\n\t}\n\n\tBOOL DrawIcon(int x, int y, HICON hIcon)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::DrawIcon(m_hDC, x, y, hIcon);\n\t}\n\n\tBOOL DrawIcon(POINT point, HICON hIcon)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::DrawIcon(m_hDC, point.x, point.y, hIcon);\n\t}\n\n\tBOOL DrawIconEx(int x, int y, HICON hIcon, int cxWidth, int cyWidth, UINT uStepIfAniCur = 0, HBRUSH hbrFlickerFreeDraw = NULL, UINT uFlags = DI_NORMAL)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::DrawIconEx(m_hDC, x, y, hIcon, cxWidth, cyWidth, uStepIfAniCur, hbrFlickerFreeDraw, uFlags);\n\t}\n\n\tBOOL DrawIconEx(POINT point, HICON hIcon, SIZE size, UINT uStepIfAniCur = 0, HBRUSH hbrFlickerFreeDraw = NULL, UINT uFlags = DI_NORMAL)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::DrawIconEx(m_hDC, point.x, point.y, hIcon, size.cx, size.cy, uStepIfAniCur, hbrFlickerFreeDraw, uFlags);\n\t}\n\n\tBOOL DrawState(POINT pt, SIZE size, HBITMAP hBitmap, UINT nFlags, HBRUSH hBrush = NULL)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::DrawState(m_hDC, hBrush, NULL, (LPARAM)hBitmap, 0, pt.x, pt.y, size.cx, size.cy, nFlags | DST_BITMAP);\n\t}\n\n\tBOOL DrawState(POINT pt, SIZE size, HICON hIcon, UINT nFlags, HBRUSH hBrush = NULL)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::DrawState(m_hDC, hBrush, NULL, (LPARAM)hIcon, 0, pt.x, pt.y, size.cx, size.cy, nFlags | DST_ICON);\n\t}\n\n\tBOOL DrawState(POINT pt, SIZE size, LPCTSTR lpszText, UINT nFlags, BOOL bPrefixText = TRUE, int nTextLen = 0, HBRUSH hBrush = NULL)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::DrawState(m_hDC, hBrush, NULL, (LPARAM)lpszText, (WPARAM)nTextLen, pt.x, pt.y, size.cx, size.cy, nFlags | (bPrefixText ? DST_PREFIXTEXT : DST_TEXT));\n\t}\n\n\tBOOL DrawState(POINT pt, SIZE size, DRAWSTATEPROC lpDrawProc, LPARAM lData, UINT nFlags, HBRUSH hBrush = NULL)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::DrawState(m_hDC, hBrush, lpDrawProc, lData, 0, pt.x, pt.y, size.cx, size.cy, nFlags | DST_COMPLEX);\n\t}\n\n// Ellipse and Polygon Functions\n\tBOOL Chord(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::Chord(m_hDC, x1, y1, x2, y2, x3, y3, x4, y4);\n\t}\n\n\tBOOL Chord(LPCRECT lpRect, POINT ptStart, POINT ptEnd)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::Chord(m_hDC, lpRect->left, lpRect->top, lpRect->right, lpRect->bottom, ptStart.x, ptStart.y, ptEnd.x, ptEnd.y);\n\t}\n\n\tvoid DrawFocusRect(LPCRECT lpRect)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\t::DrawFocusRect(m_hDC, lpRect);\n\t}\n\n\tBOOL Ellipse(int x1, int y1, int x2, int y2)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::Ellipse(m_hDC, x1, y1, x2, y2);\n\t}\n\n\tBOOL Ellipse(LPCRECT lpRect)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::Ellipse(m_hDC, lpRect->left, lpRect->top, lpRect->right, lpRect->bottom);\n\t}\n\n\tBOOL Pie(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::Pie(m_hDC, x1, y1, x2, y2, x3, y3, x4, y4);\n\t}\n\n\tBOOL Pie(LPCRECT lpRect, POINT ptStart, POINT ptEnd)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::Pie(m_hDC, lpRect->left, lpRect->top, lpRect->right, lpRect->bottom, ptStart.x, ptStart.y, ptEnd.x, ptEnd.y);\n\t}\n\n\tBOOL Polygon(const POINT* lpPoints, int nCount)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::Polygon(m_hDC, lpPoints, nCount);\n\t}\n\n\tBOOL PolyPolygon(const POINT* lpPoints, const INT* lpPolyCounts, int nCount)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::PolyPolygon(m_hDC, lpPoints, lpPolyCounts, nCount);\n\t}\n\n\tBOOL Rectangle(int x1, int y1, int x2, int y2)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::Rectangle(m_hDC, x1, y1, x2, y2);\n\t}\n\n\tBOOL Rectangle(LPCRECT lpRect)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::Rectangle(m_hDC, lpRect->left, lpRect->top, lpRect->right, lpRect->bottom);\n\t}\n\n\tBOOL RoundRect(int x1, int y1, int x2, int y2, int x3, int y3)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::RoundRect(m_hDC, x1, y1, x2, y2, x3, y3);\n\t}\n\n\tBOOL RoundRect(LPCRECT lpRect, POINT point)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::RoundRect(m_hDC, lpRect->left, lpRect->top, lpRect->right, lpRect->bottom, point.x, point.y);\n\t}\n\n// Bitmap Functions\n\tBOOL PatBlt(int x, int y, int nWidth, int nHeight, DWORD dwRop)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::PatBlt(m_hDC, x, y, nWidth, nHeight, dwRop);\n\t}\n\n\tBOOL BitBlt(int x, int y, int nWidth, int nHeight, HDC hSrcDC,\n\t\tint xSrc, int ySrc, DWORD dwRop)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::BitBlt(m_hDC, x, y, nWidth, nHeight, hSrcDC, xSrc, ySrc, dwRop);\n\t}\n\n\tBOOL StretchBlt(int x, int y, int nWidth, int nHeight, HDC hSrcDC, int xSrc, int ySrc, int nSrcWidth, int nSrcHeight, DWORD dwRop)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::StretchBlt(m_hDC, x, y, nWidth, nHeight, hSrcDC, xSrc, ySrc, nSrcWidth, nSrcHeight, dwRop);\n\t}\n\n\tCOLORREF GetPixel(int x, int y) const\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::GetPixel(m_hDC, x, y);\n\t}\n\n\tCOLORREF GetPixel(POINT point) const\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::GetPixel(m_hDC, point.x, point.y);\n\t}\n\n\tCOLORREF SetPixel(int x, int y, COLORREF crColor)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::SetPixel(m_hDC, x, y, crColor);\n\t}\n\n\tCOLORREF SetPixel(POINT point, COLORREF crColor)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::SetPixel(m_hDC, point.x, point.y, crColor);\n\t}\n\n\tBOOL FloodFill(int x, int y, COLORREF crColor)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::FloodFill(m_hDC, x, y, crColor);\n\t}\n\n\tBOOL ExtFloodFill(int x, int y, COLORREF crColor, UINT nFillType)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::ExtFloodFill(m_hDC, x, y, crColor, nFillType);\n\t}\n\n\tBOOL MaskBlt(int x, int y, int nWidth, int nHeight, HDC hSrcDC, int xSrc, int ySrc, HBITMAP hMaskBitmap, int xMask, int yMask, DWORD dwRop)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::MaskBlt(m_hDC, x, y, nWidth, nHeight, hSrcDC, xSrc, ySrc, hMaskBitmap, xMask, yMask, dwRop);\n\t}\n\n\tBOOL PlgBlt(LPPOINT lpPoint, HDC hSrcDC, int xSrc, int ySrc, int nWidth, int nHeight, HBITMAP hMaskBitmap, int xMask, int yMask)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::PlgBlt(m_hDC, lpPoint, hSrcDC, xSrc, ySrc, nWidth, nHeight, hMaskBitmap, xMask, yMask);\n\t}\n\n\tBOOL SetPixelV(int x, int y, COLORREF crColor)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::SetPixelV(m_hDC, x, y, crColor);\n\t}\n\n\tBOOL SetPixelV(POINT point, COLORREF crColor)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::SetPixelV(m_hDC, point.x, point.y, crColor);\n\t}\n\n\tBOOL TransparentBlt(int x, int y, int nWidth, int nHeight, HDC hSrcDC, int xSrc, int ySrc, int nSrcWidth, int nSrcHeight, UINT crTransparent)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::TransparentBlt(m_hDC, x, y, nWidth, nHeight, hSrcDC, xSrc, ySrc, nSrcWidth, nSrcHeight, crTransparent);\n\t}\n\n\tBOOL GradientFill(const PTRIVERTEX pVertices, DWORD nVertices, void* pMeshElements, DWORD nMeshElements, DWORD dwMode)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::GradientFill(m_hDC, pVertices, nVertices, pMeshElements, nMeshElements, dwMode);\n\t}\n\n\tBOOL GradientFillRect(RECT& rect, COLORREF clr1, COLORREF clr2, bool bHorizontal)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\n\t\tTRIVERTEX arrTvx[2] = { { 0 }, { 0 } };\n\n\t\tarrTvx[0].x = rect.left;\n\t\tarrTvx[0].y = rect.top;\n\t\tarrTvx[0].Red = MAKEWORD(0, GetRValue(clr1));\n\t\tarrTvx[0].Green = MAKEWORD(0, GetGValue(clr1));\n\t\tarrTvx[0].Blue = MAKEWORD(0, GetBValue(clr1));\n\t\tarrTvx[0].Alpha = 0;\n\n\t\tarrTvx[1].x = rect.right;\n\t\tarrTvx[1].y = rect.bottom;\n\t\tarrTvx[1].Red = MAKEWORD(0, GetRValue(clr2));\n\t\tarrTvx[1].Green = MAKEWORD(0, GetGValue(clr2));\n\t\tarrTvx[1].Blue = MAKEWORD(0, GetBValue(clr2));\n\t\tarrTvx[1].Alpha = 0;\n\n\t\tGRADIENT_RECT gr = { 0, 1 };\n\n\t\treturn ::GradientFill(m_hDC, arrTvx, 2, &gr, 1, bHorizontal ? GRADIENT_FILL_RECT_H : GRADIENT_FILL_RECT_V);\n\t}\n\n\tBOOL AlphaBlend(int x, int y, int nWidth, int nHeight, HDC hSrcDC, int xSrc, int ySrc, int nSrcWidth, int nSrcHeight, BLENDFUNCTION bf)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::AlphaBlend(m_hDC, x, y, nWidth, nHeight, hSrcDC, xSrc, ySrc, nSrcWidth, nSrcHeight, bf);\n\t}\n\n// Extra bitmap functions\n\t// Helper function for painting a disabled toolbar or menu bitmap\n\t// This function can take either an HBITMAP (for SS) or a DC with \n\t//           the bitmap already painted (for cmdbar)\n\tBOOL DitherBlt(int x, int y, int nWidth, int nHeight, HDC hSrcDC, HBITMAP hBitmap, int xSrc, int ySrc,\n\t\t\tHBRUSH hBrushBackground = ::GetSysColorBrush(COLOR_3DFACE),\n\t\t\tHBRUSH hBrush3DEffect = ::GetSysColorBrush(COLOR_3DHILIGHT),\n\t\t\tHBRUSH hBrushDisabledImage = ::GetSysColorBrush(COLOR_3DSHADOW))\n\t{\n\t\tATLASSERT((m_hDC != NULL) || (hBitmap != NULL));\n\t\tATLASSERT((nWidth > 0) && (nHeight > 0));\n\t\t\n\t\t// Create a generic DC for all BitBlts\n\t\tCDCT<false> dc = (hSrcDC != NULL) ? hSrcDC : ::CreateCompatibleDC(m_hDC);\n\t\tATLASSERT(dc.m_hDC != NULL);\n\t\tif(dc.m_hDC == NULL)\n\t\t\treturn FALSE;\n\t\t\n\t\t// Create a DC for the monochrome DIB section\n\t\tCDCT<true> dcBW = ::CreateCompatibleDC(m_hDC);\n\t\tATLASSERT(dcBW.m_hDC != NULL);\n\t\tif(dcBW.m_hDC == NULL)\n\t\t{\n\t\t\tif(hSrcDC == NULL)\n\t\t\t\tdc.DeleteDC();\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t// Create the monochrome DIB section with a black and white palette\n\t\tstruct RGBBWBITMAPINFO\n\t\t{\n\t\t\tBITMAPINFOHEADER bmiHeader; \n\t\t\tRGBQUAD bmiColors[2]; \n\t\t};\n\n\t\tRGBBWBITMAPINFO rgbBWBitmapInfo = \n\t\t{\n\t\t\t{ sizeof(BITMAPINFOHEADER), nWidth, nHeight, 1, 1, BI_RGB, 0, 0, 0, 0, 0 },\n\t\t\t{ { 0x00, 0x00, 0x00, 0x00 }, { 0xFF, 0xFF, 0xFF, 0x00 } }\n\t\t};\n\n\t\tVOID* pbitsBW;\n\t\tCBitmap bmpBW = ::CreateDIBSection(dcBW, (LPBITMAPINFO)&rgbBWBitmapInfo, DIB_RGB_COLORS, &pbitsBW, NULL, 0);\n\t\tATLASSERT(bmpBW.m_hBitmap != NULL);\n\t\tif(bmpBW.m_hBitmap == NULL)\n\t\t{\n\t\t\tif(hSrcDC == NULL)\n\t\t\t\tdc.DeleteDC();\n\t\t\treturn FALSE;\n\t\t}\n\t\t\n\t\t// Attach the monochrome DIB section and the bitmap to the DCs\n\t\tHBITMAP hbmOldBW = dcBW.SelectBitmap(bmpBW);\n\t\tHBITMAP hbmOldDC = NULL;\n\t\tif(hBitmap != NULL)\n\t\t\thbmOldDC = dc.SelectBitmap(hBitmap);\n\n\t\t// Block: Dark gray removal: we want (128, 128, 128) pixels to become black and not white\n\t\t{\n\t\t\tCDCT<true> dcTemp1 = ::CreateCompatibleDC(m_hDC);\n\t\t\tCDCT<true> dcTemp2 = ::CreateCompatibleDC(m_hDC);\n\t\t\tCBitmap bmpTemp1;\n\t\t\tbmpTemp1.CreateCompatibleBitmap(dc, nWidth, nHeight);\n\t\t\tCBitmap bmpTemp2;\n\t\t\tbmpTemp2.CreateBitmap(nWidth, nHeight, 1, 1, NULL);\n\t\t\tHBITMAP hOldBmp1 = dcTemp1.SelectBitmap(bmpTemp1);\n\t\t\tHBITMAP hOldBmp2 = dcTemp2.SelectBitmap(bmpTemp2);\n\t\t\t// Let's copy our image, it will be altered\n\t\t\tdcTemp1.BitBlt(0, 0, nWidth, nHeight, dc, xSrc, ySrc, SRCCOPY);\n\n\t\t\t// All dark gray pixels will become white, the others black\n\t\t\tdcTemp1.SetBkColor(RGB(128, 128, 128));\n\t\t\tdcTemp2.BitBlt(0, 0, nWidth, nHeight, dcTemp1, 0, 0, SRCCOPY);\n\t\t\t// Do an XOR to set to black these white pixels\n\t\t\tdcTemp1.BitBlt(0, 0, nWidth, nHeight, dcTemp2, 0, 0, SRCINVERT);\n\n\t\t\t// BitBlt the bitmap into the monochrome DIB section\n\t\t\t// The DIB section will do a true monochrome conversion\n\t\t\t// The magenta background being closer to white will become white\n\t\t\tdcBW.BitBlt(0, 0, nWidth, nHeight, dcTemp1, 0, 0, SRCCOPY);\n\n\t\t\t// Cleanup\n\t\t\tdcTemp1.SelectBitmap(hOldBmp1);\n\t\t\tdcTemp2.SelectBitmap(hOldBmp2);\n\t\t}\n\t\t\n\t\t// Paint the destination rectangle using hBrushBackground\n\t\tif(hBrushBackground != NULL)\n\t\t{\n\t\t\tRECT rc = { x, y, x + nWidth, y + nHeight };\n\t\t\tFillRect(&rc, hBrushBackground);\n\t\t}\n\n\t\t// BitBlt the black bits in the monochrome bitmap into hBrush3DEffect color in the destination DC\n\t\t// The magic ROP comes from the Charles Petzold's book\n\t\tHBRUSH hOldBrush = SelectBrush(hBrush3DEffect);\n\t\tBitBlt(x + 1, y + 1, nWidth, nHeight, dcBW, 0, 0, 0xB8074A);\n\n\t\t// BitBlt the black bits in the monochrome bitmap into hBrushDisabledImage color in the destination DC\n\t\tSelectBrush(hBrushDisabledImage);\n\t\tBitBlt(x, y, nWidth, nHeight, dcBW, 0, 0, 0xB8074A);\n\n\t\tSelectBrush(hOldBrush);\n\t\tdcBW.SelectBitmap(hbmOldBW);\n\t\tdc.SelectBitmap(hbmOldDC);\n\n\t\tif(hSrcDC == NULL)\n\t\t\tdc.DeleteDC();\n\n\t\treturn TRUE;\n\t}\n\n// Text Functions\n\tBOOL TextOut(int x, int y, LPCTSTR lpszString, int nCount = -1)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\tif(nCount == -1)\n\t\t\tnCount = lstrlen(lpszString);\n\t\treturn ::TextOut(m_hDC, x, y, lpszString, nCount);\n\t}\n\n\tBOOL ExtTextOut(int x, int y, UINT nOptions, LPCRECT lpRect, LPCTSTR lpszString, int nCount = -1, LPINT lpDxWidths = NULL)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\tif(nCount == -1)\n\t\t\tnCount = lstrlen(lpszString);\n\t\tATLASSERT((nCount >= 0) && (nCount <= 8192));\n\t\treturn ::ExtTextOut(m_hDC, x, y, nOptions, lpRect, lpszString, (UINT)nCount, lpDxWidths);\n\t}\n\n\tSIZE TabbedTextOut(int x, int y, LPCTSTR lpszString, int nCount = -1, int nTabPositions = 0, LPINT lpnTabStopPositions = NULL, int nTabOrigin = 0)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\tif(nCount == -1)\n\t\t\tnCount = lstrlen(lpszString);\n\t\tLONG lRes = ::TabbedTextOut(m_hDC, x, y, lpszString, nCount, nTabPositions, lpnTabStopPositions, nTabOrigin);\n\t\tSIZE size = { GET_X_LPARAM(lRes), GET_Y_LPARAM(lRes) };\n\t\treturn size;\n\t}\n\n\tint DrawText(LPCTSTR lpstrText, int cchText, LPRECT lpRect, UINT uFormat)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\tATLASSERT((uFormat & DT_MODIFYSTRING) == 0);\n\t\treturn ::DrawText(m_hDC, lpstrText, cchText, lpRect, uFormat);\n\t}\n\n\tint DrawText(LPTSTR lpstrText, int cchText, LPRECT lpRect, UINT uFormat)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::DrawText(m_hDC, lpstrText, cchText, lpRect, uFormat);\n\t}\n\n\tint DrawTextEx(LPTSTR lpstrText, int cchText, LPRECT lpRect, UINT uFormat, LPDRAWTEXTPARAMS lpDTParams = NULL)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::DrawTextEx(m_hDC, lpstrText, cchText, lpRect, uFormat, lpDTParams);\n\t}\n\n\t// Note - ::DrawShadowText() is present only if comctl32.dll version 6 is loaded\n\tint DrawShadowText(LPCWSTR lpstrText, int cchText, LPRECT lpRect, DWORD dwFlags, COLORREF clrText, COLORREF clrShadow, int xOffset, int yOffset)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\tATLASSERT(lpRect != NULL);\n\t\treturn ::DrawShadowText(m_hDC, lpstrText, cchText, lpRect, dwFlags, clrText, clrShadow, xOffset, yOffset);\n\t}\n\n\tBOOL GetTextExtent(LPCTSTR lpszString, int nCount, LPSIZE lpSize) const\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\tif(nCount == -1)\n\t\t\tnCount = lstrlen(lpszString);\n\t\treturn ::GetTextExtentPoint32(m_hDC, lpszString, nCount, lpSize);\n\t}\n\n\tBOOL GetTextExtentExPoint(LPCTSTR lpszString, int cchString, LPSIZE lpSize, int nMaxExtent, LPINT lpnFit = NULL, LPINT alpDx = NULL)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::GetTextExtentExPoint(m_hDC, lpszString, cchString, nMaxExtent, lpnFit, alpDx, lpSize);\n\t}\n\n\tDWORD GetTabbedTextExtent(LPCTSTR lpszString, int nCount = -1, int nTabPositions = 0, LPINT lpnTabStopPositions = NULL) const\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\tif(nCount == -1)\n\t\t\tnCount = lstrlen(lpszString);\n\t\treturn ::GetTabbedTextExtent(m_hDC, lpszString, nCount, nTabPositions, lpnTabStopPositions);\n\t}\n\n\tBOOL GrayString(HBRUSH hBrush, BOOL (CALLBACK* lpfnOutput)(HDC, LPARAM, int), LPARAM lpData, int nCount, int x, int y, int nWidth, int nHeight)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::GrayString(m_hDC, hBrush, (GRAYSTRINGPROC)lpfnOutput, lpData, nCount, x, y, nWidth, nHeight);\n\t}\n\n\tUINT GetTextAlign() const\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::GetTextAlign(m_hDC);\n\t}\n\n\tUINT SetTextAlign(UINT nFlags)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::SetTextAlign(m_hDC, nFlags);\n\t}\n\n\tint GetTextFace(LPTSTR lpszFacename, int nCount) const\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::GetTextFace(m_hDC, nCount, lpszFacename);\n\t}\n\n\tint GetTextFaceLen() const\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::GetTextFace(m_hDC, 0, NULL);\n\t}\n\n#ifdef _OLEAUTO_H_\n\tBOOL GetTextFace(BSTR& bstrFace) const\n\t{\n\t\tUSES_CONVERSION;\n\t\tATLASSERT(m_hDC != NULL);\n\t\tATLASSERT(bstrFace == NULL);\n\n\t\tint nLen = GetTextFaceLen();\n\t\tif(nLen == 0)\n\t\t\treturn FALSE;\n\n\t\tATL::CTempBuffer<TCHAR, _WTL_STACK_ALLOC_THRESHOLD> buff;\n\t\tLPTSTR lpszText = buff.Allocate(nLen);\n\t\tif(lpszText == NULL)\n\t\t\treturn FALSE;\n\n\t\tif(!GetTextFace(lpszText, nLen))\n\t\t\treturn FALSE;\n\n\t\tbstrFace = ::SysAllocString(T2OLE(lpszText));\n\t\treturn (bstrFace != NULL) ? TRUE : FALSE;\n\t}\n#endif\n\n#ifdef __ATLSTR_H__\n\tint GetTextFace(ATL::CString& strFace) const\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\n\t\tint nLen = GetTextFaceLen();\n\t\tif(nLen == 0)\n\t\t\treturn 0;\n\n\t\tLPTSTR lpstr = strFace.GetBufferSetLength(nLen);\n\t\tif(lpstr == NULL)\n\t\t\treturn 0;\n\t\tint nRet = GetTextFace(lpstr, nLen);\n\t\tstrFace.ReleaseBuffer();\n\t\treturn nRet;\n\t}\n#endif // __ATLSTR_H__\n\n\tBOOL GetTextMetrics(LPTEXTMETRIC lpMetrics) const\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::GetTextMetrics(m_hDC, lpMetrics);\n\t}\n\n\tint SetTextJustification(int nBreakExtra, int nBreakCount)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::SetTextJustification(m_hDC, nBreakExtra, nBreakCount);\n\t}\n\n\tint GetTextCharacterExtra() const\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::GetTextCharacterExtra(m_hDC);\n\t}\n\n\tint SetTextCharacterExtra(int nCharExtra)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::SetTextCharacterExtra(m_hDC, nCharExtra);\n\t}\n\n// Advanced Drawing\n\tBOOL DrawEdge(LPRECT lpRect, UINT nEdge, UINT nFlags)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::DrawEdge(m_hDC, lpRect, nEdge, nFlags);\n\t}\n\n\tBOOL DrawFrameControl(LPRECT lpRect, UINT nType, UINT nState)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::DrawFrameControl(m_hDC, lpRect, nType, nState);\n\t}\n\n// Scrolling Functions\n\tBOOL ScrollDC(int dx, int dy, LPCRECT lpRectScroll, LPCRECT lpRectClip, HRGN hRgnUpdate, LPRECT lpRectUpdate)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::ScrollDC(m_hDC, dx, dy, lpRectScroll, lpRectClip, hRgnUpdate, lpRectUpdate);\n\t}\n\n// Font Functions\n\tBOOL GetCharWidth(UINT nFirstChar, UINT nLastChar, LPINT lpBuffer) const\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::GetCharWidth(m_hDC, nFirstChar, nLastChar, lpBuffer);\n\t}\n\n\t// GetCharWidth32 is not supported under Win9x\n\tBOOL GetCharWidth32(UINT nFirstChar, UINT nLastChar, LPINT lpBuffer) const\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::GetCharWidth32(m_hDC, nFirstChar, nLastChar, lpBuffer);\n\t}\n\n\tDWORD SetMapperFlags(DWORD dwFlag)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::SetMapperFlags(m_hDC, dwFlag);\n\t}\n\n\tBOOL GetAspectRatioFilter(LPSIZE lpSize) const\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::GetAspectRatioFilterEx(m_hDC, lpSize);\n\t}\n\n\tBOOL GetCharABCWidths(UINT nFirstChar, UINT nLastChar, LPABC lpabc) const\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::GetCharABCWidths(m_hDC, nFirstChar, nLastChar, lpabc);\n\t}\n\n\tDWORD GetFontData(DWORD dwTable, DWORD dwOffset, LPVOID lpData, DWORD cbData) const\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::GetFontData(m_hDC, dwTable, dwOffset, lpData, cbData);\n\t}\n\n\tint GetKerningPairs(int nPairs, LPKERNINGPAIR lpkrnpair) const\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::GetKerningPairs(m_hDC, nPairs, lpkrnpair);\n\t}\n\n\tUINT GetOutlineTextMetrics(UINT cbData, LPOUTLINETEXTMETRIC lpotm) const\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::GetOutlineTextMetrics(m_hDC, cbData, lpotm);\n\t}\n\n\tDWORD GetGlyphOutline(UINT nChar, UINT nFormat, LPGLYPHMETRICS lpgm, DWORD cbBuffer, LPVOID lpBuffer, const MAT2* lpmat2) const\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::GetGlyphOutline(m_hDC, nChar, nFormat, lpgm, cbBuffer, lpBuffer, lpmat2);\n\t}\n\n\tBOOL GetCharABCWidths(UINT nFirstChar, UINT nLastChar, LPABCFLOAT lpABCF) const\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::GetCharABCWidthsFloat(m_hDC, nFirstChar, nLastChar, lpABCF);\n\t}\n\n\tBOOL GetCharWidth(UINT nFirstChar, UINT nLastChar, float* lpFloatBuffer) const\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::GetCharWidthFloat(m_hDC, nFirstChar, nLastChar, lpFloatBuffer);\n\t}\n\n// Printer/Device Escape Functions\n\tint Escape(int nEscape, int nCount, LPCSTR lpszInData, LPVOID lpOutData)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::Escape(m_hDC, nEscape, nCount, lpszInData, lpOutData);\n\t}\n\n\tint Escape(int nEscape, int nInputSize, LPCSTR lpszInputData,\n\t\tint nOutputSize, LPSTR lpszOutputData)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::ExtEscape(m_hDC, nEscape, nInputSize, lpszInputData, nOutputSize, lpszOutputData);\n\t}\n\n\tint DrawEscape(int nEscape, int nInputSize, LPCSTR lpszInputData)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::DrawEscape(m_hDC, nEscape, nInputSize, lpszInputData);\n\t}\n\n\t// Escape helpers\n\tint StartDoc(LPCTSTR lpszDocName)  // old Win3.0 version\n\t{\n\t\tDOCINFO di = {};\n\t\tdi.cbSize = sizeof(DOCINFO);\n\t\tdi.lpszDocName = lpszDocName;\n\t\treturn StartDoc(&di);\n\t}\n\n\tint StartDoc(LPDOCINFO lpDocInfo)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::StartDoc(m_hDC, lpDocInfo);\n\t}\n\n\tint StartPage()\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::StartPage(m_hDC);\n\t}\n\n\tint EndPage()\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::EndPage(m_hDC);\n\t}\n\n\tint SetAbortProc(BOOL (CALLBACK* lpfn)(HDC, int))\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::SetAbortProc(m_hDC, (ABORTPROC)lpfn);\n\t}\n\n\tint AbortDoc()\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::AbortDoc(m_hDC);\n\t}\n\n\tint EndDoc()\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::EndDoc(m_hDC);\n\t}\n\n// MetaFile Functions\n\tBOOL PlayMetaFile(HMETAFILE hMF)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\tif(::GetDeviceCaps(m_hDC, TECHNOLOGY) == DT_METAFILE)\n\t\t{\n\t\t\t// playing metafile in metafile, just use core windows API\n\t\t\treturn ::PlayMetaFile(m_hDC, hMF);\n\t\t}\n\n\t\t// for special playback, lParam == pDC\n\t\treturn ::EnumMetaFile(m_hDC, hMF, EnumMetaFileProc, (LPARAM)this);\n\t}\n\n\tBOOL PlayMetaFile(HENHMETAFILE hEnhMetaFile, LPCRECT lpBounds)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::PlayEnhMetaFile(m_hDC, hEnhMetaFile, lpBounds);\n\t}\n\n\tBOOL AddMetaFileComment(UINT nDataSize, const BYTE* pCommentData) // can be used for enhanced metafiles only\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::GdiComment(m_hDC, nDataSize, pCommentData);\n\t}\n\n\t// Special handling for metafile playback\n\tstatic int CALLBACK EnumMetaFileProc(HDC hDC, HANDLETABLE* pHandleTable, METARECORD* pMetaRec, int nHandles, LPARAM lParam)\n\t{\n\t\tCDCT<false>* pDC = (CDCT<false>*)lParam;\n\n\t\tswitch (pMetaRec->rdFunction)\n\t\t{\n\t\tcase META_SETMAPMODE:\n\t\t\tpDC->SetMapMode((int)(short)pMetaRec->rdParm[0]);\n\t\t\tbreak;\n\t\tcase META_SETWINDOWEXT:\n\t\t\tpDC->SetWindowExt((int)(short)pMetaRec->rdParm[1], (int)(short)pMetaRec->rdParm[0]);\n\t\t\tbreak;\n\t\tcase META_SETWINDOWORG:\n\t\t\tpDC->SetWindowOrg((int)(short)pMetaRec->rdParm[1], (int)(short)pMetaRec->rdParm[0]);\n\t\t\tbreak;\n\t\tcase META_SETVIEWPORTEXT:\n\t\t\tpDC->SetViewportExt((int)(short)pMetaRec->rdParm[1], (int)(short)pMetaRec->rdParm[0]);\n\t\t\tbreak;\n\t\tcase META_SETVIEWPORTORG:\n\t\t\tpDC->SetViewportOrg((int)(short)pMetaRec->rdParm[1], (int)(short)pMetaRec->rdParm[0]);\n\t\t\tbreak;\n\t\tcase META_SCALEWINDOWEXT:\n\t\t\tpDC->ScaleWindowExt((int)(short)pMetaRec->rdParm[3], (int)(short)pMetaRec->rdParm[2], \n\t\t\t\t(int)(short)pMetaRec->rdParm[1], (int)(short)pMetaRec->rdParm[0]);\n\t\t\tbreak;\n\t\tcase META_SCALEVIEWPORTEXT:\n\t\t\tpDC->ScaleViewportExt((int)(short)pMetaRec->rdParm[3], (int)(short)pMetaRec->rdParm[2],\n\t\t\t\t(int)(short)pMetaRec->rdParm[1], (int)(short)pMetaRec->rdParm[0]);\n\t\t\tbreak;\n\t\tcase META_OFFSETVIEWPORTORG:\n\t\t\tpDC->OffsetViewportOrg((int)(short)pMetaRec->rdParm[1], (int)(short)pMetaRec->rdParm[0]);\n\t\t\tbreak;\n\t\tcase META_SAVEDC:\n\t\t\tpDC->SaveDC();\n\t\t\tbreak;\n\t\tcase META_RESTOREDC:\n\t\t\tpDC->RestoreDC((int)(short)pMetaRec->rdParm[0]);\n\t\t\tbreak;\n\t\tcase META_SETBKCOLOR:\n\t\t\tpDC->SetBkColor(*(UNALIGNED COLORREF*)&pMetaRec->rdParm[0]);\n\t\t\tbreak;\n\t\tcase META_SETTEXTCOLOR:\n\t\t\tpDC->SetTextColor(*(UNALIGNED COLORREF*)&pMetaRec->rdParm[0]);\n\t\t\tbreak;\n\n\t\t// need to watch out for SelectObject(HFONT), for custom font mapping\n\t\tcase META_SELECTOBJECT:\n\t\t\t{\n\t\t\t\tHGDIOBJ hObject = pHandleTable->objectHandle[pMetaRec->rdParm[0]];\n\t\t\t\tUINT nObjType = ::GetObjectType(hObject);\n\t\t\t\tif(nObjType == 0)\n\t\t\t\t{\n\t\t\t\t\t// object type is unknown, determine if it is a font\n\t\t\t\t\tHFONT hStockFont = (HFONT)::GetStockObject(SYSTEM_FONT);\n\t\t\t\t\tHFONT hFontOld = (HFONT)::SelectObject(pDC->m_hDC, hStockFont);\n\t\t\t\t\tHGDIOBJ hObjOld = ::SelectObject(pDC->m_hDC, hObject);\n\t\t\t\t\tif(hObjOld == hStockFont)\n\t\t\t\t\t{\n\t\t\t\t\t\t// got the stock object back, so must be selecting a font\n\t\t\t\t\t\tpDC->SelectFont((HFONT)hObject);\n\t\t\t\t\t\tbreak;  // don't play the default record\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\t// didn't get the stock object back, so restore everything\n\t\t\t\t\t\t::SelectObject(pDC->m_hDC, hFontOld);\n\t\t\t\t\t\t::SelectObject(pDC->m_hDC, hObjOld);\n\t\t\t\t\t}\n\t\t\t\t\t// and fall through to PlayMetaFileRecord...\n\t\t\t\t}\n\t\t\t\telse if(nObjType == OBJ_FONT)\n\t\t\t\t{\n\t\t\t\t\t// play back as CDCHandle::SelectFont(HFONT)\n\t\t\t\t\tpDC->SelectFont((HFONT)hObject);\n\t\t\t\t\tbreak;  // don't play the default record\n\t\t\t\t}\n\t\t\t}\n\t\t\t// fall through...\n\n\t\tdefault:\n\t\t\t::PlayMetaFileRecord(hDC, pHandleTable, pMetaRec, nHandles);\n\t\t\tbreak;\n\t\t}\n\n\t\treturn 1;\n\t}\n\n// Path Functions\n\tBOOL AbortPath()\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::AbortPath(m_hDC);\n\t}\n\n\tBOOL BeginPath()\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::BeginPath(m_hDC);\n\t}\n\n\tBOOL CloseFigure()\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::CloseFigure(m_hDC);\n\t}\n\n\tBOOL EndPath()\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::EndPath(m_hDC);\n\t}\n\n\tBOOL FillPath()\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::FillPath(m_hDC);\n\t}\n\n\tBOOL FlattenPath()\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::FlattenPath(m_hDC);\n\t}\n\n\tBOOL StrokeAndFillPath()\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::StrokeAndFillPath(m_hDC);\n\t}\n\n\tBOOL StrokePath()\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::StrokePath(m_hDC);\n\t}\n\n\tBOOL WidenPath()\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::WidenPath(m_hDC);\n\t}\n\n\tBOOL GetMiterLimit(PFLOAT pfMiterLimit) const\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::GetMiterLimit(m_hDC, pfMiterLimit);\n\t}\n\n\tBOOL SetMiterLimit(float fMiterLimit)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::SetMiterLimit(m_hDC, fMiterLimit, NULL);\n\t}\n\n\tint GetPath(LPPOINT lpPoints, LPBYTE lpTypes, int nCount) const\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::GetPath(m_hDC, lpPoints, lpTypes, nCount);\n\t}\n\n\tBOOL SelectClipPath(int nMode)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::SelectClipPath(m_hDC, nMode);\n\t}\n\n// Misc Helper Functions\n\tstatic CBrushHandle PASCAL GetHalftoneBrush()\n\t{\n\t\tHBRUSH halftoneBrush = NULL;\n\t\tWORD grayPattern[8] = {};\n\t\tfor(int i = 0; i < 8; i++)\n\t\t\tgrayPattern[i] = (WORD)(0x5555 << (i & 1));\n\t\tHBITMAP grayBitmap = CreateBitmap(8, 8, 1, 1, &grayPattern);\n\t\tif(grayBitmap != NULL)\n\t\t{\n\t\t\thalftoneBrush = ::CreatePatternBrush(grayBitmap);\n\t\t\tDeleteObject(grayBitmap);\n\t\t}\n\t\treturn CBrushHandle(halftoneBrush);\n\t}\n\n\tvoid DrawDragRect(LPCRECT lpRect, SIZE size, LPCRECT lpRectLast, SIZE sizeLast, HBRUSH hBrush = NULL, HBRUSH hBrushLast = NULL)\n\t{\n\t\t// first, determine the update region and select it\n\t\tCRgn rgnOutside;\n\t\trgnOutside.CreateRectRgnIndirect(lpRect);\n\t\tRECT rect = *lpRect;\n\t\t::InflateRect(&rect, -size.cx, -size.cy);\n\t\t::IntersectRect(&rect, &rect, lpRect);\n\t\tCRgn rgnInside;\n\t\trgnInside.CreateRectRgnIndirect(&rect);\n\t\tCRgn rgnNew;\n\t\trgnNew.CreateRectRgn(0, 0, 0, 0);\n\t\trgnNew.CombineRgn(rgnOutside, rgnInside, RGN_XOR);\n\n\t\tHBRUSH hBrushOld = NULL;\n\t\tCBrush brushHalftone;\n\t\tif(hBrush == NULL)\n\t\t\tbrushHalftone = hBrush = CDCHandle::GetHalftoneBrush();\n\t\tif(hBrushLast == NULL)\n\t\t\thBrushLast = hBrush;\n\n\t\tCRgn rgnLast;\n\t\tCRgn rgnUpdate;\n\t\tif(lpRectLast != NULL)\n\t\t{\n\t\t\t// find difference between new region and old region\n\t\t\trgnLast.CreateRectRgn(0, 0, 0, 0);\n\t\t\trgnOutside.SetRectRgn(lpRectLast->left, lpRectLast->top, lpRectLast->right, lpRectLast->bottom);\n\t\t\trect = *lpRectLast;\n\t\t\t::InflateRect(&rect, -sizeLast.cx, -sizeLast.cy);\n\t\t\t::IntersectRect(&rect, &rect, lpRectLast);\n\t\t\trgnInside.SetRectRgn(rect.left, rect.top, rect.right, rect.bottom);\n\t\t\trgnLast.CombineRgn(rgnOutside, rgnInside, RGN_XOR);\n\n\t\t\t// only diff them if brushes are the same\n\t\t\tif(hBrush == hBrushLast)\n\t\t\t{\n\t\t\t\trgnUpdate.CreateRectRgn(0, 0, 0, 0);\n\t\t\t\trgnUpdate.CombineRgn(rgnLast, rgnNew, RGN_XOR);\n\t\t\t}\n\t\t}\n\t\tif((hBrush != hBrushLast) && (lpRectLast != NULL))\n\t\t{\n\t\t\t// brushes are different -- erase old region first\n\t\t\tSelectClipRgn(rgnLast);\n\t\t\tGetClipBox(&rect);\n\t\t\thBrushOld = SelectBrush(hBrushLast);\n\t\t\tPatBlt(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, PATINVERT);\n\t\t\tSelectBrush(hBrushOld);\n\t\t\thBrushOld = NULL;\n\t\t}\n\n\t\t// draw into the update/new region\n\t\tSelectClipRgn(rgnUpdate.IsNull() ? rgnNew : rgnUpdate);\n\t\tGetClipBox(&rect);\n\t\thBrushOld = SelectBrush(hBrush);\n\t\tPatBlt(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, PATINVERT);\n\n\t\t// cleanup DC\n\t\tif(hBrushOld != NULL)\n\t\t\tSelectBrush(hBrushOld);\n\t\tSelectClipRgn(NULL);\n\t}\n\n\tvoid FillSolidRect(LPCRECT lpRect, COLORREF clr)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\n\t\tCOLORREF clrOld = ::SetBkColor(m_hDC, clr);\n\t\tATLASSERT(clrOld != CLR_INVALID);\n\t\tif(clrOld != CLR_INVALID)\n\t\t{\n\t\t\t::ExtTextOut(m_hDC, 0, 0, ETO_OPAQUE, lpRect, NULL, 0, NULL);\n\t\t\t::SetBkColor(m_hDC, clrOld);\n\t\t}\n\t}\n\n\tvoid FillSolidRect(int x, int y, int cx, int cy, COLORREF clr)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\n\t\tRECT rect = { x, y, x + cx, y + cy };\n\t\tFillSolidRect(&rect, clr);\n\t}\n\n\tvoid Draw3dRect(LPCRECT lpRect, COLORREF clrTopLeft, COLORREF clrBottomRight)\n\t{\n\t\tDraw3dRect(lpRect->left, lpRect->top, lpRect->right - lpRect->left,\n\t\t\tlpRect->bottom - lpRect->top, clrTopLeft, clrBottomRight);\n\t}\n\n\tvoid Draw3dRect(int x, int y, int cx, int cy, COLORREF clrTopLeft, COLORREF clrBottomRight)\n\t{\n\t\tFillSolidRect(x, y, cx - 1, 1, clrTopLeft);\n\t\tFillSolidRect(x, y, 1, cy - 1, clrTopLeft);\n\t\tFillSolidRect(x + cx, y, -1, cy, clrBottomRight);\n\t\tFillSolidRect(x, y + cy, cx, -1, clrBottomRight);\n\t}\n\n// DIB support\n\tint SetDIBitsToDevice(int x, int y, DWORD dwWidth, DWORD dwHeight, int xSrc, int ySrc, UINT uStartScan, UINT cScanLines, CONST VOID* lpvBits, CONST BITMAPINFO* lpbmi, UINT uColorUse)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::SetDIBitsToDevice(m_hDC, x, y, dwWidth, dwHeight, xSrc, ySrc, uStartScan, cScanLines, lpvBits, lpbmi, uColorUse);\n\t}\n\n\tint StretchDIBits(int x, int y, int nWidth, int nHeight, int xSrc, int ySrc, int nSrcWidth, int nSrcHeight, CONST VOID* lpvBits, CONST BITMAPINFO* lpbmi, UINT uColorUse, DWORD dwRop)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::StretchDIBits(m_hDC, x, y, nWidth, nHeight, xSrc, ySrc, nSrcWidth, nSrcHeight, lpvBits, lpbmi, uColorUse, dwRop);\n\t}\n\n\tUINT GetDIBColorTable(UINT uStartIndex, UINT cEntries, RGBQUAD* pColors) const\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::GetDIBColorTable(m_hDC, uStartIndex, cEntries, pColors);\n\t}\n\n\tUINT SetDIBColorTable(UINT uStartIndex, UINT cEntries, CONST RGBQUAD* pColors)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::SetDIBColorTable(m_hDC, uStartIndex, cEntries, pColors);\n\t}\n\n// OpenGL support\n#if !defined(_ATL_NO_OPENGL)\n\tint ChoosePixelFormat(CONST PIXELFORMATDESCRIPTOR* ppfd)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::ChoosePixelFormat(m_hDC, ppfd);\n\t}\n\n\tint DescribePixelFormat(int iPixelFormat, UINT nBytes, LPPIXELFORMATDESCRIPTOR ppfd)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::DescribePixelFormat(m_hDC, iPixelFormat, nBytes, ppfd);\n\t}\n\n\tint GetPixelFormat() const\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::GetPixelFormat(m_hDC);\n\t}\n\n\tBOOL SetPixelFormat(int iPixelFormat, CONST PIXELFORMATDESCRIPTOR* ppfd)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::SetPixelFormat(m_hDC, iPixelFormat, ppfd);\n\t}\n\n\tBOOL SwapBuffers()\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::SwapBuffers(m_hDC);\n\t}\n\n\tHGLRC wglCreateContext()\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::wglCreateContext(m_hDC);\n\t}\n\n\tHGLRC wglCreateLayerContext(int iLayerPlane)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::wglCreateLayerContext(m_hDC, iLayerPlane);\n\t}\n\n\tBOOL wglMakeCurrent(HGLRC hglrc)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::wglMakeCurrent(m_hDC, hglrc);\n\t}\n\n\tBOOL wglUseFontBitmaps(DWORD dwFirst, DWORD dwCount, DWORD listBase)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::wglUseFontBitmaps(m_hDC, dwFirst, dwCount, listBase);\n\t}\n\n\tBOOL wglUseFontOutlines(DWORD dwFirst, DWORD dwCount, DWORD listBase, FLOAT deviation, FLOAT extrusion, int format, LPGLYPHMETRICSFLOAT lpgmf)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::wglUseFontOutlines(m_hDC, dwFirst, dwCount, listBase, deviation, extrusion, format, lpgmf);\n\t}\n\n\tBOOL wglDescribeLayerPlane(int iPixelFormat, int iLayerPlane, UINT nBytes, LPLAYERPLANEDESCRIPTOR plpd)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::wglDescribeLayerPlane(m_hDC, iPixelFormat, iLayerPlane, nBytes, plpd);\n\t}\n\n\tint wglSetLayerPaletteEntries(int iLayerPlane, int iStart, int cEntries, CONST COLORREF* pclr)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::wglSetLayerPaletteEntries(m_hDC, iLayerPlane, iStart, cEntries, pclr);\n\t}\n\n\tint wglGetLayerPaletteEntries(int iLayerPlane, int iStart, int cEntries, COLORREF* pclr)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::wglGetLayerPaletteEntries(m_hDC, iLayerPlane, iStart, cEntries, pclr);\n\t}\n\n\tBOOL wglRealizeLayerPalette(int iLayerPlane, BOOL bRealize)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::wglRealizeLayerPalette(m_hDC, iLayerPlane, bRealize);\n\t}\n\n\tBOOL wglSwapLayerBuffers(UINT uPlanes)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::wglSwapLayerBuffers(m_hDC, uPlanes);\n\t}\n#endif // !defined(_ATL_NO_OPENGL)\n\n\tCOLORREF GetDCPenColor() const\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::GetDCPenColor(m_hDC);\n\t}\n\n\tCOLORREF SetDCPenColor(COLORREF clr)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::SetDCPenColor(m_hDC, clr);\n\t}\n\n\tCOLORREF GetDCBrushColor() const\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::GetDCBrushColor(m_hDC);\n\t}\n\n\tCOLORREF SetDCBrushColor(COLORREF clr)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::SetDCBrushColor(m_hDC, clr);\n\t}\n\n\tDWORD GetFontUnicodeRanges(LPGLYPHSET lpgs) const\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::GetFontUnicodeRanges(m_hDC, lpgs);\n\t}\n\n\tDWORD GetGlyphIndices(LPCTSTR lpstr, int cch, LPWORD pgi, DWORD dwFlags) const\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::GetGlyphIndices(m_hDC, lpstr, cch, pgi, dwFlags);\n\t}\n\n\tBOOL GetTextExtentPointI(LPWORD pgiIn, int cgi, LPSIZE lpSize) const\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::GetTextExtentPointI(m_hDC, pgiIn, cgi, lpSize);\n\t}\n\n\tBOOL GetTextExtentExPointI(LPWORD pgiIn, int cgi, int nMaxExtent, LPINT lpnFit, LPINT alpDx, LPSIZE lpSize) const\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::GetTextExtentExPointI(m_hDC, pgiIn, cgi, nMaxExtent, lpnFit, alpDx, lpSize);\n\t}\n\n\tBOOL GetCharWidthI(UINT giFirst, UINT cgi, LPWORD pgi, LPINT lpBuffer) const\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::GetCharWidthI(m_hDC, giFirst, cgi, pgi, lpBuffer);\n\t}\n\n\tBOOL GetCharABCWidthsI(UINT giFirst, UINT cgi, LPWORD pgi, LPABC lpabc) const\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::GetCharABCWidthsI(m_hDC, giFirst, cgi, pgi, lpabc);\n\t}\n\n\tBOOL ColorCorrectPalette(HPALETTE hPalette, DWORD dwFirstEntry, DWORD dwNumOfEntries)\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\treturn ::ColorCorrectPalette(m_hDC, hPalette, dwFirstEntry, dwNumOfEntries);\n\t}\n};\n\n\n///////////////////////////////////////////////////////////////////////////////\n// CDC Helpers\n\nclass CPaintDC : public CDC\n{\npublic:\n// Data members\n\tHWND m_hWnd;\n\tPAINTSTRUCT m_ps;\n\n// Constructor/destructor\n\tCPaintDC(HWND hWnd)\n\t{\n\t\tATLASSERT(::IsWindow(hWnd));\n\t\tm_hWnd = hWnd;\n\t\tm_hDC = ::BeginPaint(hWnd, &m_ps);\n\t}\n\n\t~CPaintDC()\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\tATLASSERT(::IsWindow(m_hWnd));\n\t\t::EndPaint(m_hWnd, &m_ps);\n\t\tDetach();\n\t}\n};\n\nclass CClientDC : public CDC\n{\npublic:\n// Data members\n\tHWND m_hWnd;\n\n// Constructor/destructor\n\tCClientDC(HWND hWnd)\n\t{\n\t\tATLASSERT((hWnd == NULL) || ::IsWindow(hWnd));\n\t\tm_hWnd = hWnd;\n\t\tm_hDC = ::GetDC(hWnd);\n\t}\n\n\t~CClientDC()\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\t::ReleaseDC(m_hWnd, Detach());\n\t}\n};\n\nclass CWindowDC : public CDC\n{\npublic:\n// Data members\n\tHWND m_hWnd;\n\n// Constructor/destructor\n\tCWindowDC(HWND hWnd)\n\t{\n\t\tATLASSERT((hWnd == NULL) || ::IsWindow(hWnd));\n\t\tm_hWnd = hWnd;\n\t\tm_hDC = ::GetWindowDC(hWnd);\n\t}\n\n\t~CWindowDC()\n\t{\n\t\tATLASSERT(m_hDC != NULL);\n\t\t::ReleaseDC(m_hWnd, Detach());\n\t}\n};\n\nclass CMemoryDC : public CDC\n{\npublic:\n// Data members\n\tHDC m_hDCOriginal;\n\tRECT m_rcPaint;\n\tCBitmap m_bmp;\n\tHBITMAP m_hBmpOld;\n\n// Constructor/destructor\n\tCMemoryDC(HDC hDC, const RECT& rcPaint) : m_hDCOriginal(hDC), m_hBmpOld(NULL)\n\t{\n\t\tm_rcPaint = rcPaint;\n\t\tCreateCompatibleDC(m_hDCOriginal);\n\t\tATLASSERT(m_hDC != NULL);\n\t\tm_bmp.CreateCompatibleBitmap(m_hDCOriginal, m_rcPaint.right - m_rcPaint.left, m_rcPaint.bottom - m_rcPaint.top);\n\t\tATLASSERT(m_bmp.m_hBitmap != NULL);\n\t\tm_hBmpOld = SelectBitmap(m_bmp);\n\t\tSetViewportOrg(-m_rcPaint.left, -m_rcPaint.top);\n\t}\n\n\t~CMemoryDC()\n\t{\n\t\t::BitBlt(m_hDCOriginal, m_rcPaint.left, m_rcPaint.top, m_rcPaint.right - m_rcPaint.left, m_rcPaint.bottom - m_rcPaint.top, m_hDC, m_rcPaint.left, m_rcPaint.top, SRCCOPY);\n\t\tSelectBitmap(m_hBmpOld);\n\t}\n};\n\n\n///////////////////////////////////////////////////////////////////////////////\n// Enhanced metafile support\n\nclass CEnhMetaFileInfo\n{\npublic:\n// Data members\n\tHENHMETAFILE m_hEMF;\n\tBYTE* m_pBits;\n\tTCHAR* m_pDesc;\n\tENHMETAHEADER m_header;\n\tPIXELFORMATDESCRIPTOR m_pfd;\n\n// Constructor/destructor\n\tCEnhMetaFileInfo(HENHMETAFILE hEMF) : m_hEMF(hEMF), m_pBits(NULL), m_pDesc(NULL)\n\t{\n\t\tmemset(&m_header, 0, sizeof(m_header));\n\t\tmemset(&m_pfd, 0, sizeof(m_pfd));\n\t}\n\n\t~CEnhMetaFileInfo()\n\t{\n\t\tdelete [] m_pBits;\n\t\tdelete [] m_pDesc;\n\t}\n\n// Operations\n\tBYTE* GetEnhMetaFileBits()\n\t{\n\t\tATLASSERT(m_hEMF != NULL);\n\t\tUINT nBytes = ::GetEnhMetaFileBits(m_hEMF, 0, NULL);\n\t\tdelete [] m_pBits;\n\t\tm_pBits = NULL;\n\t\tATLTRY(m_pBits = new BYTE[nBytes]);\n\t\tif (m_pBits != NULL)\n\t\t\t::GetEnhMetaFileBits(m_hEMF, nBytes, m_pBits);\n\t\treturn m_pBits;\n\t}\n\n\tLPTSTR GetEnhMetaFileDescription()\n\t{\n\t\tATLASSERT(m_hEMF != NULL);\n\t\tUINT nLen = ::GetEnhMetaFileDescription(m_hEMF, 0, NULL);\n\t\tdelete [] m_pDesc;\n\t\tm_pDesc = NULL;\n\t\tATLTRY(m_pDesc = new TCHAR[nLen]);\n\t\tif (m_pDesc != NULL)\n\t\t\tnLen = ::GetEnhMetaFileDescription(m_hEMF, nLen, m_pDesc);\n\t\treturn m_pDesc;\n\t}\n\n\tENHMETAHEADER* GetEnhMetaFileHeader()\n\t{\n\t\tATLASSERT(m_hEMF != NULL);\n\t\tmemset(&m_header, 0, sizeof(m_header));\n\t\tm_header.iType = EMR_HEADER;\n\t\tm_header.nSize = sizeof(ENHMETAHEADER);\n\t\tUINT n = ::GetEnhMetaFileHeader(m_hEMF, sizeof(ENHMETAHEADER), &m_header);\n\t\treturn (n != 0) ? &m_header : NULL;\n\t}\n\n\tPIXELFORMATDESCRIPTOR* GetEnhMetaFilePixelFormat()\n\t{\n\t\tATLASSERT(m_hEMF != NULL);\n\t\tmemset(&m_pfd, 0, sizeof(m_pfd));\n\t\tUINT n = ::GetEnhMetaFilePixelFormat(m_hEMF, sizeof(m_pfd), &m_pfd);\n\t\treturn (n != 0) ? &m_pfd : NULL;\n\t}\n};\n\n\ntemplate <bool t_bManaged>\nclass CEnhMetaFileT\n{\npublic:\n// Data members\n\tHENHMETAFILE m_hEMF;\n\n// Constructor/destructor\n\tCEnhMetaFileT(HENHMETAFILE hEMF = NULL) : m_hEMF(hEMF)\n\t{\n\t}\n\n\t~CEnhMetaFileT()\n\t{\n\t\tif(t_bManaged && (m_hEMF != NULL))\n\t\t\tDeleteObject();\n\t}\n\n// Operations\n\tCEnhMetaFileT<t_bManaged>& operator =(HENHMETAFILE hEMF)\n\t{\n\t\tAttach(hEMF);\n\t\treturn *this;\n\t}\n\n\tvoid Attach(HENHMETAFILE hEMF)\n\t{\n\t\tif(t_bManaged && (m_hEMF != NULL) && (m_hEMF != hEMF))\n\t\t\tDeleteObject();\n\t\tm_hEMF = hEMF;\n\t}\n\n\tHENHMETAFILE Detach()\n\t{\n\t\tHENHMETAFILE hEMF = m_hEMF;\n\t\tm_hEMF = NULL;\n\t\treturn hEMF;\n\t}\n\n\toperator HENHMETAFILE() const { return m_hEMF; }\n\n\tbool IsNull() const { return (m_hEMF == NULL); }\n\n\tBOOL DeleteObject()\n\t{\n\t\tATLASSERT(m_hEMF != NULL);\n\t\tBOOL bRet = ::DeleteEnhMetaFile(m_hEMF);\n\t\tm_hEMF = NULL;\n\t\treturn bRet;\n\t}\n\n\tUINT GetEnhMetaFileBits(UINT cbBuffer, LPBYTE lpbBuffer) const\n\t{\n\t\tATLASSERT(m_hEMF != NULL);\n\t\treturn ::GetEnhMetaFileBits(m_hEMF, cbBuffer, lpbBuffer);\n\t}\n\n\tUINT GetEnhMetaFileDescription(UINT cchBuffer, LPTSTR lpszDescription) const\n\t{\n\t\tATLASSERT(m_hEMF != NULL);\n\t\treturn ::GetEnhMetaFileDescription(m_hEMF, cchBuffer, lpszDescription);\n\t}\n\n\tUINT GetEnhMetaFileHeader(LPENHMETAHEADER lpemh) const\n\t{\n\t\tATLASSERT(m_hEMF != NULL);\n\t\tlpemh->iType = EMR_HEADER;\n\t\tlpemh->nSize = sizeof(ENHMETAHEADER);\n\t\treturn ::GetEnhMetaFileHeader(m_hEMF, sizeof(ENHMETAHEADER), lpemh);\n\t}\n\n\tUINT GetEnhMetaFilePaletteEntries(UINT cEntries, LPPALETTEENTRY lppe) const\n\t{\n\t\tATLASSERT(m_hEMF != NULL);\n\t\treturn ::GetEnhMetaFilePaletteEntries(m_hEMF, cEntries, lppe);\n\t}\n\n\tUINT GetEnhMetaFilePixelFormat(DWORD cbBuffer, PIXELFORMATDESCRIPTOR* ppfd) const\n\t{\n\t\tATLASSERT(m_hEMF != NULL);\n\t\treturn ::GetEnhMetaFilePixelFormat(m_hEMF, cbBuffer, ppfd);\n\t}\n};\n\ntypedef CEnhMetaFileT<false>   CEnhMetaFileHandle;\ntypedef CEnhMetaFileT<true>    CEnhMetaFile;\n\n\nclass CEnhMetaFileDC : public CDC\n{\npublic:\n// Constructor/destructor\n\tCEnhMetaFileDC()\n\t{\n\t}\n\n\tCEnhMetaFileDC(HDC hdc, LPCRECT lpRect)\n\t{\n\t\tCreate(hdc, NULL, lpRect, NULL);\n\t\tATLASSERT(m_hDC != NULL);\n\t}\n\n\tCEnhMetaFileDC(HDC hdcRef, LPCTSTR lpFilename, LPCRECT lpRect, LPCTSTR lpDescription)\n\t{\n\t\tCreate(hdcRef, lpFilename, lpRect, lpDescription);\n\t\tATLASSERT(m_hDC != NULL);\n\t}\n\n\t~CEnhMetaFileDC()\n\t{\n\t\tHENHMETAFILE hEMF = Close();\n\t\tif (hEMF != NULL)\n\t\t\t::DeleteEnhMetaFile(hEMF);\n\t}\n\n// Operations\n\tvoid Create(HDC hdcRef, LPCTSTR lpFilename, LPCRECT lpRect, LPCTSTR lpDescription)\n\t{\n\t\tATLASSERT(m_hDC == NULL);\n\t\tm_hDC = ::CreateEnhMetaFile(hdcRef, lpFilename, lpRect, lpDescription);\n\t}\n\n\tHENHMETAFILE Close()\n\t{\n\t\tHENHMETAFILE hEMF = NULL;\n\t\tif (m_hDC != NULL)\n\t\t{\n\t\t\thEMF = ::CloseEnhMetaFile(m_hDC);\n\t\t\tm_hDC = NULL;\n\t\t}\n\t\treturn hEMF;\n\t}\n};\n\n} // namespace WTL\n\n#endif // __ATLGDI_H__\n"
  },
  {
    "path": "Examples/WhisperDesktop/Utils/WTL/atlres.h",
    "content": "// Windows Template Library - WTL version 10.0\n// Copyright (C) Microsoft Corporation, WTL Team. All rights reserved.\n//\n// This file is a part of the Windows Template Library.\n// The use and distribution terms for this software are covered by the\n// Microsoft Public License (http://opensource.org/licenses/MS-PL)\n// which can be found in the file MS-PL.txt at the root folder.\n\n#ifndef __ATLRES_H__\n#define __ATLRES_H__\n\n#pragma once\n\n\n#ifdef RC_INVOKED\n#ifndef _INC_WINDOWS\n\n  #define _INC_WINDOWS\n\n  #define VS_VERSION_INFO     1\n\n  #ifdef APSTUDIO_INVOKED\n    #define APSTUDIO_HIDDEN_SYMBOLS // Ignore following symbols\n  #endif // APSTUDIO_INVOKED\n\n  #ifndef WINVER\n    #define WINVER 0x0500\n  #endif // !WINVER\n\n  #include <winresrc.h>\n\n    // operation messages sent to DLGINIT\n  #define LB_ADDSTRING    (WM_USER+1)\n  #define CB_ADDSTRING    (WM_USER+3)\n\n  #ifdef APSTUDIO_INVOKED\n    #undef APSTUDIO_HIDDEN_SYMBOLS\n  #endif // APSTUDIO_INVOKED\n\n  #ifdef IDC_STATIC\n    #undef IDC_STATIC\n  #endif // IDC_STATIC\n  #define IDC_STATIC      (-1)\n\n#endif // !_INC_WINDOWS\n#endif // RC_INVOKED\n\n#ifdef APSTUDIO_INVOKED\n  #define APSTUDIO_HIDDEN_SYMBOLS\n#endif // APSTUDIO_INVOKED\n\n///////////////////////////////////////////////////////////////////////////////\n// ATL resource types\n\n#ifndef RC_INVOKED\n  #define RT_DLGINIT  MAKEINTRESOURCE(240)\n  #define RT_TOOLBAR  MAKEINTRESOURCE(241)\n#endif // RC_INVOKED\n\n///////////////////////////////////////////////////////////////////////////////\n\n#ifdef APSTUDIO_INVOKED\n  #undef APSTUDIO_HIDDEN_SYMBOLS\n#endif // APSTUDIO_INVOKED\n\n///////////////////////////////////////////////////////////////////////////////\n// Standard window components\n\n#define ID_SEPARATOR                    0       // special separator value\n#define ID_DEFAULT_PANE                 0       // default status bar pane\n\n#ifndef RC_INVOKED  // code only\n// standard control bars (IDW = window ID)\n  #define ATL_IDW_TOOLBAR               0xE800  // main Toolbar for window\n  #define ATL_IDW_STATUS_BAR            0xE801  // Status bar window\n  #define ATL_IDW_COMMAND_BAR           0xE802  // Command bar window\n\n// parts of a frame window\n  #define ATL_IDW_CLIENT                0xE900\n  #define ATL_IDW_PANE_FIRST            0xE900  // first pane (256 max)\n  #define ATL_IDW_PANE_LAST             0xE9FF\n  #define ATL_IDW_HSCROLL_FIRST         0xEA00  // first Horz scrollbar (16 max)\n  #define ATL_IDW_VSCROLL_FIRST         0xEA10  // first Vert scrollbar (16 max)\n\n  #define ATL_IDW_SIZE_BOX              0xEA20  // size box for splitters\n  #define ATL_IDW_PANE_SAVE             0xEA21  // to shift ATL_IDW_PANE_FIRST\n\n// bands for a rebar\n  #define ATL_IDW_BAND_FIRST            0xEB00\n  #define ATL_IDW_BAND_LAST             0xEBFF\n#endif // !RC_INVOKED\n\n///////////////////////////////////////////////////////////////////////////////\n// Standard Commands\n\n// File commands\n#define ID_FILE_NEW                     0xE100\n#define ID_FILE_OPEN                    0xE101\n#define ID_FILE_CLOSE                   0xE102\n#define ID_FILE_SAVE                    0xE103\n#define ID_FILE_SAVE_AS                 0xE104\n#define ID_FILE_PAGE_SETUP              0xE105\n#define ID_FILE_PRINT_SETUP             0xE106\n#define ID_FILE_PRINT                   0xE107\n#define ID_FILE_PRINT_DIRECT            0xE108\n#define ID_FILE_PRINT_PREVIEW           0xE109\n#define ID_FILE_UPDATE                  0xE10A\n#define ID_FILE_SAVE_COPY_AS            0xE10B\n#define ID_FILE_SEND_MAIL               0xE10C\n\n#define ID_FILE_MRU_FIRST               0xE110\n#define ID_FILE_MRU_FILE1               0xE110          // range - 16 max\n#define ID_FILE_MRU_FILE2               0xE111\n#define ID_FILE_MRU_FILE3               0xE112\n#define ID_FILE_MRU_FILE4               0xE113\n#define ID_FILE_MRU_FILE5               0xE114\n#define ID_FILE_MRU_FILE6               0xE115\n#define ID_FILE_MRU_FILE7               0xE116\n#define ID_FILE_MRU_FILE8               0xE117\n#define ID_FILE_MRU_FILE9               0xE118\n#define ID_FILE_MRU_FILE10              0xE119\n#define ID_FILE_MRU_FILE11              0xE11A\n#define ID_FILE_MRU_FILE12              0xE11B\n#define ID_FILE_MRU_FILE13              0xE11C\n#define ID_FILE_MRU_FILE14              0xE11D\n#define ID_FILE_MRU_FILE15              0xE11E\n#define ID_FILE_MRU_FILE16              0xE11F\n#define ID_FILE_MRU_LAST                0xE11F\n\n// Edit commands\n#define ID_EDIT_CLEAR                   0xE120\n#define ID_EDIT_CLEAR_ALL               0xE121\n#define ID_EDIT_COPY                    0xE122\n#define ID_EDIT_CUT                     0xE123\n#define ID_EDIT_FIND                    0xE124\n#define ID_EDIT_PASTE                   0xE125\n#define ID_EDIT_PASTE_LINK              0xE126\n#define ID_EDIT_PASTE_SPECIAL           0xE127\n#define ID_EDIT_REPEAT                  0xE128\n#define ID_EDIT_REPLACE                 0xE129\n#define ID_EDIT_SELECT_ALL              0xE12A\n#define ID_EDIT_UNDO                    0xE12B\n#define ID_EDIT_REDO                    0xE12C\n#define ID_EDIT_DELETE                  ID_EDIT_CLEAR\n#define ID_EDIT_FIND_NEXT               ID_EDIT_REPEAT\n#define ID_EDIT_FIND_PREVIOUS           0xE12D\n\n// Window commands\n#define ID_WINDOW_NEW                   0xE130\n#define ID_WINDOW_ARRANGE               0xE131\n#define ID_WINDOW_CASCADE               0xE132\n#define ID_WINDOW_TILE_HORZ             0xE133\n#define ID_WINDOW_TILE_VERT             0xE134\n#define ID_WINDOW_SPLIT                 0xE135\n#ifndef RC_INVOKED      // code only\n  #define ATL_IDM_WINDOW_FIRST          0xE130\n  #define ATL_IDM_WINDOW_LAST           0xE13F\n  #define ATL_IDM_FIRST_MDICHILD        0xFF00  // window list starts here\n  #define ATL_IDM_LAST_MDICHILD         0xFFFD\n#endif // !RC_INVOKED\n// TabView\n#define ID_WINDOW_TABFIRST              0xFF00\t// = ATL_IDM_FIRST_MDICHILD\n#define ID_WINDOW_TABLAST               0xFFFD\n#define ID_WINDOW_SHOWTABLIST           0xFFFE\n\n// Help and App commands\n#define ID_APP_ABOUT                    0xE140\n#define ID_APP_EXIT                     0xE141\n#define ID_HELP_INDEX                   0xE142\n#define ID_HELP_FINDER                  0xE143\n#define ID_HELP_USING                   0xE144\n#define ID_CONTEXT_HELP                 0xE145      // shift-F1\n// special commands for processing help\n#define ID_HELP                         0xE146      // first attempt for F1\n#define ID_DEFAULT_HELP                 0xE147      // last attempt\n\n// Misc\n#define ID_NEXT_PANE                    0xE150\n#define ID_PREV_PANE                    0xE151\n#define ID_PANE_CLOSE                   0xE152\n#define ID_PANE_NEXT                    ID_NEXT_PANE\n#define ID_PANE_PREVIOUS                ID_PREV_PANE\n\n// Format\n#define ID_FORMAT_FONT                  0xE160\n\n// Scroll\n#define ID_SCROLL_UP                    0xE170\n#define ID_SCROLL_DOWN                  0xE171\n#define ID_SCROLL_PAGE_UP               0xE172\n#define ID_SCROLL_PAGE_DOWN             0xE173\n#define ID_SCROLL_TOP                   0xE174\n#define ID_SCROLL_BOTTOM                0xE175\n#define ID_SCROLL_LEFT                  0xE176\n#define ID_SCROLL_RIGHT                 0xE177\n#define ID_SCROLL_PAGE_LEFT             0xE178\n#define ID_SCROLL_PAGE_RIGHT            0xE179\n#define ID_SCROLL_ALL_LEFT              0xE17A\n#define ID_SCROLL_ALL_RIGHT             0xE17B\n\n// OLE commands\n#define ID_OLE_INSERT_NEW               0xE200\n#define ID_OLE_EDIT_LINKS               0xE201\n#define ID_OLE_EDIT_CONVERT             0xE202\n#define ID_OLE_EDIT_CHANGE_ICON         0xE203\n#define ID_OLE_EDIT_PROPERTIES          0xE204\n#define ID_OLE_VERB_FIRST               0xE210     // range - 16 max\n#ifndef RC_INVOKED      // code only\n  #define ID_OLE_VERB_LAST              0xE21F\n#endif // !RC_INVOKED\n\n// View commands (same number used as IDW used for toolbar and status bar)\n#define ID_VIEW_TOOLBAR                 0xE800\n#define ID_VIEW_STATUS_BAR              0xE801\n#define ID_VIEW_REFRESH                 0xE803\n#define ID_VIEW_RIBBON                  0xE804\n\n///////////////////////////////////////////////////////////////////////////////\n// Standard control IDs\n\n#ifdef IDC_STATIC\n  #undef IDC_STATIC\n#endif // IDC_STATIC\n#define IDC_STATIC              (-1)     // all static controls\n\n///////////////////////////////////////////////////////////////////////////////\n// Standard string error/warnings\n\n// idle status bar message\n#define ATL_IDS_IDLEMESSAGE             0xE001\n\n#ifndef RC_INVOKED      // code only\n  #define ATL_IDS_SCFIRST               0xEF00\n#endif // !RC_INVOKED\n\n#define ATL_IDS_SCSIZE                  0xEF00\n#define ATL_IDS_SCMOVE                  0xEF01\n#define ATL_IDS_SCMINIMIZE              0xEF02\n#define ATL_IDS_SCMAXIMIZE              0xEF03\n#define ATL_IDS_SCNEXTWINDOW            0xEF04\n#define ATL_IDS_SCPREVWINDOW            0xEF05\n#define ATL_IDS_SCCLOSE                 0xEF06\n#define ATL_IDS_SCRESTORE               0xEF12\n#define ATL_IDS_SCTASKLIST              0xEF13\n\n#define ATL_IDS_MDICHILD                0xEF1F\n#define ATL_IDS_MRU_FILE                0xEFDA\n\n///////////////////////////////////////////////////////////////////////////////\n// Misc. control IDs\n\n// Property Sheet control id's (determined with Spy++)\n#define ID_APPLY_NOW                    0x3021\n#define ID_WIZBACK                      0x3023\n#define ID_WIZNEXT                      0x3024\n#define ID_WIZFINISH                    0x3025\n#define ATL_IDC_TAB_CONTROL             0x3020\n\n#endif // __ATLRES_H__\n"
  },
  {
    "path": "Examples/WhisperDesktop/Utils/WTL/atluser.h",
    "content": "// Windows Template Library - WTL version 10.0\n// Copyright (C) Microsoft Corporation, WTL Team. All rights reserved.\n//\n// This file is a part of the Windows Template Library.\n// The use and distribution terms for this software are covered by the\n// Microsoft Public License (http://opensource.org/licenses/MS-PL)\n// which can be found in the file MS-PL.txt at the root folder.\n\n#ifndef __ATLUSER_H__\n#define __ATLUSER_H__\n\n#pragma once\n\n#ifndef __ATLAPP_H__\n\t#error atluser.h requires atlapp.h to be included first\n#endif\n\n\n///////////////////////////////////////////////////////////////////////////////\n// Classes in this file:\n//\n// CMenuItemInfo\n// CMenuT<t_bManaged>\n// CAcceleratorT<t_bManaged>\n// CIconT<t_bManaged>\n// CCursorT<t_bManaged>\n// CResource\n//\n// Global functions:\n//   AtlMessageBox()\n//\n//   AtlLoadAccelerators()\n//   AtlLoadMenu()\n//   AtlLoadBitmap()\n//   AtlLoadSysBitmap()\n//   AtlLoadCursor()\n//   AtlLoadSysCursor()\n//   AtlLoadIcon()\n//   AtlLoadSysIcon()\n//   AtlLoadBitmapImage()\n//   AtlLoadCursorImage()\n//   AtlLoadIconImage()\n//   AtlLoadSysBitmapImage()\n//   AtlLoadSysCursorImage()\n//   AtlLoadSysIconImage()\n//   AtlLoadString()\n\n\nnamespace WTL\n{\n\n///////////////////////////////////////////////////////////////////////////////\n// AtlMessageBox - accepts both memory and resource based strings\n\ninline int AtlMessageBox(HWND hWndOwner, ATL::_U_STRINGorID message, ATL::_U_STRINGorID title = (LPCTSTR)NULL, UINT uType = MB_OK | MB_ICONINFORMATION)\n{\n\tATLASSERT((hWndOwner == NULL) || ::IsWindow(hWndOwner));\n\n\tLPTSTR lpstrMessage = NULL;\n\tif(IS_INTRESOURCE(message.m_lpstr))\n\t{\n\t\tfor(int nLen = 256; ; nLen *= 2)\n\t\t{\n\t\t\tATLTRY(lpstrMessage = new TCHAR[nLen]);\n\t\t\tif(lpstrMessage == NULL)\n\t\t\t{\n\t\t\t\tATLASSERT(FALSE);\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\tint nRes = ::LoadString(ModuleHelper::GetResourceInstance(), LOWORD(message.m_lpstr), lpstrMessage, nLen);\n\t\t\tif(nRes < nLen - 1)\n\t\t\t\tbreak;\n\t\t\tdelete [] lpstrMessage;\n\t\t\tlpstrMessage = NULL;\n\t\t}\n\n\t\tmessage.m_lpstr = lpstrMessage;\n\t}\n\n\tLPTSTR lpstrTitle = NULL;\n\tif(IS_INTRESOURCE(title.m_lpstr) && (LOWORD(title.m_lpstr) != 0))\n\t{\n\t\tfor(int nLen = 256; ; nLen *= 2)\n\t\t{\n\t\t\tATLTRY(lpstrTitle = new TCHAR[nLen]);\n\t\t\tif(lpstrTitle == NULL)\n\t\t\t{\n\t\t\t\tATLASSERT(FALSE);\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\tint nRes = ::LoadString(ModuleHelper::GetResourceInstance(), LOWORD(title.m_lpstr), lpstrTitle, nLen);\n\t\t\tif(nRes < nLen - 1)\n\t\t\t\tbreak;\n\t\t\tdelete [] lpstrTitle;\n\t\t\tlpstrTitle = NULL;\n\t\t}\n\n\t\ttitle.m_lpstr = lpstrTitle;\n\t}\n\n\tint nRet = ::MessageBox(hWndOwner, message.m_lpstr, title.m_lpstr, uType);\n\n\tdelete [] lpstrMessage;\n\tdelete [] lpstrTitle;\n\n\treturn nRet;\n}\n\n\n///////////////////////////////////////////////////////////////////////////////\n// CMenu\n\nclass CMenuItemInfo : public MENUITEMINFO\n{\npublic:\n\tCMenuItemInfo()\n\t{\n\t\tmemset(this, 0, sizeof(MENUITEMINFO));\n\t\tcbSize = sizeof(MENUITEMINFO);\n\t}\n};\n\n\n// forward declarations\ntemplate <bool t_bManaged> class CMenuT;\ntypedef CMenuT<false>   CMenuHandle;\ntypedef CMenuT<true>    CMenu;\n\n\ntemplate <bool t_bManaged>\nclass CMenuT\n{\npublic:\n// Data members\n\tHMENU m_hMenu;\n\n// Constructor/destructor/operators\n\tCMenuT(HMENU hMenu = NULL) : m_hMenu(hMenu)\n\t{ }\n\n\t~CMenuT()\n\t{\n\t\tif(t_bManaged && (m_hMenu != NULL))\n\t\t\tDestroyMenu();\n\t}\n\n\tCMenuT<t_bManaged>& operator =(HMENU hMenu)\n\t{\n\t\tAttach(hMenu);\n\t\treturn *this;\n\t}\n\n\tvoid Attach(HMENU hMenuNew)\n\t{\n\t\tATLASSERT(::IsMenu(hMenuNew));\n\t\tif(t_bManaged && (m_hMenu != NULL) && (m_hMenu != hMenuNew))\n\t\t\t::DestroyMenu(m_hMenu);\n\t\tm_hMenu = hMenuNew;\n\t}\n\n\tHMENU Detach()\n\t{\n\t\tHMENU hMenu = m_hMenu;\n\t\tm_hMenu = NULL;\n\t\treturn hMenu;\n\t}\n\n\toperator HMENU() const { return m_hMenu; }\n\n\tbool IsNull() const { return (m_hMenu == NULL); }\n\n\tBOOL IsMenu() const\n\t{\n\t\treturn ::IsMenu(m_hMenu);\n\t}\n\n// Create/destroy methods\n\tBOOL CreateMenu()\n\t{\n\t\tATLASSERT(m_hMenu == NULL);\n\t\tm_hMenu = ::CreateMenu();\n\t\treturn (m_hMenu != NULL) ? TRUE : FALSE;\n\t}\n\n\tBOOL CreatePopupMenu()\n\t{\n\t\tATLASSERT(m_hMenu == NULL);\n\t\tm_hMenu = ::CreatePopupMenu();\n\t\treturn (m_hMenu != NULL) ? TRUE : FALSE;\n\t}\n\n\tBOOL LoadMenu(ATL::_U_STRINGorID menu)\n\t{\n\t\tATLASSERT(m_hMenu == NULL);\n\t\tm_hMenu = ::LoadMenu(ModuleHelper::GetResourceInstance(), menu.m_lpstr);\n\t\treturn (m_hMenu != NULL) ? TRUE : FALSE;\n\t}\n\n\tBOOL LoadMenuIndirect(const void* lpMenuTemplate)\n\t{\n\t\tATLASSERT(m_hMenu == NULL);\n\t\tm_hMenu = ::LoadMenuIndirect(lpMenuTemplate);\n\t\treturn (m_hMenu != NULL) ? TRUE : FALSE;\n\t}\n\n\tBOOL DestroyMenu()\n\t{\n\t\tif (m_hMenu == NULL)\n\t\t\treturn FALSE;\n\t\tBOOL bRet = ::DestroyMenu(m_hMenu);\n\t\tif(bRet)\n\t\t\tm_hMenu = NULL;\n\t\treturn bRet;\n\t}\n\n// Menu Operations\n\tBOOL DeleteMenu(UINT nPosition, UINT nFlags)\n\t{\n\t\tATLASSERT(::IsMenu(m_hMenu));\n\t\treturn ::DeleteMenu(m_hMenu, nPosition, nFlags);\n\t}\n\n\tBOOL TrackPopupMenu(UINT nFlags, int x, int y, HWND hWnd, LPCRECT lpRect = NULL)\n\t{\n\t\tATLASSERT(::IsMenu(m_hMenu));\n\t\tx = _FixTrackMenuPopupX(x, y);\n\t\treturn ::TrackPopupMenu(m_hMenu, nFlags, x, y, 0, hWnd, lpRect);\n\t}\n\n\tBOOL TrackPopupMenuEx(UINT uFlags, int x, int y, HWND hWnd, LPTPMPARAMS lptpm = NULL)\n\t{\n\t\tATLASSERT(::IsMenu(m_hMenu));\n\t\tx = _FixTrackMenuPopupX(x, y);\n\t\treturn ::TrackPopupMenuEx(m_hMenu, uFlags, x, y, hWnd, lptpm);\n\t}\n\n\t// helper that fixes popup menu X position when it's off-screen\n\tstatic int _FixTrackMenuPopupX(int x, int y)\n\t{\n\t\tPOINT pt = { x, y };\n\t\tHMONITOR hMonitor = ::MonitorFromPoint(pt, MONITOR_DEFAULTTONULL);\n\t\tif(hMonitor == NULL)\n\t\t{\n\t\t\tHMONITOR hMonitorNear = ::MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST);\n\t\t\tif(hMonitorNear != NULL)\n\t\t\t{\n\t\t\t\tMONITORINFO mi = { sizeof(MONITORINFO) };\n\t\t\t\tif(::GetMonitorInfo(hMonitorNear, &mi) != FALSE)\n\t\t\t\t{\n\t\t\t\t\tif(x < mi.rcWork.left)\n\t\t\t\t\t\tx = mi.rcWork.left;\n\t\t\t\t\telse if(x > mi.rcWork.right)\n\t\t\t\t\t\tx = mi.rcWork.right;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn x;\n\t}\n\n\tBOOL GetMenuInfo(LPMENUINFO lpMenuInfo) const\n\t{\n\t\tATLASSERT(::IsMenu(m_hMenu));\n\t\treturn ::GetMenuInfo(m_hMenu, lpMenuInfo);\n\t}\n\n\tBOOL SetMenuInfo(LPCMENUINFO lpMenuInfo)\n\t{\n\t\tATLASSERT(::IsMenu(m_hMenu));\n\t\treturn ::SetMenuInfo(m_hMenu, lpMenuInfo);\n\t}\n\n// Menu Item Operations\n\tBOOL AppendMenu(UINT nFlags, UINT_PTR nIDNewItem = 0, LPCTSTR lpszNewItem = NULL)\n\t{\n\t\tATLASSERT(::IsMenu(m_hMenu));\n\t\treturn ::AppendMenu(m_hMenu, nFlags, nIDNewItem, lpszNewItem);\n\t}\n\n\tBOOL AppendMenu(UINT nFlags, HMENU hSubMenu, LPCTSTR lpszNewItem)\n\t{\n\t\tATLASSERT(::IsMenu(m_hMenu));\n\t\tATLASSERT(::IsMenu(hSubMenu));\n\t\treturn ::AppendMenu(m_hMenu, nFlags | MF_POPUP, (UINT_PTR)hSubMenu, lpszNewItem);\n\t}\n\n\tBOOL AppendMenu(UINT nFlags, UINT_PTR nIDNewItem, HBITMAP hBmp)\n\t{\n\t\tATLASSERT(::IsMenu(m_hMenu));\n\t\treturn ::AppendMenu(m_hMenu, nFlags | MF_BITMAP, nIDNewItem, (LPCTSTR)hBmp);\n\t}\n\n\tBOOL AppendMenu(UINT nFlags, HMENU hSubMenu, HBITMAP hBmp)\n\t{\n\t\tATLASSERT(::IsMenu(m_hMenu));\n\t\tATLASSERT(::IsMenu(hSubMenu));\n\t\treturn ::AppendMenu(m_hMenu, nFlags | (MF_BITMAP | MF_POPUP), (UINT_PTR)hSubMenu, (LPCTSTR)hBmp);\n\t}\n\n\tUINT CheckMenuItem(UINT nIDCheckItem, UINT nCheck)\n\t{\n\t\tATLASSERT(::IsMenu(m_hMenu));\n\t\treturn (UINT)::CheckMenuItem(m_hMenu, nIDCheckItem, nCheck);\n\t}\n\n\tUINT EnableMenuItem(UINT nIDEnableItem, UINT nEnable)\n\t{\n\t\tATLASSERT(::IsMenu(m_hMenu));\n\t\treturn ::EnableMenuItem(m_hMenu, nIDEnableItem, nEnable);\n\t}\n\n\tBOOL HiliteMenuItem(HWND hWnd, UINT uIDHiliteItem, UINT uHilite)\n\t{\n\t\tATLASSERT(::IsMenu(m_hMenu));\n\t\treturn ::HiliteMenuItem(hWnd, m_hMenu, uIDHiliteItem, uHilite);\n\t}\n\n\tint GetMenuItemCount() const\n\t{\n\t\tATLASSERT(::IsMenu(m_hMenu));\n\t\treturn ::GetMenuItemCount(m_hMenu);\n\t}\n\n\tUINT GetMenuItemID(int nPos) const\n\t{\n\t\tATLASSERT(::IsMenu(m_hMenu));\n\t\treturn ::GetMenuItemID(m_hMenu, nPos);\n\t}\n\n\tUINT GetMenuState(UINT nID, UINT nFlags) const\n\t{\n\t\tATLASSERT(::IsMenu(m_hMenu));\n\t\treturn ::GetMenuState(m_hMenu, nID, nFlags);\n\t}\n\n\tint GetMenuString(UINT nIDItem, LPTSTR lpString, int nMaxCount, UINT nFlags) const\n\t{\n\t\tATLASSERT(::IsMenu(m_hMenu));\n\t\treturn ::GetMenuString(m_hMenu, nIDItem, lpString, nMaxCount, nFlags);\n\t}\n\n\tint GetMenuStringLen(UINT nIDItem, UINT nFlags) const\n\t{\n\t\tATLASSERT(::IsMenu(m_hMenu));\n\t\treturn ::GetMenuString(m_hMenu, nIDItem, NULL, 0, nFlags);\n\t}\n\n\tBOOL GetMenuString(UINT nIDItem, BSTR& bstrText, UINT nFlags) const\n\t{\n\t\tUSES_CONVERSION;\n\t\tATLASSERT(::IsMenu(m_hMenu));\n\t\tATLASSERT(bstrText == NULL);\n\n\t\tint nLen = GetMenuStringLen(nIDItem, nFlags);\n\t\tif(nLen == 0)\n\t\t{\n\t\t\tbstrText = ::SysAllocString(OLESTR(\"\"));\n\t\t\treturn (bstrText != NULL) ? TRUE : FALSE;\n\t\t}\n\n\t\tnLen++;   // increment to include terminating NULL char\n\t\tATL::CTempBuffer<TCHAR, _WTL_STACK_ALLOC_THRESHOLD> buff;\n\t\tLPTSTR lpszText = buff.Allocate(nLen);\n\t\tif(lpszText == NULL)\n\t\t\treturn FALSE;\n\n\t\tif(!GetMenuString(nIDItem, lpszText, nLen, nFlags))\n\t\t\treturn FALSE;\n\n\t\tbstrText = ::SysAllocString(T2OLE(lpszText));\n\t\treturn (bstrText != NULL) ? TRUE : FALSE;\n\t}\n\n#ifdef __ATLSTR_H__\n\tint GetMenuString(UINT nIDItem, ATL::CString& strText, UINT nFlags) const\n\t{\n\t\tATLASSERT(::IsMenu(m_hMenu));\n\n\t\tint nLen = GetMenuStringLen(nIDItem, nFlags);\n\t\tif(nLen == 0)\n\t\t\treturn 0;\n\n\t\tnLen++;   // increment to include terminating NULL char\n\t\tLPTSTR lpstr = strText.GetBufferSetLength(nLen);\n\t\tif(lpstr == NULL)\n\t\t\treturn 0;\n\t\tint nRet = GetMenuString(nIDItem, lpstr, nLen, nFlags);\n\t\tstrText.ReleaseBuffer();\n\t\treturn nRet;\n\t}\n#endif // __ATLSTR_H__\n\n\tCMenuHandle GetSubMenu(int nPos) const\n\t{\n\t\tATLASSERT(::IsMenu(m_hMenu));\n\t\treturn CMenuHandle(::GetSubMenu(m_hMenu, nPos));\n\t}\n\n\tBOOL InsertMenu(UINT nPosition, UINT nFlags, UINT_PTR nIDNewItem = 0, LPCTSTR lpszNewItem = NULL)\n\t{\n\t\tATLASSERT(::IsMenu(m_hMenu));\n\t\treturn ::InsertMenu(m_hMenu, nPosition, nFlags, nIDNewItem, lpszNewItem);\n\t}\n\n\tBOOL InsertMenu(UINT nPosition, UINT nFlags, HMENU hSubMenu, LPCTSTR lpszNewItem)\n\t{\n\t\tATLASSERT(::IsMenu(m_hMenu));\n\t\tATLASSERT(::IsMenu(hSubMenu));\n\t\treturn ::InsertMenu(m_hMenu, nPosition, nFlags | MF_POPUP, (UINT_PTR)hSubMenu, lpszNewItem);\n\t}\n\n\tBOOL InsertMenu(UINT nPosition, UINT nFlags, UINT_PTR nIDNewItem, HBITMAP hBmp)\n\t{\n\t\tATLASSERT(::IsMenu(m_hMenu));\n\t\treturn ::InsertMenu(m_hMenu, nPosition, nFlags | MF_BITMAP, nIDNewItem, (LPCTSTR)hBmp);\n\t}\n\n\tBOOL InsertMenu(UINT nPosition, UINT nFlags, HMENU hSubMenu, HBITMAP hBmp)\n\t{\n\t\tATLASSERT(::IsMenu(m_hMenu));\n\t\tATLASSERT(::IsMenu(hSubMenu));\n\t\treturn ::InsertMenu(m_hMenu, nPosition, nFlags | (MF_BITMAP | MF_POPUP), (UINT_PTR)hSubMenu, (LPCTSTR)hBmp);\n\t}\n\n\tBOOL ModifyMenu(UINT nPosition, UINT nFlags, UINT_PTR nIDNewItem = 0, LPCTSTR lpszNewItem = NULL)\n\t{\n\t\tATLASSERT(::IsMenu(m_hMenu));\n\t\treturn ::ModifyMenu(m_hMenu, nPosition, nFlags, nIDNewItem, lpszNewItem);\n\t}\n\n\tBOOL ModifyMenu(UINT nPosition, UINT nFlags, HMENU hSubMenu, LPCTSTR lpszNewItem)\n\t{\n\t\tATLASSERT(::IsMenu(m_hMenu));\n\t\tATLASSERT(::IsMenu(hSubMenu));\n\t\treturn ::ModifyMenu(m_hMenu, nPosition, nFlags | MF_POPUP, (UINT_PTR)hSubMenu, lpszNewItem);\n\t}\n\n\tBOOL ModifyMenu(UINT nPosition, UINT nFlags, UINT_PTR nIDNewItem, HBITMAP hBmp)\n\t{\n\t\tATLASSERT(::IsMenu(m_hMenu));\n\t\treturn ::ModifyMenu(m_hMenu, nPosition, nFlags | MF_BITMAP, nIDNewItem, (LPCTSTR)hBmp);\n\t}\n\n\tBOOL ModifyMenu(UINT nPosition, UINT nFlags, HMENU hSubMenu, HBITMAP hBmp)\n\t{\n\t\tATLASSERT(::IsMenu(m_hMenu));\n\t\tATLASSERT(::IsMenu(hSubMenu));\n\t\treturn ::ModifyMenu(m_hMenu, nPosition, nFlags | (MF_BITMAP | MF_POPUP), (UINT_PTR)hSubMenu, (LPCTSTR)hBmp);\n\t}\n\n\tBOOL RemoveMenu(UINT nPosition, UINT nFlags)\n\t{\n\t\tATLASSERT(::IsMenu(m_hMenu));\n\t\treturn ::RemoveMenu(m_hMenu, nPosition, nFlags);\n\t}\n\n\tBOOL SetMenuItemBitmaps(UINT nPosition, UINT nFlags, HBITMAP hBmpUnchecked, HBITMAP hBmpChecked)\n\t{\n\t\tATLASSERT(::IsMenu(m_hMenu));\n\t\treturn ::SetMenuItemBitmaps(m_hMenu, nPosition, nFlags, hBmpUnchecked, hBmpChecked);\n\t}\n\n\tBOOL CheckMenuRadioItem(UINT nIDFirst, UINT nIDLast, UINT nIDItem, UINT nFlags)\n\t{\n\t\tATLASSERT(::IsMenu(m_hMenu));\n\t\treturn ::CheckMenuRadioItem(m_hMenu, nIDFirst, nIDLast, nIDItem, nFlags);\n\t}\n\n\tBOOL GetMenuItemInfo(UINT uItem, BOOL bByPosition, LPMENUITEMINFO lpmii) const\n\t{\n\t\tATLASSERT(::IsMenu(m_hMenu));\n\t\treturn (BOOL)::GetMenuItemInfo(m_hMenu, uItem, bByPosition, lpmii);\n\t}\n\n\tBOOL SetMenuItemInfo(UINT uItem, BOOL bByPosition, LPMENUITEMINFO lpmii)\n\t{\n\t\tATLASSERT(::IsMenu(m_hMenu));\n\t\treturn (BOOL)::SetMenuItemInfo(m_hMenu, uItem, bByPosition, lpmii);\n\t}\n\n\tBOOL InsertMenuItem(UINT uItem, BOOL bByPosition, LPMENUITEMINFO lpmii)\n\t{\n\t\tATLASSERT(::IsMenu(m_hMenu));\n\t\treturn (BOOL)::InsertMenuItem(m_hMenu, uItem, bByPosition, lpmii);\n\t}\n\n\tUINT GetMenuDefaultItem(BOOL bByPosition = FALSE, UINT uFlags = 0U) const\n\t{\n\t\tATLASSERT(::IsMenu(m_hMenu));\n\t\treturn ::GetMenuDefaultItem(m_hMenu, (UINT)bByPosition, uFlags);\n\t}\n\n\tBOOL SetMenuDefaultItem(UINT uItem = (UINT)-1,  BOOL bByPosition = FALSE)\n\t{\n\t\tATLASSERT(::IsMenu(m_hMenu));\n\t\treturn ::SetMenuDefaultItem(m_hMenu, uItem, (UINT)bByPosition);\n\t}\n\n\tBOOL GetMenuItemRect(HWND hWnd, UINT uItem, LPRECT lprcItem) const\n\t{\n\t\tATLASSERT(::IsMenu(m_hMenu));\n\t\treturn ::GetMenuItemRect(hWnd, m_hMenu, uItem, lprcItem);\n\t}\n\n\tint MenuItemFromPoint(HWND hWnd, POINT point) const\n\t{\n\t\tATLASSERT(::IsMenu(m_hMenu));\n\t\treturn ::MenuItemFromPoint(hWnd, m_hMenu, point);\n\t}\n\n// Context Help Functions\n\tBOOL SetMenuContextHelpId(DWORD dwContextHelpId)\n\t{\n\t\tATLASSERT(::IsMenu(m_hMenu));\n\t\treturn ::SetMenuContextHelpId(m_hMenu, dwContextHelpId);\n\t}\n\n\tDWORD GetMenuContextHelpId() const\n\t{\n\t\tATLASSERT(::IsMenu(m_hMenu));\n\t\treturn ::GetMenuContextHelpId(m_hMenu);\n\t}\n};\n\n\n///////////////////////////////////////////////////////////////////////////////\n// CAccelerator\n\ntemplate <bool t_bManaged>\nclass CAcceleratorT\n{\npublic:\n\tHACCEL m_hAccel;\n\n// Constructor/destructor/operators\n\tCAcceleratorT(HACCEL hAccel = NULL) : m_hAccel(hAccel)\n\t{ }\n\n\t~CAcceleratorT()\n\t{\n\t\tif(t_bManaged && (m_hAccel != NULL))\n\t\t\t::DestroyAcceleratorTable(m_hAccel);\n\t}\n\n\tCAcceleratorT<t_bManaged>& operator =(HACCEL hAccel)\n\t{\n\t\tAttach(hAccel);\n\t\treturn *this;\n\t}\n\n\tvoid Attach(HACCEL hAccel)\n\t{\n\t\tif(t_bManaged && (m_hAccel != NULL))\n\t\t\t::DestroyAcceleratorTable(m_hAccel);\n\t\tm_hAccel = hAccel;\n\t}\n\n\tHACCEL Detach()\n\t{\n\t\tHACCEL hAccel = m_hAccel;\n\t\tm_hAccel = NULL;\n\t\treturn hAccel;\n\t}\n\n\toperator HACCEL() const { return m_hAccel; }\n\n\tbool IsNull() const { return m_hAccel == NULL; }\n\n// Create/destroy methods\n\tHACCEL LoadAccelerators(ATL::_U_STRINGorID accel)\n\t{\n\t\tATLASSERT(m_hAccel == NULL);\n\t\tm_hAccel = ::LoadAccelerators(ModuleHelper::GetResourceInstance(), accel.m_lpstr);\n\t\treturn m_hAccel;\n\t}\n\n\tHACCEL CreateAcceleratorTable(LPACCEL pAccel, int cEntries)\n\t{\n\t\tATLASSERT(m_hAccel == NULL);\n\t\tATLASSERT(pAccel != NULL);\n\t\tm_hAccel = ::CreateAcceleratorTable(pAccel, cEntries);\n\t\treturn m_hAccel;\n\t}\n\n\tvoid DestroyObject()\n\t{\n\t\tif(m_hAccel != NULL)\n\t\t{\n\t\t\t::DestroyAcceleratorTable(m_hAccel);\n\t\t\tm_hAccel = NULL;\n\t\t}\n\t}\n\n// Operations\n\tint CopyAcceleratorTable(LPACCEL lpAccelDst, int cEntries)\n\t{\n\t\tATLASSERT(m_hAccel != NULL);\n\t\tATLASSERT(lpAccelDst != NULL);\n\t\treturn ::CopyAcceleratorTable(m_hAccel, lpAccelDst, cEntries);\n\t}\n\n\tint GetEntriesCount() const\n\t{\n\t\tATLASSERT(m_hAccel != NULL);\n\t\treturn ::CopyAcceleratorTable(m_hAccel, NULL, 0);\n\t}\n\n\tBOOL TranslateAccelerator(HWND hWnd, LPMSG pMsg)\n\t{\n\t\tATLASSERT(m_hAccel != NULL);\n\t\tATLASSERT(::IsWindow(hWnd));\n\t\tATLASSERT(pMsg != NULL);\n\t\treturn ::TranslateAccelerator(hWnd, m_hAccel, pMsg);\n\t}\n};\n\ntypedef CAcceleratorT<false>   CAcceleratorHandle;\ntypedef CAcceleratorT<true>    CAccelerator;\n\n\n///////////////////////////////////////////////////////////////////////////////\n// CIcon\n\ntemplate <bool t_bManaged>\nclass CIconT\n{\npublic:\n\tHICON m_hIcon;\n\n// Constructor/destructor/operators\n\tCIconT(HICON hIcon = NULL) : m_hIcon(hIcon)\n\t{ }\n\n\t~CIconT()\n\t{\n\t\tif(t_bManaged && (m_hIcon != NULL))\n\t\t\t::DestroyIcon(m_hIcon);\n\t}\n\n\tCIconT<t_bManaged>& operator =(HICON hIcon)\n\t{\n\t\tAttach(hIcon);\n\t\treturn *this;\n\t}\n\n\tvoid Attach(HICON hIcon)\n\t{\n\t\tif(t_bManaged && (m_hIcon != NULL))\n\t\t\t::DestroyIcon(m_hIcon);\n\t\tm_hIcon = hIcon;\n\t}\n\n\tHICON Detach()\n\t{\n\t\tHICON hIcon = m_hIcon;\n\t\tm_hIcon = NULL;\n\t\treturn hIcon;\n\t}\n\n\toperator HICON() const { return m_hIcon; }\n\n\tbool IsNull() const { return m_hIcon == NULL; }\n\n// Create/destroy methods\n\tHICON LoadIcon(ATL::_U_STRINGorID icon)\n\t{\n\t\tATLASSERT(m_hIcon == NULL);\n\t\tm_hIcon = ::LoadIcon(ModuleHelper::GetResourceInstance(), icon.m_lpstr);\n\t\treturn m_hIcon;\n\t}\n\n\tHICON LoadIcon(ATL::_U_STRINGorID icon, int cxDesired, int cyDesired, UINT fuLoad = 0)\n\t{\n\t\tATLASSERT(m_hIcon == NULL);\n\t\tm_hIcon = (HICON) ::LoadImage(ModuleHelper::GetResourceInstance(), icon.m_lpstr, IMAGE_ICON, cxDesired, cyDesired, fuLoad);\n\t\treturn m_hIcon;\n\t}\n\n\tHICON LoadOEMIcon(LPCTSTR lpstrIconName)\n\t{\n\t\tATLASSERT(m_hIcon == NULL);\n\t\tATLASSERT(IsOEMIcon(lpstrIconName));\n\t\tm_hIcon = ::LoadIcon(NULL, lpstrIconName);\n\t\treturn m_hIcon;\n\t}\n\n\tHICON CreateIcon(int nWidth, int nHeight, BYTE cPlanes, BYTE cBitsPixel, CONST BYTE* lpbANDbits, CONST BYTE *lpbXORbits)\n\t{\n\t\tATLASSERT(m_hIcon == NULL);\n\t\tATLASSERT(lpbANDbits != NULL);\n\t\tATLASSERT(lpbXORbits != NULL);\n\t\tm_hIcon = ::CreateIcon(ModuleHelper::GetResourceInstance(), nWidth, nHeight, cPlanes, cBitsPixel, lpbANDbits, lpbXORbits);\n\t\treturn m_hIcon;\n\t}\n\n\tHICON CreateIconFromResource(PBYTE pBits, DWORD dwResSize, DWORD dwVersion = 0x00030000)\n\t{\n\t\tATLASSERT(m_hIcon == NULL);\n\t\tATLASSERT(pBits != NULL);\n\t\tm_hIcon = ::CreateIconFromResource(pBits, dwResSize, TRUE, dwVersion);\n\t\treturn m_hIcon;\n\t}\n\n\tHICON CreateIconFromResourceEx(PBYTE pbBits, DWORD cbBits, DWORD dwVersion = 0x00030000, int cxDesired = 0, int cyDesired = 0, UINT uFlags = LR_DEFAULTCOLOR)\n\t{\n\t\tATLASSERT(m_hIcon == NULL);\n\t\tATLASSERT(pbBits != NULL);\n\t\tATLASSERT(cbBits > 0);\n\t\tm_hIcon = ::CreateIconFromResourceEx(pbBits, cbBits, TRUE, dwVersion, cxDesired, cyDesired, uFlags);\n\t\treturn m_hIcon;\n\t}\n\n\tHICON CreateIconIndirect(PICONINFO pIconInfo)\n\t{\n\t\tATLASSERT(m_hIcon == NULL);\n\t\tATLASSERT(pIconInfo != NULL);\n\t\tm_hIcon = ::CreateIconIndirect(pIconInfo);\n\t\treturn m_hIcon;\n\t}\n\n\tHICON ExtractIcon(LPCTSTR lpszExeFileName, UINT nIconIndex)\n\t{\n\t\tATLASSERT(m_hIcon == NULL);\n\t\tATLASSERT(lpszExeFileName != NULL);\n\t\tm_hIcon = ::ExtractIcon(ModuleHelper::GetModuleInstance(), lpszExeFileName, nIconIndex);\n\t\treturn m_hIcon;\n\t}\n\n\tHICON ExtractAssociatedIcon(HINSTANCE hInst, LPTSTR lpIconPath, LPWORD lpiIcon)\n\t{\n\t\tATLASSERT(m_hIcon == NULL);\n\t\tATLASSERT(lpIconPath != NULL);\n\t\tATLASSERT(lpiIcon != NULL);\n\t\tm_hIcon = ::ExtractAssociatedIcon(hInst, lpIconPath, lpiIcon);\n\t\treturn m_hIcon;\n\t}\n\n\tBOOL DestroyIcon()\n\t{\n\t\tATLASSERT(m_hIcon != NULL);\n\t\tBOOL bRet = ::DestroyIcon(m_hIcon);\n\t\tif(bRet != FALSE)\n\t\t\tm_hIcon = NULL;\n\t\treturn bRet;\n\t}\n\n// Operations\n\tHICON CopyIcon()\n\t{\n\t\tATLASSERT(m_hIcon != NULL);\n\t\treturn ::CopyIcon(m_hIcon);\n\t}\n\n\tHICON DuplicateIcon()\n\t{\n\t\tATLASSERT(m_hIcon != NULL);\n\t\treturn ::DuplicateIcon(NULL, m_hIcon);\n\t}\n\n\tBOOL DrawIcon(HDC hDC, int x, int y)\n\t{\n\t\tATLASSERT(m_hIcon != NULL);\n\t\treturn ::DrawIcon(hDC, x, y, m_hIcon);\n\t}\n\n\tBOOL DrawIcon(HDC hDC, POINT pt)\n\t{\n\t\tATLASSERT(m_hIcon != NULL);\n\t\treturn ::DrawIcon(hDC, pt.x, pt.y, m_hIcon);\n\t}\n\n\tBOOL DrawIconEx(HDC hDC, int x, int y, int cxWidth, int cyWidth, UINT uStepIfAniCur = 0, HBRUSH hbrFlickerFreeDraw = NULL, UINT uFlags = DI_NORMAL)\n\t{\n\t\tATLASSERT(m_hIcon != NULL);\n\t\treturn ::DrawIconEx(hDC, x, y, m_hIcon, cxWidth, cyWidth, uStepIfAniCur, hbrFlickerFreeDraw, uFlags);\n\t}\n\n\tBOOL DrawIconEx(HDC hDC, POINT pt, SIZE size, UINT uStepIfAniCur = 0, HBRUSH hbrFlickerFreeDraw = NULL, UINT uFlags = DI_NORMAL)\n\t{\n\t\tATLASSERT(m_hIcon != NULL);\n\t\treturn ::DrawIconEx(hDC, pt.x, pt.y, m_hIcon, size.cx, size.cy, uStepIfAniCur, hbrFlickerFreeDraw, uFlags);\n\t}\n\n\tBOOL GetIconInfo(PICONINFO pIconInfo) const\n\t{\n\t\tATLASSERT(m_hIcon != NULL);\n\t\tATLASSERT(pIconInfo != NULL);\n\t\treturn ::GetIconInfo(m_hIcon, pIconInfo);\n\t}\n\n#if (_WIN32_WINNT >= 0x0600)\n\tBOOL GetIconInfoEx(PICONINFOEX pIconInfo) const\n\t{\n\t\tATLASSERT(m_hIcon != NULL);\n\t\tATLASSERT(pIconInfo != NULL);\n\t\treturn ::GetIconInfoEx(m_hIcon, pIconInfo);\n\t}\n#endif // (_WIN32_WINNT >= 0x0600)\n\n#if defined(NTDDI_VERSION) && (NTDDI_VERSION >= NTDDI_LONGHORN)\n\tHRESULT LoadIconMetric(ATL::_U_STRINGorID icon, int lims)\n\t{\n\t\tATLASSERT(m_hIcon == NULL);\n\t\tUSES_CONVERSION;\n\t\treturn ::LoadIconMetric(ModuleHelper::GetResourceInstance(), T2CW(icon.m_lpstr), lims, &m_hIcon);\n\t}\n\n\tHRESULT LoadIconWithScaleDown(ATL::_U_STRINGorID icon, int cx, int cy)\n\t{\n\t\tATLASSERT(m_hIcon == NULL);\n\t\tUSES_CONVERSION;\n\t\treturn ::LoadIconWithScaleDown(ModuleHelper::GetResourceInstance(), T2CW(icon.m_lpstr), cx, cy, &m_hIcon);\n\t}\n\n\tHRESULT LoadOEMIconMetric(LPCTSTR lpstrIconName, int lims)\n\t{\n\t\tATLASSERT(m_hIcon == NULL);\n\t\tATLASSERT(IsOEMIcon(lpstrIconName));\n\t\treturn ::LoadIconMetric(NULL, (LPCWSTR)lpstrIconName, lims, &m_hIcon);\n\t}\n\n\tHRESULT LoadOEMIconWithScaleDown(LPCTSTR lpstrIconName, int cx, int cy)\n\t{\n\t\tATLASSERT(m_hIcon == NULL);\n\t\tATLASSERT(IsOEMIcon(lpstrIconName));\n\t\tUSES_CONVERSION;\n\t\treturn ::LoadIconWithScaleDown(NULL, (LPCWSTR)lpstrIconName, cx, cy, &m_hIcon);\n\t}\n#endif // defined(NTDDI_VERSION) && (NTDDI_VERSION >= NTDDI_LONGHORN)\n\n\t// Helper\n\tstatic bool IsOEMIcon(LPCTSTR lpstrIconName)\n\t{\n#if (WINVER >= 0x0600)\n\t\treturn ((lpstrIconName == IDI_APPLICATION) || (lpstrIconName == IDI_ASTERISK) || (lpstrIconName == IDI_EXCLAMATION) ||\n\t\t          (lpstrIconName == IDI_HAND) || (lpstrIconName == IDI_QUESTION) || (lpstrIconName == IDI_WINLOGO) ||\n\t\t          (lpstrIconName == IDI_SHIELD));\n#else // !(WINVER >= 0x0600)\n\t\treturn ((lpstrIconName == IDI_APPLICATION) || (lpstrIconName == IDI_ASTERISK) || (lpstrIconName == IDI_EXCLAMATION) ||\n\t\t          (lpstrIconName == IDI_HAND) || (lpstrIconName == IDI_QUESTION) || (lpstrIconName == IDI_WINLOGO));\n#endif // !(WINVER >= 0x0600)\n\t}\n};\n\ntypedef CIconT<false>   CIconHandle;\ntypedef CIconT<true>    CIcon;\n\n\n///////////////////////////////////////////////////////////////////////////////\n// CCursor\n\n// protect template member from a winuser.h macro\n#ifdef CopyCursor\n  #undef CopyCursor\n#endif\n\ntemplate <bool t_bManaged>\nclass CCursorT\n{\npublic:\n\tHCURSOR m_hCursor;\n\n// Constructor/destructor/operators\n\tCCursorT(HCURSOR hCursor = NULL) : m_hCursor(hCursor)\n\t{ }\n\n\t~CCursorT()\n\t{\n\t\tif(t_bManaged && (m_hCursor != NULL))\n\t\t\tDestroyCursor();\n\t}\n\n\tCCursorT<t_bManaged>& operator =(HCURSOR hCursor)\n\t{\n\t\tAttach(hCursor);\n\t\treturn *this;\n\t}\n\n\tvoid Attach(HCURSOR hCursor)\n\t{\n\t\tif(t_bManaged && (m_hCursor != NULL))\n\t\t\tDestroyCursor();\n\t\tm_hCursor = hCursor;\n\t}\n\n\tHCURSOR Detach()\n\t{\n\t\tHCURSOR hCursor = m_hCursor;\n\t\tm_hCursor = NULL;\n\t\treturn hCursor;\n\t}\n\n\toperator HCURSOR() const { return m_hCursor; }\n\n\tbool IsNull() const { return m_hCursor == NULL; }\n\n// Create/destroy methods\n\tHCURSOR LoadCursor(ATL::_U_STRINGorID cursor)\n\t{\n\t\tATLASSERT(m_hCursor == NULL);\n\t\tm_hCursor = ::LoadCursor(ModuleHelper::GetResourceInstance(), cursor.m_lpstr);\n\t\treturn m_hCursor;\n\t}\n\n\tHCURSOR LoadSysCursor(LPCTSTR lpstrCursorName)\n\t{\n\t\tATLASSERT(m_hCursor == NULL);\n\t\tATLASSERT((lpstrCursorName == IDC_ARROW) || (lpstrCursorName == IDC_IBEAM) || (lpstrCursorName == IDC_WAIT) ||\n\t\t\t(lpstrCursorName == IDC_CROSS) || (lpstrCursorName == IDC_UPARROW) || (lpstrCursorName == IDC_SIZE) ||\n\t\t\t(lpstrCursorName == IDC_ICON) || (lpstrCursorName == IDC_SIZENWSE) || (lpstrCursorName == IDC_SIZENESW) ||\n\t\t\t(lpstrCursorName == IDC_SIZEWE) || (lpstrCursorName == IDC_SIZENS) || (lpstrCursorName == IDC_SIZEALL) ||\n\t\t\t(lpstrCursorName == IDC_NO) || (lpstrCursorName == IDC_APPSTARTING) || (lpstrCursorName == IDC_HELP) ||\n\t\t\t(lpstrCursorName == IDC_HAND));\n\t\tm_hCursor = ::LoadCursor(NULL, lpstrCursorName);\n\t\treturn m_hCursor;\n\t}\n\n\t// deprecated\n\tHCURSOR LoadOEMCursor(LPCTSTR lpstrCursorName)\n\t{\n\t\treturn LoadSysCursor(lpstrCursorName);\n\t}\n\n\tHCURSOR LoadCursor(ATL::_U_STRINGorID cursor, int cxDesired, int cyDesired, UINT fuLoad = 0)\n\t{\n\t\tATLASSERT(m_hCursor == NULL);\n\t\tm_hCursor = (HCURSOR) ::LoadImage(ModuleHelper::GetResourceInstance(), cursor.m_lpstr, IMAGE_CURSOR, cxDesired, cyDesired, fuLoad);\n\t\treturn m_hCursor;\n\t}\n\n\tHCURSOR LoadCursorFromFile(LPCTSTR pstrFilename)\n\t{\n\t\tATLASSERT(m_hCursor == NULL);\n\t\tATLASSERT(pstrFilename != NULL);\n\t\tm_hCursor = ::LoadCursorFromFile(pstrFilename);\n\t\treturn m_hCursor;\n\t}\n\n\tHCURSOR CreateCursor(int xHotSpot, int yHotSpot, int nWidth, int nHeight, CONST VOID *pvANDPlane, CONST VOID *pvXORPlane)\n\t{\n\t\tATLASSERT(m_hCursor == NULL);\n\t\tm_hCursor = ::CreateCursor(ModuleHelper::GetResourceInstance(), xHotSpot, yHotSpot, nWidth, nHeight, pvANDPlane, pvXORPlane);\n\t\treturn m_hCursor;\n\t}\n\n\tHCURSOR CreateCursorFromResource(PBYTE pBits, DWORD dwResSize, DWORD dwVersion = 0x00030000)\n\t{\n\t\tATLASSERT(m_hCursor == NULL);\n\t\tATLASSERT(pBits != NULL);\n\t\tm_hCursor = (HCURSOR)::CreateIconFromResource(pBits, dwResSize, FALSE, dwVersion);\n\t\treturn m_hCursor;\n\t}\n\n\tHCURSOR CreateCursorFromResourceEx(PBYTE pbBits, DWORD cbBits, DWORD dwVersion = 0x00030000, int cxDesired = 0, int cyDesired = 0, UINT uFlags = LR_DEFAULTCOLOR)\n\t{\n\t\tATLASSERT(m_hCursor == NULL);\n\t\tATLASSERT(pbBits != NULL);\n\t\tATLASSERT(cbBits > 0);\n\t\tm_hCursor = (HCURSOR)::CreateIconFromResourceEx(pbBits, cbBits, FALSE, dwVersion, cxDesired, cyDesired, uFlags);\n\t\treturn m_hCursor;\n\t}\n\n\tBOOL DestroyCursor()\n\t{\n\t\tATLASSERT(m_hCursor != NULL);\n\t\tBOOL bRet = ::DestroyCursor(m_hCursor);\n\t\tif(bRet != FALSE)\n\t\t\tm_hCursor = NULL;\n\t\treturn bRet;\n\t}\n\n// Operations\n\tHCURSOR CopyCursor()\n\t{\n\t\tATLASSERT(m_hCursor != NULL);\n\t\treturn (HCURSOR)::CopyIcon((HICON)m_hCursor);\n\t}\n\n\tBOOL GetCursorInfo(LPCURSORINFO pCursorInfo)\n\t{\n\t\tATLASSERT(m_hCursor != NULL);\n\t\tATLASSERT(pCursorInfo != NULL);\n\t\treturn ::GetCursorInfo(pCursorInfo);\n\t}\n};\n\ntypedef CCursorT<false>   CCursorHandle;\ntypedef CCursorT<true>    CCursor;\n\n\n///////////////////////////////////////////////////////////////////////////////\n// CResource - Wraps a generic Windows resource.\n//             Use it with custom resource types other than the\n//             standard RT_CURSOR, RT_BITMAP, etc.\n\nclass CResource\n{\npublic:\n\tHGLOBAL m_hGlobal;\n\tHRSRC m_hResource;\n\n// Constructor/destructor\n\tCResource() : m_hGlobal(NULL), m_hResource(NULL)\n\t{ }\n\n\t~CResource()\n\t{\n\t\tRelease();\n\t}\n\n// Load methods\n\tbool Load(ATL::_U_STRINGorID Type, ATL::_U_STRINGorID ID)\n\t{\n\t\tATLASSERT(m_hResource == NULL);\n\t\tATLASSERT(m_hGlobal == NULL);\n\n\t\tm_hResource = ::FindResource(ModuleHelper::GetResourceInstance(), ID.m_lpstr, Type.m_lpstr);\n\t\tif(m_hResource == NULL)\n\t\t\treturn false;\n\n\t\tm_hGlobal = ::LoadResource(ModuleHelper::GetResourceInstance(), m_hResource);\n\t\tif(m_hGlobal == NULL)\n\t\t{\n\t\t\tm_hResource = NULL;\n\t\t\treturn false;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\tbool LoadEx(ATL::_U_STRINGorID ID, ATL::_U_STRINGorID Type, WORD wLanguage)\n\t{\n\t\tATLASSERT(m_hResource == NULL);\n\t\tATLASSERT(m_hGlobal == NULL);\n\n\t\tm_hResource = ::FindResourceEx(ModuleHelper::GetResourceInstance(), Type.m_lpstr, ID.m_lpstr, wLanguage);\n\t\tif(m_hResource == NULL)\n\t\t\treturn false;\n\n\t\tm_hGlobal = ::LoadResource(ModuleHelper::GetResourceInstance(), m_hResource);\n\t\tif(m_hGlobal == NULL)\n\t\t{\n\t\t\tm_hResource = NULL;\n\t\t\treturn false;\n\t\t}\n\n\t\treturn true;\n\t}\n\n// Misc. operations\n\tDWORD GetSize() const\n\t{\n\t\tATLASSERT(m_hResource != NULL);\n\t\treturn ::SizeofResource(ModuleHelper::GetResourceInstance(), m_hResource);\n\t}\n\n\tLPVOID Lock()\n\t{\n\t\tATLASSERT(m_hResource != NULL);\n\t\tATLASSERT(m_hGlobal != NULL);\n\t\tLPVOID pVoid = ::LockResource(m_hGlobal);\n\t\tATLASSERT(pVoid != NULL);\n\t\treturn pVoid;\n\t}\n\n\tvoid Release()\n\t{\n\t\tif(m_hGlobal != NULL)\n\t\t{\n\t\t\tFreeResource(m_hGlobal);\n\t\t\tm_hGlobal = NULL;\n\t\t\tm_hResource = NULL;\n\t\t}\n\t}\n};\n\n\n///////////////////////////////////////////////////////////////////////////////\n// Toolbar resource descriptor\n\nstruct _AtlToolBarData\n{\n\tWORD wVersion;\n\tWORD wWidth;\n\tWORD wHeight;\n\tWORD wItemCount;\n\n\tWORD* items()\n\t\t{ return (WORD*)(this+1); }\n};\n\n\n///////////////////////////////////////////////////////////////////////////////\n// Global functions for loading resources\n\ninline HACCEL AtlLoadAccelerators(ATL::_U_STRINGorID table)\n{\n\treturn ::LoadAccelerators(ModuleHelper::GetResourceInstance(), table.m_lpstr);\n}\n\ninline HMENU AtlLoadMenu(ATL::_U_STRINGorID menu)\n{\n\treturn ::LoadMenu(ModuleHelper::GetResourceInstance(), menu.m_lpstr);\n}\n\ninline HBITMAP AtlLoadBitmap(ATL::_U_STRINGorID bitmap)\n{\n\treturn ::LoadBitmap(ModuleHelper::GetResourceInstance(), bitmap.m_lpstr);\n}\n\n#ifdef OEMRESOURCE\ninline HBITMAP AtlLoadSysBitmap(ATL::_U_STRINGorID bitmap)\n{\n#ifdef _DEBUG\n\tWORD wID = LOWORD(bitmap.m_lpstr);\n\tATLASSERT((wID >= 32734) && (wID <= 32767));\n#endif // _DEBUG\n\treturn ::LoadBitmap(NULL, bitmap.m_lpstr);\n}\n#endif // OEMRESOURCE\n\ninline HCURSOR AtlLoadCursor(ATL::_U_STRINGorID cursor)\n{\n\treturn ::LoadCursor(ModuleHelper::GetResourceInstance(), cursor.m_lpstr);\n}\n\ninline HCURSOR AtlLoadSysCursor(LPCTSTR lpCursorName)\n{\n\tATLASSERT((lpCursorName == IDC_ARROW) || (lpCursorName == IDC_IBEAM) || (lpCursorName == IDC_WAIT) ||\n\t\t(lpCursorName == IDC_CROSS) || (lpCursorName == IDC_UPARROW) || (lpCursorName == IDC_SIZE) ||\n\t\t(lpCursorName == IDC_ICON) || (lpCursorName == IDC_SIZENWSE) || (lpCursorName == IDC_SIZENESW) ||\n\t\t(lpCursorName == IDC_SIZEWE) || (lpCursorName == IDC_SIZENS) || (lpCursorName == IDC_SIZEALL) ||\n\t\t(lpCursorName == IDC_NO) || (lpCursorName == IDC_APPSTARTING) || (lpCursorName == IDC_HELP) ||\n\t\t(lpCursorName == IDC_HAND));\n\treturn ::LoadCursor(NULL, lpCursorName);\n}\n\ninline HICON AtlLoadIcon(ATL::_U_STRINGorID icon)\n{\n\treturn ::LoadIcon(ModuleHelper::GetResourceInstance(), icon.m_lpstr);\n}\n\ninline HICON AtlLoadSysIcon(LPCTSTR lpIconName)\n{\n#if (WINVER >= 0x0600)\n\tATLASSERT((lpIconName == IDI_APPLICATION) || (lpIconName == IDI_ASTERISK) || (lpIconName == IDI_EXCLAMATION) ||\n\t          (lpIconName == IDI_HAND) || (lpIconName == IDI_QUESTION) || (lpIconName == IDI_WINLOGO) ||\n\t          (lpIconName == IDI_SHIELD));\n#else // !(WINVER >= 0x0600)\n\tATLASSERT((lpIconName == IDI_APPLICATION) || (lpIconName == IDI_ASTERISK) || (lpIconName == IDI_EXCLAMATION) ||\n\t          (lpIconName == IDI_HAND) || (lpIconName == IDI_QUESTION) || (lpIconName == IDI_WINLOGO));\n#endif // !(WINVER >= 0x0600)\n\treturn ::LoadIcon(NULL, lpIconName);\n}\n\ninline HBITMAP AtlLoadBitmapImage(ATL::_U_STRINGorID bitmap, UINT fuLoad = LR_DEFAULTCOLOR)\n{\n\treturn (HBITMAP)::LoadImage(ModuleHelper::GetResourceInstance(), bitmap.m_lpstr, IMAGE_BITMAP, 0, 0, fuLoad);\n}\n\ninline HCURSOR AtlLoadCursorImage(ATL::_U_STRINGorID cursor, UINT fuLoad = LR_DEFAULTCOLOR | LR_DEFAULTSIZE, int cxDesired = 0, int cyDesired = 0)\n{\n\treturn (HCURSOR)::LoadImage(ModuleHelper::GetResourceInstance(), cursor.m_lpstr, IMAGE_CURSOR, cxDesired, cyDesired, fuLoad);\n}\n\ninline HICON AtlLoadIconImage(ATL::_U_STRINGorID icon, UINT fuLoad = LR_DEFAULTCOLOR | LR_DEFAULTSIZE, int cxDesired = 0, int cyDesired = 0)\n{\n\treturn (HICON)::LoadImage(ModuleHelper::GetResourceInstance(), icon.m_lpstr, IMAGE_ICON, cxDesired, cyDesired, fuLoad);\n}\n\n#ifdef OEMRESOURCE\ninline HBITMAP AtlLoadSysBitmapImage(WORD wBitmapID, UINT fuLoad = LR_DEFAULTCOLOR)\n{\n\tATLASSERT((wBitmapID >= 32734) && (wBitmapID <= 32767));\n\tATLASSERT((fuLoad & LR_LOADFROMFILE) == 0);   // this one doesn't load from a file\n\treturn (HBITMAP)::LoadImage(NULL, MAKEINTRESOURCE(wBitmapID), IMAGE_BITMAP, 0, 0, fuLoad);\n}\n#endif // OEMRESOURCE\n\ninline HCURSOR AtlLoadSysCursorImage(ATL::_U_STRINGorID cursor, UINT fuLoad = LR_DEFAULTCOLOR | LR_DEFAULTSIZE, int cxDesired = 0, int cyDesired = 0)\n{\n#ifdef _DEBUG\n\tWORD wID = LOWORD(cursor.m_lpstr);\n\tATLASSERT(((wID >= 32512) && (wID <= 32516)) || ((wID >= 32640) && (wID <= 32648)) || (wID == 32650) || (wID == 32651));\n\tATLASSERT((fuLoad & LR_LOADFROMFILE) == 0);   // this one doesn't load from a file\n#endif // _DEBUG\n\treturn (HCURSOR)::LoadImage(NULL, cursor.m_lpstr, IMAGE_CURSOR, cxDesired, cyDesired, fuLoad);\n}\n\ninline HICON AtlLoadSysIconImage(ATL::_U_STRINGorID icon, UINT fuLoad = LR_DEFAULTCOLOR | LR_DEFAULTSIZE, int cxDesired = 0, int cyDesired = 0)\n{\n#ifdef _DEBUG\n\tWORD wID = LOWORD(icon.m_lpstr);\n\tATLASSERT((wID >= 32512) && (wID <= 32517));\n\tATLASSERT((fuLoad & LR_LOADFROMFILE) == 0);   // this one doesn't load from a file\n#endif // _DEBUG\n\treturn (HICON)::LoadImage(NULL, icon.m_lpstr, IMAGE_ICON, cxDesired, cyDesired, fuLoad);\n}\n\ninline bool AtlLoadString(UINT uID, BSTR& bstrText)\n{\n\tUSES_CONVERSION;\n\tATLASSERT(bstrText == NULL);\n\n\tLPTSTR lpstrText = NULL;\n\tint nRes = 0;\n\tfor(int nLen = 256; ; nLen *= 2)\n\t{\n\t\tATLTRY(lpstrText = new TCHAR[nLen]);\n\t\tif(lpstrText == NULL)\n\t\t\tbreak;\n\t\tnRes = ::LoadString(ModuleHelper::GetResourceInstance(), uID, lpstrText, nLen);\n\t\tif(nRes < nLen - 1)\n\t\t\tbreak;\n\t\tdelete [] lpstrText;\n\t\tlpstrText = NULL;\n\t}\n\n\tif(lpstrText != NULL)\n\t{\n\t\tif(nRes != 0)\n\t\t\tbstrText = ::SysAllocString(T2OLE(lpstrText));\n\t\tdelete [] lpstrText;\n\t}\n\n\treturn (bstrText != NULL) ? true : false;\n}\n\n} // namespace WTL\n\n#endif // __ATLUSER_H__\n"
  },
  {
    "path": "Examples/WhisperDesktop/Utils/WTL/atlwinx.h",
    "content": "// Windows Template Library - WTL version 10.0\n// Copyright (C) Microsoft Corporation, WTL Team. All rights reserved.\n//\n// This file is a part of the Windows Template Library.\n// The use and distribution terms for this software are covered by the\n// Microsoft Public License (http://opensource.org/licenses/MS-PL)\n// which can be found in the file MS-PL.txt at the root folder.\n\n#ifndef __ATLWINX_H__\n#define __ATLWINX_H__\n\n#pragma once\n\n#ifndef __ATLAPP_H__\n\t#error atlwinx.h requires atlapp.h to be included first\n#endif\n\n#include <atlwin.h>\n\n\n///////////////////////////////////////////////////////////////////////////////\n// Classes in this file:\n//\n// CWindowEx\n\n\n/////////////////////////////////////////////////////////////////////////////\n// Additional macros needed for template classes\n\n#ifndef DECLARE_WND_CLASS_EX2\n  #define DECLARE_WND_CLASS_EX2(WndClassName, EnclosingClass, style, bkgnd) \\\n  static ATL::CWndClassInfo& GetWndClassInfo() \\\n  { \\\n\tstatic ATL::CWndClassInfo wc = \\\n\t{ \\\n\t\t{ sizeof(WNDCLASSEX), style, EnclosingClass::StartWindowProc, \\\n\t\t  0, 0, NULL, NULL, NULL, (HBRUSH)(bkgnd + 1), NULL, WndClassName, NULL }, \\\n\t\t  NULL, NULL, IDC_ARROW, TRUE, 0, _T(\"\") \\\n\t}; \\\n\treturn wc; \\\n  }\n#endif // DECLARE_WND_CLASS_EX2\n\n#ifndef DECLARE_WND_SUPERCLASS2\n  #define DECLARE_WND_SUPERCLASS2(WndClassName, EnclosingClass, OrigWndClassName) \\\n  static ATL::CWndClassInfo& GetWndClassInfo() \\\n  { \\\n\tstatic ATL::CWndClassInfo wc = \\\n\t{ \\\n\t\t{ sizeof(WNDCLASSEX), 0, EnclosingClass::StartWindowProc, \\\n\t\t  0, 0, NULL, NULL, NULL, NULL, NULL, WndClassName, NULL }, \\\n\t\t  OrigWndClassName, NULL, NULL, TRUE, 0, _T(\"\") \\\n\t}; \\\n\treturn wc; \\\n  }\n#endif // DECLARE_WND_SUPERCLASS2\n\n\n///////////////////////////////////////////////////////////////////////////////\n// Command Chaining Macros\n\n#define CHAIN_COMMANDS(theChainClass) \\\n\tif(uMsg == WM_COMMAND) \\\n\t\tCHAIN_MSG_MAP(theChainClass)\n\n#define CHAIN_COMMANDS_ALT(theChainClass, msgMapID) \\\n\tif(uMsg == WM_COMMAND) \\\n\t\tCHAIN_MSG_MAP_ALT(theChainClass, msgMapID)\n\n#define CHAIN_COMMANDS_MEMBER(theChainMember) \\\n\tif(uMsg == WM_COMMAND) \\\n\t\tCHAIN_MSG_MAP_MEMBER(theChainMember)\n\n#define CHAIN_COMMANDS_ALT_MEMBER(theChainMember, msgMapID) \\\n\tif(uMsg == WM_COMMAND) \\\n\t\tCHAIN_MSG_MAP_ALT_MEMBER(theChainMember, msgMapID)\n\n\n///////////////////////////////////////////////////////////////////////////////\n// Macros for parent message map to selectively reflect control messages\n\n// NOTE: ReflectNotifications is a member of ATL's CWindowImplRoot\n//  (and overridden in 2 cases - CContainedWindowT and CAxHostWindow)\n//  Since we can't modify ATL, we'll provide the needed additions\n//  in a separate function (that is not a member of CWindowImplRoot)\n\nnamespace WTL\n{\n\ninline LRESULT WtlReflectNotificationsFiltered(HWND hWndParent, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled,\n                                               UINT uMsgFilter = WM_NULL, UINT_PTR idFromFilter = 0, HWND hWndChildFilter = NULL)\n{\n\tif((uMsgFilter != WM_NULL) && (uMsgFilter != uMsg))\n\t{\n\t\t// The notification message doesn't match the filter.\n\t\tbHandled = FALSE;\n\t\treturn 1;\n\t}\n\n\tHWND hWndChild = NULL;\n\tUINT_PTR idFrom = 0;\n\n\tswitch(uMsg)\n\t{\n\tcase WM_COMMAND:\n\t\tif(lParam != NULL)\t// not from a menu\n\t\t{\n\t\t\thWndChild = (HWND)lParam;\n\t\t\tidFrom = (UINT_PTR)LOWORD(wParam);\n\t\t}\n\t\tbreak;\n\tcase WM_NOTIFY:\n\t\thWndChild = ((LPNMHDR)lParam)->hwndFrom;\n\t\tidFrom = ((LPNMHDR)lParam)->idFrom;\n\t\tbreak;\n\tcase WM_PARENTNOTIFY:\n\t\tswitch(LOWORD(wParam))\n\t\t{\n\t\tcase WM_CREATE:\n\t\tcase WM_DESTROY:\n\t\t\thWndChild = (HWND)lParam;\n\t\t\tidFrom = (UINT_PTR)HIWORD(wParam);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\thWndChild = ::GetDlgItem(hWndParent, HIWORD(wParam));\n\t\t\tidFrom = (UINT_PTR)::GetDlgCtrlID(hWndChild);\n\t\t\tbreak;\n\t\t}\n\t\tbreak;\n\tcase WM_DRAWITEM:\n\t\tif(wParam)\t// not from a menu\n\t\t{\n\t\t\thWndChild = ((LPDRAWITEMSTRUCT)lParam)->hwndItem;\n\t\t\tidFrom = (UINT_PTR)wParam;\n\t\t}\n\t\tbreak;\n\tcase WM_MEASUREITEM:\n\t\tif(wParam)\t// not from a menu\n\t\t{\n\t\t\thWndChild = ::GetDlgItem(hWndParent, ((LPMEASUREITEMSTRUCT)lParam)->CtlID);\n\t\t\tidFrom = (UINT_PTR)wParam;\n\t\t}\n\t\tbreak;\n\tcase WM_COMPAREITEM:\n\t\tif(wParam)\t// not from a menu\n\t\t{\n\t\t\thWndChild = ((LPCOMPAREITEMSTRUCT)lParam)->hwndItem;\n\t\t\tidFrom = (UINT_PTR)wParam;\n\t\t}\n\t\tbreak;\n\tcase WM_DELETEITEM:\n\t\tif(wParam)\t// not from a menu\n\t\t{\n\t\t\thWndChild = ((LPDELETEITEMSTRUCT)lParam)->hwndItem;\n\t\t\tidFrom = (UINT_PTR)wParam;\n\t\t}\n\t\tbreak;\n\tcase WM_VKEYTOITEM:\n\tcase WM_CHARTOITEM:\n\tcase WM_HSCROLL:\n\tcase WM_VSCROLL:\n\tcase WM_CTLCOLORBTN:\n\tcase WM_CTLCOLORDLG:\n\tcase WM_CTLCOLOREDIT:\n\tcase WM_CTLCOLORLISTBOX:\n\tcase WM_CTLCOLORMSGBOX:\n\tcase WM_CTLCOLORSCROLLBAR:\n\tcase WM_CTLCOLORSTATIC:\n\t\thWndChild = (HWND)lParam;\n\t\tidFrom = (UINT_PTR)::GetDlgCtrlID(hWndChild);\n\t\tbreak;\n\tdefault:\n\t\tbreak;\n\t}\n\n\tif((hWndChild == NULL) ||\n\t\t((hWndChildFilter != NULL) && (hWndChildFilter != hWndChild)))\n\t{\n\t\t// Either hWndChild isn't valid, or\n\t\t// hWndChild doesn't match the filter.\n\t\tbHandled = FALSE;\n\t\treturn 1;\n\t}\n\n\tif((idFromFilter != 0) && (idFromFilter != idFrom))\n\t{\n\t\t// The dialog control id doesn't match the filter.\n\t\tbHandled = FALSE;\n\t\treturn 1;\n\t}\n\n\tATLASSERT(::IsWindow(hWndChild));\n\tLRESULT lResult = ::SendMessage(hWndChild, OCM__BASE + uMsg, wParam, lParam);\n\tif((lResult == 0) && (uMsg >= WM_CTLCOLORMSGBOX) && (uMsg <= WM_CTLCOLORSTATIC))\n\t{\n\t\t// Try to prevent problems with WM_CTLCOLOR* messages when\n\t\t// the message wasn't really handled\n\t\tbHandled = FALSE;\n\t}\n\n\treturn lResult;\n}\n\n} // namespace WTL\n\n// Try to prevent problems with WM_CTLCOLOR* messages when\n// the message wasn't really handled\n#define REFLECT_NOTIFICATIONS_EX() \\\n{ \\\n\tbHandled = TRUE; \\\n\tlResult = this->ReflectNotifications(uMsg, wParam, lParam, bHandled); \\\n\tif((lResult == 0) && (uMsg >= WM_CTLCOLORMSGBOX) && (uMsg <= WM_CTLCOLORSTATIC)) \\\n\t\tbHandled = FALSE; \\\n\tif(bHandled) \\\n\t\treturn TRUE; \\\n}\n\n#define REFLECT_NOTIFICATIONS_MSG_FILTERED(uMsgFilter) \\\n\t{ \\\n\t\tbHandled = TRUE; \\\n\t\tlResult = WTL::WtlReflectNotificationsFiltered(this->m_hWnd, uMsg, wParam, lParam, bHandled, uMsgFilter, 0, NULL); \\\n\t\tif(bHandled) \\\n\t\t\treturn TRUE; \\\n\t}\n\n#define REFLECT_NOTIFICATIONS_ID_FILTERED(idFromFilter) \\\n\t{ \\\n\t\tbHandled = TRUE; \\\n\t\tlResult = WTL::WtlReflectNotificationsFiltered(this->m_hWnd, uMsg, wParam, lParam, bHandled, WM_NULL, idFromFilter, NULL); \\\n\t\tif(bHandled) \\\n\t\t\treturn TRUE; \\\n\t}\n\n#define REFLECT_NOTIFICATIONS_HWND_FILTERED(hWndChildFilter) \\\n\t{ \\\n\t\tbHandled = TRUE; \\\n\t\tlResult = WTL::WtlReflectNotificationsFiltered(this->m_hWnd, uMsg, wParam, lParam, bHandled, WM_NULL, 0, hWndChildFilter); \\\n\t\tif(bHandled) \\\n\t\t\treturn TRUE; \\\n\t}\n\n#define REFLECT_NOTIFICATIONS_MSG_ID_FILTERED(uMsgFilter, idFromFilter) \\\n\t{ \\\n\t\tbHandled = TRUE; \\\n\t\tlResult = WTL::WtlReflectNotificationsFiltered(this->m_hWnd, uMsg, wParam, lParam, bHandled, uMsgFilter, idFromFilter, NULL); \\\n\t\tif(bHandled) \\\n\t\t\treturn TRUE; \\\n\t}\n\n#define REFLECT_NOTIFICATIONS_MSG_HWND_FILTERED(uMsgFilter, hWndChildFilter) \\\n\t{ \\\n\t\tbHandled = TRUE; \\\n\t\tlResult = WTL::WtlReflectNotificationsFiltered(this->m_hWnd, uMsg, wParam, lParam, bHandled, uMsgFilter, 0, hWndChildFilter); \\\n\t\tif(bHandled) \\\n\t\t\treturn TRUE; \\\n\t}\n\n#define REFLECT_COMMAND(id, code) \\\n\tif((uMsg == WM_COMMAND) && (id == LOWORD(wParam)) && (code == HIWORD(wParam))) \\\n\t{ \\\n\t\tbHandled = TRUE; \\\n\t\tlResult = this->ReflectNotifications(uMsg, wParam, lParam, bHandled); \\\n\t\tif(bHandled) \\\n\t\t\treturn TRUE; \\\n\t}\n\n#define REFLECT_COMMAND_ID(id) \\\n\tif((uMsg == WM_COMMAND) && (id == LOWORD(wParam))) \\\n\t{ \\\n\t\tbHandled = TRUE; \\\n\t\tlResult = this->ReflectNotifications(uMsg, wParam, lParam, bHandled); \\\n\t\tif(bHandled) \\\n\t\t\treturn TRUE; \\\n\t}\n\n#define REFLECT_COMMAND_CODE(code) \\\n\tif((uMsg == WM_COMMAND) && (code == HIWORD(wParam))) \\\n\t{ \\\n\t\tbHandled = TRUE; \\\n\t\tlResult = this->ReflectNotifications(uMsg, wParam, lParam, bHandled); \\\n\t\tif(bHandled) \\\n\t\t\treturn TRUE; \\\n\t}\n\n#define REFLECT_COMMAND_RANGE(idFirst, idLast) \\\n\tif((uMsg == WM_COMMAND) && (LOWORD(wParam) >= idFirst) && (LOWORD(wParam) <= idLast)) \\\n\t{ \\\n\t\tbHandled = TRUE; \\\n\t\tlResult = this->ReflectNotifications(uMsg, wParam, lParam, bHandled); \\\n\t\tif(bHandled) \\\n\t\t\treturn TRUE; \\\n\t}\n\n#define REFLECT_COMMAND_RANGE_CODE(idFirst, idLast, code) \\\n\tif((uMsg == WM_COMMAND) && (code == HIWORD(wParam)) && (LOWORD(wParam) >= idFirst) && (LOWORD(wParam) <= idLast)) \\\n\t{ \\\n\t\tbHandled = TRUE; \\\n\t\tlResult = this->ReflectNotifications(uMsg, wParam, lParam, bHandled); \\\n\t\tif(bHandled) \\\n\t\t\treturn TRUE; \\\n\t}\n\n#define REFLECT_NOTIFY(id, cd) \\\n\tif((uMsg == WM_NOTIFY) && (id == ((LPNMHDR)lParam)->idFrom) && (cd == ((LPNMHDR)lParam)->code)) \\\n\t{ \\\n\t\tbHandled = TRUE; \\\n\t\tlResult = this->ReflectNotifications(uMsg, wParam, lParam, bHandled); \\\n\t\tif(bHandled) \\\n\t\t\treturn TRUE; \\\n\t}\n\n#define REFLECT_NOTIFY_ID(id) \\\n\tif((uMsg == WM_NOTIFY) && (id == ((LPNMHDR)lParam)->idFrom)) \\\n\t{ \\\n\t\tbHandled = TRUE; \\\n\t\tlResult = this->ReflectNotifications(uMsg, wParam, lParam, bHandled); \\\n\t\tif(bHandled) \\\n\t\t\treturn TRUE; \\\n\t}\n\n#define REFLECT_NOTIFY_CODE(cd) \\\n\tif((uMsg == WM_NOTIFY) && (cd == ((LPNMHDR)lParam)->code)) \\\n\t{ \\\n\t\tbHandled = TRUE; \\\n\t\tlResult = this->ReflectNotifications(uMsg, wParam, lParam, bHandled); \\\n\t\tif(bHandled) \\\n\t\t\treturn TRUE; \\\n\t}\n\n#define REFLECT_NOTIFY_RANGE(idFirst, idLast) \\\n\tif((uMsg == WM_NOTIFY) && (((LPNMHDR)lParam)->idFrom >= idFirst) && (((LPNMHDR)lParam)->idFrom <= idLast)) \\\n\t{ \\\n\t\tbHandled = TRUE; \\\n\t\tlResult = this->ReflectNotifications(uMsg, wParam, lParam, bHandled); \\\n\t\tif(bHandled) \\\n\t\t\treturn TRUE; \\\n\t}\n\n#define REFLECT_NOTIFY_RANGE_CODE(idFirst, idLast, cd) \\\n\tif((uMsg == WM_NOTIFY) && (cd == ((LPNMHDR)lParam)->code) && (((LPNMHDR)lParam)->idFrom >= idFirst) && (((LPNMHDR)lParam)->idFrom <= idLast)) \\\n\t{ \\\n\t\tbHandled = TRUE; \\\n\t\tlResult = this->ReflectNotifications(uMsg, wParam, lParam, bHandled); \\\n\t\tif(bHandled) \\\n\t\t\treturn TRUE; \\\n\t}\n\n\n///////////////////////////////////////////////////////////////////////////////\n// GetClassLong/SetClassLong redefinition to avoid problems with class members\n\n#ifdef SetClassLongPtrA\n  #undef SetClassLongPtrA\n  inline LONG_PTR SetClassLongPtrA(HWND hWnd, int nIndex, LONG_PTR dwNewLong)\n  {\n\treturn ::SetClassLongA(hWnd, nIndex, LONG(dwNewLong));\n  }\n#endif\n\n#ifdef SetClassLongPtrW\n  #undef SetClassLongPtrW\n  inline LONG_PTR SetClassLongPtrW(HWND hWnd, int nIndex, LONG_PTR dwNewLong)\n  {\n\treturn ::SetClassLongW(hWnd, nIndex, LONG(dwNewLong));\n  }\n#endif\n\n#ifdef GetClassLongPtrA\n  #undef GetClassLongPtrA\n  inline LONG_PTR GetClassLongPtrA(HWND hWnd, int nIndex)\n  {\n\treturn ::GetClassLongA(hWnd, nIndex);\n  }\n#endif\n\n#ifdef GetClassLongPtrW\n  #undef GetClassLongPtrW\n  inline LONG_PTR GetClassLongPtrW(HWND hWnd, int nIndex)\n  {\n\treturn ::GetClassLongW(hWnd, nIndex);\n  }\n#endif\n\n\n///////////////////////////////////////////////////////////////////////////////\n// CWindowEx - extension of ATL::CWindow\n\nnamespace WTL\n{\n\nclass CWindowEx : public ATL::CWindow\n{\npublic:\n\tCWindowEx(HWND hWnd = NULL) : ATL::CWindow(hWnd)\n\t{ }\n\n\tCWindowEx& operator =(HWND hWnd)\n\t{\n\t\tm_hWnd = hWnd;\n\t\treturn *this;\n\t}\n\n\toperator HWND() const\n\t{\n\t\treturn m_hWnd;\n\t}\n\n// Methods\n\tBOOL PrintWindow(HDC hDC, UINT uFlags = 0)\n\t{\n\t\tATLASSERT(::IsWindow(m_hWnd));\n\t\treturn ::PrintWindow(m_hWnd, hDC, uFlags);\n\t}\n\n\tBOOL DragDetect(POINT pt)\n\t{\n\t\tATLASSERT(::IsWindow(m_hWnd));\n\t\treturn ::DragDetect(m_hWnd, pt);\n\t}\n\n\tBOOL DragDetect()\n\t{\n\t\tATLASSERT(::IsWindow(m_hWnd));\n\n\t\tPOINT pt = {};\n\t\t::GetCursorPos(&pt);\n\t\treturn ::DragDetect(m_hWnd, pt);\n\t}\n\n\tCWindowEx GetAncestor(UINT uFlags) const\n\t{\n\t\tATLASSERT(::IsWindow(m_hWnd));\n\t\treturn CWindowEx(::GetAncestor(m_hWnd, uFlags));\n\t}\n\n\t// Note: Does not work properly on Vista Aero and above\n\tBOOL AnimateWindow(DWORD dwFlags, DWORD dwTime = 200)\n\t{\n\t\tATLASSERT(::IsWindow(m_hWnd));\n\t\treturn ::AnimateWindow(m_hWnd, dwTime, dwFlags);\n\t}\n\n\tBOOL FlashWindowEx(DWORD dwFlags, UINT uCount, DWORD dwTimeout = 0)\n\t{\n\t\tATLASSERT(::IsWindow(m_hWnd));\n\n\t\tFLASHWINFO fi = { sizeof(FLASHWINFO) };\n\t\tfi.hwnd = m_hWnd;\n\t\tfi.dwFlags = dwFlags;\n\t\tfi.uCount = uCount;\n\t\tfi.dwTimeout = dwTimeout;\n\t\treturn ::FlashWindowEx(&fi);\n\t}\n\n\tBOOL StopFlashWindowEx()\n\t{\n\t\tATLASSERT(::IsWindow(m_hWnd));\n\n\t\tFLASHWINFO fi = { sizeof(FLASHWINFO) };\n\t\tfi.hwnd = m_hWnd;\n\t\tfi.dwFlags = FLASHW_STOP;\n\t\treturn ::FlashWindowEx(&fi);\n\t}\n\n// Class long properties\n\tDWORD GetClassLong(int nIndex) const\n\t{\n\t\tATLASSERT(::IsWindow(m_hWnd));\n\t\treturn ::GetClassLong(m_hWnd, nIndex);\n\t}\n\n\tDWORD SetClassLong(int nIndex, LONG dwNewLong)\n\t{\n\t\tATLASSERT(::IsWindow(m_hWnd));\n\t\treturn ::SetClassLong(m_hWnd, nIndex, dwNewLong);\n\t}\n\n\tULONG_PTR GetClassLongPtr(int nIndex) const\n\t{\n\t\tATLASSERT(::IsWindow(m_hWnd));\n\t\treturn ::GetClassLongPtr(m_hWnd, nIndex);\n\t}\n\n\tULONG_PTR SetClassLongPtr(int nIndex, LONG_PTR dwNewLong)\n\t{\n\t\tATLASSERT(::IsWindow(m_hWnd));\n\t\treturn ::SetClassLongPtr(m_hWnd, nIndex, dwNewLong);\n\t}\n\n// Layered windows\n\tBOOL SetLayeredWindowAttributes(COLORREF crlKey, BYTE byteAlpha, DWORD dwFlags)\n\t{\n\t\tATLASSERT(::IsWindow(m_hWnd));\n\t\tATLASSERT((GetExStyle() & WS_EX_LAYERED) != 0);\n\n\t\treturn ::SetLayeredWindowAttributes(m_hWnd, crlKey, byteAlpha, dwFlags);\n\t}\n\n\tBOOL UpdateLayeredWindow(HDC hdcDst, LPPOINT pptDst, LPSIZE psize, HDC hdcSrc, LPPOINT pptSrc, COLORREF crlKey, BLENDFUNCTION* pblend, DWORD dwFlags)\n\t{\n\t\tATLASSERT(::IsWindow(m_hWnd));\n\t\tATLASSERT((GetExStyle() & WS_EX_LAYERED) != 0);\n\n\t\treturn ::UpdateLayeredWindow(m_hWnd, hdcDst, pptDst, psize, hdcSrc, pptSrc, crlKey, pblend, dwFlags);\n\t}\n\n\tBOOL UpdateLayeredWindow(LPPOINT pptDst = NULL)\n\t{\n\t\tATLASSERT(::IsWindow(m_hWnd));\n\t\tATLASSERT((GetExStyle() & WS_EX_LAYERED) != 0);\n\n\t\treturn ::UpdateLayeredWindow(m_hWnd, NULL, pptDst, NULL, NULL, NULL, CLR_NONE, NULL, 0);\n\t}\n\n\tBOOL GetLayeredWindowAttributes(COLORREF* pcrlKey, BYTE* pbyteAlpha, DWORD* pdwFlags) const\n\t{\n\t\tATLASSERT(::IsWindow(m_hWnd));\n\t\tATLASSERT((GetExStyle() & WS_EX_LAYERED) != 0);\n\n\t\treturn ::GetLayeredWindowAttributes(m_hWnd, pcrlKey, pbyteAlpha, pdwFlags);\n\t}\n\n// Mouse tracking\n\tBOOL StartTrackMouseLeave()\n\t{\n\t\tATLASSERT(::IsWindow(m_hWnd));\n\n\t\tTRACKMOUSEEVENT tme = {};\n\t\ttme.cbSize = sizeof(TRACKMOUSEEVENT);\n\t\ttme.dwFlags = TME_LEAVE;\n\t\ttme.hwndTrack = m_hWnd;\n\t\treturn ::TrackMouseEvent(&tme);\n\t}\n\n\tBOOL StartTrackMouse(DWORD dwFlags, DWORD dwHoverTime = HOVER_DEFAULT)\n\t{\n\t\tATLASSERT(::IsWindow(m_hWnd));\n\n\t\tTRACKMOUSEEVENT tme = {};\n\t\ttme.cbSize = sizeof(TRACKMOUSEEVENT);\n\t\ttme.dwFlags = dwFlags;\n\t\ttme.hwndTrack = m_hWnd;\n\t\ttme.dwHoverTime = dwHoverTime;\n\t\treturn ::TrackMouseEvent(&tme);\n\t}\n\n\tBOOL CancelTrackMouse(DWORD dwType)\n\t{\n\t\tATLASSERT(::IsWindow(m_hWnd));\n\n\t\tTRACKMOUSEEVENT tme = {};\n\t\ttme.cbSize = sizeof(TRACKMOUSEEVENT);\n\t\ttme.dwFlags = TME_CANCEL | dwType;\n\t\ttme.hwndTrack = m_hWnd;\n\t\treturn ::TrackMouseEvent(&tme);\n\t}\n\n// CString support\n#ifdef __ATLSTR_H__\n\tint GetWindowText(ATL::CString& strText) const\n\t{\n\t\tint nLength = GetWindowTextLength();\n\t\tLPTSTR pszText = strText.GetBuffer(nLength + 1);\n\t\tnLength = ::GetWindowText(m_hWnd, pszText, nLength + 1);\n\t\tstrText.ReleaseBuffer(nLength);\n\n\t\treturn nLength;\n\t}\n\n\tUINT GetDlgItemText(int nID, ATL::CString& strText) const\n\t{\n\t\tATLASSERT(::IsWindow(m_hWnd));\n\n\t\tHWND hItem = GetDlgItem(nID);\n\t\tif(hItem != NULL)\n\t\t{\n\t\t\tint nLength = ::GetWindowTextLength(hItem);\n\t\t\tLPTSTR pszText = strText.GetBuffer(nLength + 1);\n\t\t\tnLength = ::GetWindowText(hItem, pszText, nLength + 1);\n\t\t\tstrText.ReleaseBuffer(nLength);\n\n\t\t\treturn nLength;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tstrText.Empty();\n\n\t\t\treturn 0;\n\t\t}\n\t}\n#endif // __ATLSTR_H__\n\n// Dialog window only\n\tUINT DlgGetDefID() const\n\t{\n\t\tATLASSERT(::IsWindow(m_hWnd));\n\n\t\tLRESULT lRet = ::SendMessage(m_hWnd, DM_GETDEFID, 0, 0L);\n\t\tUINT uID = 0U;\n\t\tif(HIWORD(lRet) == DC_HASDEFID)\n\t\t\tuID = LOWORD(lRet);\n\n\t\treturn uID;\n\t}\n\n\tvoid DlgSetDefID(UINT uID)\n\t{\n\t\tATLASSERT(::IsWindow(m_hWnd));\n\n\t\t::SendMessage(m_hWnd, DM_SETDEFID, uID, 0L);\n\t}\n\n\tvoid DlgReposition()\n\t{\n\t\tATLASSERT(::IsWindow(m_hWnd));\n\n\t\t::SendMessage(m_hWnd, DM_REPOSITION, 0, 0L);\n\t}\n};\n\n} // namespace WTL\n\n#endif // __ATLWINX_H__\n"
  },
  {
    "path": "Examples/WhisperDesktop/Utils/logger.cpp",
    "content": "#include \"stdafx.h\"\n#include \"logger.h\"\n#include \"miscUtils.h\"\n\nnamespace\n{\n\tusing namespace Whisper;\n\n\t// Terminal color map. 10 colors grouped in ranges [0.0, 0.1, ..., 0.9]\n\t// Lowest is red, middle is yellow, highest is green.\n\tstatic const std::array<const char*, 10> k_colors =\n\t{\n\t\t\"\\033[38;5;196m\", \"\\033[38;5;202m\", \"\\033[38;5;208m\", \"\\033[38;5;214m\", \"\\033[38;5;220m\",\n\t\t\"\\033[38;5;226m\", \"\\033[38;5;190m\", \"\\033[38;5;154m\", \"\\033[38;5;118m\", \"\\033[38;5;82m\",\n\t};\n\n\tstatic int colorIndex( const sToken& tok )\n\t{\n\t\tconst float p = tok.probability;\n\t\tconst float p3 = p * p * p;\n\t\tint col = (int)( p3 * float( k_colors.size() ) );\n\t\tcol = std::max( 0, std::min( (int)k_colors.size() - 1, col ) );\n\t\treturn col;\n\t}\n}\n\nvoid printTime( CStringA& rdi, Whisper::sTimeSpan time, bool comma )\n{\n\tWhisper::sTimeSpanFields fields = time;\n\tconst uint32_t hours = fields.days * 24 + fields.hours;\n\tconst char separator = comma ? ',' : '.';\n\trdi.AppendFormat( \"%02d:%02d:%02d%c%03d\",\n\t\t(int)hours,\n\t\t(int)fields.minutes,\n\t\t(int)fields.seconds,\n\t\tseparator,\n\t\tfields.ticks / 10'000 );\n}\n\nHRESULT logNewSegments( const iTranscribeResult* results, size_t newSegments, bool printSpecial )\n{\n\tsTranscribeLength length;\n\tCHECK( results->getSize( length ) );\n\n\tconst size_t len = length.countSegments;\n\tsize_t i = len - newSegments;\n\n\tconst sSegment* const segments = results->getSegments();\n\tconst sToken* const tokens = results->getTokens();\n\n\tCStringA str;\n\tfor( ; i < len; i++ )\n\t{\n\t\tconst sSegment& seg = segments[ i ];\n\t\tstr = \"[\";\n\t\tprintTime( str, seg.time.begin );\n\t\tstr += \" --> \";\n\t\tprintTime( str, seg.time.end );\n\t\tstr += \"]  \";\n\n\t\tfor( uint32_t j = 0; j < seg.countTokens; j++ )\n\t\t{\n\t\t\tconst sToken& tok = tokens[ seg.firstToken + j ];\n\t\t\tif( !printSpecial && ( tok.flags & eTokenFlags::Special ) )\n\t\t\t\tcontinue;\n\t\t\tstr += k_colors[ colorIndex( tok ) ];\n\t\t\tstr += tok.text;\n\t\t\tstr += \"\\033[0m\";\n\t\t}\n\t\tlogInfo( u8\"%s\", cstr( str ) );\n\t}\n\n\treturn S_OK;\n}"
  },
  {
    "path": "Examples/WhisperDesktop/Utils/logger.h",
    "content": "#pragma once\n#include <whisperWindows.h>\n#include <cstdarg>\n\nvoid logMessage( Whisper::eLogLevel lvl, const char8_t* pszFormat, va_list args );\n\n#define LOG_MESSAGE_IMPL( lvl )                \\\n\tstd::va_list args;                         \\\n\tva_start( args, pszFormat );               \\\n\tlogMessage( lvl, pszFormat, args );        \\\n\tva_end( args )\n\ninline void logError( const char8_t* pszFormat, ... )\n{\n\tLOG_MESSAGE_IMPL( Whisper::eLogLevel::Error );\n}\ninline void logWarning( const char8_t* pszFormat, ... )\n{\n\tLOG_MESSAGE_IMPL( Whisper::eLogLevel::Warning );\n}\ninline void logInfo( const char8_t* pszFormat, ... )\n{\n\tLOG_MESSAGE_IMPL( Whisper::eLogLevel::Info );\n}\ninline void logDebug( const char8_t* pszFormat, ... )\n{\n\tLOG_MESSAGE_IMPL( Whisper::eLogLevel::Debug );\n}\n\n#undef LOG_MESSAGE_IMPL\n\nHRESULT logNewSegments( const Whisper::iTranscribeResult* results, size_t newSegments, bool printSpecial = false );\n\nvoid clearLastError();\nbool getLastError( CString& rdi );\n\nvoid printTime( CStringA& rdi, Whisper::sTimeSpan time, bool comma = false );"
  },
  {
    "path": "Examples/WhisperDesktop/Utils/miscUtils.cpp",
    "content": "﻿#include \"stdafx.h\"\n#include \"miscUtils.h\"\n\nnamespace\n{\n\twchar_t* formatMessage( HRESULT hr )\n\t{\n\t\twchar_t* err;\n\t\tif( FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,\n\t\t\tNULL,\n\t\t\thr,\n\t\t\tMAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ),\n\t\t\t(LPTSTR)&err,\n\t\t\t0,\n\t\t\tNULL ) )\n\t\t\treturn err;\n\t\treturn nullptr;\n\t}\n}\n\nCString formatErrorMessage( HRESULT hr )\n{\n\tCString message;\n\tconst wchar_t* err = formatMessage( hr );\n\tif( nullptr != err )\n\t{\n\t\tmessage = err;\n\t\tLocalFree( (HLOCAL)err );\n\t\tmessage.TrimRight();\n\t}\n\telse\n\t\tmessage.Format( L\"Error code %i (0x%08X)\", hr, hr );\n\n\treturn message;\n}\n\nvoid reportFatalError( const char* what, HRESULT hr )\n{\n\tCString message;\n\tmessage.Format( L\"%S\\n%S\\n\", \"Unable to start the application.\", what );\n\tmessage += formatErrorMessage( hr );\n\t::MessageBox( nullptr, message, L\"Whisper Desktop Startup\", MB_OK | MB_ICONERROR );\n}\n\nnamespace\n{\n\tusing Whisper::eModelImplementation;\n\n\tstruct sImplString\n\t{\n\t\teModelImplementation val;\n\t\tLPCTSTR str;\n\t};\n\tstatic const std::array<sImplString, 3> s_implStrings =\n\t{\n\t\tsImplString{ eModelImplementation::GPU, L\"GPU\" },\n\t\tsImplString{ eModelImplementation::Hybrid, L\"Hybrid\" },\n\t\tsImplString{ eModelImplementation::Reference, L\"Reference\" },\n\t};\n}\n\nHRESULT implParse( const CString& s, eModelImplementation& rdi )\n{\n\tfor( const auto& is : s_implStrings )\n\t{\n\t\tif( 0 != s.CompareNoCase( is.str ) )\n\t\t\tcontinue;\n\t\trdi = is.val;;\n\t\treturn S_OK;\n\t}\n\treturn E_INVALIDARG;\n}\n\nLPCTSTR implString( eModelImplementation i )\n{\n\tfor( const auto& is : s_implStrings )\n\t\tif( is.val == i )\n\t\t\treturn is.str;\n\treturn nullptr;\n}\n\nvoid implPopulateCombobox( CComboBox& cb, Whisper::eModelImplementation impl )\n{\n\tint curSel = 0;\n\tint idx = 0;\n\tfor( const auto& is : s_implStrings )\n\t{\n\t\tcb.AddString( is.str );\n\t\tif( is.val == impl )\n\t\t\tcurSel = idx;\n\t\tidx++;\n\t}\n\tcb.SetCurSel( curSel );\n}\n\nWhisper::eModelImplementation implGetValue( CComboBox& cb )\n{\n\tint curSel = cb.GetCurSel();\n\tif( curSel < 0 )\n\t\treturn (Whisper::eModelImplementation)0;\n\treturn s_implStrings[ curSel ].val;\n}\n\nThreadPoolWork::~ThreadPoolWork()\n{\n\tif( nullptr != work )\n\t{\n\t\tCloseThreadpoolWork( work );\n\t\twork = nullptr;\n\t}\n}\n\nvoid __stdcall ThreadPoolWork::callback( PTP_CALLBACK_INSTANCE Instance, PVOID Context, PTP_WORK Work )\n{\n\tiThreadPoolCallback* cb = (iThreadPoolCallback*)Context;\n\tcb->poolCallback();\n}\n\nHRESULT ThreadPoolWork::create( iThreadPoolCallback* cb )\n{\n\tif( nullptr == cb )\n\t\treturn E_POINTER;\n\tif( nullptr != work )\n\t\treturn HRESULT_FROM_WIN32( ERROR_ALREADY_INITIALIZED );\n\n\twork = CreateThreadpoolWork( &callback, cb, nullptr );\n\tif( nullptr != work )\n\t\treturn S_OK;\n\n\treturn HRESULT_FROM_WIN32( GetLastError() );\n}\n\nHRESULT ThreadPoolWork::post()\n{\n\tif( nullptr == work )\n\t\treturn OLE_E_BLANK;\n\tSubmitThreadpoolWork( work );\n\treturn S_OK;\n}\n\nvoid makeUtf16( CString& rdi, const char* utf8 )\n{\n\tconst size_t length = strlen( utf8 );\n\tint count = MultiByteToWideChar( CP_UTF8, 0, utf8, (int)length, nullptr, 0 );\n\twchar_t* p = rdi.GetBufferSetLength( count );\n\tMultiByteToWideChar( CP_UTF8, 0, utf8, (int)length, p, count );\n\trdi.ReleaseBuffer();\n}\n\nvoid makeUtf8( CStringA& rdi, const CString& utf16 )\n{\n\tint count = WideCharToMultiByte( CP_UTF8, 0, utf16, utf16.GetLength(), nullptr, 0, nullptr, nullptr );\n\tchar* s = rdi.GetBufferSetLength( count + 1 );\n\tcount = WideCharToMultiByte( CP_UTF8, 0, utf16, utf16.GetLength(), s, count, nullptr, nullptr );\n\trdi.ReleaseBufferSetLength( count );\n}\n\nconstexpr int ofnBufferLength = 2048;\n\nbool getOpenFileName( HWND owner, LPCTSTR title, LPCTSTR filter, CString& path )\n{\n\twchar_t buffer[ ofnBufferLength ];\n\tbuffer[ 0 ] = 0;\n\tOPENFILENAME ofn;\n\tmemset( &ofn, 0, sizeof( ofn ) );\n\tofn.lStructSize = sizeof( OPENFILENAME );\n\tofn.hwndOwner = owner;\n\tofn.lpstrFilter = filter;\n\tofn.lpstrTitle = title;\n\tofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST;\n\tofn.lpstrFile = buffer;\n\tofn.nMaxFile = ofnBufferLength - 1;\n\n\tCString dir;\n\tif( path.GetLength() > 0 && path.GetLength() < ofnBufferLength )\n\t\twcsncpy_s( buffer, path, path.GetLength() );\n\n\tif( !GetOpenFileName( &ofn ) )\n\t{\n\t\tpath = L\"\";\n\t\treturn false;\n\t}\n\telse\n\t{\n\t\tpath = ofn.lpstrFile;\n\t\treturn true;\n\t}\n}\n\nbool getSaveFileName( HWND owner, LPCTSTR title, LPCTSTR filter, CString& path, DWORD* filterIndex )\n{\n\twchar_t buffer[ ofnBufferLength ];\n\tbuffer[ 0 ] = 0;\n\n\tOPENFILENAME ofn;\n\tmemset( &ofn, 0, sizeof( ofn ) );\n\tofn.lStructSize = sizeof( OPENFILENAME );\n\tofn.hwndOwner = owner;\n\tofn.lpstrFilter = filter;\n\tofn.lpstrTitle = title;\n\tofn.Flags = OFN_EXPLORER | OFN_PATHMUSTEXIST;\n\tofn.lpstrFile = buffer;\n\tofn.nMaxFile = ofnBufferLength - 1;\n\tif( nullptr != filterIndex )\n\t\tofn.nFilterIndex = *filterIndex + 1;\n\n\tif( path.GetLength() > 0 && path.GetLength() < ofnBufferLength )\n\t\twcsncpy_s( buffer, path, path.GetLength() );\n\n\tif( !GetSaveFileName( &ofn ) )\n\t\treturn false;\n\n\tpath = ofn.lpstrFile;\n\n\tif( nullptr != filterIndex )\n\t\t*filterIndex = ofn.nFilterIndex - 1;\n\n\treturn true;\n}\n\nvoid reportError( HWND owner, LPCTSTR text, LPCTSTR title, HRESULT hr )\n{\n\tif( nullptr == title )\n\t\ttitle = L\"Operation Failed\";\n\n\tCString message = text;\n\tmessage.TrimRight();\n\tif( FAILED( hr ) )\n\t{\n\t\tmessage += L\"\\n\";\n\t\tmessage += formatErrorMessage( hr );\n\t}\n\n\t::MessageBox( owner, message, title, MB_OK | MB_ICONWARNING );\n}\n\nHRESULT writeUtf8Bom( CAtlFile& file )\n{\n\tconst std::array<uint8_t, 3> bom = { 0xEF, 0xBB, 0xBF };\n\treturn file.Write( bom.data(), 3 );\n}\n\nbool isInvalidTranslate( HWND owner, uint32_t lang, bool translate )\n{\n\tif( !translate )\n\t\treturn false;\n\tconstexpr uint32_t english = 0x6E65;\n\tif( lang != english )\n\t\treturn false;\n\n\tLPCTSTR message = L\"The translate feature translates speech to English.\\nIt’s not available when the audio language is already English.\";\n\tMessageBox( owner, message, L\"Incompatible parameters\", MB_OK | MB_ICONINFORMATION );\n\treturn true;\n}"
  },
  {
    "path": "Examples/WhisperDesktop/Utils/miscUtils.h",
    "content": "#pragma once\n#include <iContext.h>\n#include \"logger.h\"\n\nCString formatErrorMessage( HRESULT hr );\n\nvoid reportFatalError( const char* what, HRESULT hr );\n\n#define CHECK( hr ) { const HRESULT __hr = ( hr ); if( FAILED( __hr ) ) return __hr; }\n\nHRESULT implParse( const CString& s, Whisper::eModelImplementation& rdi );\n\nLPCTSTR implString( Whisper::eModelImplementation i );\n\nvoid implPopulateCombobox( CComboBox& cb, Whisper::eModelImplementation impl );\n\nWhisper::eModelImplementation implGetValue( CComboBox& cb );\n\n__interface iThreadPoolCallback\n{\n\tvoid __stdcall poolCallback() noexcept;\n};\n\nclass ThreadPoolWork\n{\n\tPTP_WORK work = nullptr;\n\tstatic void __stdcall callback( PTP_CALLBACK_INSTANCE Instance, PVOID Context, PTP_WORK Work );\n\npublic:\n\n\t~ThreadPoolWork();\n\tHRESULT create( iThreadPoolCallback* cb );\n\tHRESULT post();\n};\n\nvoid makeUtf16( CString& rdi, const char* utf8 );\nvoid makeUtf8( CStringA& rdi, const CString& utf16 );\n\nbool getOpenFileName( HWND owner, LPCTSTR title, LPCTSTR filter, CString& path );\n\nbool getSaveFileName( HWND owner, LPCTSTR title, LPCTSTR filter, CString& path, DWORD* filterIndex = nullptr );\n\n#define ON_BUTTON_CLICK( id, func )  \\\n\tif( uMsg == WM_COMMAND &&        \\\n         id == LOWORD( wParam ) )    \\\n\t{                                \\\n\t\tbHandled = TRUE;             \\\n\t\tfunc();                      \\\n\t\tlResult = 0;                 \\\n\t\treturn TRUE;                 \\\n\t}\n\nvoid reportError( HWND owner, LPCTSTR text, LPCTSTR title, HRESULT hr = S_FALSE );\n\ninline const wchar_t* cstr( const CString& s ) { return s; }\ninline const char* cstr( const CStringA& s ) { return s; }\n\ninline HRESULT getLastHr()\n{\n\treturn HRESULT_FROM_WIN32( GetLastError() );\n}\n\nHRESULT writeUtf8Bom( CAtlFile& file );\n\n// Flip order of bytes from RGB to BGR, or vice versa\ninline uint32_t flipRgb( uint32_t val )\n{\n\tval = _byteswap_ulong( val );\n\treturn val >> 8;\n}\n\nbool isInvalidTranslate( HWND owner, uint32_t lang, bool translate );\n\ninline bool isChecked( CButton& btn )\n{\n\treturn btn.GetCheck() == BST_CHECKED;\n}"
  },
  {
    "path": "Examples/WhisperDesktop/WhisperDesktop.cpp",
    "content": "#include \"stdafx.h\"\n#include \"AppState.h\"\n#include \"Utils/miscUtils.h\"\n#include \"LoadModelDlg.h\"\n#include \"TranscribeDlg.h\"\n#include \"CaptureDlg.h\"\n\nstatic HRESULT dialogLoadModel( AppState& appState )\n{\n\tLoadModelDlg loadDialog{ appState };\n\tHRESULT hr = loadDialog.show();\n\tif( FAILED( hr ) )\n\t{\n\t\treportFatalError( \"Error loading the model\", hr );\n\t\treturn hr;\n\t}\n\tappState.automaticallyLoadModel = false;\n\treturn hr;\n}\n\nstatic HRESULT dialogTranscribe( AppState& appState )\n{\n\tTranscribeDlg dialog{ appState };\n\treturn dialog.show();\n}\n\nstatic HRESULT dialogCapture( AppState& appState )\n{\n\tCaptureDlg dialog{ appState };\n\treturn dialog.show();\n}\n\nusing pfnDialog = HRESULT( * )( AppState& appState );\nstatic const std::array<pfnDialog, 4> s_dialogs =\n{\n\tnullptr, // S_OK\n\t&dialogLoadModel,   // SCREEN_MODEL\n\t&dialogTranscribe,  // SCREEN_TRANSCRIBE\n\t&dialogCapture,\t    // SCREEN_CAPTURE\n};\n\nint __stdcall wWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow )\n{\n\tAppState appState;\n\tHRESULT hr = appState.startup();\n\tif( FAILED( hr ) )\n\t\treturn hr;\n\n\tappState.findModelSource();\n\n\thr = SCREEN_MODEL;\n\twhile( true )\n\t{\n\t\tpfnDialog pfn = s_dialogs[ hr ];\n\t\tif( nullptr == pfn )\n\t\t\treturn S_OK;\n\t\thr = pfn( appState );\n\t\tif( FAILED( hr ) )\n\t\t\treturn hr;\n\t\tif( hr == SCREEN_MODEL )\n\t\t\tappState.model = nullptr;\n\t}\n}"
  },
  {
    "path": "Examples/WhisperDesktop/WhisperDesktop.manifest",
    "content": "﻿<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<assembly xmlns=\"urn:schemas-microsoft-com:asm.v1\" manifestVersion=\"1.0\" xmlns:asmv3=\"urn:schemas-microsoft-com:asm.v3\">\n\t<assemblyIdentity version=\"1.0.0.0\" processorArchitecture=\"amd64\" name=\"CompanyName.ProductName.YourApplication\" type=\"win32\" />\n\t<description>Your application description here.</description>\n\t<dependency>\n\t\t<dependentAssembly>\n\t\t\t<assemblyIdentity type=\"win32\" name=\"Microsoft.Windows.Common-Controls\" version=\"6.0.0.0\" processorArchitecture=\"amd64\" publicKeyToken=\"6595b64144ccf1df\" language=\"*\" />\n\t\t</dependentAssembly>\n\t</dependency>\n\t<asmv3:application>\n\t\t<asmv3:windowsSettings>\n\t\t\t<dpiAware xmlns=\"http://schemas.microsoft.com/SMI/2005/WindowsSettings\">true</dpiAware>\n\t\t\t<dpiAwareness xmlns=\"http://schemas.microsoft.com/SMI/2016/WindowsSettings\">PerMonitorV2</dpiAwareness>\n\t\t</asmv3:windowsSettings>\n\t</asmv3:application>\n</assembly>"
  },
  {
    "path": "Examples/WhisperDesktop/WhisperDesktop.vcxproj",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project DefaultTargets=\"Build\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <ItemGroup Label=\"ProjectConfigurations\">\n    <ProjectConfiguration Include=\"Debug|x64\">\n      <Configuration>Debug</Configuration>\n      <Platform>x64</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Release|x64\">\n      <Configuration>Release</Configuration>\n      <Platform>x64</Platform>\n    </ProjectConfiguration>\n  </ItemGroup>\n  <PropertyGroup Label=\"Globals\">\n    <VCProjectVersion>16.0</VCProjectVersion>\n    <Keyword>Win32Proj</Keyword>\n    <ProjectGuid>{cd9e49f0-75a3-4f91-ac71-336109ee39c6}</ProjectGuid>\n    <RootNamespace>WhisperDesktop</RootNamespace>\n    <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>\n  </PropertyGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.Default.props\" />\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\" Label=\"Configuration\">\n    <ConfigurationType>Application</ConfigurationType>\n    <UseDebugLibraries>true</UseDebugLibraries>\n    <PlatformToolset>v143</PlatformToolset>\n    <CharacterSet>Unicode</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\" Label=\"Configuration\">\n    <ConfigurationType>Application</ConfigurationType>\n    <UseDebugLibraries>false</UseDebugLibraries>\n    <PlatformToolset>v143</PlatformToolset>\n    <WholeProgramOptimization>true</WholeProgramOptimization>\n    <CharacterSet>Unicode</CharacterSet>\n  </PropertyGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.props\" />\n  <ImportGroup Label=\"ExtensionSettings\">\n  </ImportGroup>\n  <ImportGroup Label=\"Shared\">\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <PropertyGroup Label=\"UserMacros\" />\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\n    <IncludePath>$(ProjectDir);$(SolutionDir)Whisper\\API\\;$(IncludePath)</IncludePath>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <IncludePath>$(ProjectDir);$(SolutionDir)Whisper\\API\\;$(IncludePath)</IncludePath>\n  </PropertyGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <SDLCheck>true</SDLCheck>\n      <PreprocessorDefinitions>_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <ConformanceMode>true</ConformanceMode>\n      <PrecompiledHeader>Use</PrecompiledHeader>\n      <LanguageStandard>stdcpp20</LanguageStandard>\n      <MultiProcessorCompilation>true</MultiProcessorCompilation>\n      <EnableEnhancedInstructionSet>AdvancedVectorExtensions</EnableEnhancedInstructionSet>\n    </ClCompile>\n    <Link>\n      <SubSystem>Windows</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n      <ManifestFile>WhisperDesktop.manifest</ManifestFile>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <FunctionLevelLinking>true</FunctionLevelLinking>\n      <IntrinsicFunctions>true</IntrinsicFunctions>\n      <SDLCheck>true</SDLCheck>\n      <PreprocessorDefinitions>NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <ConformanceMode>true</ConformanceMode>\n      <PrecompiledHeader>Use</PrecompiledHeader>\n      <LanguageStandard>stdcpp20</LanguageStandard>\n      <MultiProcessorCompilation>true</MultiProcessorCompilation>\n      <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>\n      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>\n      <EnableEnhancedInstructionSet>AdvancedVectorExtensions</EnableEnhancedInstructionSet>\n    </ClCompile>\n    <Link>\n      <SubSystem>Windows</SubSystem>\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\n      <OptimizeReferences>true</OptimizeReferences>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n      <ManifestFile>WhisperDesktop.manifest</ManifestFile>\n      <LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemGroup>\n    <ClInclude Include=\"CircleIndicator.h\" />\n    <ClInclude Include=\"AppState.h\" />\n    <ClInclude Include=\"CaptureDlg.h\" />\n    <ClInclude Include=\"ModelAdvancedDlg.h\" />\n    <ClInclude Include=\"Utils\\TranslateCheckbox.h\" />\n    <ClInclude Include=\"Utils\\DebugConsole.h\" />\n    <ClInclude Include=\"framework.h\" />\n    <ClInclude Include=\"Utils\\logger.h\" />\n    <ClInclude Include=\"Utils\\PendingState.h\" />\n    <ClInclude Include=\"Utils\\LanguageDropdown.h\" />\n    <ClInclude Include=\"LoadModelDlg.h\" />\n    <ClInclude Include=\"TranscribeDlg.h\" />\n    <ClInclude Include=\"Utils\\miscUtils.h\" />\n    <ClInclude Include=\"stdafx.h\" />\n    <ClInclude Include=\"Resource.h\" />\n    <ClInclude Include=\"targetver.h\" />\n    <ClInclude Include=\"Utils\\WTL\\atlapp.h\" />\n    <ClInclude Include=\"Utils\\WTL\\atlcrack.h\" />\n    <ClInclude Include=\"Utils\\WTL\\atlctrls.h\" />\n    <ClInclude Include=\"Utils\\WTL\\atlddx.h\" />\n    <ClInclude Include=\"Utils\\WTL\\atlgdi.h\" />\n    <ClInclude Include=\"Utils\\WTL\\atlres.h\" />\n    <ClInclude Include=\"Utils\\WTL\\atluser.h\" />\n    <ClInclude Include=\"Utils\\WTL\\atlwinx.h\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ClCompile Include=\"CircleIndicator.cpp\" />\n    <ClCompile Include=\"AppState.cpp\" />\n    <ClCompile Include=\"CaptureDlg.cpp\" />\n    <ClCompile Include=\"ModelAdvancedDlg.cpp\" />\n    <ClCompile Include=\"useDiscreteGpu.c\">\n      <PrecompiledHeader>NotUsing</PrecompiledHeader>\n    </ClCompile>\n    <ClCompile Include=\"Utils\\TranslateCheckbox.cpp\" />\n    <ClCompile Include=\"Utils\\DebugConsole.cpp\" />\n    <ClCompile Include=\"Utils\\logger.cpp\" />\n    <ClCompile Include=\"Utils\\PendingState.cpp\" />\n    <ClCompile Include=\"Utils\\LanguageDropdown.cpp\" />\n    <ClCompile Include=\"LoadModelDlg.cpp\" />\n    <ClCompile Include=\"TranscribeDlg.cpp\" />\n    <ClCompile Include=\"Utils\\miscUtils.cpp\" />\n    <ClCompile Include=\"stdafx.cpp\">\n      <PrecompiledHeader>Create</PrecompiledHeader>\n    </ClCompile>\n    <ClCompile Include=\"WhisperDesktop.cpp\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ResourceCompile Include=\"WhisperDesktop.rc\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Image Include=\"sunflower.ico\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Manifest Include=\"WhisperDesktop.manifest\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ProjectReference Include=\"..\\..\\Whisper\\Whisper.vcxproj\">\n      <Project>{701df8c8-e4a5-43ec-9c6b-747bbf4d8e71}</Project>\n    </ProjectReference>\n  </ItemGroup>\n  <ItemGroup>\n    <Text Include=\"Readme.txt\" />\n  </ItemGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.targets\" />\n  <ImportGroup Label=\"ExtensionTargets\">\n  </ImportGroup>\n</Project>"
  },
  {
    "path": "Examples/WhisperDesktop/WhisperDesktop.vcxproj.filters",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <ItemGroup>\n    <Filter Include=\"Source Files\">\n      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>\n      <Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>\n    </Filter>\n    <Filter Include=\"Header Files\">\n      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>\n      <Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>\n    </Filter>\n    <Filter Include=\"Resource Files\">\n      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>\n      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>\n    </Filter>\n  </ItemGroup>\n  <ItemGroup>\n    <ClInclude Include=\"framework.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"targetver.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"Resource.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"stdafx.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"LoadModelDlg.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"AppState.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"Utils\\miscUtils.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"Utils\\WTL\\atlapp.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"Utils\\WTL\\atlddx.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"Utils\\WTL\\atlctrls.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"Utils\\WTL\\atlgdi.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"Utils\\WTL\\atlres.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"Utils\\WTL\\atluser.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"Utils\\WTL\\atlwinx.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"TranscribeDlg.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"Utils\\LanguageDropdown.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"Utils\\PendingState.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"Utils\\DebugConsole.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"Utils\\logger.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"CaptureDlg.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"CircleIndicator.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"Utils\\WTL\\atlcrack.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"Utils\\TranslateCheckbox.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"ModelAdvancedDlg.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n  </ItemGroup>\n  <ItemGroup>\n    <ClCompile Include=\"WhisperDesktop.cpp\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"stdafx.cpp\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"LoadModelDlg.cpp\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"AppState.cpp\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"Utils\\miscUtils.cpp\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"TranscribeDlg.cpp\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"Utils\\LanguageDropdown.cpp\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"Utils\\PendingState.cpp\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"Utils\\DebugConsole.cpp\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"Utils\\logger.cpp\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"CaptureDlg.cpp\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"CircleIndicator.cpp\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"Utils\\TranslateCheckbox.cpp\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"ModelAdvancedDlg.cpp\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"useDiscreteGpu.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n  </ItemGroup>\n  <ItemGroup>\n    <ResourceCompile Include=\"WhisperDesktop.rc\">\n      <Filter>Resource Files</Filter>\n    </ResourceCompile>\n  </ItemGroup>\n  <ItemGroup>\n    <Image Include=\"sunflower.ico\">\n      <Filter>Resource Files</Filter>\n    </Image>\n  </ItemGroup>\n  <ItemGroup>\n    <Manifest Include=\"WhisperDesktop.manifest\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Text Include=\"Readme.txt\" />\n  </ItemGroup>\n</Project>"
  },
  {
    "path": "Examples/WhisperDesktop/framework.h",
    "content": "#pragma once\n#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers\n#define NOMINMAX\n// Windows Header Files\n#include \"targetver.h\"\n#include <windows.h>\n// ATL header files\n#include <atlstr.h>\n#include <atlfile.h>\n#include <atlbase.h>\n#include <atlwin.h>\n\n// C RunTime Header Files\n#include <stdlib.h>\n#include <malloc.h>\n#include <memory.h>\n#include <tchar.h>\n#include <assert.h>\n// C++ headers\n#include <vector>\n#include <array>\n#include <algorithm>"
  },
  {
    "path": "Examples/WhisperDesktop/stdafx.cpp",
    "content": "#include \"stdafx.h\""
  },
  {
    "path": "Examples/WhisperDesktop/stdafx.h",
    "content": "#pragma once\n#include \"framework.h\"\n\n#include <whisperWindows.h>\n\n#include \"resource.h\"\n#include \"Utils/WTL/atlapp.h\"\n#include \"Utils/WTL/atlctrls.h\""
  },
  {
    "path": "Examples/WhisperDesktop/targetver.h",
    "content": "#pragma once\n// Setup Windows SDK to only enable features available since Windows 8.0\n#include <WinSDKVer.h>\n#define _WIN32_WINNT _WIN32_WINNT_WIN8\n#define NTDDI_VERSION NTDDI_WIN8\n#include <SDKDDKVer.h>\n"
  },
  {
    "path": "Examples/WhisperDesktop/useDiscreteGpu.c",
    "content": "__declspec( dllexport ) int NvOptimusEnablement = 1;\n__declspec( dllexport ) int AmdPowerXpressRequestHighPerformance = 1;"
  },
  {
    "path": "Examples/main/Readme.txt",
    "content": "﻿This example shows how to consume the DLL from a C++ console application.\n\nThe command-line interface matches the corresponding example from whisper.cpp project."
  },
  {
    "path": "Examples/main/main.cpp",
    "content": "#include \"params.h\"\n#include \"../../Whisper/API/iContext.cl.h\"\n#include \"../../Whisper/API/iMediaFoundation.cl.h\"\n#include \"../../ComLightLib/comLightClient.h\"\n#include \"miscUtils.h\"\n#include <array>\n#include <atomic>\n#include \"textWriter.h\"\nusing namespace Whisper;\n\n#define STREAM_AUDIO 1\n\nstatic HRESULT loadWhisperModel( const wchar_t* path, const std::wstring& gpu, iModel** pp )\n{\n\tusing namespace Whisper;\n\tsModelSetup setup;\n\tsetup.impl = eModelImplementation::GPU;\n\tif( !gpu.empty() )\n\t\tsetup.adapter = gpu.c_str();\n\treturn Whisper::loadModel( path, setup, nullptr, pp );\n}\n\nnamespace\n{\n\t// Terminal color map. 10 colors grouped in ranges [0.0, 0.1, ..., 0.9]\n\t// Lowest is red, middle is yellow, highest is green.\n\tstatic const std::array<const char*, 10> k_colors =\n\t{\n\t\t\"\\033[38;5;196m\", \"\\033[38;5;202m\", \"\\033[38;5;208m\", \"\\033[38;5;214m\", \"\\033[38;5;220m\",\n\t\t\"\\033[38;5;226m\", \"\\033[38;5;190m\", \"\\033[38;5;154m\", \"\\033[38;5;118m\", \"\\033[38;5;82m\",\n\t};\n\n\tstd::string to_timestamp( sTimeSpan ts, bool comma = false )\n\t{\n\t\tsTimeSpanFields fields = ts;\n\t\tuint32_t msec = fields.ticks / 10'000;\n\t\tuint32_t hr = fields.days * 24 + fields.hours;\n\t\tuint32_t min = fields.minutes;\n\t\tuint32_t sec = fields.seconds;\n\n\t\tchar buf[ 32 ];\n\t\tsnprintf( buf, sizeof( buf ), \"%02d:%02d:%02d%s%03d\", hr, min, sec, comma ? \",\" : \".\", msec );\n\t\treturn std::string( buf );\n\t}\n\n\tstatic int colorIndex( const sToken& tok )\n\t{\n\t\tconst float p = tok.probability;\n\t\tconst float p3 = p * p * p;\n\t\tint col = (int)( p3 * float( k_colors.size() ) );\n\t\tcol = std::max( 0, std::min( (int)k_colors.size() - 1, col ) );\n\t\treturn col;\n\t}\n\n\tHRESULT __cdecl newSegmentCallback( iContext* context, uint32_t n_new, void* user_data ) noexcept\n\t{\n\t\tComLight::CComPtr<iTranscribeResult> results;\n\t\tCHECK( context->getResults( eResultFlags::Timestamps | eResultFlags::Tokens, &results ) );\n\n\t\tsTranscribeLength length;\n\t\tCHECK( results->getSize( length ) );\n\n\t\tconst whisper_params& params = *( (const whisper_params*)user_data );\n\n\t\t// print the last n_new segments\n\t\tconst uint32_t s0 = length.countSegments - n_new;\n\t\tif( s0 == 0 )\n\t\t\tprintf( \"\\n\" );\n\n\t\tconst sSegment* const segments = results->getSegments();\n\t\tconst sToken* const tokens = results->getTokens();\n\n\t\tfor( uint32_t i = s0; i < length.countSegments; i++ )\n\t\t{\n\t\t\tconst sSegment& seg = segments[ i ];\n\n\t\t\tif( params.no_timestamps )\n\t\t\t{\n\t\t\t\tif( params.print_colors )\n\t\t\t\t{\n\t\t\t\t\tfor( uint32_t j = 0; j < seg.countTokens; j++ )\n\t\t\t\t\t{\n\t\t\t\t\t\tconst sToken& tok = tokens[ seg.firstToken + j ];\n\t\t\t\t\t\tif( !params.print_special && ( tok.flags & eTokenFlags::Special ) )\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\twprintf( L\"%S%s%S\", k_colors[ colorIndex( tok ) ], utf16( tok.text ).c_str(), \"\\033[0m\" );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\twprintf( L\"%s\", utf16( seg.text ).c_str() );\n\t\t\t\tfflush( stdout );\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tstd::string speaker = \"\";\n\n\t\t\tif( params.diarize )\n\t\t\t{\n\t\t\t\teSpeakerChannel channel;\n\t\t\t\tHRESULT hr = context->detectSpeaker( seg.time, channel );\n\t\t\t\tif( SUCCEEDED( hr ) && channel != eSpeakerChannel::NoStereoData )\n\t\t\t\t{\n\t\t\t\t\tusing namespace std::string_literals;\n\t\t\t\t\tswitch( channel )\n\t\t\t\t\t{\n\t\t\t\t\tcase eSpeakerChannel::Unsure:\n\t\t\t\t\t\tspeaker = \"(speaker ?)\"s;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase eSpeakerChannel::Left:\n\t\t\t\t\t\tspeaker = \"(speaker 0)\"s;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase eSpeakerChannel::Right:\n\t\t\t\t\t\tspeaker = \"(speaker 1)\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif( params.print_colors )\n\t\t\t{\n\t\t\t\tprintf( \"[%s --> %s] %s \",\n\t\t\t\t\tto_timestamp( seg.time.begin ).c_str(),\n\t\t\t\t\tto_timestamp( seg.time.end ).c_str(),\n\t\t\t\t\tspeaker.c_str() );\n\n\t\t\t\tfor( uint32_t j = 0; j < seg.countTokens; j++ )\n\t\t\t\t{\n\t\t\t\t\tconst sToken& tok = tokens[ seg.firstToken + j ];\n\t\t\t\t\tif( !params.print_special && ( tok.flags & eTokenFlags::Special ) )\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\twprintf( L\"%S%s%S\", k_colors[ colorIndex( tok ) ], utf16( tok.text ).c_str(), \"\\033[0m\" );\n\t\t\t\t}\n\t\t\t\tprintf( \"\\n\" );\n\t\t\t}\n\t\t\telse\n\t\t\t\twprintf( L\"[%S --> %S]  %S%s\\n\", to_timestamp( seg.time.begin ).c_str(), to_timestamp( seg.time.end ).c_str(), speaker.c_str(), utf16( seg.text ).c_str() );\n\t\t}\n\t\treturn S_OK;\n\t}\n\n\tHRESULT __cdecl beginSegmentCallback( iContext* context, void* user_data ) noexcept\n\t{\n\t\tstd::atomic_bool* flag = (std::atomic_bool*)user_data;\n\t\tbool aborted = flag->load();\n\t\treturn aborted ? S_FALSE : S_OK;\n\t}\n\n\tHRESULT setupConsoleColors()\n\t{\n\t\tHANDLE h = GetStdHandle( STD_OUTPUT_HANDLE );\n\t\tif( h == INVALID_HANDLE_VALUE )\n\t\t\treturn HRESULT_FROM_WIN32( GetLastError() );\n\n\t\tDWORD mode = 0;\n\t\tif( !GetConsoleMode( h, &mode ) )\n\t\t\treturn HRESULT_FROM_WIN32( GetLastError() );\n\t\tif( 0 != ( mode & ENABLE_VIRTUAL_TERMINAL_PROCESSING ) )\n\t\t\treturn S_FALSE;\n\n\t\tmode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;\n\t\tif( !SetConsoleMode( h, mode ) )\n\t\t\treturn HRESULT_FROM_WIN32( GetLastError() );\n\t\treturn S_OK;\n\t}\n}\n\nstatic void __stdcall setPrompt( const int* ptr, int length, void* pv )\n{\n\tstd::vector<int>& vec = *( std::vector<int> * )( pv );\n\tif( length > 0 )\n\t\tvec.assign( ptr, ptr + length );\n}\n\nint wmain( int argc, wchar_t* argv[] )\n{\n\t// Whisper::dbgCompareTraces( LR\"(C:\\Temp\\2remove\\Whisper\\ref.bin)\", LR\"(C:\\Temp\\2remove\\Whisper\\gpu.bin )\" ); return 0;\n\n\t// Tell logger to use the standard output stream for the messages\n\t{\n\t\tWhisper::sLoggerSetup logSetup;\n\t\tlogSetup.flags = eLoggerFlags::UseStandardError;\n\t\tlogSetup.level = eLogLevel::Debug;\n\t\tWhisper::setupLogger( logSetup );\n\t}\n\n\twhisper_params params;\n\tif( !params.parse( argc, argv ) )\n\t\treturn 1;\n\n\tif( params.print_colors )\n\t{\n\t\tif( FAILED( setupConsoleColors() ) )\n\t\t\tparams.print_colors = false;\n\t}\n\n\tif( params.fname_inp.empty() )\n\t{\n\t\tfprintf( stderr, \"error: no input files specified\\n\" );\n\t\twhisper_print_usage( argc, argv, params );\n\t\treturn 2;\n\t}\n\n\tif( Whisper::findLanguageKeyA( params.language.c_str() ) == UINT_MAX )\n\t{\n\t\tfprintf( stderr, \"error: unknown language '%s'\\n\", params.language.c_str() );\n\t\twhisper_print_usage( argc, argv, params );\n\t\treturn 3;\n\t}\n\n\tComLight::CComPtr<iModel> model;\n\tHRESULT hr = loadWhisperModel( params.model.c_str(), params.gpu, &model );\n\tif( FAILED( hr ) )\n\t{\n\t\tprintError( \"failed to load the model\", hr );\n\t\treturn 4;\n\t}\n\n\tstd::vector<int> prompt;\n\tif( !params.prompt.empty() )\n\t{\n\t\thr = model->tokenize( params.prompt.c_str(), &setPrompt, &prompt );\n\t\tif( FAILED( hr ) )\n\t\t{\n\t\t\tprintError( \"failed to tokenize the initial prompt\", hr );\n\t\t\treturn 5;\n\t\t}\n\t}\n\n\tComLight::CComPtr<iContext> context;\n\thr = model->createContext( &context );\n\tif( FAILED( hr ) )\n\t{\n\t\tprintError( \"failed to initialize whisper context\", hr );\n\t\treturn 6;\n\t}\n\n\tComLight::CComPtr<iMediaFoundation> mf;\n\thr = initMediaFoundation( &mf );\n\tif( FAILED( hr ) )\n\t{\n\t\tprintError( \"failed to initialize Media Foundation runtime\", hr );\n\t\treturn 7;\n\t}\n\n\tfor( const std::wstring& fname : params.fname_inp )\n\t{\n\t\t// print some info about the processing\n\t\t{\n\t\t\tif( model->isMultilingual() == S_FALSE )\n\t\t\t{\n\t\t\t\tif( params.language != \"en\" || params.translate )\n\t\t\t\t{\n\t\t\t\t\tparams.language = \"en\";\n\t\t\t\t\tparams.translate = false;\n\t\t\t\t\tfprintf( stderr, \"%s: WARNING: model is not multilingual, ignoring language and translation options\\n\", __func__ );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// run the inference\n\t\tWhisper::sFullParams wparams;\n\t\tcontext->fullDefaultParams( eSamplingStrategy::Greedy, &wparams );\n\n\t\twparams.resetFlag( eFullParamsFlags::PrintRealtime | eFullParamsFlags::PrintProgress );\n\t\twparams.setFlag( eFullParamsFlags::PrintTimestamps, !params.no_timestamps );\n\t\twparams.setFlag( eFullParamsFlags::PrintSpecial, params.print_special );\n\t\twparams.setFlag( eFullParamsFlags::Translate, params.translate );\n\t\t// When there're multiple input files, assuming they're independent clips\n\t\twparams.setFlag( eFullParamsFlags::NoContext );\n\t\twparams.language = Whisper::makeLanguageKey( params.language.c_str() );\n\t\twparams.cpuThreads = params.n_threads;\n\t\tif( params.max_context != UINT_MAX )\n\t\t\twparams.n_max_text_ctx = params.max_context;\n\t\twparams.offset_ms = params.offset_t_ms;\n\t\twparams.duration_ms = params.duration_ms;\n\n\t\twparams.setFlag( eFullParamsFlags::TokenTimestamps, params.output_wts || params.max_len > 0 );\n\t\twparams.thold_pt = params.word_thold;\n\t\twparams.max_len = params.output_wts && params.max_len == 0 ? 60 : params.max_len;\n\n\t\twparams.setFlag( eFullParamsFlags::SpeedupAudio, params.speed_up );\n\n\t\tif( !prompt.empty() )\n\t\t{\n\t\t\twparams.prompt_tokens = prompt.data();\n\t\t\twparams.prompt_n_tokens = (int)prompt.size();\n\t\t}\n\n\t\t// This callback is called on each new segment\n\t\tif( !wparams.flag( eFullParamsFlags::PrintRealtime ) )\n\t\t{\n\t\t\twparams.new_segment_callback = &newSegmentCallback;\n\t\t\twparams.new_segment_callback_user_data = &params;\n\t\t}\n\n\t\t// example for abort mechanism\n\t\t// in this example, we do not abort the processing, but we could if the flag is set to true\n\t\t// the callback is called before every encoder run - if it returns false, the processing is aborted\n\t\tstd::atomic_bool is_aborted = false;\n\t\t{\n\t\t\twparams.encoder_begin_callback = &beginSegmentCallback;\n\t\t\twparams.encoder_begin_callback_user_data = &is_aborted;\n\t\t}\n\n\t\tif( STREAM_AUDIO && !wparams.flag( eFullParamsFlags::TokenTimestamps ) )\n\t\t{\n\t\t\tComLight::CComPtr<iAudioReader> reader;\n\t\t\tCHECK( mf->openAudioFile( fname.c_str(), params.diarize, &reader ) );\n\t\t\tsProgressSink progressSink{ nullptr, nullptr };\n\t\t\thr = context->runStreamed( wparams, progressSink, reader );\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// Token-level timestamps feature is not currently implemented when streaming the audio\n\t\t\t// When these timestamps are requested, fall back to buffered mode.\n\t\t\tComLight::CComPtr<iAudioBuffer> buffer;\n\t\t\tCHECK( mf->loadAudioFile( fname.c_str(), params.diarize, &buffer ) );\n\t\t\thr = context->runFull( wparams, buffer );\n\t\t}\n\n\t\tif( FAILED( hr ) )\n\t\t{\n\t\t\tprintError( \"Unable to process audio\", hr );\n\t\t\treturn 10;\n\t\t}\n\n\t\tif( params.output_txt )\n\t\t{\n\t\t\tbool timestamps = !params.no_timestamps;\n\t\t\thr = writeText( context, fname.c_str(), timestamps );\n\t\t\tif( FAILED( hr ) )\n\t\t\t\tprintError( \"Unable to produce the text file\", hr );\n\t\t}\n\n\t\tif( params.output_srt )\n\t\t{\n\t\t\thr = writeSubRip( context, fname.c_str() );\n\t\t\tif( FAILED( hr ) )\n\t\t\t\tprintError( \"Unable to produce the text file\", hr );\n\t\t}\n\n\t\tif( params.output_vtt )\n\t\t{\n\t\t\thr = writeWebVTT( context, fname.c_str() );\n\t\t\tif( FAILED( hr ) )\n\t\t\t\tprintError( \"Unable to produce the text file\", hr );\n\t\t}\n\t}\n\n\tcontext->timingsPrint();\n\tcontext = nullptr;\n\treturn 0;\n}"
  },
  {
    "path": "Examples/main/main.vcxproj",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project DefaultTargets=\"Build\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <ItemGroup Label=\"ProjectConfigurations\">\n    <ProjectConfiguration Include=\"Debug|x64\">\n      <Configuration>Debug</Configuration>\n      <Platform>x64</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Release|x64\">\n      <Configuration>Release</Configuration>\n      <Platform>x64</Platform>\n    </ProjectConfiguration>\n  </ItemGroup>\n  <PropertyGroup Label=\"Globals\">\n    <VCProjectVersion>16.0</VCProjectVersion>\n    <Keyword>Win32Proj</Keyword>\n    <ProjectGuid>{4cca7042-eb15-4f7a-b77b-5cafd2df47b2}</ProjectGuid>\n    <RootNamespace>main</RootNamespace>\n    <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>\n  </PropertyGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.Default.props\" />\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\" Label=\"Configuration\">\n    <ConfigurationType>Application</ConfigurationType>\n    <UseDebugLibraries>true</UseDebugLibraries>\n    <PlatformToolset>v143</PlatformToolset>\n    <CharacterSet>Unicode</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\" Label=\"Configuration\">\n    <ConfigurationType>Application</ConfigurationType>\n    <UseDebugLibraries>false</UseDebugLibraries>\n    <PlatformToolset>v143</PlatformToolset>\n    <WholeProgramOptimization>true</WholeProgramOptimization>\n    <CharacterSet>Unicode</CharacterSet>\n  </PropertyGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.props\" />\n  <ImportGroup Label=\"ExtensionSettings\">\n  </ImportGroup>\n  <ImportGroup Label=\"Shared\">\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <PropertyGroup Label=\"UserMacros\" />\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <SDLCheck>true</SDLCheck>\n      <PreprocessorDefinitions>NOMINMAX;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <ConformanceMode>true</ConformanceMode>\n      <LanguageStandard>stdcpp20</LanguageStandard>\n      <EnableEnhancedInstructionSet>AdvancedVectorExtensions</EnableEnhancedInstructionSet>\n    </ClCompile>\n    <Link>\n      <SubSystem>Console</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <FunctionLevelLinking>true</FunctionLevelLinking>\n      <IntrinsicFunctions>true</IntrinsicFunctions>\n      <SDLCheck>true</SDLCheck>\n      <PreprocessorDefinitions>NOMINMAX;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <ConformanceMode>true</ConformanceMode>\n      <LanguageStandard>stdcpp20</LanguageStandard>\n      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>\n      <EnableEnhancedInstructionSet>AdvancedVectorExtensions</EnableEnhancedInstructionSet>\n    </ClCompile>\n    <Link>\n      <SubSystem>Console</SubSystem>\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\n      <OptimizeReferences>true</OptimizeReferences>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemGroup>\n    <ClCompile Include=\"..\\WhisperDesktop\\useDiscreteGpu.c\" />\n    <ClCompile Include=\"main.cpp\" />\n    <ClCompile Include=\"miscUtils.cpp\" />\n    <ClCompile Include=\"params.cpp\" />\n    <ClCompile Include=\"textWriter.cpp\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ClInclude Include=\"miscUtils.h\" />\n    <ClInclude Include=\"params.h\" />\n    <ClInclude Include=\"textWriter.h\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ProjectReference Include=\"..\\..\\Whisper\\Whisper.vcxproj\">\n      <Project>{701df8c8-e4a5-43ec-9c6b-747bbf4d8e71}</Project>\n    </ProjectReference>\n  </ItemGroup>\n  <ItemGroup>\n    <Text Include=\"Readme.txt\" />\n  </ItemGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.targets\" />\n  <ImportGroup Label=\"ExtensionTargets\">\n  </ImportGroup>\n</Project>"
  },
  {
    "path": "Examples/main/main.vcxproj.filters",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <ItemGroup>\n    <ClCompile Include=\"main.cpp\" />\n    <ClCompile Include=\"params.cpp\" />\n    <ClCompile Include=\"miscUtils.cpp\" />\n    <ClCompile Include=\"..\\WhisperDesktop\\useDiscreteGpu.c\" />\n    <ClCompile Include=\"textWriter.cpp\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ClInclude Include=\"params.h\" />\n    <ClInclude Include=\"miscUtils.h\" />\n    <ClInclude Include=\"textWriter.h\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Text Include=\"Readme.txt\" />\n  </ItemGroup>\n</Project>"
  },
  {
    "path": "Examples/main/miscUtils.cpp",
    "content": "#include \"miscUtils.h\"\n#define WIN32_LEAN_AND_MEAN\n#include <windows.h>\n\nstd::string utf8( const std::wstring& utf16 )\n{\n\tint count = WideCharToMultiByte( CP_UTF8, 0, utf16.c_str(), (int)utf16.length(), nullptr, 0, nullptr, nullptr );\n\tstd::string str( count, 0 );\n\tWideCharToMultiByte( CP_UTF8, 0, utf16.c_str(), -1, &str[ 0 ], count, nullptr, nullptr );\n\treturn str;\n}\n\nstd::wstring utf16( const std::string& u8 )\n{\n\tint count = MultiByteToWideChar( CP_UTF8, 0, u8.c_str(), (int)u8.length(), nullptr, 0 );\n\tstd::wstring str( count, 0 );\n\tMultiByteToWideChar( CP_UTF8, 0, u8.c_str(), (int)u8.length(), &str[ 0 ], count );\n\treturn str;\n}\n\nnamespace\n{\n\twchar_t* formatMessage( HRESULT hr )\n\t{\n\t\twchar_t* err;\n\t\tif( FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,\n\t\t\tNULL,\n\t\t\thr,\n\t\t\tMAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ),\n\t\t\t(LPTSTR)&err,\n\t\t\t0,\n\t\t\tNULL ) )\n\t\t\treturn err;\n\t\treturn nullptr;\n\t}\n}\n\nvoid printError( const char* what, HRESULT hr )\n{\n\tconst wchar_t* err = formatMessage( hr );\n\tif( nullptr != err )\n\t{\n\t\tfwprintf( stderr, L\"%S: %s\\n\", what, err );\n\t\tLocalFree( (HLOCAL)err );\n\t}\n\telse\n\t\tfprintf( stderr, \"%s: error code %i (0x%08X)\\n\", what, hr, hr );\n}"
  },
  {
    "path": "Examples/main/miscUtils.h",
    "content": "#pragma once\n#include <string>\n\nstd::string utf8( const std::wstring& utf16 );\n\nstd::wstring utf16( const std::string& u8 );\n\nusing HRESULT = long;\nvoid printError( const char* what, HRESULT hr );"
  },
  {
    "path": "Examples/main/params.cpp",
    "content": "#include \"params.h\"\n#include <algorithm>\n#include <thread>\n#include \"miscUtils.h\"\n#include \"../../Whisper/API/iContext.cl.h\"\n\nwhisper_params::whisper_params()\n{\n#ifdef _DEBUG\n\tn_threads = 2;\n#else\n\tn_threads = std::min( 4u, std::thread::hardware_concurrency() );\n#endif\t\n}\n\nnamespace\n{\n\tconst char* cstr( bool b )\n\t{\n\t\treturn b ? \"true\" : \"false\";\n\t}\n}\n\nvoid whisper_print_usage( int argc, wchar_t** argv, const whisper_params& params )\n{\n\tfprintf( stderr, \"\\n\" );\n\tfprintf( stderr, \"usage: %S [options] file0.wav file1.wav ...\\n\", argv[ 0 ] );\n\tfprintf( stderr, \"\\n\" );\n\tfprintf( stderr, \"options:\\n\" );\n\tfprintf( stderr, \"  -h,       --help          [default] show this help message and exit\\n\" );\n\tfprintf( stderr, \"  -la,      --list-adapters List graphic adapters and exit\\n\" );\n\tfprintf( stderr, \"  -gpu,     --use-gpu       The graphic adapter to use for inference\\n\" );\n\tfprintf( stderr, \"  -t N,     --threads N     [%-7d] number of threads to use during computation\\n\", params.n_threads );\n\tfprintf( stderr, \"  -p N,     --processors N  [%-7d] number of processors to use during computation\\n\", params.n_processors );\n\tfprintf( stderr, \"  -ot N,    --offset-t N    [%-7d] time offset in milliseconds\\n\", params.offset_t_ms );\n\tfprintf( stderr, \"  -on N,    --offset-n N    [%-7d] segment index offset\\n\", params.offset_n );\n\tfprintf( stderr, \"  -d  N,    --duration N    [%-7d] duration of audio to process in milliseconds\\n\", params.duration_ms );\n\tfprintf( stderr, \"  -mc N,    --max-context N [%-7d] maximum number of text context tokens to store\\n\", params.max_context );\n\tfprintf( stderr, \"  -ml N,    --max-len N     [%-7d] maximum segment length in characters\\n\", params.max_len );\n\tfprintf( stderr, \"  -wt N,    --word-thold N  [%-7.2f] word timestamp probability threshold\\n\", params.word_thold );\n\tfprintf( stderr, \"  -su,      --speed-up      [%-7s] speed up audio by x2 (reduced accuracy)\\n\", cstr( params.speed_up ) );\n\tfprintf( stderr, \"  -tr,      --translate     [%-7s] translate from source language to english\\n\", cstr( params.translate ) );\n\tfprintf( stderr, \"  -di,      --diarize       [%-7s] stereo audio diarization\\n\", cstr( params.diarize ) );\n\tfprintf( stderr, \"  -otxt,    --output-txt    [%-7s] output result in a text file\\n\", cstr( params.output_txt ) );\n\tfprintf( stderr, \"  -ovtt,    --output-vtt    [%-7s] output result in a vtt file\\n\", cstr( params.output_vtt ) );\n\tfprintf( stderr, \"  -osrt,    --output-srt    [%-7s] output result in a srt file\\n\", cstr( params.output_srt ) );\n\tfprintf( stderr, \"  -owts,    --output-words  [%-7s] output script for generating karaoke video\\n\", cstr( params.output_wts ) );\n\tfprintf( stderr, \"  -ps,      --print-special [%-7s] print special tokens\\n\", cstr( params.print_special ) );\n\tfprintf( stderr, \"  -nc,      --no-colors     [%-7s] do not print colors\\n\", cstr( !params.print_colors ) );\n\tfprintf( stderr, \"  -nt,      --no-timestamps [%-7s] do not print timestamps\\n\", cstr( params.no_timestamps ) );\n\tfprintf( stderr, \"  -l LANG,  --language LANG [%-7s] spoken language\\n\", params.language.c_str() );\n\tfprintf( stderr, \"  -m FNAME, --model FNAME   [%-7S] model path\\n\", params.model.c_str() );\n\tfprintf( stderr, \"  -f FNAME, --file FNAME    [%-7s] path of the input audio file\\n\", \"\" );\n\tfprintf( stderr, \"  --prompt                            initial prompt for the model\\n\" );\n\tfprintf( stderr, \"\\n\" );\n}\n\nstatic void __stdcall pfnListAdapter( const wchar_t* name, void* )\n{\n\twprintf( L\"\\\"%s\\\"\\n\", name );\n}\n\nstatic void listGpus()\n{\n\tprintf( \"    Available graphic adapters:\\n\" );\n\tHRESULT hr = Whisper::listGPUs( &pfnListAdapter, nullptr );\n\tif( SUCCEEDED( hr ) )\n\t\treturn;\n\tprintError( \"Unable to enumerate GPUs\", hr );\n}\n\nbool whisper_params::parse( int argc, wchar_t* argv[] )\n{\n\tfor( int i = 1; i < argc; i++ )\n\t{\n\t\tstd::wstring arg = argv[ i ];\n\n\t\tif( arg[ 0 ] != '-' )\n\t\t{\n\t\t\tfname_inp.push_back( arg );\n\t\t\tcontinue;\n\t\t}\n\n\t\tif( arg == L\"-h\" || arg == L\"--help\" )\n\t\t{\n\t\t\twhisper_print_usage( argc, argv, *this );\n\t\t\treturn false;\n\t\t}\n\n\t\tif( arg == L\"-la\" || arg == L\"--list-adapters\" )\n\t\t{\n\t\t\tlistGpus();\n\t\t\treturn false;\n\t\t}\n\n\t\telse if( arg == L\"-t\" || arg == L\"--threads\" ) { n_threads = std::stoul( argv[ ++i ] ); }\n\t\telse if( arg == L\"-p\" || arg == L\"--processors\" ) { n_processors = std::stoul( argv[ ++i ] ); }\n\t\telse if( arg == L\"-ot\" || arg == L\"--offset-t\" ) { offset_t_ms = std::stoul( argv[ ++i ] ); }\n\t\telse if( arg == L\"-on\" || arg == L\"--offset-n\" ) { offset_n = std::stoul( argv[ ++i ] ); }\n\t\telse if( arg == L\"-d\" || arg == L\"--duration\" ) { duration_ms = std::stoul( argv[ ++i ] ); }\n\t\telse if( arg == L\"-mc\" || arg == L\"--max-context\" ) { max_context = std::stoul( argv[ ++i ] ); }\n\t\telse if( arg == L\"-ml\" || arg == L\"--max-len\" ) { max_len = std::stoul( argv[ ++i ] ); }\n\t\telse if( arg == L\"-wt\" || arg == L\"--word-thold\" ) { word_thold = std::stof( argv[ ++i ] ); }\n\t\telse if( arg == L\"-su\" || arg == L\"--speed-up\" ) { speed_up = true; }\n\t\telse if( arg == L\"-tr\" || arg == L\"--translate\" ) { translate = true; }\n\t\telse if( arg == L\"-di\" || arg == L\"--diarize\" ) { diarize = true; }\n\t\telse if( arg == L\"-otxt\" || arg == L\"--output-txt\" ) { output_txt = true; }\n\t\telse if( arg == L\"-ovtt\" || arg == L\"--output-vtt\" ) { output_vtt = true; }\n\t\telse if( arg == L\"-osrt\" || arg == L\"--output-srt\" ) { output_srt = true; }\n\t\telse if( arg == L\"-owts\" || arg == L\"--output-words\" ) { output_wts = true; }\n\t\telse if( arg == L\"-ps\" || arg == L\"--print-special\" ) { print_special = true; }\n\t\telse if( arg == L\"-nc\" || arg == L\"--no-colors\" ) { print_colors = false; }\n\t\telse if( arg == L\"-nt\" || arg == L\"--no-timestamps\" ) { no_timestamps = true; }\n\t\telse if( arg == L\"-l\" || arg == L\"--language\" ) { language = utf8( argv[ ++i ] ); }\n\t\telse if( arg == L\"-m\" || arg == L\"--model\" ) { model = argv[ ++i ]; }\n\t\telse if( arg == L\"-f\" || arg == L\"--file\" ) { fname_inp.push_back( argv[ ++i ] ); }\n\t\telse if( arg == L\"-gpu\" || arg == L\"--use-gpu\" ) { gpu = argv[ ++i ]; }\n\t\telse if( arg == L\"--prompt\" ) { prompt = utf8( argv[ ++i ] ); }\n\t\telse\n\t\t{\n\t\t\tfprintf( stderr, \"error: unknown argument: %S\\n\", arg.c_str() );\n\t\t\twhisper_print_usage( argc, argv, *this );\n\t\t\treturn false;\n\t\t}\n\t}\n\treturn true;\n}"
  },
  {
    "path": "Examples/main/params.h",
    "content": "#pragma once\n#include <vector>\n#include <string>\n\n// command-line parameters\nstruct whisper_params\n{\n\tuint32_t n_threads;\n\tuint32_t n_processors = 1;\n\tuint32_t offset_t_ms = 0;\n\tuint32_t offset_n = 0;\n\tuint32_t duration_ms = 0;\n\tuint32_t max_context = UINT_MAX;\n\tuint32_t max_len = 0;\n\n\tfloat word_thold = 0.01f;\n\n\tbool speed_up = false;\n\tbool translate = false;\n\tbool diarize = false;\n\tbool output_txt = false;\n\tbool output_vtt = false;\n\tbool output_srt = false;\n\tbool output_wts = false;\n\tbool print_special = false;\n\tbool print_colors = true;\n\tbool no_timestamps = false;\n\n\tstd::string language = \"en\";\n\tstd::wstring model = L\"models/ggml-base.en.bin\";\n\tstd::wstring gpu;\n\tstd::string prompt;\n\tstd::vector<std::wstring> fname_inp;\n\n\twhisper_params();\n\n\tbool parse( int argc, wchar_t* argv[] );\n};\n\nvoid whisper_print_usage( int argc, wchar_t** argv, const whisper_params& params );"
  },
  {
    "path": "Examples/main/textWriter.cpp",
    "content": "#include \"textWriter.h\"\n#include \"../../ComLightLib/comLightClient.h\"\n#include <array>\n#define WIN32_LEAN_AND_MEAN\n#include <pathcch.h>\n#include <atlstr.h>\n#include <atlfile.h>\n#pragma comment(lib, \"Pathcch.lib\")\n\nnamespace\n{\n\tHRESULT replaceExtension( CString& path, LPCTSTR inputPath, LPCTSTR ext )\n\t{\n\t\tpath = inputPath;\n\n\t\tconst size_t len = (size_t)path.GetLength() + 4;\n\t\twchar_t* buffer = path.GetBufferSetLength( (int)len );\n\t\tconst HRESULT hr = PathCchRenameExtension( buffer, len, ext );\n\t\tpath.ReleaseBuffer();\n\t\treturn hr;\n\t}\n\n\t// Abstract base class for text writers\n\tclass Writer\n\t{\n\tprotected:\n\t\tCAtlFile file;\n\t\tvirtual HRESULT impl( const Whisper::sSegment* const segments, const size_t length ) = 0;\n\n\tpublic:\n\t\tHRESULT write( Whisper::iContext* context, LPCTSTR audioPath, LPCTSTR ext )\n\t\t{\n\t\t\tCString path;\n\t\t\tCHECK( replaceExtension( path, audioPath, ext ) );\n\t\t\tCHECK( file.Create( path, GENERIC_WRITE, 0, CREATE_ALWAYS ) );\n\n\t\t\tusing namespace Whisper;\n\n\t\t\tconst eResultFlags resultFlags = eResultFlags::Timestamps | eResultFlags::Tokens;\n\t\t\tComLight::CComPtr<iTranscribeResult> result;\n\t\t\tCHECK( context->getResults( resultFlags, &result ) );\n\n\t\t\tsTranscribeLength len;\n\t\t\tCHECK( result->getSize( len ) );\n\t\t\tconst sSegment* const segments = result->getSegments();\n\n\t\t\treturn impl( segments, len.countSegments );\n\t\t}\n\t};\n\n\tHRESULT writeUtf8Bom( CAtlFile& file )\n\t{\n\t\tconst std::array<uint8_t, 3> bom = { 0xEF, 0xBB, 0xBF };\n\t\treturn file.Write( bom.data(), 3 );\n\t}\n\n\tvoid printTime( CStringA& rdi, Whisper::sTimeSpan time, bool comma = false )\n\t{\n\t\tWhisper::sTimeSpanFields fields = time;\n\t\tconst uint32_t hours = fields.days * 24 + fields.hours;\n\t\tconst char separator = comma ? ',' : '.';\n\t\trdi.AppendFormat( \"%02d:%02d:%02d%c%03d\",\n\t\t\t(int)hours,\n\t\t\t(int)fields.minutes,\n\t\t\t(int)fields.seconds,\n\t\t\tseparator,\n\t\t\tfields.ticks / 10'000 );\n\t}\n\n\tconst char* skipBlank( const char* rsi )\n\t{\n\t\twhile( true )\n\t\t{\n\t\t\tconst char c = *rsi;\n\t\t\tif( c == ' ' || c == '\\t' )\n\t\t\t{\n\t\t\t\trsi++;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\treturn rsi;\n\t\t}\n\t}\n\n\tinline const char* cstr( const CStringA& s ) { return s; }\n\n\tHRESULT writeString( CAtlFile& file, const CStringA& line )\n\t{\n\t\tif( line.GetLength() > 0 )\n\t\t\tCHECK( file.Write( cstr( line ), (DWORD)line.GetLength() ) );\n\t\treturn S_OK;\n\t}\n\n\t// Writer for UTF-8 text files\n\tclass TextWriter : public Writer\n\t{\n\t\tconst bool timestamps;\n\n\t\tHRESULT impl( const Whisper::sSegment* const segments, const size_t length ) override final\n\t\t{\n\t\t\tCHECK( writeUtf8Bom( file ) );\n\t\t\tusing namespace Whisper;\n\n\t\t\tCStringA line;\n\t\t\tfor( size_t i = 0; i < length; i++ )\n\t\t\t{\n\t\t\t\tconst sSegment& seg = segments[ i ];\n\n\t\t\t\tif( timestamps )\n\t\t\t\t{\n\t\t\t\t\tline = \"[\";\n\t\t\t\t\tprintTime( line, seg.time.begin );\n\t\t\t\t\tline += \" --> \";\n\t\t\t\t\tprintTime( line, seg.time.end );\n\t\t\t\t\tline += \"]  \";\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tline = \"\";\n\n\t\t\t\tline += skipBlank( seg.text );\n\t\t\t\tline += \"\\r\\n\";\n\t\t\t\tCHECK( writeString( file, line ) );\n\t\t\t}\n\t\t\treturn S_OK;\n\t\t}\n\tpublic:\n\t\tTextWriter( bool tt ) : timestamps( tt ) { }\n\t};\n\n\t// Writer for SubRip format: https://en.wikipedia.org/wiki/SubRip#SubRip_file_format\n\tclass SubRipWriter : public Writer\n\t{\n\t\tHRESULT impl( const Whisper::sSegment* const segments, const size_t length ) override final\n\t\t{\n\t\t\tCHECK( writeUtf8Bom( file ) );\n\t\t\tusing namespace Whisper;\n\n\t\t\tCStringA line;\n\t\t\tfor( size_t i = 0; i < length; i++ )\n\t\t\t{\n\t\t\t\tconst sSegment& seg = segments[ i ];\n\n\t\t\t\tline.Format( \"%zu\\r\\n\", i + 1 );\n\t\t\t\tprintTime( line, seg.time.begin, true );\n\t\t\t\tline += \" --> \";\n\t\t\t\tprintTime( line, seg.time.end, true );\n\t\t\t\tline += \"\\r\\n\";\n\t\t\t\tline += skipBlank( seg.text );\n\t\t\t\tline += \"\\r\\n\\r\\n\";\n\t\t\t\tCHECK( writeString( file, line ) );\n\t\t\t}\n\t\t\treturn S_OK;\n\t\t}\n\t};\n\n\t// Writer for WebVTT format: https://en.wikipedia.org/wiki/WebVTT\n\tclass VttWriter : public Writer\n\t{\n\t\tHRESULT impl( const Whisper::sSegment* const segments, const size_t length ) override final\n\t\t{\n\t\t\tCHECK( writeUtf8Bom( file ) );\n\t\t\tusing namespace Whisper;\n\n\t\t\tCStringA line;\n\t\t\tline = \"WEBVTT\\r\\n\\r\\n\";\n\t\t\tCHECK( writeString( file, line ) );\n\n\t\t\tfor( size_t i = 0; i < length; i++ )\n\t\t\t{\n\t\t\t\tconst sSegment& seg = segments[ i ];\n\t\t\t\tline = \"\";\n\n\t\t\t\tprintTime( line, seg.time.begin );\n\t\t\t\tline += \" --> \";\n\t\t\t\tprintTime( line, seg.time.end );\n\t\t\t\tline += \"\\r\\n\";\n\t\t\t\tline += skipBlank( seg.text );\n\t\t\t\tline += \"\\r\\n\\r\\n\";\n\t\t\t\tCHECK( writeString( file, line ) );\n\t\t\t}\n\t\t\treturn S_OK;\n\t\t}\n\t};\n}\n\nHRESULT writeText( Whisper::iContext* context, LPCTSTR audioPath, bool timestamps )\n{\n\tTextWriter writer{ timestamps };\n\treturn writer.write( context, audioPath, L\".txt\" );\n}\n\nHRESULT writeSubRip( Whisper::iContext* context, LPCTSTR audioPath )\n{\n\tSubRipWriter writer;\n\treturn writer.write( context, audioPath, L\".srt\" );\n}\n\nHRESULT writeWebVTT( Whisper::iContext* context, LPCTSTR audioPath )\n{\n\tVttWriter writer;\n\treturn writer.write( context, audioPath, L\".vtt\" );\n}"
  },
  {
    "path": "Examples/main/textWriter.h",
    "content": "#pragma once\n#include \"../../Whisper/API/iContext.cl.h\"\n\n// These functions print output segments into text files of various formats\nHRESULT writeText( Whisper::iContext* context, LPCTSTR audioPath, bool timestamps );\nHRESULT writeSubRip( Whisper::iContext* context, LPCTSTR audioPath );\nHRESULT writeWebVTT( Whisper::iContext* context, LPCTSTR audioPath );"
  },
  {
    "path": "LICENSE",
    "content": "Mozilla Public License Version 2.0\n==================================\n\n1. Definitions\n--------------\n\n1.1. \"Contributor\"\n    means each individual or legal entity that creates, contributes to\n    the creation of, or owns Covered Software.\n\n1.2. \"Contributor Version\"\n    means the combination of the Contributions of others (if any) used\n    by a Contributor and that particular Contributor's Contribution.\n\n1.3. \"Contribution\"\n    means Covered Software of a particular Contributor.\n\n1.4. \"Covered Software\"\n    means Source Code Form to which the initial Contributor has attached\n    the notice in Exhibit A, the Executable Form of such Source Code\n    Form, and Modifications of such Source Code Form, in each case\n    including portions thereof.\n\n1.5. \"Incompatible With Secondary Licenses\"\n    means\n\n    (a) that the initial Contributor has attached the notice described\n        in Exhibit B to the Covered Software; or\n\n    (b) that the Covered Software was made available under the terms of\n        version 1.1 or earlier of the License, but not also under the\n        terms of a Secondary License.\n\n1.6. \"Executable Form\"\n    means any form of the work other than Source Code Form.\n\n1.7. \"Larger Work\"\n    means a work that combines Covered Software with other material, in\n    a separate file or files, that is not Covered Software.\n\n1.8. \"License\"\n    means this document.\n\n1.9. \"Licensable\"\n    means having the right to grant, to the maximum extent possible,\n    whether at the time of the initial grant or subsequently, any and\n    all of the rights conveyed by this License.\n\n1.10. \"Modifications\"\n    means any of the following:\n\n    (a) any file in Source Code Form that results from an addition to,\n        deletion from, or modification of the contents of Covered\n        Software; or\n\n    (b) any new file in Source Code Form that contains any Covered\n        Software.\n\n1.11. \"Patent Claims\" of a Contributor\n    means any patent claim(s), including without limitation, method,\n    process, and apparatus claims, in any patent Licensable by such\n    Contributor that would be infringed, but for the grant of the\n    License, by the making, using, selling, offering for sale, having\n    made, import, or transfer of either its Contributions or its\n    Contributor Version.\n\n1.12. \"Secondary License\"\n    means either the GNU General Public License, Version 2.0, the GNU\n    Lesser General Public License, Version 2.1, the GNU Affero General\n    Public License, Version 3.0, or any later versions of those\n    licenses.\n\n1.13. \"Source Code Form\"\n    means the form of the work preferred for making modifications.\n\n1.14. \"You\" (or \"Your\")\n    means an individual or a legal entity exercising rights under this\n    License. For legal entities, \"You\" includes any entity that\n    controls, is controlled by, or is under common control with You. For\n    purposes of this definition, \"control\" means (a) the power, direct\n    or indirect, to cause the direction or management of such entity,\n    whether by contract or otherwise, or (b) ownership of more than\n    fifty percent (50%) of the outstanding shares or beneficial\n    ownership of such entity.\n\n2. License Grants and Conditions\n--------------------------------\n\n2.1. Grants\n\nEach Contributor hereby grants You a world-wide, royalty-free,\nnon-exclusive license:\n\n(a) under intellectual property rights (other than patent or trademark)\n    Licensable by such Contributor to use, reproduce, make available,\n    modify, display, perform, distribute, and otherwise exploit its\n    Contributions, either on an unmodified basis, with Modifications, or\n    as part of a Larger Work; and\n\n(b) under Patent Claims of such Contributor to make, use, sell, offer\n    for sale, have made, import, and otherwise transfer either its\n    Contributions or its Contributor Version.\n\n2.2. Effective Date\n\nThe licenses granted in Section 2.1 with respect to any Contribution\nbecome effective for each Contribution on the date the Contributor first\ndistributes such Contribution.\n\n2.3. Limitations on Grant Scope\n\nThe licenses granted in this Section 2 are the only rights granted under\nthis License. No additional rights or licenses will be implied from the\ndistribution or licensing of Covered Software under this License.\nNotwithstanding Section 2.1(b) above, no patent license is granted by a\nContributor:\n\n(a) for any code that a Contributor has removed from Covered Software;\n    or\n\n(b) for infringements caused by: (i) Your and any other third party's\n    modifications of Covered Software, or (ii) the combination of its\n    Contributions with other software (except as part of its Contributor\n    Version); or\n\n(c) under Patent Claims infringed by Covered Software in the absence of\n    its Contributions.\n\nThis License does not grant any rights in the trademarks, service marks,\nor logos of any Contributor (except as may be necessary to comply with\nthe notice requirements in Section 3.4).\n\n2.4. Subsequent Licenses\n\nNo Contributor makes additional grants as a result of Your choice to\ndistribute the Covered Software under a subsequent version of this\nLicense (see Section 10.2) or under the terms of a Secondary License (if\npermitted under the terms of Section 3.3).\n\n2.5. Representation\n\nEach Contributor represents that the Contributor believes its\nContributions are its original creation(s) or it has sufficient rights\nto grant the rights to its Contributions conveyed by this License.\n\n2.6. Fair Use\n\nThis License is not intended to limit any rights You have under\napplicable copyright doctrines of fair use, fair dealing, or other\nequivalents.\n\n2.7. Conditions\n\nSections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted\nin Section 2.1.\n\n3. Responsibilities\n-------------------\n\n3.1. Distribution of Source Form\n\nAll distribution of Covered Software in Source Code Form, including any\nModifications that You create or to which You contribute, must be under\nthe terms of this License. You must inform recipients that the Source\nCode Form of the Covered Software is governed by the terms of this\nLicense, and how they can obtain a copy of this License. You may not\nattempt to alter or restrict the recipients' rights in the Source Code\nForm.\n\n3.2. Distribution of Executable Form\n\nIf You distribute Covered Software in Executable Form then:\n\n(a) such Covered Software must also be made available in Source Code\n    Form, as described in Section 3.1, and You must inform recipients of\n    the Executable Form how they can obtain a copy of such Source Code\n    Form by reasonable means in a timely manner, at a charge no more\n    than the cost of distribution to the recipient; and\n\n(b) You may distribute such Executable Form under the terms of this\n    License, or sublicense it under different terms, provided that the\n    license for the Executable Form does not attempt to limit or alter\n    the recipients' rights in the Source Code Form under this License.\n\n3.3. Distribution of a Larger Work\n\nYou may create and distribute a Larger Work under terms of Your choice,\nprovided that You also comply with the requirements of this License for\nthe Covered Software. If the Larger Work is a combination of Covered\nSoftware with a work governed by one or more Secondary Licenses, and the\nCovered Software is not Incompatible With Secondary Licenses, this\nLicense permits You to additionally distribute such Covered Software\nunder the terms of such Secondary License(s), so that the recipient of\nthe Larger Work may, at their option, further distribute the Covered\nSoftware under the terms of either this License or such Secondary\nLicense(s).\n\n3.4. Notices\n\nYou may not remove or alter the substance of any license notices\n(including copyright notices, patent notices, disclaimers of warranty,\nor limitations of liability) contained within the Source Code Form of\nthe Covered Software, except that You may alter any license notices to\nthe extent required to remedy known factual inaccuracies.\n\n3.5. Application of Additional Terms\n\nYou may choose to offer, and to charge a fee for, warranty, support,\nindemnity or liability obligations to one or more recipients of Covered\nSoftware. However, You may do so only on Your own behalf, and not on\nbehalf of any Contributor. You must make it absolutely clear that any\nsuch warranty, support, indemnity, or liability obligation is offered by\nYou alone, and You hereby agree to indemnify every Contributor for any\nliability incurred by such Contributor as a result of warranty, support,\nindemnity or liability terms You offer. You may include additional\ndisclaimers of warranty and limitations of liability specific to any\njurisdiction.\n\n4. Inability to Comply Due to Statute or Regulation\n---------------------------------------------------\n\nIf it is impossible for You to comply with any of the terms of this\nLicense with respect to some or all of the Covered Software due to\nstatute, judicial order, or regulation then You must: (a) comply with\nthe terms of this License to the maximum extent possible; and (b)\ndescribe the limitations and the code they affect. Such description must\nbe placed in a text file included with all distributions of the Covered\nSoftware under this License. Except to the extent prohibited by statute\nor regulation, such description must be sufficiently detailed for a\nrecipient of ordinary skill to be able to understand it.\n\n5. Termination\n--------------\n\n5.1. The rights granted under this License will terminate automatically\nif You fail to comply with any of its terms. However, if You become\ncompliant, then the rights granted under this License from a particular\nContributor are reinstated (a) provisionally, unless and until such\nContributor explicitly and finally terminates Your grants, and (b) on an\nongoing basis, if such Contributor fails to notify You of the\nnon-compliance by some reasonable means prior to 60 days after You have\ncome back into compliance. Moreover, Your grants from a particular\nContributor are reinstated on an ongoing basis if such Contributor\nnotifies You of the non-compliance by some reasonable means, this is the\nfirst time You have received notice of non-compliance with this License\nfrom such Contributor, and You become compliant prior to 30 days after\nYour receipt of the notice.\n\n5.2. If You initiate litigation against any entity by asserting a patent\ninfringement claim (excluding declaratory judgment actions,\ncounter-claims, and cross-claims) alleging that a Contributor Version\ndirectly or indirectly infringes any patent, then the rights granted to\nYou by any and all Contributors for the Covered Software under Section\n2.1 of this License shall terminate.\n\n5.3. In the event of termination under Sections 5.1 or 5.2 above, all\nend user license agreements (excluding distributors and resellers) which\nhave been validly granted by You or Your distributors under this License\nprior to termination shall survive termination.\n\n************************************************************************\n*                                                                      *\n*  6. Disclaimer of Warranty                                           *\n*  -------------------------                                           *\n*                                                                      *\n*  Covered Software is provided under this License on an \"as is\"       *\n*  basis, without warranty of any kind, either expressed, implied, or  *\n*  statutory, including, without limitation, warranties that the       *\n*  Covered Software is free of defects, merchantable, fit for a        *\n*  particular purpose or non-infringing. The entire risk as to the     *\n*  quality and performance of the Covered Software is with You.        *\n*  Should any Covered Software prove defective in any respect, You     *\n*  (not any Contributor) assume the cost of any necessary servicing,   *\n*  repair, or correction. This disclaimer of warranty constitutes an   *\n*  essential part of this License. No use of any Covered Software is   *\n*  authorized under this License except under this disclaimer.         *\n*                                                                      *\n************************************************************************\n\n************************************************************************\n*                                                                      *\n*  7. Limitation of Liability                                          *\n*  --------------------------                                          *\n*                                                                      *\n*  Under no circumstances and under no legal theory, whether tort      *\n*  (including negligence), contract, or otherwise, shall any           *\n*  Contributor, or anyone who distributes Covered Software as          *\n*  permitted above, be liable to You for any direct, indirect,         *\n*  special, incidental, or consequential damages of any character      *\n*  including, without limitation, damages for lost profits, loss of    *\n*  goodwill, work stoppage, computer failure or malfunction, or any    *\n*  and all other commercial damages or losses, even if such party      *\n*  shall have been informed of the possibility of such damages. This   *\n*  limitation of liability shall not apply to liability for death or   *\n*  personal injury resulting from such party's negligence to the       *\n*  extent applicable law prohibits such limitation. Some               *\n*  jurisdictions do not allow the exclusion or limitation of           *\n*  incidental or consequential damages, so this exclusion and          *\n*  limitation may not apply to You.                                    *\n*                                                                      *\n************************************************************************\n\n8. Litigation\n-------------\n\nAny litigation relating to this License may be brought only in the\ncourts of a jurisdiction where the defendant maintains its principal\nplace of business and such litigation shall be governed by laws of that\njurisdiction, without reference to its conflict-of-law provisions.\nNothing in this Section shall prevent a party's ability to bring\ncross-claims or counter-claims.\n\n9. Miscellaneous\n----------------\n\nThis License represents the complete agreement concerning the subject\nmatter hereof. If any provision of this License is held to be\nunenforceable, such provision shall be reformed only to the extent\nnecessary to make it enforceable. Any law or regulation which provides\nthat the language of a contract shall be construed against the drafter\nshall not be used to construe this License against a Contributor.\n\n10. Versions of the License\n---------------------------\n\n10.1. New Versions\n\nMozilla Foundation is the license steward. Except as provided in Section\n10.3, no one other than the license steward has the right to modify or\npublish new versions of this License. Each version will be given a\ndistinguishing version number.\n\n10.2. Effect of New Versions\n\nYou may distribute the Covered Software under the terms of the version\nof the License under which You originally received the Covered Software,\nor under the terms of any subsequent version published by the license\nsteward.\n\n10.3. Modified Versions\n\nIf you create software not governed by this License, and you want to\ncreate a new license for such software, you may create and use a\nmodified version of this License if you rename the license and remove\nany references to the name of the license steward (except to note that\nsuch modified license differs from this License).\n\n10.4. Distributing Source Code Form that is Incompatible With Secondary\nLicenses\n\nIf You choose to distribute Source Code Form that is Incompatible With\nSecondary Licenses under the terms of this version of the License, the\nnotice described in Exhibit B of this License must be attached.\n\nExhibit A - Source Code Form License Notice\n-------------------------------------------\n\n  This Source Code Form is subject to the terms of the Mozilla Public\n  License, v. 2.0. If a copy of the MPL was not distributed with this\n  file, You can obtain one at http://mozilla.org/MPL/2.0/.\n\nIf it is not possible or desirable to put the notice in a particular\nfile, then You may include the notice in a location (such as a LICENSE\nfile in a relevant directory) where a recipient would be likely to look\nfor such a notice.\n\nYou may add additional accurate notices of copyright ownership.\n\nExhibit B - \"Incompatible With Secondary Licenses\" Notice\n---------------------------------------------------------\n\n  This Source Code Form is \"Incompatible With Secondary Licenses\", as\n  defined by the Mozilla Public License, v. 2.0.\n"
  },
  {
    "path": "Readme.md",
    "content": "﻿This project is a Windows port of the [whisper.cpp](https://github.com/ggerganov/whisper.cpp) implementation.<br/>\nWhich in turn is a C++ port of [OpenAI's Whisper](https://github.com/openai/whisper) automatic speech recognition (ASR) model.\n\n# Quick Start Guide\n\nDownload WhisperDesktop.zip from the “Releases” section of this repository, unpack the ZIP, and run WhisperDesktop.exe.\n\nOn the first screen it will ask you to download a model.<br/>\nI recommend `ggml-medium.bin` (1.42GB in size), because I’ve mostly tested the software with that model.<br/>\n![Load Model Screen](gui-load-model.png)\n\nThe next screen allows to transcribe an audio file.<br/>\n![Transcribe Screen](gui-transcribe.png)\n\nThere’s another screen which allows to capture and transcribe or translate live audio from a microphone.<br/>\n![Capture Screen](gui-capture.png)\n\n# Features\n\n* Vendor-agnostic GPGPU based on DirectCompute; another name for that technology is “compute shaders in Direct3D 11”\n\n* Plain C++ implementation, no runtime dependencies except essential OS components\n\n* Much faster than OpenAI’s implementation.<br/>\nOn my desktop computer with GeForce [1080Ti](https://en.wikipedia.org/wiki/GeForce_10_series#GeForce_10_(10xx)_series_for_desktops) GPU,\nmedium model, [3:24 min speech](https://upload.wikimedia.org/wikipedia/commons/1/1f/George_W_Bush_Columbia_FINAL.ogg)\ntook 45 seconds to transcribe with PyTorch and CUDA, but only 19 seconds with my implementation and DirectCompute.<br/>\nFunfact: that’s 9.63 gigabytes runtime dependencies, versus 431 kilobytes `Whisper.dll`\n\n* Mixed F16 / F32 precision: Windows \n[requires support](https://learn.microsoft.com/en-us/windows/win32/direct3ddxgi/format-support-for-direct3d-feature-level-10-0-hardware#dxgi_format_r16_floatfcs-54)\nof `R16_FLOAT` buffers since D3D version 10.0\n\n* Built-in performance profiler which measures execution time of individual compute shaders\n\n* Low memory usage\n\n* Media Foundation for audio handling, supports most audio and video formats (with the notable exception of Ogg Vorbis),\nand most audio capture devices which work on Windows (except some professional ones, which only implementing [ASIO](https://en.wikipedia.org/wiki/Audio_Stream_Input/Output) API).\n\n* Voice activity detection for audio capture.<br/>\nThe implementation is based on the [2009 article](https://www.researchgate.net/publication/255667085_A_simple_but_efficient_real-time_voice_activity_detection_algorithm)\n“A simple but efficient real-time voice activity detection algorithm” by Mohammad Moattar and Mahdi Homayoonpoor.\n\n* Easy to use COM-style API. Idiomatic C# wrapper [available on nuget](https://www.nuget.org/packages/WhisperNet/).<br/>\nVersion 1.10 [introduced](https://github.com/Const-me/Whisper/tree/master/WhisperPS)\nscripting support for PowerShell 5.1, that’s the older “Windows PowerShell” version which comes pre-installed on Windows.\n\n* Pre-built binaries available\n\nThe only supported platform is 64-bit Windows.<br/>\nShould work on Windows 8.1 or newer, but I have only tested on Windows 10.<br/>\nThe library requires a Direct3D 11.0 capable GPU, which in 2023 simply means “any hardware GPU”.\nThe most recent GPU without D3D 11.0 support was Intel [Sandy Bridge](https://en.wikipedia.org/wiki/Sandy_Bridge) from 2011.\n\nOn the CPU side, the library requires [AVX1](https://en.wikipedia.org/wiki/Advanced_Vector_Extensions) and [F16C](https://en.wikipedia.org/wiki/F16C) support.\n\n# Developer Guide\n\n## Build Instructions\n\n1. Clone this repository\n\n2. Open `WhisperCpp.sln` in Visual Studio 2022. I’m using the freeware community edition, version 17.4.4.\n\n3. Switch to `Release` configuration\n\n4. Build and run `CompressShaders` C# project, in the `Tools` subfolder of the solution.\nTo run that project, right click in visual studio, “Set as startup project”, then in the main menu of VS “Debug / Start Without Debugging”.\nWhen completed successfully, you should see a console window with a line like that:<br/>\n`Compressed 46 compute shaders, 123.5 kb -> 18.0 kb`\n\n5. Build `Whisper` project to get the native DLL, or `WhisperNet` for the C# wrapper and nuget package, or the examples.\n\n## Other Notes\n\nIf you gonna consume the library in a software built with Visual C++ 2022 or newer, you probably redistribute Visual C++ runtime DLLs in the form of the `.msm` merge module,\nor [vc_redist.x64.exe](https://learn.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist?view=msvc-170) binary.<br/>\nIf you do that, right click on the `Whisper` project, Properties, C/C++, Code Generation,\nswitch “Runtime Library” setting from `Multi-threaded (/MT)` to `Multi-threaded DLL (/MD)`,\nand rebuild: the binary will become smaller.\n\nThe library includes [RenderDoc](https://renderdoc.org/) GPU debugger integration.<br/>\nWhen launched your program from RenderDoc, hold F12 key to capture the compute calls.<br/>\nIf you gonna debug HLSL shaders, use the debug build of the DLL, it includes debug build of the shaders and you’ll get better UX in the debugger.\n\nThe repository includes a lot of code which was only used for development:\ncouple alternative model implementations, compatible FP64 versions of some compute shaders, debug tracing and the tool to compare the traces, etc.<br/>\nThat stuff is disabled by preprocessor macros or `constexpr` flags, I hope it’s fine to keep here.\n\n## Performance Notes\n\nI have a limited selection of GPUs in this house.<br/>\nSpecifically, I have optimized for nVidia 1080Ti, Radeon Vega 8 inside Ryzen 7 5700G, and Radeon Vega 7 inside Ryzen 5 5600U.<br/>\n[Here’s the summary](https://github.com/Const-me/Whisper/blob/master/SampleClips/summary.tsv).\n\nThe nVidia delivers relative speed 5.8 for the large model, 10.6 for the medium model.<br/>\nThe AMD Ryzen 5 5600U APU delivers relative speed about 2.2 for the medium model. Not great, but still, much faster than realtime.\n\nI have also tested on [nVidia 1650](https://en.wikipedia.org/wiki/GeForce_16_series#Desktop): slower than 1080Ti but pretty good, much faster than realtime.<br/>\nI have also tested on Intel HD Graphics 4000 inside Core i7-3612QM, the relative speed was 0.14 for medium model, 0.44 for small model.\nThat’s much slower than realtime, but I was happy to find my software works even on the integrated mobile GPU [launched](https://ark.intel.com/products/64901) in 2012.\n\nI’m not sure the performance is ideal on discrete AMD GPUs, or integrated Intel GPUs, have not specifically optimized for them.<br/>\nIdeally, they might need slightly different builds of a couple of the most expensive compute shaders, `mulMatTiled.hlsl` and `mulMatByRowTiled.hlsl`<br/>\nAnd maybe other adjustments, like the `useReshapedMatMul()` value in `Whisper/D3D/device.h` header file.\n\nI don’t know how to measure that, but I have a feeling the bottleneck is memory, not compute.<br/>\nSomeone on Hacker News [has tested](https://news.ycombinator.com/item?id=34408429) on [3060Ti](https://en.wikipedia.org/wiki/GeForce_30_series#Desktop),\nthe version with GDDR6 memory.\nCompared to 1080Ti, that GPU has 1.3x FP32 FLOPS, but 0.92x VRAM bandwidth.\nThe app was about 10% slower on the 3060Ti.\n\n## Further Optimisations\n\nI have only spent a few days optimizing performance of these shaders.<br/>\nIt might be possible to do much better, here’s a few ideas.\n\n* Newer GPUs like Radeon Vega or nVidia 1650 have higher FP16 performance compared to FP32, yet my compute shaders are only using FP32 data type.<br/>\n[Half The Precision, Twice The Fun](https://therealmjp.github.io/posts/shader-fp16/)\n\n* In the current version, FP16 tensors are using shader resource views to upcast loaded values, and unordered access views to downcast stored ones.<br/>\nMight be a good idea to switch to [byte address buffers](https://learn.microsoft.com/en-us/windows/win32/direct3d11/direct3d-11-advanced-stages-cs-resources#byte-address-buffer),\nload/store complete 4-bytes values, and upcast / downcast in HLSL with `f16tof32` / `f32tof16` intrinsics.\n\n* In the current version all shaders are compiled offline, and `Whisper.dll` includes DXBC byte codes.<br/>\nThe HLSL compiler `D3DCompiler_47.dll` is an OS component, and is pretty fast.\nFor the expensive compute shaders, it’s probably a good idea to ship HLSL instead of DXBC,\nand [compile](https://learn.microsoft.com/en-us/windows/win32/api/d3dcompiler/nf-d3dcompiler-d3dcompile) on startup\nwith environment-specific [values](https://learn.microsoft.com/en-us/windows/win32/api/d3dcommon/ns-d3dcommon-d3d_shader_macro) for the macros.\n\n* It might be a good idea to upgrade the whole thing from D3D11 to D3D12.<br/>\nThe newer API is harder to use, but includes potentially useful features not exposed to D3D11:\n[wave intrinsics](https://github.com/Microsoft/DirectXShaderCompiler/wiki/Wave-Intrinsics),\nand [explicit FP16](https://github.com/microsoft/DirectXShaderCompiler/wiki/16-Bit-Scalar-Types).\n\n## Missing Features\n\nAutomatic language detection is not implemented.\n\nIn the current version there’s high latency for realtime audio capture.<br/>\nSpecifically, depending on voice detection the figure is about 5-10 seconds.<br/>\nAt least in my tests, the model wasn’t happy when I supplied too short pieces of the audio.<br/>\nI have increased the latency and called it a day, but ideally this needs a better fix for optimal UX.\n\n# Final Words\n\nFrom my perspective, this is an unpaid hobby project, which I completed over the 2022-23 winter holydays.<br/>\nThe code probably has bugs.<br/>\nThe software is provided “as is”, without warranty of any kind.\n\nThanks to [Georgi Gerganov](https://github.com/ggerganov) for [whisper.cpp](https://github.com/ggerganov/whisper.cpp) implementation,\nand the models in GGML binary format.<br/>\nI don’t program Python, and I don’t know anything about the ML ecosystem.<br/>\nI wouldn’t even start this project without a good C++ reference implementation, to test my version against.\n\nThat whisper.cpp project has an example which [uses](https://github.com/ggerganov/whisper.cpp/blob/master/examples/talk/gpt-2.cpp)\nthe same GGML implementation to run another OpenAI’s model, [GPT-2](https://en.wikipedia.org/wiki/GPT-2).<br/>\nIt shouldn’t be hard to support that ML model with the compute shaders and relevant infrastructure already implemented in this project.\n\nIf you find this useful, I’ll be very grateful if you consider a donation to [“Come Back Alive” foundation](https://savelife.in.ua/en/)."
  },
  {
    "path": "SampleClips/Readme.txt",
    "content": "﻿This folder contains 2 sample speech audio clips, `jfk.wav` and `columbia.wma`\n\njfk.wav is from whisper.cpp repository.\n\ncolumbia.wma is from Wikipedia: https://upload.wikimedia.org/wikipedia/commons/1/1f/George_W_Bush_Columbia_FINAL.ogg\nI re-encoded the audio from Ogg Vorbis into Windows Media Audio, because Media Foundation is unable to decode Vorbis.\n\nThe rest of the text files in this folder are the outputs of the in-app performance profiler, when the app was transcribing these two audio clips on three different computers.\n\nThe “1080ti” files are from my desktop, which has nVidia GeForce 1080Ti GPU.\n\nThe “vega8” files are from the same desktop, when using the GPU integrated into AMD Ryzen 7 5700G processor.\n\nThe “vega7” files are from my laptop, the GPU is integrated into AMD Ryzen 5 5600U processor.\nThe laptop model is HP ProBook 445 G8. While running the tests, the laptop was on battery power.\n\nThe “1650” files are from another desktop with nVidia GeForce 1650.\n\nThe file names with “medium” in the middle were made with “ggml-medium.bin” Whisper model, with “large” were made with “ggml-large.bin” model.\n\nIn theory, 1080ti delivers 10.6 TFlops FP32 and 484.4 GB/second VRAM bandwidth.\n\nThe AMD APU in that desktop delivers 2.0 TFlops FP32, and 53.3 GB/second memory bandwidth.\n\nThat variant of 1650 delivers 2.6 TFlops FP32, and 128.1 GB/second VRAM bandwidth.\n\nThe AMD APU in the laptop delivers 1.6 TFlops FP32, and 51.2 GB/second memory bandwidth.\n\nThe loading times in these logs make little sense because they depend heavily on the disk cache implemented in the OS kernel.\nIdeally, I needed to reboot Windows before every test, but I didn’t do that."
  },
  {
    "path": "SampleClips/columbia-large-1080ti.txt",
    "content": "﻿    CPU Tasks\nLoadModel       950.578 milliseconds\nRunComplete     27.5329 seconds\nRun     27.434 seconds\nCallbacks       10.6484 milliseconds, 44 calls, 242.009 microseconds average\nSpectrogram     199.106 milliseconds, 41 calls, 4.85624 milliseconds average\nSample  58.7404 milliseconds, 527 calls, 111.462 microseconds average\nEncode  11.3813 seconds, 9 calls, 1.26459 seconds average\nDecode  16.0418 seconds, 9 calls, 1.78242 seconds average\nDecodeStep      15.9829 seconds, 527 calls, 30.3281 milliseconds average\n    GPU Tasks\nLoadModel       805.211 milliseconds\nRun     27.338 seconds\nEncode  11.3967 seconds, 9 calls, 1.2663 seconds average\nEncodeLayer     9.78685 seconds, 288 calls, 33.9821 milliseconds average\nDecode  15.9412 seconds, 9 calls, 1.77125 seconds average\nDecodeStep      15.9412 seconds, 527 calls, 30.249 milliseconds average\nDecodeLayer     15.0511 seconds, 16864 calls, 892.499 microseconds average\n    Compute Shaders\nmulMatTiled     12.0503 seconds, 6345 calls, 1.89919 milliseconds average\nmulMatByRowTiled        9.45404 seconds, 199430 calls, 47.4053 microseconds average\nnorm    1.32432 seconds, 51704 calls, 25.6135 microseconds average\nfmaRepeat1      583.884 milliseconds, 51704 calls, 11.2928 microseconds average\naddRepeatEx     536.551 milliseconds, 51168 calls, 10.4861 microseconds average\nsoftMaxFixed    534.105 milliseconds, 17152 calls, 31.1395 microseconds average\ncopyConvert     500.4 milliseconds, 34880 calls, 14.3463 microseconds average\ncopyTranspose   377.38 milliseconds, 34304 calls, 11.001 microseconds average\naddRepeatScale  315.294 milliseconds, 33728 calls, 9.34814 microseconds average\naddRepeatGelu   283.978 milliseconds, 17170 calls, 16.5392 microseconds average\nsoftMaxLong     245.57 milliseconds, 527 calls, 465.976 microseconds average\nscaleInPlace    226.545 milliseconds, 17152 calls, 13.2081 microseconds average\nsoftMax 212.206 milliseconds, 16864 calls, 12.5834 microseconds average\naddRepeat       209.397 milliseconds, 17728 calls, 11.8117 microseconds average\nconvolutionMain2Fixed   184.615 milliseconds, 9 calls, 20.5128 milliseconds average\ndiagMaskInf     107.423 milliseconds, 16864 calls, 6.36998 microseconds average\nconvolutionMain 74.7954 milliseconds, 9 calls, 8.3106 milliseconds average\nconvolutionPrep1        20.9316 milliseconds, 18 calls, 1.16287 milliseconds average\nconvolutionPrep2        3.8103 milliseconds, 18 calls, 211.683 microseconds average\naddRows 3.7939 milliseconds, 527 calls, 7.19905 microseconds average\nadd     1.0895 milliseconds, 9 calls, 121.056 microseconds average\n    Memory Usage\nModel   892.591 KB RAM, 2.8815 GB VRAM\nContext 92.2612 MB RAM, 1.14026 GB VRAM\nTotal   93.1329 MB RAM, 4.02176 GB VRAM\n"
  },
  {
    "path": "SampleClips/columbia-large-1650.txt",
    "content": "    CPU Tasks\nLoadModel       7.95251 seconds\nRunComplete     109.423 seconds\nRun     109.351 seconds\nCallbacks       12.7226 milliseconds, 44 calls, 289.15 microseconds average\nSpectrogram     270.286 milliseconds, 41 calls, 6.59235 milliseconds average\nSample  69.0965 milliseconds, 527 calls, 131.113 microseconds average\nEncode  35.943 seconds, 9 calls, 3.99366 seconds average\nDecode  73.3946 seconds, 9 calls, 8.15496 seconds average\nDecodeStep      73.3251 seconds, 527 calls, 139.137 milliseconds average\n    GPU Tasks\nLoadModel       7.55659 seconds\nRun     109.16 seconds\nEncode  36.3141 seconds, 9 calls, 4.0349 seconds average\nEncodeLayer     29.8405 seconds, 288 calls, 103.613 milliseconds average\nDecode  72.8459 seconds, 9 calls, 8.09398 seconds average\nDecodeStep      72.8458 seconds, 527 calls, 138.227 milliseconds average\nDecodeLayer     69.0153 seconds, 16864 calls, 4.09247 milliseconds average\n    Compute Shaders\nmulMatTiled     36.8159 seconds, 6345 calls, 5.80234 milliseconds average\nmulMatByRowTiled        28.0431 seconds, 199430 calls, 140.616 microseconds average\ncopyTranspose   8.11917 seconds, 34304 calls, 236.683 microseconds average\nfmaRepeat1      7.85961 seconds, 51704 calls, 152.012 microseconds average\naddRepeatScale  4.11915 seconds, 33728 calls, 122.129 microseconds average\nsoftMaxFixed    3.22072 seconds, 17152 calls, 187.775 microseconds average\ncopyConvert     2.8333 seconds, 34880 calls, 81.2298 microseconds average\naddRepeatEx     2.78075 seconds, 51168 calls, 54.3455 microseconds average\nnorm    2.76591 seconds, 51704 calls, 53.495 microseconds average\naddRepeatGelu   2.35162 seconds, 17170 calls, 136.961 microseconds average\nsoftMaxLong     2.24788 seconds, 527 calls, 4.26543 milliseconds average\nsoftMax 2.21477 seconds, 16864 calls, 131.331 microseconds average\nconvolutionMain2Fixed   1.38064 seconds, 9 calls, 153.405 milliseconds average\naddRepeat       1.30665 seconds, 17728 calls, 73.7057 microseconds average\nscaleInPlace    1.10329 seconds, 17152 calls, 64.3245 microseconds average\ndiagMaskInf     937.457 milliseconds, 16864 calls, 55.5892 microseconds average\nconvolutionMain 374.967 milliseconds, 9 calls, 41.663 milliseconds average\nconvolutionPrep1        119.171 milliseconds, 18 calls, 6.62059 milliseconds average\nconvolutionPrep2        27.8894 milliseconds, 18 calls, 1.54941 milliseconds average\naddRows 5.2536 milliseconds, 527 calls, 9.96888 microseconds average\nadd     2.8285 milliseconds, 9 calls, 314.278 microseconds average\n    Memory Usage\nModel   892.591 KB RAM, 2.8815 GB VRAM\nContext 92.2612 MB RAM, 1.14026 GB VRAM\nTotal   93.1329 MB RAM, 4.02176 GB VRAM\n"
  },
  {
    "path": "SampleClips/columbia-large-vega7.txt",
    "content": "    CPU Tasks\nLoadModel       2.88964 seconds\nRunComplete     140.747 seconds\nRun     140.661 seconds\nCallbacks       20.302 milliseconds, 44 calls, 461.409 microseconds average\nSpectrogram     468.419 milliseconds, 41 calls, 11.4249 milliseconds average\nSample  139.558 milliseconds, 527 calls, 264.815 microseconds average\nEncode  87.5396 seconds, 9 calls, 9.72662 seconds average\nDecode  53.0971 seconds, 9 calls, 5.89968 seconds average\nDecodeStep      52.9566 seconds, 527 calls, 100.487 milliseconds average\n    GPU Tasks\nLoadModel       1.86694 seconds\nRun     140.175 seconds\nEncode  88.7441 seconds, 9 calls, 9.86046 seconds average\nEncodeLayer     75.809 seconds, 288 calls, 263.226 milliseconds average\nDecode  51.4306 seconds, 9 calls, 5.71451 seconds average\nDecodeStep      51.43 seconds, 527 calls, 97.5901 milliseconds average\nDecodeLayer     48.1822 seconds, 16864 calls, 2.85711 milliseconds average\n    Compute Shaders\nmulMatTiledEx   69.1011 seconds, 2880 calls, 23.9934 milliseconds average\nmulMatTiled     21.009 seconds, 3465 calls, 6.06321 milliseconds average\nmulMatByRowTiled        20.0965 seconds, 166278 calls, 120.861 microseconds average\nmulMatByRowTiledEx      9.61326 seconds, 33152 calls, 289.975 microseconds average\nsoftMaxFixed    3.7631 seconds, 17152 calls, 219.397 microseconds average\nnorm    2.23806 seconds, 51704 calls, 43.2859 microseconds average\nconvolutionMain2Fixed   2.12825 seconds, 9 calls, 236.472 milliseconds average\nmatReshapePanels        2.0333 seconds, 1737 calls, 1.17058 milliseconds average\naddRepeatGelu   1.5491 seconds, 17170 calls, 90.2211 microseconds average\nscaleInPlace    1.32928 seconds, 17152 calls, 77.5001 microseconds average\ncopyConvert     1.23135 seconds, 34880 calls, 35.3026 microseconds average\nfmaRepeat1      1.10337 seconds, 51704 calls, 21.3401 microseconds average\naddRepeatEx     1.00095 seconds, 51168 calls, 19.562 microseconds average\ncopyTranspose   846.807 milliseconds, 34304 calls, 24.6854 microseconds average\naddRepeat       704.028 milliseconds, 17728 calls, 39.7128 microseconds average\nsoftMaxLong     608.58 milliseconds, 527 calls, 1.1548 milliseconds average\nconvolutionMain 522.249 milliseconds, 9 calls, 58.0277 milliseconds average\naddRepeatScale  500.937 milliseconds, 33728 calls, 14.8523 microseconds average\nsoftMax 236.054 milliseconds, 16864 calls, 13.9975 microseconds average\ndiagMaskInf     171.964 milliseconds, 16864 calls, 10.1971 microseconds average\nconvolutionPrep1        60.7331 milliseconds, 18 calls, 3.37406 milliseconds average\nconvolutionPrep2        33.441 milliseconds, 18 calls, 1.85783 milliseconds average\nadd     12.0883 milliseconds, 9 calls, 1.34314 milliseconds average\naddRows 1.9724 milliseconds, 527 calls, 3.74269 microseconds average\n    Memory Usage\nModel   892.591 KB RAM, 2.8815 GB VRAM\nContext 92.2612 MB RAM, 1.19934 GB VRAM\nTotal   93.1329 MB RAM, 4.08084 GB VRAM\n"
  },
  {
    "path": "SampleClips/columbia-large-vega8.txt",
    "content": "﻿    CPU Tasks\nLoadModel       1.49776 seconds\nRunComplete     110.474 seconds\nRun     110.407 seconds\nCallbacks       14.0412 milliseconds, 44 calls, 319.118 microseconds average\nSpectrogram     201.605 milliseconds, 41 calls, 4.91719 milliseconds average\nSample  65.5117 milliseconds, 527 calls, 124.311 microseconds average\nEncode  64.8806 seconds, 9 calls, 7.20896 seconds average\nDecode  45.5097 seconds, 9 calls, 5.05663 seconds average\nDecodeStep      45.4439 seconds, 527 calls, 86.2313 milliseconds average\n    GPU Tasks\nLoadModel       951.086 milliseconds\nRun     110.123 seconds\nEncode  65.7998 seconds, 9 calls, 7.31109 seconds average\nEncodeLayer     56.2581 seconds, 288 calls, 195.341 milliseconds average\nDecode  44.3232 seconds, 9 calls, 4.9248 seconds average\nDecodeStep      44.3227 seconds, 527 calls, 84.1039 milliseconds average\nDecodeLayer     41.6477 seconds, 16864 calls, 2.46962 milliseconds average\n    Compute Shaders\nmulMatTiledEx   51.4882 seconds, 2880 calls, 17.8778 milliseconds average\nmulMatByRowTiled        17.0626 seconds, 166278 calls, 102.615 microseconds average\nmulMatTiled     15.2984 seconds, 3465 calls, 4.41513 milliseconds average\nmulMatByRowTiledEx      9.04106 seconds, 33152 calls, 272.715 microseconds average\nsoftMaxFixed    3.04176 seconds, 17152 calls, 177.342 microseconds average\nnorm    1.98157 seconds, 51704 calls, 38.3254 microseconds average\nconvolutionMain2Fixed   1.8145 seconds, 9 calls, 201.611 milliseconds average\nmatReshapePanels        1.526 seconds, 1737 calls, 878.526 microseconds average\naddRepeatGelu   1.24631 seconds, 17170 calls, 72.5866 microseconds average\nscaleInPlace    1.23743 seconds, 17152 calls, 72.1448 microseconds average\ncopyConvert     1.02044 seconds, 34880 calls, 29.2557 microseconds average\nfmaRepeat1      993.664 milliseconds, 51704 calls, 19.2183 microseconds average\naddRepeatEx     953.85 milliseconds, 51168 calls, 18.6415 microseconds average\ncopyTranspose   705.073 milliseconds, 34304 calls, 20.5537 microseconds average\naddRepeat       581.089 milliseconds, 17728 calls, 32.778 microseconds average\naddRepeatScale  553.89 milliseconds, 33728 calls, 16.4222 microseconds average\nsoftMaxLong     387.949 milliseconds, 527 calls, 736.147 microseconds average\nconvolutionMain 363.351 milliseconds, 9 calls, 40.3723 milliseconds average\nsoftMax 242.956 milliseconds, 16864 calls, 14.4068 microseconds average\ndiagMaskInf     179.046 milliseconds, 16864 calls, 10.617 microseconds average\nconvolutionPrep1        45.2096 milliseconds, 18 calls, 2.51164 milliseconds average\nconvolutionPrep2        28.6853 milliseconds, 18 calls, 1.59363 milliseconds average\nadd     8.1107 milliseconds, 9 calls, 901.189 microseconds average\naddRows 1.542 milliseconds, 527 calls, 2.926 microseconds average\n    Memory Usage\nModel   892.591 KB RAM, 2.8815 GB VRAM\nContext 92.2612 MB RAM, 1.19934 GB VRAM\nTotal   93.1329 MB RAM, 4.08084 GB VRAM\n"
  },
  {
    "path": "SampleClips/columbia-medium-1080ti.txt",
    "content": "﻿    CPU Tasks\nLoadModel       600.5 milliseconds\nRunComplete     14.9475 seconds\nRun     14.8676 seconds\nCallbacks       8.0039 milliseconds, 37 calls, 216.322 microseconds average\nSpectrogram     193.196 milliseconds, 42 calls, 4.59991 milliseconds average\nSample  52.0611 milliseconds, 511 calls, 101.881 microseconds average\nEncode  5.97889 seconds, 10 calls, 597.889 milliseconds average\nDecode  8.8778 seconds, 10 calls, 887.78 milliseconds average\nDecodeStep      8.82556 seconds, 511 calls, 17.2712 milliseconds average\n    GPU Tasks\nLoadModel       457.133 milliseconds\nRun     14.7971 seconds\nEncode  6.01034 seconds, 10 calls, 601.034 milliseconds average\nEncodeLayer     5.11447 seconds, 240 calls, 21.3103 milliseconds average\nDecode  8.78676 seconds, 10 calls, 878.676 milliseconds average\nDecodeStep      8.78674 seconds, 511 calls, 17.1952 milliseconds average\nDecodeLayer     8.10499 seconds, 12264 calls, 660.876 microseconds average\n    Compute Shaders\nmulMatTiled     6.3857 seconds, 5290 calls, 1.20713 milliseconds average\nmulMatByRowTiled        4.79001 seconds, 144789 calls, 33.0827 microseconds average\nnormFixed       417.279 milliseconds, 37793 calls, 11.0412 microseconds average\nfmaRepeat1      399.385 milliseconds, 37793 calls, 10.5677 microseconds average\nsoftMaxFixed    382.654 milliseconds, 12504 calls, 30.6025 microseconds average\naddRepeatEx     378.135 milliseconds, 37272 calls, 10.1453 microseconds average\ncopyConvert     319.573 milliseconds, 25488 calls, 12.5382 microseconds average\ncopyTranspose   258.327 milliseconds, 25008 calls, 10.3298 microseconds average\nsoftMaxLong     244.787 milliseconds, 511 calls, 479.035 microseconds average\naddRepeatScale  223.995 milliseconds, 24528 calls, 9.13223 microseconds average\naddRepeatGelu   181.428 milliseconds, 12524 calls, 14.4864 microseconds average\nsoftMax 150.065 milliseconds, 12264 calls, 12.2362 microseconds average\nscaleInPlace    147.891 milliseconds, 12504 calls, 11.8275 microseconds average\naddRepeat       145.362 milliseconds, 12984 calls, 11.1955 microseconds average\nconvolutionMain2Fixed   124.04 milliseconds, 10 calls, 12.404 milliseconds average\ndiagMaskInf     78.8542 milliseconds, 12264 calls, 6.42973 microseconds average\nconvolutionMain 66.8723 milliseconds, 10 calls, 6.68723 milliseconds average\nconvolutionPrep1        15.4358 milliseconds, 20 calls, 771.79 microseconds average\nconvolutionPrep2        3.8144 milliseconds, 20 calls, 190.72 microseconds average\naddRows 3.5214 milliseconds, 511 calls, 6.89119 microseconds average\nadd     929.8 microseconds, 10 calls, 92.98 microseconds average\n    Memory Usage\nModel   877.966 KB RAM, 1.42785 GB VRAM\nContext 91.0716 MB RAM, 785.219 MB VRAM\nTotal   91.929 MB RAM, 2.19467 GB VRAM\n"
  },
  {
    "path": "SampleClips/columbia-medium-1650.txt",
    "content": "    CPU Tasks\nLoadModel       939.886 milliseconds\nRunComplete     48.7479 seconds\nRun     48.6305 seconds\nCallbacks       10.5582 milliseconds, 37 calls, 285.357 microseconds average\nSpectrogram     280.966 milliseconds, 42 calls, 6.68965 milliseconds average\nSample  65.5797 milliseconds, 511 calls, 128.336 microseconds average\nEncode  19.0653 seconds, 10 calls, 1.90653 seconds average\nDecode  29.5369 seconds, 10 calls, 2.95369 seconds average\nDecodeStep      29.4709 seconds, 511 calls, 57.6731 milliseconds average\n    GPU Tasks\nLoadModel       586.498 milliseconds\nRun     48.4589 seconds\nEncode  19.2258 seconds, 10 calls, 1.92258 seconds average\nEncodeLayer     15.7109 seconds, 240 calls, 65.4622 milliseconds average\nDecode  29.233 seconds, 10 calls, 2.9233 seconds average\nDecodeStep      29.233 seconds, 511 calls, 57.2074 milliseconds average\nDecodeLayer     27.6558 seconds, 12264 calls, 2.25504 milliseconds average\n    Compute Shaders\nmulMatTiled     19.1596 seconds, 5290 calls, 3.62186 milliseconds average\nmulMatByRowTiled        12.681 seconds, 144789 calls, 87.5829 microseconds average\nfmaRepeat1      3.10945 seconds, 37793 calls, 82.2758 microseconds average\ncopyTranspose   2.83737 seconds, 25008 calls, 113.458 microseconds average\nsoftMaxFixed    1.6294 seconds, 12504 calls, 130.31 microseconds average\naddRepeatScale  1.54396 seconds, 24528 calls, 62.9467 microseconds average\naddRepeatEx     1.06992 seconds, 37272 calls, 28.7056 microseconds average\nnormFixed       1.06753 seconds, 37793 calls, 28.2467 microseconds average\ncopyConvert     994.495 milliseconds, 25488 calls, 39.0182 microseconds average\nconvolutionMain2Fixed   954.715 milliseconds, 10 calls, 95.4715 milliseconds average\nsoftMax 742.126 milliseconds, 12264 calls, 60.5126 microseconds average\naddRepeatGelu   506.056 milliseconds, 12524 calls, 40.4069 microseconds average\nsoftMaxLong     491.226 milliseconds, 511 calls, 961.303 microseconds average\nscaleInPlace    438.956 milliseconds, 12504 calls, 35.1053 microseconds average\naddRepeat       403.997 milliseconds, 12984 calls, 31.1149 microseconds average\ndiagMaskInf     366.713 milliseconds, 12264 calls, 29.9016 microseconds average\nconvolutionMain 276.364 milliseconds, 10 calls, 27.6364 milliseconds average\nconvolutionPrep1        44.9126 milliseconds, 20 calls, 2.24563 milliseconds average\nconvolutionPrep2        20.0013 milliseconds, 20 calls, 1.00006 milliseconds average\naddRows 7.2369 milliseconds, 511 calls, 14.1622 microseconds average\nadd     2.453 milliseconds, 10 calls, 245.3 microseconds average\n    Memory Usage\nModel   877.966 KB RAM, 1.42785 GB VRAM\nContext 91.0716 MB RAM, 785.219 MB VRAM\nTotal   91.929 MB RAM, 2.19467 GB VRAM\n"
  },
  {
    "path": "SampleClips/columbia-medium-vega7.txt",
    "content": "    CPU Tasks\nLoadModel       1.99675 seconds\nRunComplete     81.256 seconds\nRun     81.1666 seconds\nCallbacks       17.8976 milliseconds, 37 calls, 483.719 microseconds average\nSpectrogram     483.273 milliseconds, 42 calls, 11.5065 milliseconds average\nSample  140.511 milliseconds, 511 calls, 274.972 microseconds average\nEncode  50.3768 seconds, 10 calls, 5.03768 seconds average\nDecode  30.7646 seconds, 10 calls, 3.07646 seconds average\nDecodeStep      30.6234 seconds, 511 calls, 59.9284 milliseconds average\n    GPU Tasks\nLoadModel       976.318 milliseconds\nRun     80.8284 seconds\nEncode  51.1656 seconds, 10 calls, 5.11656 seconds average\nEncodeLayer     43.8924 seconds, 240 calls, 182.885 milliseconds average\nDecode  29.6502 seconds, 10 calls, 2.96502 seconds average\nDecodeStep      29.6441 seconds, 511 calls, 58.012 milliseconds average\nDecodeLayer     26.9439 seconds, 12264 calls, 2.19699 milliseconds average\n    Compute Shaders\nmulMatTiledEx   37.1919 seconds, 2400 calls, 15.4966 milliseconds average\nmulMatTiled     13.9953 seconds, 2890 calls, 4.84268 milliseconds average\nmulMatByRowTiled        11.8792 seconds, 120741 calls, 98.3858 microseconds average\nmulMatByRowTiledEx      4.47094 seconds, 24048 calls, 185.917 microseconds average\nsoftMaxFixed    2.44162 seconds, 12504 calls, 195.267 microseconds average\nconvolutionMain2Fixed   1.51096 seconds, 10 calls, 151.096 milliseconds average\nmatReshapePanels        1.38964 seconds, 1450 calls, 958.371 microseconds average\naddRepeatGelu   963.292 milliseconds, 12524 calls, 76.9157 microseconds average\nnormFixed       925.912 milliseconds, 37793 calls, 24.4996 microseconds average\ncopyConvert     875.162 milliseconds, 25488 calls, 34.3362 microseconds average\nscaleInPlace    770.121 milliseconds, 12504 calls, 61.59 microseconds average\nfmaRepeat1      696.227 milliseconds, 37793 calls, 18.4221 microseconds average\ncopyTranspose   657.921 milliseconds, 25008 calls, 26.3084 microseconds average\naddRepeatEx     630.019 milliseconds, 37272 calls, 16.9033 microseconds average\nsoftMaxLong     623.51 milliseconds, 511 calls, 1.22018 milliseconds average\nconvolutionMain 471.348 milliseconds, 10 calls, 47.1348 milliseconds average\naddRepeatScale  379.836 milliseconds, 24528 calls, 15.4858 microseconds average\naddRepeat       354.984 milliseconds, 12984 calls, 27.3401 microseconds average\nsoftMax 197.387 milliseconds, 12264 calls, 16.0948 microseconds average\ndiagMaskInf     131.012 milliseconds, 12264 calls, 10.6827 microseconds average\nconvolutionPrep2        49.7619 milliseconds, 20 calls, 2.48809 milliseconds average\nconvolutionPrep1        42.2907 milliseconds, 20 calls, 2.11454 milliseconds average\nadd     10.5473 milliseconds, 10 calls, 1.05473 milliseconds average\naddRows 2.1075 milliseconds, 511 calls, 4.12427 microseconds average\n    Memory Usage\nModel   877.966 KB RAM, 1.42785 GB VRAM\nContext 91.0716 MB RAM, 833.407 MB VRAM\nTotal   91.929 MB RAM, 2.24172 GB VRAM\n"
  },
  {
    "path": "SampleClips/columbia-medium-vega8.txt",
    "content": "﻿    CPU Tasks\nLoadModel       841.605 milliseconds\nRunComplete     62.1145 seconds\nRun     62.0268 seconds\nCallbacks       10.184 milliseconds, 37 calls, 275.243 microseconds average\nSpectrogram     200.241 milliseconds, 42 calls, 4.76764 milliseconds average\nSample  63.0473 milliseconds, 511 calls, 123.38 microseconds average\nEncode  37.2409 seconds, 10 calls, 3.72409 seconds average\nDecode  24.7715 seconds, 10 calls, 2.47715 seconds average\nDecodeStep      24.7082 seconds, 511 calls, 48.3526 milliseconds average\n    GPU Tasks\nLoadModel       410.579 milliseconds\nRun     61.8044 seconds\nEncode  37.8702 seconds, 10 calls, 3.78702 seconds average\nEncodeLayer     32.1896 seconds, 240 calls, 134.123 milliseconds average\nDecode  23.9262 seconds, 10 calls, 2.39262 seconds average\nDecodeStep      23.9233 seconds, 511 calls, 46.8167 milliseconds average\nDecodeLayer     21.6888 seconds, 12264 calls, 1.76849 milliseconds average\n    Compute Shaders\nmulMatTiledEx   27.5216 seconds, 2400 calls, 11.4673 milliseconds average\nmulMatTiled     10.2385 seconds, 2890 calls, 3.54273 milliseconds average\nmulMatByRowTiled        9.38114 seconds, 120741 calls, 77.6964 microseconds average\nmulMatByRowTiledEx      4.19991 seconds, 24048 calls, 174.647 microseconds average\nsoftMaxFixed    1.95105 seconds, 12504 calls, 156.034 microseconds average\nconvolutionMain2Fixed   1.31354 seconds, 10 calls, 131.354 milliseconds average\nmatReshapePanels        1.04699 seconds, 1450 calls, 722.064 microseconds average\naddRepeatGelu   777.683 milliseconds, 12524 calls, 62.0954 microseconds average\nscaleInPlace    750.056 milliseconds, 12504 calls, 59.9853 microseconds average\ncopyConvert     701.517 milliseconds, 25488 calls, 27.5234 microseconds average\nnormFixed       697.931 milliseconds, 37793 calls, 18.4672 microseconds average\nfmaRepeat1      529.007 milliseconds, 37793 calls, 13.9975 microseconds average\naddRepeatEx     511.269 milliseconds, 37272 calls, 13.7172 microseconds average\ncopyTranspose   459.017 milliseconds, 25008 calls, 18.3548 microseconds average\nsoftMaxLong     382.205 milliseconds, 511 calls, 747.955 microseconds average\nconvolutionMain 328.996 milliseconds, 10 calls, 32.8996 milliseconds average\naddRepeat       305.31 milliseconds, 12984 calls, 23.5144 microseconds average\naddRepeatScale  261.749 milliseconds, 24528 calls, 10.6715 microseconds average\nsoftMax 148.894 milliseconds, 12264 calls, 12.1407 microseconds average\ndiagMaskInf     104.681 milliseconds, 12264 calls, 8.53562 microseconds average\nconvolutionPrep2        45.8033 milliseconds, 20 calls, 2.29017 milliseconds average\nconvolutionPrep1        32.3779 milliseconds, 20 calls, 1.61889 milliseconds average\nadd     7.3228 milliseconds, 10 calls, 732.28 microseconds average\naddRows 1.6948 milliseconds, 511 calls, 3.31663 microseconds average\n    Memory Usage\nModel   877.966 KB RAM, 1.42785 GB VRAM\nContext 91.0716 MB RAM, 833.407 MB VRAM\nTotal   91.929 MB RAM, 2.24172 GB VRAM\n"
  },
  {
    "path": "SampleClips/jfk-large-1080ti.txt",
    "content": "﻿    CPU Tasks\nLoadModel       945.134 milliseconds\nRunComplete     2.19628 seconds\nRun     2.08991 seconds\nCallbacks       762.3 microseconds, 4 calls, 190.575 microseconds average\nSpectrogram     12.2602 milliseconds, 3 calls, 4.08673 milliseconds average\nSample  3.2495 milliseconds, 27 calls, 120.352 microseconds average\nEncode  1.31469 seconds\nDecode  774.432 milliseconds\nDecodeStep      771.17 milliseconds, 27 calls, 28.5618 milliseconds average\n    GPU Tasks\nLoadModel       803.014 milliseconds\nRun     2.07007 seconds\nEncode  1.29615 seconds\nEncodeLayer     1.11858 seconds, 32 calls, 34.9556 milliseconds average\nDecode  773.917 milliseconds\nDecodeStep      773.915 milliseconds, 27 calls, 28.6635 milliseconds average\nDecodeLayer     719.389 milliseconds, 864 calls, 832.626 microseconds average\n    Compute Shaders\nmulMatTiled     1.20543 seconds, 705 calls, 1.70983 milliseconds average\nmulMatByRowTiled        471.054 milliseconds, 10010 calls, 47.0584 microseconds average\nnorm    72.3989 milliseconds, 2684 calls, 26.9743 microseconds average\nfmaRepeat1      41.7212 milliseconds, 2684 calls, 15.5444 microseconds average\ncopyTranspose   41.168 milliseconds, 1792 calls, 22.9732 microseconds average\nsoftMaxFixed    40.8861 milliseconds, 896 calls, 45.6318 microseconds average\naddRepeatEx     30.1243 milliseconds, 2656 calls, 11.342 microseconds average\ncopyConvert     28.9217 milliseconds, 1856 calls, 15.5828 microseconds average\nsoftMaxLong     25.3209 milliseconds, 27 calls, 937.811 microseconds average\nconvolutionMain2Fixed   19.8769 milliseconds\naddRepeatScale  18.2236 milliseconds, 1728 calls, 10.5461 microseconds average\naddRepeatGelu   15.7554 milliseconds, 898 calls, 17.545 microseconds average\naddRepeat       14.2968 milliseconds, 960 calls, 14.8925 microseconds average\nscaleInPlace    13.9332 milliseconds, 896 calls, 15.5504 microseconds average\nsoftMax 8.5928 milliseconds, 864 calls, 9.94537 microseconds average\nconvolutionMain 8.532 milliseconds\ndiagMaskInf     5.6745 milliseconds, 864 calls, 6.56771 microseconds average\nconvolutionPrep1        2.303 milliseconds, 2 calls, 1.1515 milliseconds average\nconvolutionPrep2        422.9 microseconds, 2 calls, 211.45 microseconds average\naddRows 198.7 microseconds, 27 calls, 7.35926 microseconds average\nadd     119.8 microseconds\n    Memory Usage\nModel   892.591 KB RAM, 2.8815 GB VRAM\nContext 1.98376 MB RAM, 1.07641 GB VRAM\nTotal   2.85543 MB RAM, 3.95791 GB VRAM\n"
  },
  {
    "path": "SampleClips/jfk-large-1650.txt",
    "content": "    CPU Tasks\nLoadModel       7.92578 seconds\nRunComplete     8.33686 seconds\nRun     8.25683 seconds\nCallbacks       337.7 microseconds, 4 calls, 84.425 microseconds average\nSpectrogram     16.4214 milliseconds, 3 calls, 5.4738 milliseconds average\nSample  3.8768 milliseconds, 27 calls, 143.585 microseconds average\nEncode  4.14309 seconds\nDecode  4.11338 seconds\nDecodeStep      4.10947 seconds, 27 calls, 152.203 milliseconds average\n    GPU Tasks\nLoadModel       7.53025 seconds\nRun     8.05464 seconds\nEncode  3.98133 seconds\nEncodeLayer     3.29696 seconds, 32 calls, 103.03 milliseconds average\nDecode  4.07331 seconds\nDecodeStep      4.07331 seconds, 27 calls, 150.863 milliseconds average\nDecodeLayer     3.81856 seconds, 864 calls, 4.41963 milliseconds average\n    Compute Shaders\nmulMatTiled     3.6307 seconds, 705 calls, 5.14993 milliseconds average\nmulMatByRowTiled        1.45034 seconds, 10010 calls, 144.889 microseconds average\nfmaRepeat1      774.011 milliseconds, 2684 calls, 288.38 microseconds average\ncopyTranspose   416.996 milliseconds, 1792 calls, 232.699 microseconds average\naddRepeatScale  223.798 milliseconds, 1728 calls, 129.513 microseconds average\nnorm    211.821 milliseconds, 2684 calls, 78.9197 microseconds average\nsoftMaxFixed    209.124 milliseconds, 896 calls, 233.398 microseconds average\ncopyConvert     186.898 milliseconds, 1856 calls, 100.699 microseconds average\naddRepeatEx     176.985 milliseconds, 2656 calls, 66.6361 microseconds average\nsoftMaxLong     162.482 milliseconds, 27 calls, 6.01787 milliseconds average\nconvolutionMain2Fixed   154.739 milliseconds\naddRepeatGelu   132.088 milliseconds, 898 calls, 147.091 microseconds average\nsoftMax 98.1905 milliseconds, 864 calls, 113.646 microseconds average\nscaleInPlace    57.3956 milliseconds, 896 calls, 64.0576 microseconds average\nconvolutionMain 45.7933 milliseconds\naddRepeat       36.9315 milliseconds, 960 calls, 38.4703 microseconds average\ndiagMaskInf     28.2987 milliseconds, 864 calls, 32.7531 microseconds average\nconvolutionPrep1        9.0334 milliseconds, 2 calls, 4.5167 milliseconds average\nconvolutionPrep2        1.1608 milliseconds, 2 calls, 580.4 microseconds average\nadd     320 microseconds\naddRows 244 microseconds, 27 calls, 9.03704 microseconds average\n    Memory Usage\nModel   892.591 KB RAM, 2.8815 GB VRAM\nContext 1.98376 MB RAM, 1.07641 GB VRAM\nTotal   2.85543 MB RAM, 3.95791 GB VRAM\n"
  },
  {
    "path": "SampleClips/jfk-large-vega7.txt",
    "content": "    CPU Tasks\nLoadModel       3.22847 seconds\nRunComplete     14.2729 seconds\nRun     14.186 seconds\nCallbacks       674.7 microseconds, 4 calls, 168.675 microseconds average\nSpectrogram     29.6112 milliseconds, 3 calls, 9.8704 milliseconds average\nSample  7.7473 milliseconds, 27 calls, 286.937 microseconds average\nEncode  11.8931 seconds\nDecode  2.29185 seconds\nDecodeStep      2.28406 seconds, 27 calls, 84.5949 milliseconds average\n    GPU Tasks\nLoadModel       1.9083 seconds\nRun     14.0698 seconds\nEncode  11.9404 seconds\nEncodeLayer     10.2786 seconds, 32 calls, 321.205 milliseconds average\nDecode  2.12941 seconds\nDecodeStep      2.12938 seconds, 27 calls, 78.8661 milliseconds average\nDecodeLayer     1.98655 seconds, 864 calls, 2.29924 milliseconds average\n    Compute Shaders\nmulMatTiledEx   8.49883 seconds, 320 calls, 26.5589 milliseconds average\nmulMatTiled     2.04655 seconds, 385 calls, 5.31573 milliseconds average\nmulMatByRowTiled        982.48 milliseconds, 8346 calls, 117.719 microseconds average\nmulMatByRowTiledEx      481.123 milliseconds, 1664 calls, 289.137 microseconds average\nconvolutionMain2Fixed   415.244 milliseconds\nsoftMaxFixed    404.223 milliseconds, 896 calls, 451.142 microseconds average\nmatReshapePanels        210.915 milliseconds, 193 calls, 1.09282 milliseconds average\nnorm    154.589 milliseconds, 2684 calls, 57.5965 microseconds average\nconvolutionMain 126.883 milliseconds\naddRepeatGelu   112.131 milliseconds, 898 calls, 124.867 microseconds average\ncopyConvert     100.589 milliseconds, 1856 calls, 54.1968 microseconds average\nscaleInPlace    91.3539 milliseconds, 896 calls, 101.957 microseconds average\nfmaRepeat1      86.7731 milliseconds, 2684 calls, 32.3298 microseconds average\ncopyTranspose   77.5852 milliseconds, 1792 calls, 43.2953 microseconds average\naddRepeat       76.3677 milliseconds, 960 calls, 79.5497 microseconds average\naddRepeatEx     70.8699 milliseconds, 2656 calls, 26.6829 microseconds average\nsoftMaxLong     39.4035 milliseconds, 27 calls, 1.45939 milliseconds average\naddRepeatScale  25.0842 milliseconds, 1728 calls, 14.5163 microseconds average\nsoftMax 14.0555 milliseconds, 864 calls, 16.2679 microseconds average\nconvolutionPrep1        13.9331 milliseconds, 2 calls, 6.96655 milliseconds average\ndiagMaskInf     8.1717 milliseconds, 864 calls, 9.45799 microseconds average\nconvolutionPrep2        5.2098 milliseconds, 2 calls, 2.6049 milliseconds average\nadd     2.9724 milliseconds\naddRows 91.2 microseconds, 27 calls, 3.37778 microseconds average\n    Memory Usage\nModel   892.591 KB RAM, 2.8815 GB VRAM\nContext 1.98376 MB RAM, 1.13447 GB VRAM\nTotal   2.85543 MB RAM, 4.01597 GB VRAM\n"
  },
  {
    "path": "SampleClips/jfk-large-vega8.txt",
    "content": "﻿    CPU Tasks\nLoadModel       1.57347 seconds\nRunComplete     9.46787 seconds\nRun     9.40671 seconds\nCallbacks       292.1 microseconds, 4 calls, 73.025 microseconds average\nSpectrogram     12.2725 milliseconds, 3 calls, 4.09083 milliseconds average\nSample  3.5692 milliseconds, 27 calls, 132.193 microseconds average\nEncode  7.26322 seconds\nDecode  2.14291 seconds\nDecodeStep      2.13933 seconds, 27 calls, 79.2343 milliseconds average\n    GPU Tasks\nLoadModel       991.351 milliseconds\nRun     9.36883 seconds\nEncode  7.35144 seconds\nEncodeLayer     6.25822 seconds, 32 calls, 195.569 milliseconds average\nDecode  2.01739 seconds\nDecodeStep      2.01737 seconds, 27 calls, 74.7173 milliseconds average\nDecodeLayer     1.88943 seconds, 864 calls, 2.18684 milliseconds average\n    Compute Shaders\nmulMatTiledEx   5.37127 seconds, 320 calls, 16.7852 milliseconds average\nmulMatTiled     1.17596 seconds, 385 calls, 3.05444 milliseconds average\nmulMatByRowTiled        878.04 milliseconds, 8346 calls, 105.205 microseconds average\nmulMatByRowTiledEx      460.074 milliseconds, 1664 calls, 276.486 microseconds average\nsoftMaxFixed    288.221 milliseconds, 896 calls, 321.675 microseconds average\nconvolutionMain2Fixed   201.063 milliseconds\nnorm    141.073 milliseconds, 2684 calls, 52.5606 microseconds average\nmatReshapePanels        138.851 milliseconds, 193 calls, 719.436 microseconds average\naddRepeatGelu   89.1783 milliseconds, 898 calls, 99.3077 microseconds average\ncopyConvert     83.2232 milliseconds, 1856 calls, 44.8401 microseconds average\nscaleInPlace    77.8363 milliseconds, 896 calls, 86.8709 microseconds average\nfmaRepeat1      77.8123 milliseconds, 2684 calls, 28.9912 microseconds average\naddRepeatEx     76.9018 milliseconds, 2656 calls, 28.954 microseconds average\naddRepeat       66.8479 milliseconds, 960 calls, 69.6332 microseconds average\ncopyTranspose   62.5101 milliseconds, 1792 calls, 34.8829 microseconds average\naddRepeatScale  40.5807 milliseconds, 1728 calls, 23.4842 microseconds average\nconvolutionMain 39.8186 milliseconds\nsoftMaxLong     32.0594 milliseconds, 27 calls, 1.18739 milliseconds average\nsoftMax 15.9281 milliseconds, 864 calls, 18.4353 microseconds average\ndiagMaskInf     12.6164 milliseconds, 864 calls, 14.6023 microseconds average\nconvolutionPrep1        5.4486 milliseconds, 2 calls, 2.7243 milliseconds average\nconvolutionPrep2        4.0996 milliseconds, 2 calls, 2.0498 milliseconds average\nadd     883.4 microseconds\naddRows 73.4 microseconds, 27 calls, 2.71852 microseconds average\n    Memory Usage\nModel   892.591 KB RAM, 2.8815 GB VRAM\nContext 1.98376 MB RAM, 1.13447 GB VRAM\nTotal   2.85543 MB RAM, 4.01597 GB VRAM\n"
  },
  {
    "path": "SampleClips/jfk-medium-1080ti.txt",
    "content": "﻿    CPU Tasks\nLoadModel       593.861 milliseconds\nRunComplete     1.13909 seconds\nRun     1.06578 seconds\nCallbacks       279.4 microseconds, 4 calls, 69.85 microseconds average\nSpectrogram     12.0744 milliseconds, 3 calls, 4.0248 milliseconds average\nSample  3.0016 milliseconds, 28 calls, 107.2 microseconds average\nEncode  614.44 milliseconds\nDecode  451.048 milliseconds\nDecodeStep      448.036 milliseconds, 28 calls, 16.0013 milliseconds average\n    GPU Tasks\nLoadModel       452.128 milliseconds\nRun     1.0518 seconds\nEncode  599.964 milliseconds\nEncodeLayer     506.67 milliseconds, 24 calls, 21.1113 milliseconds average\nDecode  451.832 milliseconds\nDecodeStep      451.828 milliseconds, 28 calls, 16.1367 milliseconds average\nDecodeLayer     412.142 milliseconds, 672 calls, 613.307 microseconds average\n    Compute Shaders\nmulMatTiled     562.478 milliseconds, 529 calls, 1.06329 milliseconds average\nmulMatByRowTiled        256.062 milliseconds, 7803 calls, 32.8158 microseconds average\nsoftMaxFixed    27.1687 milliseconds, 696 calls, 39.0355 microseconds average\nnormFixed       24.1828 milliseconds, 2093 calls, 11.5541 microseconds average\nfmaRepeat1      23.3089 milliseconds, 2093 calls, 11.1366 microseconds average\naddRepeatEx     22.3395 milliseconds, 2064 calls, 10.8234 microseconds average\nsoftMaxLong     19.7192 milliseconds, 28 calls, 704.257 microseconds average\ncopyConvert     19.301 milliseconds, 1440 calls, 13.4035 microseconds average\ncopyTranspose   15.3011 milliseconds, 1392 calls, 10.9922 microseconds average\naddRepeatScale  13.6043 milliseconds, 1344 calls, 10.1222 microseconds average\nconvolutionMain2Fixed   12.1242 milliseconds\naddRepeatGelu   11.6172 milliseconds, 698 calls, 16.6436 microseconds average\naddRepeat       11.5331 milliseconds, 744 calls, 15.5015 microseconds average\nscaleInPlace    9.5743 milliseconds, 696 calls, 13.7562 microseconds average\nconvolutionMain 7.0349 milliseconds\nsoftMax 5.8329 milliseconds, 672 calls, 8.67991 microseconds average\ndiagMaskInf     4.5297 milliseconds, 672 calls, 6.74062 microseconds average\nconvolutionPrep1        1.5258 milliseconds, 2 calls, 762.9 microseconds average\nconvolutionPrep2        383 microseconds, 2 calls, 191.5 microseconds average\naddRows 194.6 microseconds, 28 calls, 6.95 microseconds average\nadd     95.2 microseconds\n    Memory Usage\nModel   877.966 KB RAM, 1.42785 GB VRAM\nContext 1.9831 MB RAM, 723.782 MB VRAM\nTotal   2.84049 MB RAM, 2.13467 GB VRAM\n"
  },
  {
    "path": "SampleClips/jfk-medium-1650.txt",
    "content": "    CPU Tasks\nLoadModel       2.20693 seconds\nRunComplete     3.16174 seconds\nRun     3.07912 seconds\nCallbacks       387.3 microseconds, 4 calls, 96.825 microseconds average\nSpectrogram     16.201 milliseconds, 3 calls, 5.40033 milliseconds average\nSample  3.3725 milliseconds, 28 calls, 120.446 microseconds average\nEncode  2.07037 seconds\nDecode  1.00834 seconds\nDecodeStep      1.00495 seconds, 28 calls, 35.8911 milliseconds average\n    GPU Tasks\nLoadModel       1.81217 seconds\nRun     2.94117 seconds\nEncode  1.95373 seconds\nEncodeLayer     1.56747 seconds, 24 calls, 65.3115 milliseconds average\nDecode  987.441 milliseconds\nDecodeStep      987.44 milliseconds, 28 calls, 35.2657 milliseconds average\nDecodeLayer     915.401 milliseconds, 672 calls, 1.3622 milliseconds average\n    Compute Shaders\nmulMatTiled     1.68817 seconds, 529 calls, 3.19125 milliseconds average\nmulMatByRowTiled        562.722 milliseconds, 7803 calls, 72.1161 microseconds average\nconvolutionMain2Fixed   99.873 milliseconds\nsoftMaxFixed    84.045 milliseconds, 696 calls, 120.754 microseconds average\ncopyTranspose   80.3619 milliseconds, 1392 calls, 57.7313 microseconds average\nfmaRepeat1      71.9629 milliseconds, 2093 calls, 34.3827 microseconds average\nconvolutionMain 60.588 milliseconds\naddRepeatScale  53.2349 milliseconds, 1344 calls, 39.6093 microseconds average\nnormFixed       34.7651 milliseconds, 2093 calls, 16.6102 microseconds average\naddRepeatEx     31.9206 milliseconds, 2064 calls, 15.4654 microseconds average\ncopyConvert     30.7856 milliseconds, 1440 calls, 21.3789 microseconds average\naddRepeatGelu   25.5167 milliseconds, 698 calls, 36.5569 microseconds average\nsoftMaxLong     25.3214 milliseconds, 28 calls, 904.336 microseconds average\nscaleInPlace    24.1527 milliseconds, 696 calls, 34.7022 microseconds average\nsoftMax 21.0692 milliseconds, 672 calls, 31.353 microseconds average\naddRepeat       19.8584 milliseconds, 744 calls, 26.6914 microseconds average\ndiagMaskInf     12.5615 milliseconds, 672 calls, 18.6927 microseconds average\nconvolutionPrep1        6.113 milliseconds, 2 calls, 3.0565 milliseconds average\nconvolutionPrep2        1.2294 milliseconds, 2 calls, 614.7 microseconds average\nadd     532.9 microseconds\naddRows 178.9 microseconds, 28 calls, 6.38929 microseconds average\n    Memory Usage\nModel   877.966 KB RAM, 1.42785 GB VRAM\nContext 1.9831 MB RAM, 723.782 MB VRAM\nTotal   2.84049 MB RAM, 2.13467 GB VRAM\n"
  },
  {
    "path": "SampleClips/jfk-medium-vega7.txt",
    "content": "    CPU Tasks\nLoadModel       1.79203 seconds\nRunComplete     8.79853 seconds\nRun     8.71884 seconds\nCallbacks       626.8 microseconds, 4 calls, 156.7 microseconds average\nSpectrogram     17.3373 milliseconds, 3 calls, 5.7791 milliseconds average\nSample  5.449 milliseconds, 28 calls, 194.607 microseconds average\nEncode  7.29966 seconds\nDecode  1.41824 seconds\nDecodeStep      1.41276 seconds, 28 calls, 50.4557 milliseconds average\n    GPU Tasks\nLoadModel       930.123 milliseconds\nRun     8.64946 seconds\nEncode  7.34021 seconds\nEncodeLayer     6.40759 seconds, 24 calls, 266.983 milliseconds average\nDecode  1.30925 seconds\nDecodeStep      1.30389 seconds, 28 calls, 46.5676 milliseconds average\nDecodeLayer     1.19422 seconds, 672 calls, 1.77711 milliseconds average\n    Compute Shaders\nmulMatTiledEx   4.91773 seconds, 240 calls, 20.4906 milliseconds average\nmulMatTiled     1.47531 seconds, 289 calls, 5.10489 milliseconds average\nmulMatByRowTiled        627.1 milliseconds, 6507 calls, 96.3731 microseconds average\nsoftMaxFixed    268.285 milliseconds, 696 calls, 385.467 microseconds average\nconvolutionMain2Fixed   266.261 milliseconds\nmulMatByRowTiledEx      241.609 milliseconds, 1296 calls, 186.427 microseconds average\nmatReshapePanels        156.683 milliseconds, 145 calls, 1.08057 milliseconds average\nconvolutionMain 102.091 milliseconds\ncopyConvert     77.6113 milliseconds, 1440 calls, 53.8967 microseconds average\naddRepeatGelu   71.5118 milliseconds, 698 calls, 102.452 microseconds average\ncopyTranspose   63.3929 milliseconds, 1392 calls, 45.5409 microseconds average\nnormFixed       60.9615 milliseconds, 2093 calls, 29.1264 microseconds average\nscaleInPlace    59.9341 milliseconds, 696 calls, 86.1122 microseconds average\nfmaRepeat1      56.3539 milliseconds, 2093 calls, 26.9249 microseconds average\naddRepeatEx     51.8785 milliseconds, 2064 calls, 25.1349 microseconds average\naddRepeat       48.1192 milliseconds, 744 calls, 64.6763 microseconds average\nsoftMaxLong     28.3411 milliseconds, 28 calls, 1.01218 milliseconds average\naddRepeatScale  21.3646 milliseconds, 1344 calls, 15.8963 microseconds average\nsoftMax 10.198 milliseconds, 672 calls, 15.1756 microseconds average\nconvolutionPrep1        9.1072 milliseconds, 2 calls, 4.5536 milliseconds average\ndiagMaskInf     8.3764 milliseconds, 672 calls, 12.4649 microseconds average\nconvolutionPrep2        7.4623 milliseconds, 2 calls, 3.73115 milliseconds average\nadd     2.3886 milliseconds\naddRows 97.6 microseconds, 28 calls, 3.48571 microseconds average\n    Memory Usage\nModel   877.966 KB RAM, 1.42785 GB VRAM\nContext 1.9831 MB RAM, 771.235 MB VRAM\nTotal   2.84049 MB RAM, 2.18101 GB VRAM\n"
  },
  {
    "path": "SampleClips/jfk-medium-vega8.txt",
    "content": "﻿    CPU Tasks\nLoadModel       827.449 milliseconds\nRunComplete     4.95485 seconds\nRun     4.90459 seconds\nCallbacks       343.6 microseconds, 4 calls, 85.9 microseconds average\nSpectrogram     12.0208 milliseconds, 3 calls, 4.00693 milliseconds average\nSample  3.798 milliseconds, 28 calls, 135.643 microseconds average\nEncode  3.78211 seconds\nDecode  1.12187 seconds\nDecodeStep      1.11805 seconds, 28 calls, 39.9304 milliseconds average\n    GPU Tasks\nLoadModel       429.525 milliseconds\nRun     4.86319 seconds\nEncode  3.82894 seconds\nEncodeLayer     3.23846 seconds, 24 calls, 134.936 milliseconds average\nDecode  1.03424 seconds\nDecodeStep      1.03083 seconds, 28 calls, 36.8153 milliseconds average\nDecodeLayer     934.97 milliseconds, 672 calls, 1.39132 milliseconds average\n    Compute Shaders\nmulMatTiledEx   2.59201 seconds, 240 calls, 10.8 milliseconds average\nmulMatTiled     733.345 milliseconds, 289 calls, 2.53753 milliseconds average\nmulMatByRowTiled        492.505 milliseconds, 6507 calls, 75.6884 microseconds average\nmulMatByRowTiledEx      226.315 milliseconds, 1296 calls, 174.626 microseconds average\nsoftMaxFixed    169.603 milliseconds, 696 calls, 243.683 microseconds average\nconvolutionMain2Fixed   130.131 milliseconds\nmatReshapePanels        85.7723 milliseconds, 145 calls, 591.533 microseconds average\naddRepeatGelu   52.8833 milliseconds, 698 calls, 75.764 microseconds average\ncopyConvert     49.8477 milliseconds, 1440 calls, 34.6165 microseconds average\nscaleInPlace    47.7803 milliseconds, 696 calls, 68.6499 microseconds average\nnormFixed       44.0434 milliseconds, 2093 calls, 21.0432 microseconds average\nfmaRepeat1      38.6945 milliseconds, 2093 calls, 18.4876 microseconds average\ncopyTranspose   36.6512 milliseconds, 1392 calls, 26.3299 microseconds average\naddRepeatEx     33.6887 milliseconds, 2064 calls, 16.322 microseconds average\naddRepeat       32.9016 milliseconds, 744 calls, 44.2226 microseconds average\nconvolutionMain 32.8426 milliseconds\nsoftMaxLong     19.8753 milliseconds, 28 calls, 709.832 microseconds average\naddRepeatScale  15.8724 milliseconds, 1344 calls, 11.8098 microseconds average\nsoftMax 5.5277 milliseconds, 672 calls, 8.22574 microseconds average\ndiagMaskInf     5.1549 milliseconds, 672 calls, 7.67098 microseconds average\nconvolutionPrep2        3.9464 milliseconds, 2 calls, 1.9732 milliseconds average\nconvolutionPrep1        3.4569 milliseconds, 2 calls, 1.72845 milliseconds average\nadd     722.7 microseconds\naddRows 80 microseconds, 28 calls, 2.85714 microseconds average\n    Memory Usage\nModel   877.966 KB RAM, 1.42785 GB VRAM\nContext 1.9831 MB RAM, 771.235 MB VRAM\nTotal   2.84049 MB RAM, 2.18101 GB VRAM\n"
  },
  {
    "path": "SampleClips/summary.tsv",
    "content": "Audio Clip\tModel\tGPU\tTotal, sec\tRelative speed\tEncode, sec\tDecode, sec\tRAM, MB\tVRAM, MB\njfk.wav\tmedium\tGeForce 1080Ti\t1.13909\t9.6568\t0.599964\t0.451832\t2.84049\t2185.90208\njfk.wav\tmedium\tGeForce 1650\t3.16174\t3.4791\t1.95373\t0.987441\t2.84049\t2185.90208\njfk.wav\tmedium\tRyzen 5 5600U\t8.79853\t1.2502\t7.34021\t1.30925\t2.84049\t2233.35424\njfk.wav\tmedium\tRyzen 7 5700G\t4.95485\t2.2200\t3.82894\t1.03424\t2.84049\t2233.35424\njfk.wav\tlarge\tGeForce 1080Ti\t2.19628\t5.0085\t1.29615\t0.7739170000000001\t2.85543\t4052.89984\njfk.wav\tlarge\tGeForce 1650\t8.33686\t1.3194\t3.98133\t4.07331\t2.85543\t4052.89984\njfk.wav\tlarge\tRyzen 5 5600U\t14.2729\t0.7707\t11.9404\t2.12941\t2.85543\t4112.35328\njfk.wav\tlarge\tRyzen 7 5700G\t9.46787\t1.1618\t7.35144\t2.01739\t2.85543\t4112.35328\ncolumbia.wma\tmedium\tGeForce 1080Ti\t14.9475\t13.2973\t6.01034\t8.78676\t91.929\t2247.34208\ncolumbia.wma\tmedium\tGeForce 1650\t48.7479\t4.0773\t19.2258\t29.233\t91.929\t2247.34208\ncolumbia.wma\tmedium\tRyzen 5 5600U\t81.256\t2.4461\t51.1656\t29.6502\t91.929\t2295.52128\ncolumbia.wma\tmedium\tRyzen 7 5700G\t62.1145\t3.1999\t37.8702\t23.9262\t91.929\t2295.52128\ncolumbia.wma\tlarge\tGeForce 1080Ti\t27.5329\t7.2191\t11.3967\t15.9412\t93.1329\t4118.28224\ncolumbia.wma\tlarge\tGeForce 1650\t109.423\t1.8165\t36.3141\t72.8459\t93.1329\t4118.28224\ncolumbia.wma\tlarge\tRyzen 5 5600U\t140.747\t1.4122\t88.7441\t51.4306\t93.1329\t4178.78016\ncolumbia.wma\tlarge\tRyzen 7 5700G\t110.474\t1.7992\t65.7998\t44.3232\t93.1329\t4178.78016\n"
  },
  {
    "path": "Tools/CompressShaders/Cabinet.cs",
    "content": "﻿using System.ComponentModel;\nusing System.Runtime.InteropServices;\n\nnamespace CompressShaders\n{\n\t/// <summary>Lossless data compressor implemented by <c>Cabinet.dll</c> Windows component</summary>\n\t/// <remarks>\n\t/// <para>That compression API was introduced in Windows 8.0, and is the only reason why the library won’t build for Windows 7 OS.</para>\n\t/// <para>Whisper.dll consumes that component in runtime, to decompress these shader binaries</para>\n\t/// <para>If you wonder why not gzip — because the OS doesn’t include an API for that, at least not an API usable from C or C++.<br/>\n\t/// .NET standard library includes gzip algorithm, but we don't want Whisper.dll to depend on .NET.</para>\n\t/// </remarks>\n\t[Obsolete]\n\tstatic class Cabinet\n\t{\n\t\t/// <summary>Compression algorithm</summary>\n\t\t/// <seealso href=\"https://learn.microsoft.com/en-us/windows/win32/cmpapi/using-the-compression-api#selecting-the-compression-algorithm\" />\n\t\tenum eCompressionAlgorithm: uint\n\t\t{\n\t\t\tMSZIP = 2,\n\t\t\tXPRESS = 3,\n\t\t\tXPRESS_HUFF = 4,\n\t\t\tLZMS = 5,\n\t\t}\n\t\t/// <summary>The value should match <c>constexpr DWORD compressionAlgorithm</c> constant,<br/>in <c>Whisper/D3D/shaders.cpp</c> source file</summary>\n\t\tconst eCompressionAlgorithm algo = eCompressionAlgorithm.MSZIP;\n\n\t\t[DllImport( \"Cabinet.dll\", SetLastError = true )]\n\t\tstatic extern bool CreateCompressor( eCompressionAlgorithm Algorithm, IntPtr AllocationRoutines, out IntPtr CompressorHandle );\n\n\t\t[DllImport( \"Cabinet.dll\", SetLastError = true )]\n\t\tstatic extern bool CloseCompressor( IntPtr CompressorHandle );\n\n\t\t[DllImport( \"Cabinet.dll\", SetLastError = true )]\n\t\tstatic extern bool Compress( IntPtr CompressorHandle, [In] byte[] UncompressedData, IntPtr UncompressedDataSize, [Out] byte[] CompressedBuffer, IntPtr CompressedBufferSize, out IntPtr CompressedDataSize );\n\n\t\t/// <summary>Compress an array of bytes into another, smaller array of bytes</summary>\n\t\t/// <remarks>In practice, the compression ratio is about 7.1 for the shader binaries in Release configuration.</remarks>\n\t\tpublic static byte[] compressBuffer( byte[] src )\n\t\t{\n\t\t\tif( src.Length <= 0 )\n\t\t\t\tthrow new ArgumentException( \"The source buffer is empty\" );\n\t\t\tIntPtr hCompressor;\n\t\t\tif( !CreateCompressor( algo, IntPtr.Zero, out hCompressor ) )\n\t\t\t\tthrow new Win32Exception( \"Unable to create the compressor\" );\n\t\t\ttry\n\t\t\t{\n\t\t\t\tbyte[] dest = new byte[ src.Length * 2 ];\n\t\t\t\tIntPtr srcSize = new IntPtr( src.Length );\n\t\t\t\tIntPtr destSize = new IntPtr( src.Length * 2 );\n\t\t\t\tif( !Compress( hCompressor, src, srcSize, dest, destSize, out destSize ) )\n\t\t\t\t\tthrow new Win32Exception( \"Compress failed\" );\n\t\t\t\tArray.Resize( ref dest, (int)destSize );\n\t\t\t\treturn dest;\n\t\t\t}\n\t\t\tfinally\n\t\t\t{\n\t\t\t\tCloseCompressor( hCompressor );\n\t\t\t}\n\t\t}\n\t}\n}"
  },
  {
    "path": "Tools/CompressShaders/CompressShaders.cs",
    "content": "﻿using System.Runtime.CompilerServices;\nnamespace CompressShaders;\n\nrecord struct sShaderBinary\n{\n\tpublic string name;\n\tpublic byte[] data;\n\n\tpublic sShaderBinary( string path )\n\t{\n\t\tname = Path.GetFileNameWithoutExtension( path );\n\t\tdata = File.ReadAllBytes( path );\n\t}\n\n\tpublic bool wave64 => name.EndsWith( \"64\" );\n\tpublic string uniqueName => wave64 ? name.Substring( 0, name.Length - 2 ) : name;\n}\n\nsealed class FoundShaders\n{\n\tpublic readonly sShaderBinary[] binaries;\n\tpublic readonly string[] names;\n\tpublic readonly int[] wave32, wave64;\n\n\tpublic FoundShaders( IEnumerable<sShaderBinary> found )\n\t{\n\t\tbinaries = found\n\t\t\t.OrderBy( b => b.name )\n\t\t\t.ToArray();\n\n\t\tnames = binaries\n\t\t\t.Select( b => b.uniqueName )\n\t\t\t.Distinct()\n\t\t\t.ToArray();\n\n\t\twave32 = new int[ names.Length ];\n\t\twave64 = new int[ names.Length ];\n\t\tfor( int i = 0; i < names.Length; i++ )\n\t\t{\n\t\t\tint i32 = findIndex( names[ i ], false );\n\t\t\tint i64 = findIndex( names[ i ], true );\n\t\t\tif( i32 >= 0 && i64 >= 0 )\n\t\t\t{\n\t\t\t\twave32[ i ] = i32;\n\t\t\t\twave64[ i ] = i64;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif( i32 >= 0 )\n\t\t\t{\n\t\t\t\twave32[ i ] = wave64[ i ] = i32;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tthrow new ApplicationException( $\"Wave64 shader {names[ i ]} doesn't have the corresponding Wave32 one\" );\n\t\t}\n\t}\n\n\tint findIndex( string name, bool wave64 )\n\t{\n\t\tfor( int i = 0; i < binaries.Length; i++ )\n\t\t{\n\t\t\tsShaderBinary sb = binaries[ i ];\n\t\t\tif( sb.uniqueName != name )\n\t\t\t\tcontinue;\n\t\t\tif( sb.wave64 == wave64 )\n\t\t\t\treturn i;\n\t\t}\n\t\treturn -1;\n\t}\n}\n\nclass Program\n{\n\tstatic string getSolutionRoot( [CallerFilePath] string? path = null )\n\t{\n\t\tstring? dir = Path.GetDirectoryName( path );\n\t\tdir = Path.GetDirectoryName( dir );\n\t\tdir = Path.GetDirectoryName( dir );\n\t\treturn dir ?? throw new ApplicationException();\n\t}\n\n#if DEBUG\n\tconst string config = \"Debug\";\n#else\n\tconst string config = \"Release\";\n#endif\n\n\tstatic string shadersBinDir( string root )\n\t{\n\t\treturn Path.Combine( root, \"ComputeShaders\", \"x64\", config );\n\t}\n\n\tstatic IEnumerable<sShaderBinary> readShaders( string root )\n\t{\n\t\tstring dir = shadersBinDir( root );\n\t\tforeach( string path in Directory.EnumerateFiles( dir, \"*.cso\" ) )\n\t\t\tyield return new sShaderBinary( path );\n\t}\n\n\tstatic void writeHeader( string root, IEnumerable<string> names )\n\t{\n\t\tstring path = Path.Combine( root, \"Whisper\", \"D3D\", \"shaderNames.h\" );\n\t\tusing var stream = File.CreateText( path );\n\t\tstream.WriteLine( @\"// This header is generated by a tool\n#pragma once\n#include <stdint.h>\n\nnamespace DirectCompute\n{\n\tenum struct eComputeShader: uint16_t\n\t{\" );\n\n\t\tint id = 0;\n\t\tforeach( string name in names )\n\t\t{\n\t\t\tstream.WriteLine( \"\\t\\t{0} = {1},\", name, id );\n\t\t\tid++;\n\t\t}\n\t\tstream.Write( @\"\t};\n\n\tconst char* computeShaderName( eComputeShader cs );\n}\" );\n\t}\n\n\tstatic void writeCpp( string root, IEnumerable<string> names )\n\t{\n\t\tstring path = Path.Combine( root, \"Whisper\", \"D3D\", \"shaderNames.cpp\" );\n\t\tShaderNames.write( path, names );\n\t}\n\n\tstatic void writePayloadIDs( StreamWriter stream, string varName, int[] ids )\n\t{\n\t\tstream.Write( @\"\nstatic const std::array<uint8_t, {0}> {1} = {{\", ids.Length, varName );\n\n\t\tfor( int i = 0; i < ids.Length; i++ )\n\t\t{\n\t\t\tif( 0 == i % 16 )\n\t\t\t\tstream.Write( \"\\r\\n\\t\" );\n\t\t\telse\n\t\t\t\tstream.Write( ' ' );\n\t\t\tstream.Write( \"{0},\", ids[ i ] );\n\t\t}\n\t\tstream.Write( @\"\n};\" );\n\t}\n\n\tstatic void writePayload( string root, FoundShaders shaders, out int cbSource, out int cbCompressed )\n\t{\n\t\tMemoryStream ms = new MemoryStream();\n\t\tList<int> offsets = new List<int>();\n\t\tforeach( var bin in shaders.binaries )\n\t\t{\n\t\t\toffsets.Add( (int)ms.Length );\n\t\t\tms.Write( bin.data );\n\t\t}\n\t\toffsets.Add( (int)ms.Length );\n\n\t\tbyte[] dxbc = ms.ToArray();\n\t\tbyte[] compressed = LZ4.compressBuffer( dxbc );\n\t\tcbSource = dxbc.Length;\n\t\tcbCompressed = compressed.Length;\n\n\t\tstring path = Path.Combine( root, \"Whisper\", \"D3D\", $\"shaderData-{config}.inl\" );\n\t\tusing var stream = File.CreateText( path );\n\t\tstream.Write( @\"// This source file is generated by a tool\n\n// This array contains concatenated and compressed DXBC binaries for all compiled compute shaders\nstatic const std::array<uint8_t, {0}> s_compressedShaders =\n{{\", compressed.Length );\n\n\t\tfor( int i = 0; i < compressed.Length; i++ )\n\t\t{\n\t\t\tif( 0 == i % 16 )\n\t\t\t\tstream.Write( \"\\r\\n\\t\" );\n\t\t\telse\n\t\t\t\tstream.Write( ' ' );\n\t\t\tstream.Write( \"0x{0:X02},\", compressed[ i ] );\n\t\t}\n\n\t\tstream.Write( @\"\n}};\n\n// This array contains start offsets of shader binaries in the decompressed DXBC blob.\n// It includes one more entry for the end of the complete decompressed blob.\nstatic const std::array<uint32_t, {0}> s_shaderOffsets = {{\", offsets.Count );\n\n\t\tfor( int i = 0; i < offsets.Count; i++ )\n\t\t{\n\t\t\tif( 0 == i % 16 )\n\t\t\t\tstream.Write( \"\\r\\n\\t\" );\n\t\t\telse\n\t\t\t\tstream.Write( ' ' );\n\t\t\tstream.Write( \"{0},\", offsets[ i ] );\n\t\t}\n\t\tstream.Write( @\"\n};\" );\n\n\t\tstream.Write( @\"\n// Index = eComputeShader enum value, value = index of the shader binary to use on nVidia and Intel GPUs\" );\n\t\twritePayloadIDs( stream, \"s_shaderBlobs32\", shaders.wave32 );\n\t\tstream.Write( @\"\n// Index = eComputeShader enum value, value = index of the shader binary to use on AMD GPUs\" );\n\t\twritePayloadIDs( stream, \"s_shaderBlobs64\", shaders.wave64 );\n\n\t\tulong fp64Flags = 0;\n\t\tfor( int i = 0; i < shaders.binaries.Length; i++ )\n\t\t{\n\t\t\tbool fp64 = DetectFp64.usesFp64( shaders.binaries[ i ].data );\n\t\t\tif( fp64 )\n\t\t\t\tfp64Flags |= (ulong)1 << i;\n\t\t}\n\n\t\tstream.Write( @\"\n// Bitmap of the shader binaries which use FP64 arithmetic instructions\nconstexpr uint64_t fp64ShadersBitmap = 0x{0:X}ull;\", fp64Flags );\n\t}\n\n\tstatic void mainImpl()\n\t{\n\t\tstring root = getSolutionRoot();\n\t\tLanguageCodes.produce( root );\n\n\t\tFoundShaders shaders = new FoundShaders( readShaders( root ) );\n\n\t\twriteHeader( root, shaders.names );\n\t\twriteCpp( root, shaders.names );\n\t\twritePayload( root, shaders, out int cbIn, out int cbOut );\n\t\tConsole.WriteLine( \"Compressed {0} compute shaders, {1:F1} kb -> {2:F1} kb\", shaders.binaries.Length, cbIn / 1024.0, cbOut / 1024.0 );\n\t}\n\n\tstatic int Main( string[] args )\n\t{\n\t\ttry\n\t\t{\n\t\t\tmainImpl();\n\t\t\treturn 0;\n\t\t}\n\t\tcatch( Exception ex )\n\t\t{\n\t\t\tConsole.WriteLine( ex.Message );\n\t\t\treturn ex.HResult;\n\t\t}\n\t}\n}"
  },
  {
    "path": "Tools/CompressShaders/CompressShaders.csproj",
    "content": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\t<PropertyGroup>\n\t\t<OutputType>Exe</OutputType>\n\t\t<TargetFramework>net6.0</TargetFramework>\n\t\t<ImplicitUsings>enable</ImplicitUsings>\n\t\t<Nullable>enable</Nullable>\n\t\t<CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>\n\t\t<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>\n\t</PropertyGroup>\n\t<ItemGroup>\n\t  <PackageReference Include=\"K4os.Compression.LZ4\" Version=\"1.3.5\" />\n\t</ItemGroup>\n</Project>"
  },
  {
    "path": "Tools/CompressShaders/DetectFp64.cs",
    "content": "﻿#pragma warning disable CS0649\nusing System.Runtime.InteropServices;\n\nnamespace CompressShaders\n{\n\tstatic class DetectFp64\n\t{\n\t\tstruct DXBCHeader\n\t\t{\n\t\t\tpublic uint FourCC; // Four character code \"DXBC\"\n\t\t\tpublic uint Hash0;            // 32-bit hash of the DXBC file\n\t\t\tpublic uint Hash1;            // 32-bit hash of the DXBC file\n\t\t\tpublic uint Hash2;            // 32-bit hash of the DXBC file\n\t\t\tpublic uint Hash3;            // 32-bit hash of the DXBC file\n\t\t\tpublic uint unknownOne;\n\t\t\tpublic uint TotalSize;        // Total size of the DXBC file in bytes\n\t\t\tpublic int NumChunks;        // Number of chunks in the DXBC file\n\t\t};\n\n\t\tpublic static bool usesFp64( ReadOnlySpan<byte> dxbc )\n\t\t{\n\t\t\tReadOnlySpan<DXBCHeader> dxbcHeaderSpan = MemoryMarshal.Cast<byte, DXBCHeader>( dxbc );\n\t\t\tDXBCHeader dxbcHeader = dxbcHeaderSpan[ 0 ];\n\n\t\t\tint cbHeader = Marshal.SizeOf<DXBCHeader>();\n\t\t\tint nChunks = dxbcHeader.NumChunks;\n\t\t\tReadOnlySpan<int> chunkOffsets = MemoryMarshal.Cast<byte, int>( dxbc.Slice( cbHeader, nChunks * 4 ) );\n\t\t\tforeach( int off in chunkOffsets )\n\t\t\t{\n\t\t\t\tuint id = MemoryMarshal.Cast<byte, uint>( dxbc.Slice( off, 4 ) )[ 0 ];\n\t\t\t\tconst uint SFI0 = 0x30494653;\n\t\t\t\tif( id != SFI0 )\n\t\t\t\t\tcontinue;\n\t\t\t\tint size = MemoryMarshal.Cast<byte, int>( dxbc.Slice( off + 4, 4 ) )[ 0 ];\n\t\t\t\tif( size < 4 )\n\t\t\t\t\tthrow new ApplicationException();\n\t\t\t\tuint data = MemoryMarshal.Cast<byte, uint>( dxbc.Slice( off + 8, 4 ) )[ 0 ];\n\t\t\t\treturn 0 != ( data & 1u );\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t}\n}"
  },
  {
    "path": "Tools/CompressShaders/LZ4.cs",
    "content": "﻿namespace CompressShaders;\nusing K4os.Compression.LZ4;\n\n/// <summary>Lossless data compressor which uses LZ4-HC compressor</summary>\n/// <seealso href=\"https://github.com/lz4/lz4\" />\n/// <seealso href=\"https://github.com/MiloszKrajewski/K4os.Compression.LZ4\" />\nstatic class LZ4\n{\n\t// compression speed drops rapidly when not using FAST mode, while decompression speed stays the same\n\t// Actually, it is usually faster for high compression levels as there is less data to process\n\t// https://github.com/MiloszKrajewski/K4os.Compression.LZ4#compression-levels\n\tconst LZ4Level compressionLevel = LZ4Level.L12_MAX;\n\n\tpublic static byte[] compressBuffer( byte[] src )\n\t{\n\t\tint maxLength = LZ4Codec.MaximumOutputSize( src.Length );\n\t\tbyte[] output = new byte[ maxLength ];\n\t\tint cb = LZ4Codec.Encode( src, output, compressionLevel );\n\t\tif( cb > 0 )\n\t\t{\n\t\t\tArray.Resize( ref output, cb );\n\t\t\treturn output;\n\t\t}\n\t\tthrow new ApplicationException( $\"LZ4Codec.Encode failed with status {cb}\" );\n\t}\n}"
  },
  {
    "path": "Tools/CompressShaders/LanguageCodes.cs",
    "content": "﻿using System.Globalization;\nusing System.Text.RegularExpressions;\n\nnamespace CompressShaders\n{\n\tstatic class LanguageCodes\n\t{\n\t\trecord struct Row\n\t\t{\n\t\t\tpublic string keySource;\n\t\t\tpublic uint keyValue;\n\t\t\tpublic int code;\n\t\t\tpublic string name;\n\t\t}\n\n\t\tstatic uint makeKey( string str )\n\t\t{\n\t\t\tif( str.Length > 4 )\n\t\t\t\tthrow new ArgumentException();\n\t\t\tuint k = 0;\n\t\t\tint shift = 0;\n\t\t\tforeach( char c in str )\n\t\t\t{\n\t\t\t\tif( c >= 0x80 )\n\t\t\t\t\tthrow new ArgumentException();\n\t\t\t\tuint u = (uint)c;\n\t\t\t\tk |= ( u << shift );\n\t\t\t\tshift += 8;\n\t\t\t}\n\t\t\treturn k;\n\t\t}\n\n\t\tstatic IEnumerable<Row> load( string path )\n\t\t{\n\t\t\tusing var stm = File.OpenText( path );\n\t\t\twhile( true )\n\t\t\t{\n\t\t\t\tstring? line = stm.ReadLine();\n\t\t\t\tif( null == line )\n\t\t\t\t\tbreak;\n\t\t\t\tif( string.IsNullOrWhiteSpace( line ) )\n\t\t\t\t\tcontinue;\n\t\t\t\tstring[] fields = line.Split( '\\t' );\n\t\t\t\tyield return new Row()\n\t\t\t\t{\n\t\t\t\t\tkeySource = fields[ 0 ],\n\t\t\t\t\tkeyValue = makeKey( fields[ 0 ] ),\n\t\t\t\t\tcode = int.Parse( fields[ 1 ] ),\n\t\t\t\t\tname = fields[ 2 ]\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\tstatic void writeCpp( string inl, Row[] data )\n\t\t{\n\t\t\t// TODO [very low]: sort them by the key here, then in C++ use binary search instead of the hash map\n\t\t\tusing var stm = File.CreateText( inl );\n\t\t\tstm.WriteLine( \"// This file is generated by a tool, from the `languageCodez.tsv` file in this repository\" );\n\t\t\tforeach( Row row in data )\n\t\t\t\tstm.WriteLine( \"Lang{{ 0x{0:X}, {1}, \\\"{2}\\\" }},\", row.keyValue, row.code, row.name );\n\t\t}\n\n\t\tstatic readonly CultureInfo ci = new CultureInfo( \"en-US\", false );\n\t\tstatic string titleCase( this string name ) =>\n\t\t\tci.TextInfo.ToTitleCase( name.ToLower( ci ) );\n\n\t\tstatic void writeCs( string cs, Row[] data )\n\t\t{\n\t\t\tusing var stm = File.CreateText( cs );\n\t\t\tstm.WriteLine( @\"// This file is generated by a tool, from the `languageCodez.tsv` file in this repository\nnamespace Whisper\n{\n\t/// <summary>Supported languages</summary>\n\t/// <remarks>The values of this enum are zero-padded ASCII strings.<br/>\n\t/// It seems OpenAI tried to implement ISO 639-1, but they used the version of the standard from 1988.</remarks>\n\tpublic enum eLanguage: uint\n\t{\" );\n\n\t\t\tforeach( Row row in data )\n\t\t\t{\n\t\t\t\tstring tc = row.name.titleCase();\n\t\t\t\tstm.WriteLine( \"\t\t/// <summary>{0}</summary>\", tc );\n\t\t\t\ttc = Regex.Replace( tc, @\"\\s+\", string.Empty );\n\t\t\t\tstm.WriteLine( \"\t\t{0} = 0x{1:X},  // \\\"{2}\\\"\", tc, row.keyValue, row.keySource );\n\t\t\t}\n\t\t\tstm.Write( @\"\t}\n}\" );\n\t\t}\n\n\t\tstatic void produce( string tsv, string inl, string cs )\n\t\t{\n\t\t\tRow[] data = load( tsv ).OrderBy( r => r.name ).ToArray();\n\t\t\twriteCpp( inl, data );\n\t\t\twriteCs( cs, data );\n\t\t}\n\n\t\tpublic static void produce( string solutionRoot )\n\t\t{\n\t\t\tstring tsv = Path.Combine( solutionRoot, \"Whisper\\\\Whisper\\\\languageCodez.tsv\" );\n\t\t\tstring inl = Path.Combine( solutionRoot, \"Whisper\\\\Whisper\\\\languageCodez.inl\" );\n\t\t\tstring cs = Path.Combine( solutionRoot, \"WhisperNet\\\\API\\\\eLanguage.cs\" );\n\t\t\tproduce( tsv, inl, cs );\n\t\t}\n\t}\n}"
  },
  {
    "path": "Tools/CompressShaders/Readme.txt",
    "content": "﻿This project builds a C# console app which serves as a code generator for a few pieces of Whisper.dll and WhisperNet.dll.\n\nSpecifically, it generates two things.\n\n1. It compresses the compiled DXBC shaders into a blob of bytes, and prints std::array with these bytes into shaderData-Release.inl and shaderData-Debug.inl C++ files.\n\n2. It parses the `languageCodez.tsv`, and generates both C++ and C# code with the data from that table.\n\nThe tool uses relative paths across source files.\nThese paths will break if you move the source of the tool, or the source data of the tool."
  },
  {
    "path": "Tools/CompressShaders/ShaderNames.cs",
    "content": "﻿static class ShaderNames\n{\n\tpublic static void write( string path, IEnumerable<string> names )\n\t{\n\t\tstring[] arr = names.ToArray();\n\t\tusing var stream = File.CreateText( path );\n\t\tstream.WriteLine( @\"// This source file is generated by a tool\n#include \"\"stdafx.h\"\"\n#include \"\"shaderNames.h\"\"\n\" );\n\n\t\tstream.WriteLine( \"static const std::array<const char*, {0}> s_shaderNames = \", arr.Length );\n\t\tstream.WriteLine( \"{\" );\n\t\tforeach( string name in arr )\n\t\t\tstream.WriteLine( @\"\t\"\"{0}\"\",\", name );\n\n\t\tstream.Write( @\"};\n\nconst char* DirectCompute::computeShaderName( eComputeShader cs )\n{\n\tconst uint16_t i = (uint16_t)cs;\n\tif( i < s_shaderNames.size() )\n\t\treturn s_shaderNames[ i ];\n\treturn nullptr;\n}\" );\n\t}\n}"
  },
  {
    "path": "Tools/CompressTables/CompressTables.cs",
    "content": "﻿namespace CompressTables;\nusing CompressShaders;\nusing System.IO;\nusing System.Runtime.CompilerServices;\n\n/// <summary>Utility app to compress lookup tables data with LZ4-HC, and generate C++ source with the data</summary>\ninternal class Program\n{\n\tstatic string getSolutionRoot( [CallerFilePath] string? path = null )\n\t{\n\t\tstring? dir = Path.GetDirectoryName( path );\n\t\tdir = Path.GetDirectoryName( dir );\n\t\tdir = Path.GetDirectoryName( dir );\n\t\treturn dir ?? throw new ApplicationException();\n\t}\n\n\tstatic void writeArray( byte[] compressed, string path )\n\t{\n\t\tusing var stream = File.CreateText( path );\n\t\tstream.WriteLine( \"// This source file is generated by a tool\" );\n\t\tstream.Write( @\"static const std::array<uint8_t, {0}> s_tableData = {{\", compressed.Length );\n\n\t\tfor( int i = 0; i < compressed.Length; i++ )\n\t\t{\n\t\t\tif( 0 == i % 32 )\n\t\t\t\tstream.Write( \"\\r\\n\\t\" );\n\t\t\telse\n\t\t\t\tstream.Write( ' ' );\n\t\t\tstream.Write( \"0x{0:X02},\", compressed[ i ] );\n\t\t}\n\t\tstream.Write( @\"\n};\" );\n\t}\n\n\tstatic void Main( string[] args )\n\t{\n\t\tbyte[] source = File.ReadAllBytes( @\"C:\\Temp\\2remove\\Whisper\\tables.bin\" );\n\t\tbyte[] result = LZ4.compressBuffer( source );\n\n\t\tstring root = getSolutionRoot();\n\t\tstring path = Path.Combine( root, \"Whisper\", \"ML\", \"LookupTablesData.inl\" );\n\t\twriteArray( result, path );\n\t}\n}"
  },
  {
    "path": "Tools/CompressTables/CompressTables.csproj",
    "content": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\t<PropertyGroup>\n\t\t<OutputType>Exe</OutputType>\n\t\t<TargetFramework>net7.0</TargetFramework>\n\t\t<ImplicitUsings>enable</ImplicitUsings>\n\t\t<Nullable>enable</Nullable>\n\t\t<CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>\n\t\t<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>\n\t</PropertyGroup>\n\t<ItemGroup>\n\t  <Compile Include=\"..\\CompressShaders\\LZ4.cs\" Link=\"LZ4.cs\" />\n\t</ItemGroup>\n\t<ItemGroup>\n\t  <PackageReference Include=\"K4os.Compression.LZ4\" Version=\"1.3.5\" />\n\t</ItemGroup>\n</Project>"
  },
  {
    "path": "Tools/PerfSummary/LogParser.cs",
    "content": "﻿using System.Globalization;\nusing System.Text.RegularExpressions;\n\nnamespace PerfSummary\n{\n\tenum eInputClip: byte\n\t{\n\t\tjfk,\n\t\tcolumbia,\n\t}\n\tenum eWhisperModel: byte\n\t{\n\t\tmedium,\n\t\tlarge\n\t}\n\n\tstruct LogName\n\t{\n\t\tpublic readonly eInputClip clip;\n\t\tpublic readonly eWhisperModel model;\n\t\tpublic readonly string gpu;\n\n\t\tpublic override string ToString() => $\"{clip}-{model}-{gpu}\";\n\n\t\tpublic static LogName? tryParse( string path )\n\t\t{\n\t\t\tstring? ext = Path.GetExtension( path );\n\t\t\tif( ext == null || !ext.Equals( \".txt\", StringComparison.InvariantCultureIgnoreCase ) )\n\t\t\t\treturn null;\n\n\t\t\tstring name = Path.GetFileNameWithoutExtension( path );\n\t\t\tstring[] fields = name.Split( '-' );\n\t\t\tif( fields.Length != 3 )\n\t\t\t\treturn null;\n\n\t\t\treturn new LogName( fields );\n\t\t}\n\n\t\tLogName( string[] fields )\n\t\t{\n\t\t\tclip = Enum.Parse<eInputClip>( fields[ 0 ] );\n\t\t\tmodel = Enum.Parse<eWhisperModel>( fields[ 1 ] );\n\t\t\tgpu = fields[ 2 ];\n\t\t}\n\t}\n\n\trecord class LogData\n\t{\n\t\tpublic LogName name { get; init; }\n\t\t// The numbers are seconds\n\t\tpublic double runComplete { get; init; }\n\t\tpublic double encode { get; init; }\n\t\tpublic double decode { get; init; }\n\t\t// The numbers are megabytes\n\t\tpublic double ram { get; init; }\n\t\tpublic double vram { get; init; }\n\t}\n\n\tstatic class LogParser\n\t{\n\t\tpublic static IEnumerable<LogData> parse( string folder )\n\t\t{\n\t\t\tforeach( string path in Directory.EnumerateFiles( folder, \"*.txt\" ) )\n\t\t\t{\n\t\t\t\tLogName? name = LogName.tryParse( path );\n\t\t\t\tif( name == null )\n\t\t\t\t\tcontinue;\n\t\t\t\tyield return parseFile( name.Value, path );\n\t\t\t}\n\t\t}\n\n\t\tenum eSection: byte\n\t\t{\n\t\t\tCPU, GPU, Shaders, Memory\n\t\t}\n\t\tstatic readonly (string, eSection)[] sectionMarkers = new (string, eSection)[]\n\t\t{\n\t\t\t(\"CPU Tasks\", eSection.CPU),\n\t\t\t(\"GPU Tasks\", eSection.GPU),\n\t\t\t(\"Compute Shaders\", eSection.Shaders),\n\t\t\t(\"Memory Usage\", eSection.Memory),\n\t\t};\n\n\t\tstatic bool tryParseSection( ref eSection? section, string line )\n\t\t{\n\t\t\tforeach( (string marker, eSection s) in sectionMarkers )\n\t\t\t{\n\t\t\t\tif( line.Contains( marker ) )\n\t\t\t\t{\n\t\t\t\t\tsection = s;\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\tstatic bool tryParseTime( ref double? val, string key, string line )\n\t\t{\n\t\t\tif( !line.StartsWith( key ) )\n\t\t\t\treturn false;\n\t\t\tif( !char.IsWhiteSpace( line[ key.Length ] ) )\n\t\t\t\treturn false;\n\n\t\t\tline = line.Substring( key.Length ).TrimStart();\n\t\t\tint comma = line.IndexOf( ',' );\n\t\t\tif( comma > 0 )\n\t\t\t\tline = line.Substring( 0, comma );\n\t\t\tstring[] fields = line.Split( ' ', StringSplitOptions.RemoveEmptyEntries );\n\t\t\tif( fields.Length != 2 )\n\t\t\t\tthrow new ArgumentException();\n\n\t\t\tdouble v = double.Parse( fields[ 0 ], CultureInfo.InvariantCulture );\n\t\t\tswitch( fields[ 1 ] )\n\t\t\t{\n\t\t\t\tcase \"seconds\":\n\t\t\t\t\tval = v;\n\t\t\t\t\treturn true;\n\t\t\t\tcase \"milliseconds\":\n\t\t\t\t\tval = v / 1E3;\n\t\t\t\t\treturn true;\n\t\t\t\tcase \"microseconds\":\n\t\t\t\t\tval = v / 1E6;\n\t\t\t\t\treturn true;\n\t\t\t}\n\t\t\tthrow new ArgumentException();\n\t\t}\n\n\t\tstatic readonly Regex reMemory = new Regex( @\"^Total\\s+([0-9\\.]+)\\s+(\\S+)\\s+RAM, ([0-9\\.]+)\\s+(\\S+)\\s+VRAM$\" );\n\n\t\tstatic double parseMemory( Match m, int iv )\n\t\t{\n\t\t\tdouble v = double.Parse( m.Groups[ iv ].Value, CultureInfo.InvariantCulture );\n\t\t\tstring u = m.Groups[ iv + 1 ].Value;\n\t\t\tswitch( u )\n\t\t\t{\n\t\t\t\tcase \"bytes\":\n\t\t\t\t\treturn v / ( 1 << 20 );\n\t\t\t\tcase \"KB\":\n\t\t\t\t\treturn v / ( 1 << 10 );\n\t\t\t\tcase \"MB\":\n\t\t\t\t\treturn v;\n\t\t\t\tcase \"GB\":\n\t\t\t\t\treturn v * ( 1 << 10 );\n\t\t\t}\n\t\t\tthrow new ArgumentException();\n\t\t}\n\n\t\tstatic bool tryParseMemory( ref double? ram, ref double? vram, string line )\n\t\t{\n\t\t\tMatch m = reMemory.Match( line );\n\t\t\tif( !m.Success )\n\t\t\t\treturn false;\n\t\t\tram = parseMemory( m, 1 );\n\t\t\tvram = parseMemory( m, 3 );\n\t\t\treturn true;\n\t\t}\n\n\t\tstatic LogData parseFile( in LogName name, string path )\n\t\t{\n\t\t\tusing var reader = File.OpenText( path );\n\t\t\tdouble? runComplete = null;\n\t\t\tdouble? encode = null;\n\t\t\tdouble? decode = null;\n\t\t\tdouble? ram = null;\n\t\t\tdouble? vram = null;\n\t\t\teSection? section = null;\n\n\t\t\twhile( true )\n\t\t\t{\n\t\t\t\tstring? line = reader.ReadLine();\n\t\t\t\tif( line == null )\n\t\t\t\t\tbreak;\n\t\t\t\tif( string.IsNullOrEmpty( line ) )\n\t\t\t\t\tcontinue;\n\t\t\t\tif( tryParseSection( ref section, line ) )\n\t\t\t\t\tcontinue;\n\n\t\t\t\tswitch( section )\n\t\t\t\t{\n\t\t\t\t\tcase eSection.CPU:\n\t\t\t\t\t\ttryParseTime( ref runComplete, \"RunComplete\", line );\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase eSection.GPU:\n\t\t\t\t\t\ttryParseTime( ref encode, \"Encode\", line );\n\t\t\t\t\t\ttryParseTime( ref decode, \"Decode\", line );\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase eSection.Memory:\n\t\t\t\t\t\ttryParseMemory( ref ram, ref vram, line );\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif( null == runComplete || null == encode || null == decode || null == ram || null == vram )\n\t\t\t\tthrow new ArgumentException();\n\n\t\t\treturn new LogData()\n\t\t\t{\n\t\t\t\tname = name,\n\t\t\t\trunComplete = runComplete.Value,\n\t\t\t\tencode = encode.Value,\n\t\t\t\tdecode = decode.Value,\n\t\t\t\tram = ram.Value,\n\t\t\t\tvram = vram.Value,\n\t\t\t};\n\t\t}\n\t}\n}"
  },
  {
    "path": "Tools/PerfSummary/PerfSummary.cs",
    "content": "﻿using System.Runtime.CompilerServices;\n\nnamespace PerfSummary\n{\n\tinternal class Program\n\t{\n\t\tstatic string getSolutionRoot( [CallerFilePath] string? path = null )\n\t\t{\n\t\t\tstring? dir = Path.GetDirectoryName( path );\n\t\t\tdir = Path.GetDirectoryName( dir );\n\t\t\tdir = Path.GetDirectoryName( dir );\n\t\t\treturn dir ?? throw new ApplicationException();\n\t\t}\n\n\t\tstatic void Main( string[] args )\n\t\t{\n\t\t\tstring root = getSolutionRoot();\n\t\t\troot = Path.Combine( root, \"SampleClips\" );\n\n\t\t\tLogData[] logs = LogParser.parse( root )\n\t\t\t\t.OrderBy( x => x.name.clip )\n\t\t\t\t.ThenBy( x => x.name.model )\n\t\t\t\t.ThenBy( x => x.name.gpu )\n\t\t\t\t.ToArray();\n\n\t\t\tSummary.print( logs, root );\n\t\t}\n\t}\n}"
  },
  {
    "path": "Tools/PerfSummary/PerfSummary.csproj",
    "content": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\t<PropertyGroup>\n\t\t<OutputType>Exe</OutputType>\n\t\t<TargetFramework>net6.0</TargetFramework>\n\t\t<ImplicitUsings>enable</ImplicitUsings>\n\t\t<Nullable>enable</Nullable>\n\t\t<CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>\n\t\t<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>\n\t</PropertyGroup>\n</Project>"
  },
  {
    "path": "Tools/PerfSummary/Summary.cs",
    "content": "﻿using System.Globalization;\n\nnamespace PerfSummary\n{\n\tstatic class Summary\n\t{\n\t\tpublic static void print( LogData[] logs, string folder )\n\t\t{\n\t\t\tstring path = Path.Combine( folder, \"summary.tsv\" );\n\t\t\tusing var writer = File.CreateText( path );\n\n\t\t\tstring[] header = new string[]\n\t\t\t{\n\t\t\t\t\"Audio Clip\", \"Model\", \"GPU\",\n\t\t\t\t\"Total, sec\", \"Relative speed\",\n\t\t\t\t\"Encode, sec\", \"Decode, sec\",\n\t\t\t\t\"RAM, MB\", \"VRAM, MB\"\n\t\t\t};\n\t\t\twriter.fields( header );\n\n\t\t\tforeach( var item in logs )\n\t\t\t\twriter.fields( item.makeFields() );\n\t\t}\n\n\t\tstatic IEnumerable<string> makeFields( this LogData log )\n\t\t{\n\t\t\tyield return log.clip();\n\t\t\tyield return log.name.model.ToString();\n\t\t\tyield return log.gpu();\n\t\t\tyield return log.runComplete.print();\n\t\t\tyield return log.relative();\n\t\t\tyield return log.encode.print();\n\t\t\tyield return log.decode.print();\n\t\t\tyield return log.ram.print();\n\t\t\tyield return log.vram.print();\n\t\t}\n\n\t\tstatic string print( this double v ) =>\n\t\t\tv.ToString( CultureInfo.InvariantCulture );\n\n\t\tstatic string relative( this LogData log )\n\t\t{\n\t\t\tdouble duration;\n\t\t\tswitch( log.name.clip )\n\t\t\t{\n\t\t\t\tcase eInputClip.jfk:\n\t\t\t\t\tduration = 11;\n\t\t\t\t\tbreak;\n\t\t\t\tcase eInputClip.columbia:\n\t\t\t\t\tduration = TimeSpan.FromTicks( 1987620000 ).TotalSeconds;\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tthrow new NotImplementedException();\n\t\t\t}\n\n\t\t\tdouble rel = duration / log.runComplete;\n\t\t\treturn rel.ToString( \"F4\", CultureInfo.InvariantCulture );\n\t\t}\n\n\t\tstatic string clip( this LogData log )\n\t\t{\n\t\t\treturn log.name.clip switch\n\t\t\t{\n\t\t\t\teInputClip.jfk => \"jfk.wav\",\n\t\t\t\teInputClip.columbia => \"columbia.wma\",\n\t\t\t\t _ => throw new ArgumentException()\n\t\t\t};\n\t\t}\n\n\t\tstatic string gpu( this LogData log )\n\t\t{\n\t\t\treturn log.name.gpu switch\n\t\t\t{\n\t\t\t\t\"1080ti\" => \"GeForce 1080Ti\",\n\t\t\t\t\"1650\" => \"GeForce 1650\",\n\t\t\t\t\"vega7\" => \"Ryzen 5 5600U\",\n\t\t\t\t\"vega8\" => \"Ryzen 7 5700G\",\n\t\t\t\t_ => log.name.gpu\n\t\t\t};\n\t\t}\n\n\t\tstatic void fields( this StreamWriter writer, IEnumerable<string> fields )\n\t\t{\n\t\t\tstring line = string.Join( \"\\t\", fields );\n\t\t\twriter.WriteLine( line );\n\t\t}\n\t}\n}"
  },
  {
    "path": "Tools/compareTraces/CommandLineArgs.cpp",
    "content": "#include \"stdafx.h\"\n#include \"CommandLineArgs.h\"\n#include <charconv>\n\nstatic bool printUsage()\n{\n\tfprintf( stderr, \"Usage: compareTraces.exe trace1.bin trace2.bin [-diff N]\\n\" );\n\treturn false;\n}\n\nbool CommandLineArgs::parse( int argc, wchar_t* argv[] )\n{\n\tsize_t idx = 0;\n\tCString sw;\n\tCStringA tmp;\n\tfor( int i = 1; i < argc; i++ )\n\t{\n\t\tif( argv[ i ][ 0 ] != L'-' )\n\t\t{\n\t\t\tif( idx >= 2 )\n\t\t\t\treturn printUsage();\n\t\t\tinputs[ idx ] = argv[ i ];\n\t\t\tidx++;\n\t\t\tcontinue;\n\t\t}\n\t\tsw = argv[ i ];\n\t\tif( 0 == sw.CompareNoCase( L\"-diff\" ) )\n\t\t{\n\t\t\ti++;\n\t\t\tif( i >= argc )\n\t\t\t\treturn printUsage();\n\t\t\ttmp.Format( \"%S\", argv[ i ] );\n\t\t\ttmp.Trim();\n\t\t\tuint64_t v;\n\t\t\tauto res = std::from_chars( tmp, cstr( tmp ) + tmp.GetLength(), v );\n\t\t\tif( res.ec != (std::errc)0 )\n\t\t\t{\n\t\t\t\tfprintf( stderr, \"Unable to parse string into number\\n\" );\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tprintDiff = v;\n\t\t\tcontinue;\n\t\t}\n\t\treturn printUsage();\n\t}\n\n\tif( idx != 2 )\n\t\treturn printUsage();\n\n\treturn true;\n}"
  },
  {
    "path": "Tools/compareTraces/CommandLineArgs.h",
    "content": "#pragma once\n\nstruct CommandLineArgs\n{\n\tint64_t printDiff = -1;\n\tstd::array<CString, 2> inputs;\n\n\tbool parse( int argc, wchar_t* argv[] );\n};"
  },
  {
    "path": "Tools/compareTraces/Readme.txt",
    "content": "﻿This project builds a C++ console tool which compares debug traces of the model.\n\nTracing files easily exceed 1GB, and by default they’re disabled with a preprocessor macro in stdafx.h of the Whisper project.\n\nWhen enabled, the main GPU implementation saves a trace into C:\\Temp\\2remove\\Whisper\\gpu.bin\n\nThe reference CPU implementation saves a trace into C:\\Temp\\2remove\\Whisper\\ref.bin\n\nThis code in this project is optimized for development speed. For this reason it requires AVX2 CPU, uses memory-mapped IO instead of proper parsing, and checks little to no errors."
  },
  {
    "path": "Tools/compareTraces/TraceReader.cpp",
    "content": "#include \"stdafx.h\"\n#include \"TraceReader.h\"\nusing namespace Tracing;\n\nconst sTraceItem& TraceReader::operator[]( size_t idx ) const\n{\n\tif( idx >= countItems )\n\t\tthrow E_BOUNDS;\n\treturn items[ idx ];\n}\n\nCStringA TraceReader::getName( const sTraceItem& item ) const\n{\n\tconst size_t idx = item.stringIndex;\n\tif( idx >= countStrings )\n\t\tthrow E_BOUNDS;\n\tconst char* const source = stringData + stringIndex[ idx ];\n\tCStringA res;\n\tres.Format( source, item.formatArgs[ 0 ], item.formatArgs[ 1 ], item.formatArgs[ 2 ], item.formatArgs[ 3 ] );\n\treturn res;\n}\n\nHRESULT TraceReader::open( LPCTSTR path )\n{\n\tCHECK( file.Create( path, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING ) );\n\tCHECK( mapping.MapFile( file ) );\n\n\tconst uint8_t* rsi = mapping;\n\tconst sFileHeader& header = *(const sFileHeader*)rsi;\n\tif( header.magic != header.correctMagic )\n\t\treturn E_INVALIDARG;\n\tcountItems = header.countItems;\n\tcountStrings = header.countStrings;\n\n\trsi += sizeof( sFileHeader );\n\tpayloadPointer = rsi;\n\n\trsi += header.bytesPayload;\n\tstringIndex = (const uint32_t*)( rsi );\n\tstringData = (const char*)( rsi + countStrings * 4 );\n\n\trsi += header.bytesStrings;\n\titems = (const sTraceItem*)rsi;\n\n\treturn S_OK;\n}"
  },
  {
    "path": "Tools/compareTraces/TraceReader.h",
    "content": "#pragma once\n#include \"../../Whisper/Utils/Trace/TraceStructures.h\"\n#include <atlstr.h>\n#include <atlfile.h>\n\nnamespace Tracing\n{\n\tclass TraceReader\n\t{\n\t\tconst uint8_t* payloadPointer = nullptr;\n\t\tconst sTraceItem* items = nullptr;\n\t\tsize_t countItems = 0;\n\t\tsize_t countStrings = 0;\n\t\tconst uint32_t* stringIndex = nullptr;\n\t\tconst char* stringData = nullptr;\n\n\t\tCAtlFile file;\n\t\tCAtlFileMapping<uint8_t> mapping;\n\n\tpublic:\n\n\t\tTraceReader() = default;\n\t\t~TraceReader() = default;\n\n\t\tHRESULT open( LPCTSTR path );\n\t\tsize_t size() const { return countItems; }\n\t\tconst sTraceItem& operator[]( size_t idx ) const;\n\t\tCStringA getName( const sTraceItem& item ) const;\n\n\t\tconst void* payload( const sTraceItem& item ) const\n\t\t{\n\t\t\treturn payloadPointer + item.payloadOffset;\n\t\t}\n\t};\n}"
  },
  {
    "path": "Tools/compareTraces/compare.cpp",
    "content": "#include \"stdafx.h\"\n#include \"../../Whisper/API/iContext.cl.h\"\n#include \"TraceReader.h\"\n#include \"../../Whisper/ML/testUtils.h\"\n#include \"compare.h\"\nusing namespace Tracing;\nusing namespace DirectCompute;\n\nnamespace\n{\n\tinline const char* cstr( eItemType it )\n\t{\n\t\tswitch( it )\n\t\t{\n\t\tcase eItemType::Buffer: return \"Buffer\";\n\t\tcase eItemType::Tensor: return \"Tensor\";\n\t\t}\n\t\tthrow E_INVALIDARG;\n\t}\n\tinline const char* cstr( const CStringA& s ) { return s; }\n\n\tinline int tensorDims( __m128i vec )\n\t{\n\t\tconst __m128i one = _mm_set1_epi32( 1 );\n\t\tconst uint32_t bitmapOnes = (uint32_t)_mm_movemask_ps( _mm_castsi128_ps( _mm_cmpeq_epi32( vec, one ) ) );\n\t\tconst uint32_t bitmapNotOnes = bitmapOnes ^ 0b1111u;\n\t\tunsigned long idx;\n\t\tif( !_BitScanReverse( &idx, bitmapNotOnes ) )\n\t\t\treturn 0;\n\t\treturn idx + 1;\n\t}\n\n\tint printSize( __m128i vec )\n\t{\n\t\tconst int sz = tensorDims( vec );\n\t\tswitch( sz )\n\t\t{\n\t\tcase 0:\n\t\t\tprintf( \"[ scalar ]\" );\n\t\t\tbreak;\n\t\tcase 1:\n\t\t\tprintf( \"[ %i ]\", _mm_cvtsi128_si32( vec ) );\n\t\t\tbreak;\n\t\tcase 2:\n\t\t\tprintf( \"[ %i, %i ]\", _mm_cvtsi128_si32( vec ), _mm_extract_epi32( vec, 1 ) );\n\t\t\tbreak;\n\t\tcase 3:\n\t\t\tprintf( \"[ %i, %i, %i ]\", _mm_cvtsi128_si32( vec ), _mm_extract_epi32( vec, 1 ), _mm_extract_epi32( vec, 2 ) );\n\t\t\tbreak;\n\t\tcase 4:\n\t\t\tprintf( \"[ %i, %i, %i, %i ]\", _mm_cvtsi128_si32( vec ), _mm_extract_epi32( vec, 1 ), _mm_extract_epi32( vec, 2 ), _mm_extract_epi32( vec, 3 ) );\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tthrow E_UNEXPECTED;\n\t\t}\n\t\treturn sz;\n\t}\n\n\tclass Comparer\n\t{\n\t\tTraceReader& readerA;\n\t\tTraceReader& readerB;\n\n\t\tbool diffBuffers( size_t i, const sTraceItem& a, const sTraceItem& b, const CStringA& name )\n\t\t{\n\t\t\tconst size_t lenA = *(const uint64_t*)a.size.data();\n\t\t\tconst size_t lenB = *(const uint64_t*)b.size.data();\n\t\t\tif( lenA != lenB )\n\t\t\t{\n\t\t\t\tprintf( \"Buffer %zu \\\"%s\\\": different size, %zu in trace A, %zu in trace B\\n\", i, cstr( name ), lenA, lenB );\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif( a.dataType != b.dataType )\n\t\t\t{\n\t\t\t\tprintf( \"Buffer %zu \\\"%s\\\": different data types\\n\", i, cstr( name ) );\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tswitch( a.dataType )\n\t\t\t{\n\t\t\tcase eDataType::FP32:\n\t\t\t\treturn buffersFp32( i, name, (const float*)readerA.payload( a ), (const float*)readerB.payload( b ), lenA );\n\t\t\t}\n\t\t\tthrow E_NOTIMPL;\n\t\t}\n\n\t\tbool diffTensors( size_t i, const sTraceItem& a, const sTraceItem& b, const CStringA& name )\n\t\t{\n\t\t\tconst __m128i ne1 = load( a.size );\n\t\t\tconst __m128i ne2 = load( b.size );\n\t\t\tif( !vectorEqual( ne1, ne2 ) )\n\t\t\t{\n\t\t\t\tprintf( \"Tensor %zu \\\"%s\\\" - different size: trace A size is \", i, cstr( name ) );\n\t\t\t\tprintSize( ne1 );\n\t\t\t\tprintf( \", trace B size is \" );\n\t\t\t\tprintSize( ne2 );\n\t\t\t\tprintf( \"\\n\" );\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tconst __m128i stride1 = load( a.stride );\n\t\t\tconst __m128i stride2 = load( b.stride );\n\t\t\tif( !vectorEqual( stride1, stride2 ) )\n\t\t\t{\n\t\t\t\tprintf( \"Tensor %zu \\\"%s\\\" - different memory layout\\n\", i, cstr( name ) );\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tif( a.dataType != b.dataType )\n\t\t\t{\n\t\t\t\tprintf( \"Tensor %zu \\\"%s\\\": different data types\\n\", i, cstr( name ) );\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tsize_t elements = (uint32_t)_mm_cvtsi128_si32( ne1 );\n\t\t\telements *= (uint32_t)_mm_extract_epi32( ne1, 1 );\n\t\t\telements *= (uint32_t)_mm_extract_epi32( ne1, 2 );\n\t\t\telements *= (uint32_t)_mm_extract_epi32( ne1, 3 );\n\n\t\t\tswitch( a.dataType )\n\t\t\t{\n\t\t\tcase eDataType::FP32:\n\t\t\t\treturn tensorsFp32( i, name, (const float*)readerA.payload( a ), (const float*)readerB.payload( b ), elements, ne1, stride1 );\n\t\t\t}\n\t\t\tthrow E_NOTIMPL;\n\t\t}\n\n\tprotected:\n\t\tvirtual bool buffersFp32( size_t idx, const CStringA& name, const float* a, const float* b, size_t length ) = 0;\n\t\tvirtual bool tensorsFp32( size_t idx, const CStringA& name, const float* a, const float* b, size_t length, __m128i ne, __m128i nb ) = 0;\n\n\tpublic:\n\n\t\tComparer( TraceReader& t1, TraceReader& t2 ) :\n\t\t\treaderA( t1 ), readerB( t2 ) { }\n\n\t\tbool compare( size_t i )\n\t\t{\n\t\t\tconst sTraceItem& a = readerA[ i ];\n\t\t\tconst sTraceItem& b = readerB[ i ];\n\t\t\tCStringA name1 = readerA.getName( a );\n\t\t\tCStringA name2 = readerB.getName( b );\n\n\t\t\tif( a.itemType != b.itemType )\n\t\t\t{\n\t\t\t\tprintf( \"Item %zu: different type, trace A %s \\\"%s\\\", trace B %s \\\"%s\\\"\\n\", i,\n\t\t\t\t\tcstr( a.itemType ), cstr( name1 ), cstr( b.itemType ), cstr( name2 ) );\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tif( name1 != name2 )\n\t\t\t{\n\t\t\t\tprintf( \"%s %zu: different names, they are \\\"%s\\\" and \\\"%s\\\"\\n\", cstr( a.itemType ), i, cstr( name1 ), cstr( name2 ) );\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tswitch( a.itemType )\n\t\t\t{\n\t\t\tcase eItemType::Buffer:\n\t\t\t\treturn diffBuffers( i, a, b, name1 );\n\t\t\tcase eItemType::Tensor:\n\t\t\t\treturn diffTensors( i, a, b, name1 );\n\t\t\tdefault:\n\t\t\t\tthrow E_INVALIDARG;\n\t\t\t}\n\t\t}\n\t};\n\n\tclass PrintSummary : public Comparer\n\t{\n\t\tbool buffersFp32( size_t idx, const CStringA& name, const float* a, const float* b, size_t length ) override;\n\t\tbool tensorsFp32( size_t idx, const CStringA& name, const float* a, const float* b, size_t length, __m128i ne, __m128i nb ) override;\n\n\tpublic:\n\t\tPrintSummary( TraceReader& a, TraceReader& b ) : Comparer( a, b ) { }\n\t};\n\n\tbool PrintSummary::buffersFp32( size_t idx, const CStringA& name, const float* a, const float* b, size_t length )\n\t{\n\t\tsTensorDiff diff = computeDiff( a, b, length );\n\t\tprintf( \"%s %zu \\\"%s\\\": \", cstr( eItemType::Buffer ), idx, cstr( name ) );\n\t\tdiff.print();\n\t\treturn true;\n\t}\n\n\tbool PrintSummary::tensorsFp32( size_t idx, const CStringA& name, const float* a, const float* b, size_t length, __m128i ne, __m128i nb )\n\t{\n\t\tprintSize( ne );\n\t\tprintf( \" \" );\n\t\tsTensorDiff diff = computeDiff( a, b, length );\n\t\tprintf( \"%s %zu \\\"%s\\\": \", cstr( eItemType::Tensor ), idx, cstr( name ) );\n\t\tdiff.print();\n\t\treturn true;\n\t}\n\n\tclass PrintDiff : public Comparer\n\t{\n\t\tbool buffersFp32( size_t idx, const CStringA& name, const float* a, const float* b, size_t length ) override;\n\t\tbool tensorsFp32( size_t idx, const CStringA& name, const float* a, const float* b, size_t length, __m128i ne, __m128i nb ) override;\n\tpublic:\n\t\tPrintDiff( TraceReader& a, TraceReader& b ) : Comparer( a, b ) { }\n\t};\n\n\tbool PrintDiff::buffersFp32( size_t idx, const CStringA& name, const float* A, const float* B, size_t length )\n\t{\n\t\tprintf( \"idx\\tA\\tB\\tA(hex)\\tB(hex)\\tdiff\\n\" );\n\t\tfor( size_t i = 0; i < length; i++ )\n\t\t{\n\t\t\tconst float a = *A;\n\t\t\tconst float b = *B;\n\t\t\t__m128 vf = _mm_setr_ps( a, b, 0, 0 );\n\t\t\t__m128i vi = _mm_castps_si128( vf );\n\t\t\tconst float diff = std::abs( a - b );\n\t\t\tprintf( \"%zu\\t%g\\t%g\\t0x%08X\\t0x%08X\\t%g\\n\",\n\t\t\t\ti, a, b, _mm_cvtsi128_si32( vi ), _mm_extract_epi32( vi, 1 ), diff );\n\t\t}\n\t\treturn true;\n\t}\n\n\tstd::array<uint32_t, 4> storeSize( __m128i v )\n\t{\n\t\tstd::array<uint32_t, 4> a;\n\t\t_mm_storeu_si128( ( __m128i* )a.data(), v );\n\t\treturn a;\n\t}\n\n\tstd::array<size_t, 4> storeStrides( __m128i v )\n\t{\n\t\tconst __m128i zero = _mm_setzero_si128();\n\t\tstd::array<size_t, 4> a;\n\t\t_mm_storeu_si128( ( __m128i* ) & a[ 0 ], _mm_unpacklo_epi32( v, zero ) );\n\t\t_mm_storeu_si128( ( __m128i* ) & a[ 2 ], _mm_unpackhi_epi32( v, zero ) );\n\t\treturn a;\n\t}\n\n\tbool PrintDiff::tensorsFp32( size_t idx, const CStringA& name, const float* A, const float* B, size_t length, __m128i ne, __m128i nb )\n\t{\n\t\tconst int dims = tensorDims( ne );\n\t\tconst std::array<uint32_t, 4> size = storeSize( ne );\n\t\tconst std::array<size_t, 4> strides = storeStrides( ne );\n\t\tCStringA line;\n\t\tif( dims > 4 )\n\t\t\tthrow E_UNEXPECTED;\n\n\t\tfor( int i = 0; i < dims; i++ )\n\t\t{\n\t\t\tconst char c = \"xyzw\"[ i ];\n\t\t\tline.AppendChar( c );\n\t\t\tline.AppendChar( '\\t' );\n\t\t}\n\t\tline += \"A\\tB\\tA(hex)\\tB(hex)\\tdiff\\n\";\n\t\tprintf( \"%s\", cstr( line ) );\n\n\t\tif( 0 == dims )\n\t\t{\n\t\t\tconst float a = *A;\n\t\t\tconst float b = *B;\n\t\t\t__m128 vf = _mm_setr_ps( a, b, 0, 0 );\n\t\t\t__m128i vi = _mm_castps_si128( vf );\n\t\t\tconst float diff = std::abs( a - b );\n\t\t\tprintf( \"%g\\t%g\\t0x%08X\\t0x%08X\\t%g\\n\",\n\t\t\t\ta, b, _mm_cvtsi128_si32( vi ), _mm_extract_epi32( vi, 1 ), diff );\n\t\t\treturn true;\n\t\t}\n\n\t\tsize_t offLayer2 = 0;\n\t\tfor( uint32_t w = 0; w < size[ 3 ]; w++, offLayer2 += strides[ 3 ] )\n\t\t{\n\t\t\tsize_t offLayer = offLayer2;\n\t\t\tfor( uint32_t z = 0; z < size[ 2 ]; z++, offLayer += strides[ 2 ] )\n\t\t\t{\n\t\t\t\tsize_t offRow = offLayer;\n\t\t\t\tfor( uint32_t y = 0; y < size[ 1 ]; y++, offRow += strides[ 1 ] )\n\t\t\t\t{\n\t\t\t\t\tsize_t off = offRow;\n\t\t\t\t\tfor( uint32_t x = 0; x < size[ 0 ]; x++, off += strides[ 0 ] )\n\t\t\t\t\t{\n\t\t\t\t\t\tline.Format( \"%i\\t\", x );\n\t\t\t\t\t\tif( dims > 1 )\n\t\t\t\t\t\t\tline.AppendFormat( \"%i\\t\", y );\n\t\t\t\t\t\tif( dims > 2 )\n\t\t\t\t\t\t\tline.AppendFormat( \"%i\\t\", z );\n\t\t\t\t\t\tif( dims > 3 )\n\t\t\t\t\t\t\tline.AppendFormat( \"%i\\t\", w );\n\n\t\t\t\t\t\tconst float a = A[ off ];\n\t\t\t\t\t\tconst float b = B[ off ];\n\t\t\t\t\t\t__m128 vf = _mm_setr_ps( a, b, 0, 0 );\n\t\t\t\t\t\t__m128i vi = _mm_castps_si128( vf );\n\t\t\t\t\t\tconst float diff = std::abs( a - b );\n\t\t\t\t\t\tline.AppendFormat( \"%g\\t%g\\t0x%08X\\t0x%08X\\t%g\\n\",\n\t\t\t\t\t\t\ta, b, _mm_cvtsi128_si32( vi ), _mm_extract_epi32( vi, 1 ), diff );\n\t\t\t\t\t\tprintf( \"%s\", cstr( line ) );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n}\n\nHRESULT compareTraces( const CommandLineArgs& arguments )\n{\n\tconst wchar_t* pathA = arguments.inputs[ 0 ];\n\tconst wchar_t* pathB = arguments.inputs[ 1 ];\n\n\tTraceReader a, b;\n\tHRESULT hr = a.open( pathA );\n\tif( FAILED( hr ) )\n\t{\n\t\tfwprintf( stderr, L\"Unable to load trace A from \\\"%s\\\"\", pathA );\n\t\tprintError( hr );\n\t\treturn hr;\n\t}\n\n\thr = b.open( pathB );\n\tif( FAILED( hr ) )\n\t{\n\t\tfwprintf( stderr, L\"Unable to load trace B from \\\"%s\\\"\", pathA );\n\t\tprintError( hr );\n\t\treturn hr;\n\t}\n\n\twprintf( L\"Trace A:   %s\\n\", pathA );\n\twprintf( L\"Trace B:   %s\\n\", pathB );\n\tconst size_t sizeA = a.size();\n\tconst size_t sizeB = b.size();\n\tconst size_t count = std::min( sizeA, sizeB );\n\n\tif( arguments.printDiff >= 0 )\n\t{\n\t\tif( arguments.printDiff >= (int64_t)count )\n\t\t{\n\t\t\tfprintf( stderr, \"Trace A has %zu entries, trace B %zu entries; entry %zu ain't there\\n\",\n\t\t\t\tsizeA, sizeB, (size_t)arguments.printDiff );\n\t\t\treturn E_INVALIDARG;\n\t\t}\n\t\ttry\n\t\t{\n\t\t\tPrintDiff print{ a, b };\n\t\t\tprint.compare( arguments.printDiff );\n\t\t\treturn S_OK;\n\t\t}\n\t\tcatch( HRESULT hr )\n\t\t{\n\t\t\treturn hr;\n\t\t}\n\t}\n\n\tprintf( \"Trace A has %zu entries, trace B %zu entries, comparing first %zu\\n\", sizeA, sizeB, count );\n\n\ttry\n\t{\n\t\tPrintSummary print{ a, b };\n\t\tfor( size_t i = 0; i < count; i++ )\n\t\t\tif( !print.compare( i ) )\n\t\t\t\treturn S_FALSE;\n\t\treturn S_OK;\n\t}\n\tcatch( HRESULT hr )\n\t{\n\t\treturn hr;\n\t}\n}"
  },
  {
    "path": "Tools/compareTraces/compare.h",
    "content": "#pragma once\n#include \"CommandLineArgs.h\"\n\nHRESULT compareTraces( const CommandLineArgs& arguments );"
  },
  {
    "path": "Tools/compareTraces/compareTraces.cpp",
    "content": "#include \"stdafx.h\"\n#include <stdio.h>\n#include \"compare.h\"\n#include \"CommandLineArgs.h\"\n\nint wmain( int argc, wchar_t* argv[] )\n{\n\tCommandLineArgs cla;\n\tif( !cla.parse( argc, argv ) )\n\t\treturn 1;\n\n\tHRESULT hr = compareTraces( cla );\n\tif( SUCCEEDED( hr ) )\n\t\treturn 0;\n\treturn hr;\n}"
  },
  {
    "path": "Tools/compareTraces/compareTraces.vcxproj",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project DefaultTargets=\"Build\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <ItemGroup Label=\"ProjectConfigurations\">\n    <ProjectConfiguration Include=\"Debug|x64\">\n      <Configuration>Debug</Configuration>\n      <Platform>x64</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Release|x64\">\n      <Configuration>Release</Configuration>\n      <Platform>x64</Platform>\n    </ProjectConfiguration>\n  </ItemGroup>\n  <PropertyGroup Label=\"Globals\">\n    <VCProjectVersion>16.0</VCProjectVersion>\n    <Keyword>Win32Proj</Keyword>\n    <ProjectGuid>{8478a77c-d851-4c63-9511-1770cc82d33e}</ProjectGuid>\n    <RootNamespace>compareTraces</RootNamespace>\n    <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>\n  </PropertyGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.Default.props\" />\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\" Label=\"Configuration\">\n    <ConfigurationType>Application</ConfigurationType>\n    <UseDebugLibraries>true</UseDebugLibraries>\n    <PlatformToolset>v143</PlatformToolset>\n    <CharacterSet>Unicode</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\" Label=\"Configuration\">\n    <ConfigurationType>Application</ConfigurationType>\n    <UseDebugLibraries>false</UseDebugLibraries>\n    <PlatformToolset>v143</PlatformToolset>\n    <WholeProgramOptimization>true</WholeProgramOptimization>\n    <CharacterSet>Unicode</CharacterSet>\n  </PropertyGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.props\" />\n  <ImportGroup Label=\"ExtensionSettings\">\n  </ImportGroup>\n  <ImportGroup Label=\"Shared\">\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <PropertyGroup Label=\"UserMacros\" />\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <SDLCheck>true</SDLCheck>\n      <PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <ConformanceMode>true</ConformanceMode>\n      <LanguageStandard>stdcpp20</LanguageStandard>\n      <PrecompiledHeader>Use</PrecompiledHeader>\n      <EnableEnhancedInstructionSet>AdvancedVectorExtensions</EnableEnhancedInstructionSet>\n    </ClCompile>\n    <Link>\n      <SubSystem>Console</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <FunctionLevelLinking>true</FunctionLevelLinking>\n      <IntrinsicFunctions>true</IntrinsicFunctions>\n      <SDLCheck>true</SDLCheck>\n      <PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <ConformanceMode>true</ConformanceMode>\n      <LanguageStandard>stdcpp20</LanguageStandard>\n      <PrecompiledHeader>Use</PrecompiledHeader>\n      <EnableEnhancedInstructionSet>AdvancedVectorExtensions</EnableEnhancedInstructionSet>\n    </ClCompile>\n    <Link>\n      <SubSystem>Console</SubSystem>\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\n      <OptimizeReferences>true</OptimizeReferences>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemGroup>\n    <ClCompile Include=\"CommandLineArgs.cpp\" />\n    <ClCompile Include=\"compareTraces.cpp\" />\n    <ClCompile Include=\"compare.cpp\" />\n    <ClCompile Include=\"stdafx.cpp\">\n      <PrecompiledHeader Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">Create</PrecompiledHeader>\n      <PrecompiledHeader Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">Create</PrecompiledHeader>\n    </ClCompile>\n    <ClCompile Include=\"testUtils.cpp\" />\n    <ClCompile Include=\"TraceReader.cpp\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ClInclude Include=\"CommandLineArgs.h\" />\n    <ClInclude Include=\"compare.h\" />\n    <ClInclude Include=\"stdafx.h\" />\n    <ClInclude Include=\"TraceReader.h\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Text Include=\"Readme.txt\" />\n  </ItemGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.targets\" />\n  <ImportGroup Label=\"ExtensionTargets\">\n  </ImportGroup>\n</Project>"
  },
  {
    "path": "Tools/compareTraces/compareTraces.vcxproj.filters",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <ItemGroup>\n    <ClCompile Include=\"compareTraces.cpp\" />\n    <ClCompile Include=\"TraceReader.cpp\" />\n    <ClCompile Include=\"compare.cpp\" />\n    <ClCompile Include=\"stdafx.cpp\" />\n    <ClCompile Include=\"testUtils.cpp\" />\n    <ClCompile Include=\"CommandLineArgs.cpp\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ClInclude Include=\"TraceReader.h\" />\n    <ClInclude Include=\"stdafx.h\" />\n    <ClInclude Include=\"compare.h\" />\n    <ClInclude Include=\"CommandLineArgs.h\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Text Include=\"Readme.txt\" />\n  </ItemGroup>\n</Project>"
  },
  {
    "path": "Tools/compareTraces/stdafx.cpp",
    "content": "#include \"stdafx.h\"\n\nnamespace\n{\n\twchar_t* formatMessage( HRESULT hr )\n\t{\n\t\twchar_t* err;\n\t\tif( FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,\n\t\t\tNULL,\n\t\t\thr,\n\t\t\tMAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ),\n\t\t\t(LPTSTR)&err,\n\t\t\t0,\n\t\t\tNULL ) )\n\t\t\treturn err;\n\t\treturn nullptr;\n\t}\n}\n\nvoid printError( HRESULT hr )\n{\n\tconst wchar_t* err = formatMessage( hr );\n\tif( nullptr != err )\n\t{\n\t\tfwprintf( stderr, L\"%s\\n\", err );\n\t\tLocalFree( (HLOCAL)err );\n\t}\n\telse\n\t\tfprintf( stderr, \"Error code %i (0x%08X)\\n\", hr, hr );\n}"
  },
  {
    "path": "Tools/compareTraces/stdafx.h",
    "content": "#pragma once\n#include <stdint.h>\n#include <assert.h>\n\n#define WIN32_LEAN_AND_MEAN\n#define NOMINMAX\n#include <windows.h>\n#include <atlstr.h>\n#include <d3d11.h>\n\n#include <vector>\n#include <array>\n#include <emmintrin.h>\n#include <smmintrin.h>\n\n#define CHECK( hr ) { const HRESULT __hr = ( hr ); if( FAILED( __hr ) ) return __hr; }\n\ninline __m128i load16( const int* rsi )\n{\n\treturn _mm_loadu_si128( ( const __m128i* )rsi );\n}\ninline __m128i load16( const uint32_t* rsi )\n{\n\treturn _mm_loadu_si128( ( const __m128i* )rsi );\n}\ninline __m128i load( const std::array<uint32_t, 4>& arr )\n{\n\treturn load16( arr.data() );\n}\n\ninline bool vectorEqual( __m128i a, __m128i b )\n{\n\t__m128i xx = _mm_xor_si128( a, b );\n\treturn (bool)_mm_testz_si128( xx, xx );\n}\n\nvoid printError( HRESULT hr );\n\ninline const char* cstr( const CStringA& s ) { return s; }\ninline const wchar_t* cstr( const CString& s ) { return s; }"
  },
  {
    "path": "Tools/compareTraces/testUtils.cpp",
    "content": "#include \"stdafx.h\"\n#include \"../../Whisper/ML/testUtils.h\"\n#include <immintrin.h>\nusing namespace DirectCompute;\n\nnamespace\n{\n\tusing DirectCompute::sTensorDiff;\n\n\t__forceinline __m256 load( const float* rsi )\n\t{\n\t\treturn _mm256_loadu_ps( rsi );\n\t}\n\n\t__forceinline __m256 load( const uint16_t* rsi )\n\t{\n\t\tconst __m128i iv = _mm_load_si128( ( const __m128i* )rsi );\n\t\treturn _mm256_cvtph_ps( iv );\n\t}\n\n\t__forceinline void loadPartial( const uint16_t* x, const uint16_t* y, size_t count, __m256& fx, __m256& fy )\n\t{\n\t\t__m128i ix, iy;\n\t\tswitch( count )\n\t\t{\n\t\tcase 1: // load 2 bytes\n\t\t\tix = _mm_cvtsi32_si128( *x );\n\t\t\tiy = _mm_cvtsi32_si128( *y );\n\t\t\tbreak;\n\t\tcase 2: // load 4 bytes\n\t\t\tix = _mm_cvtsi32_si128( *(const int*)x );\n\t\t\tiy = _mm_cvtsi32_si128( *(const int*)y );\n\t\t\tbreak;\n\t\tcase 3: // load 6 bytes\n\t\t\tix = _mm_cvtsi32_si128( *(const int*)x );\n\t\t\tiy = _mm_cvtsi32_si128( *(const int*)y );\n\t\t\tix = _mm_insert_epi16( ix, x[ 2 ], 2 );\n\t\t\tiy = _mm_insert_epi16( iy, y[ 2 ], 2 );\n\t\t\tbreak;\n\t\tcase 4: // load 8 bytes\n\t\t\tix = _mm_cvtsi64_si128( *(const int64_t*)x );\n\t\t\tiy = _mm_cvtsi64_si128( *(const int64_t*)y );\n\t\t\tbreak;\n\t\tcase 5: // load 10 bytes\n\t\t\tix = _mm_cvtsi64_si128( *(const int64_t*)x );\n\t\t\tiy = _mm_cvtsi64_si128( *(const int64_t*)y );\n\t\t\tix = _mm_insert_epi16( ix, x[ 4 ], 4 );\n\t\t\tiy = _mm_insert_epi16( iy, y[ 4 ], 4 );\n\t\t\tbreak;\n\t\tcase 6: // load 12 bytes\n\t\t\tix = _mm_cvtsi64_si128( *(const int64_t*)x );\n\t\t\tiy = _mm_cvtsi64_si128( *(const int64_t*)y );\n\t\t\tix = _mm_insert_epi32( ix, *(const int*)( x + 4 ), 2 );\n\t\t\tiy = _mm_insert_epi32( iy, *(const int*)( y + 4 ), 2 );\n\t\t\tbreak;\n\t\tcase 7: // load 14 bytes\n\t\t\tix = _mm_cvtsi64_si128( *(const int64_t*)x );\n\t\t\tiy = _mm_cvtsi64_si128( *(const int64_t*)y );\n\t\t\tix = _mm_insert_epi32( ix, *(const int*)( x + 4 ), 2 );\n\t\t\tiy = _mm_insert_epi32( iy, *(const int*)( y + 4 ), 2 );\n\t\t\tix = _mm_insert_epi16( ix, x[ 6 ], 6 );\n\t\t\tiy = _mm_insert_epi16( iy, y[ 6 ], 6 );\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tfx = fy = _mm256_setzero_ps();\n\t\t\treturn;\n\t\t}\n\n\t\tfx = _mm256_cvtph_ps( ix );\n\t\tfy = _mm256_cvtph_ps( iy );\n\t}\n\n\tinline __m128 loadFloat2( const float* rsi )\n\t{\n\t\treturn _mm_castpd_ps( _mm_load_sd( (const double*)rsi ) );\n\t}\n\tinline __m128 loadFloat3( const float* rsi )\n\t{\n\t\t__m128 f = loadFloat2( rsi );\n\t\tf = _mm_insert_ps( f, _mm_load_ss( rsi + 2 ), 0x20 );\n\t\treturn f;\n\t}\n\t__forceinline void loadPartial( const float* x, const float* y, size_t count, __m256& fx, __m256& fy )\n\t{\n\t\t__m128 low1, high1;\n\t\t__m128 low2, high2;\n\t\thigh1 = high2 = _mm_setzero_ps();\n\t\tswitch( count )\n\t\t{\n\t\tcase 1:\n\t\t\tlow1 = _mm_load_ss( x );\n\t\t\tlow2 = _mm_load_ss( y );\n\t\t\tbreak;\n\t\tcase 2:\n\t\t\tlow1 = loadFloat2( x );\n\t\t\tlow2 = loadFloat2( y );\n\t\t\tbreak;\n\t\tcase 3:\n\t\t\tlow1 = loadFloat3( x );\n\t\t\tlow2 = loadFloat3( y );\n\t\t\tbreak;\n\t\tcase 4:\n\t\t\tlow1 = _mm_loadu_ps( x );\n\t\t\tlow2 = _mm_loadu_ps( y );\n\t\t\tbreak;\n\t\tcase 5:\n\t\t\tlow1 = _mm_loadu_ps( x );\n\t\t\tlow2 = _mm_loadu_ps( y );\n\t\t\thigh1 = _mm_load_ss( x + 4 );\n\t\t\thigh2 = _mm_load_ss( y + 4 );\n\t\t\tbreak;\n\t\tcase 6:\n\t\t\tlow1 = _mm_loadu_ps( x );\n\t\t\tlow2 = _mm_loadu_ps( y );\n\t\t\thigh1 = loadFloat2( x + 4 );\n\t\t\thigh2 = loadFloat2( y + 4 );\n\t\t\tbreak;\n\t\tcase 7: // load 14 bytes\n\t\t\tlow1 = _mm_loadu_ps( x );\n\t\t\tlow2 = _mm_loadu_ps( y );\n\t\t\thigh1 = loadFloat3( x + 4 );\n\t\t\thigh2 = loadFloat3( y + 4 );\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tfx = fy = _mm256_setzero_ps();\n\t\t\treturn;\n\t\t}\n\n\t\tfx = _mm256_setr_m128( low1, high1 );\n\t\tfy = _mm256_setr_m128( low2, high2 );\n\t}\n\n\t__forceinline float horizontalMaximum( __m256 v )\n\t{\n\t\t__m128 s = _mm256_extractf128_ps( v, 1 );\n\t\ts = _mm_max_ps( s, _mm256_castps256_ps128( v ) );\n\t\ts = _mm_max_ps( s, _mm_movehl_ps( s, s ) );\n\t\ts = _mm_max_ss( s, _mm_movehdup_ps( s ) );\n\t\treturn _mm_cvtss_f32( s );\n\t}\n\n\t__forceinline double horizontalSum( __m256 v )\n\t{\n\t\t__m256d d = _mm256_cvtps_pd( _mm256_extractf128_ps( v, 1 ) );\n\t\td = _mm256_add_pd( d, _mm256_cvtps_pd( _mm256_castps256_ps128( v ) ) );\n\n\t\t__m128d s = _mm256_extractf128_pd( d, 1 );\n\t\ts = _mm_add_pd( s, _mm256_castpd256_pd128( d ) );\n\t\ts = _mm_add_sd( s, _mm_unpackhi_pd( s, s ) );\n\t\treturn _mm_cvtsd_f64( s );\n\t}\n\n\t__m256 maskInfNan( __m256 diff, __m256 a, __m256 b )\n\t{\n\t\t__m256i ai = _mm256_castps_si256( a );\n\t\t__m256i bi = _mm256_castps_si256( b );\n\t\t__m256i eqi = _mm256_cmpeq_epi32( ai, bi );\n\t\t__m256 eq = _mm256_castsi256_ps( eqi );\n\t\treturn _mm256_andnot_ps( eq, diff );\n\t}\n\n\tclass DiffAcc\n\t{\n\t\t__m256 maxAbs = _mm256_setzero_ps();\n\t\t__m256 sumSquares = _mm256_setzero_ps();\n\n\tpublic:\n\n\t\t__forceinline void add( __m256 a, __m256 b )\n\t\t{\n\t\t\tconst __m256 neg0 = _mm256_set1_ps( -0.0f );\n\t\t\t__m256 diff = _mm256_sub_ps( b, a );\n\t\t\tdiff = maskInfNan( diff, a, b );\n\t\t\tsumSquares = _mm256_fmadd_ps( diff, diff, sumSquares );\n\t\t\tconst __m256 absDiff = _mm256_andnot_ps( neg0, diff );\n\t\t\tmaxAbs = _mm256_max_ps( maxAbs, absDiff );\n\t\t}\n\n\t\t__forceinline sTensorDiff reduce( size_t count )\n\t\t{\n\t\t\tsTensorDiff res;\n\t\t\tres.maxAbsDiff = horizontalMaximum( maxAbs );\n\t\t\tres.avgDiffSquared = (float)( horizontalSum( sumSquares ) / (double)(int64_t)count );\n\t\t\tres.length = count;\n\t\t\treturn res;\n\t\t}\n\t};\n\n\ttemplate<class E>\n\tstatic sTensorDiff __declspec( noinline ) diffVectors( const E* a, const E* b, size_t length )\n\t{\n\t\t// const E* const aEnd = a + length;\n\t\tconst E* const aEndAligned = a + ( length / 8 ) * 8;\n\t\tconst size_t remainder = length % 8;\n\n\t\tDiffAcc acc;\n\t\tfor( ; a < aEndAligned; a += 8, b += 8 )\n\t\t\tacc.add( load( a ), load( b ) );\n\n\t\tif( remainder != 0 )\n\t\t{\n\t\t\t__m256 va, vb;\n\t\t\tloadPartial( a, b, remainder, va, vb );\n\t\t\tacc.add( va, vb );\n\t\t}\n\n\t\treturn acc.reduce( length );\n\t}\n}\n\nsTensorDiff DirectCompute::computeDiff( const float* a, const float* b, size_t length )\n{\n\treturn diffVectors( a, b, length );\n}\n\nsTensorDiff DirectCompute::computeDiff( const uint16_t* a, const uint16_t* b, size_t length )\n{\n\treturn diffVectors( a, b, length );\n}\n\nvoid DirectCompute::sTensorDiff::print() const\n{\n\tprintf( \"%zu elements, maxAbsDiff = %g, avgDiffSquared = %g\\n\", length, maxAbsDiff, avgDiffSquared );\n}"
  },
  {
    "path": "Tools/copy-binaries.cmd",
    "content": "if exist ..\\..\\Release\\Next rmdir /s /q ..\\..\\Release\\Next\nmkdir ..\\..\\Release\\Next\n\nrem Desktop example\nmkdir ..\\..\\Release\\Next\\WhisperDesktop\ncopy ..\\x64\\Release\\Whisper.dll ..\\..\\Release\\Next\\WhisperDesktop\\\ncopy ..\\x64\\Release\\WhisperDesktop.exe ..\\..\\Release\\Next\\WhisperDesktop\\\ncopy ..\\Whisper\\Utils\\LZ4\\LICENSE ..\\..\\Release\\Next\\WhisperDesktop\\lz4.txt\n\nrem Library\nmkdir ..\\..\\Release\\Next\\Library\nmkdir ..\\..\\Release\\Next\\Library\\Binary\ncopy ..\\x64\\Release\\Whisper.dll ..\\..\\Release\\Next\\Library\\Binary\\\ncopy ..\\Whisper\\Utils\\LZ4\\LICENSE ..\\..\\Release\\Next\\Library\\Binary\\lz4.txt\nmkdir ..\\..\\Release\\Next\\Library\\Include\ncopy ..\\Whisper\\API\\*.* ..\\..\\Release\\Next\\Library\\Include\\\nmkdir ..\\..\\Release\\Next\\Library\\Linker\ncopy ..\\x64\\Release\\Whisper.lib ..\\..\\Release\\Next\\Library\\Linker\\\n\nrem debug symbols\ncopy ..\\x64\\Release\\Whisper.pdb ..\\..\\Release\\Next\\\n\nrem C++ CLI example\nmkdir ..\\..\\Release\\Next\\cli\ncopy ..\\x64\\Release\\Whisper.dll ..\\..\\Release\\Next\\cli\\\ncopy ..\\x64\\Release\\main.exe ..\\..\\Release\\Next\\cli\\\ncopy ..\\Whisper\\Utils\\LZ4\\LICENSE ..\\..\\Release\\Next\\cli\\lz4.txt\n\nrem PowerShell module\nmkdir ..\\..\\Release\\Next\\WhisperPS\ncopy ..\\WhisperPS\\bin\\Release\\*.dll ..\\..\\Release\\Next\\WhisperPS\\\ncopy ..\\WhisperPS\\bin\\Release\\WhisperPS.psd1 ..\\..\\Release\\Next\\WhisperPS\\\ncopy ..\\WhisperPS\\bin\\Release\\WhisperPS.dll.config ..\\..\\Release\\Next\\WhisperPS\\\ncopy ..\\WhisperPS\\bin\\Release\\WhisperPS.dll-Help.xml ..\\..\\Release\\Next\\WhisperPS\\\ncopy ..\\Whisper\\Utils\\LZ4\\LICENSE ..\\..\\Release\\Next\\WhisperPS\\lz4.txt\n\nrem Deploy that PowerShell module locally\nrmdir /s /q %USERPROFILE%\\Documents\\WindowsPowerShell\\Modules\\WhisperPS\nmkdir %USERPROFILE%\\Documents\\WindowsPowerShell\\Modules\\WhisperPS\ncopy ..\\..\\Release\\Next\\WhisperPS\\* %USERPROFILE%\\Documents\\WindowsPowerShell\\Modules\\WhisperPS\\\n\nrem Zip these things, using command-line 7zip.exe with maximum level\nrem https://www.7-zip.org/download.html\n\nset ZIP=\"C:\\Program Files\\7-Zip\\7z.exe\" a -mmt1 -mx9 -bd\npushd ..\\..\\Release\\Next\n%ZIP% -tzip .\\WhisperDesktop.zip .\\WhisperDesktop\\*.*\n%ZIP% -tzip -r .\\Library.zip .\\Library\\Include .\\Library\\Linker .\\Library\\Binary\n%ZIP% -t7z -sdel .\\Whisper.pdb.7z .\\Whisper.pdb\n%ZIP% -tzip .\\cli.zip .\\cli\\*.*\n%ZIP% -tzip -r .\\WhisperPS.zip .\\WhisperPS\npopd"
  },
  {
    "path": "Whisper/API/MfStructs.h",
    "content": "#pragma once\n\nnamespace Whisper\n{\n\tstruct sCaptureDevice\n\t{\n\t\t// The display name is suitable for showing to the user, but might not be unique.\n\t\tconst wchar_t* displayName;\n\n\t\t// Endpoint ID for an audio capture device\n\t\t// It uniquely identifies the device on the system, but is not a readable string.\n\t\tconst wchar_t* endpoint;\n\t};\n\n\tusing pfnFoundCaptureDevices = HRESULT( __stdcall* )( int len, const sCaptureDevice* buffer, void* pv );\n\n\t// Flags for the audio capture\n\tenum struct eCaptureFlags : uint32_t\n\t{\n\t\t// When the capture device supports stereo, keep stereo PCM samples in addition to mono\n\t\tStereo = 1,\n\t};\n\n\t// Parameters for audio capture\n\tstruct sCaptureParams\n\t{\n\t\tfloat minDuration = 2.0f;\n\t\tfloat maxDuration = 3.0f;\n\t\tfloat dropStartSilence = 0.25f;\n\t\tfloat pauseDuration = 0.333f;\n\t\t// Flags for the audio capture\n\t\tuint32_t flags = 0;\n\t};\n\n\tenum struct eCaptureStatus : uint8_t\n\t{\n\t\tListening = 1,\n\t\tVoice = 2,\n\t\tTranscribing = 4,\n\t\tStalled = 0x80,\n\t};\n\n\t// Return S_OK to continue, or S_FALSE to stop the capture session\n\tusing pfnShouldCancel = HRESULT( __stdcall* )( void* pv ) noexcept;\n\n\tusing pfnCaptureStatus = HRESULT( __stdcall* )( void* pv, eCaptureStatus status ) noexcept;\n\n\tstruct sCaptureCallbacks\n\t{\n\t\tpfnShouldCancel shouldCancel;\n\t\tpfnCaptureStatus captureStatus;\n\t\tvoid* pv;\n\t};\n}"
  },
  {
    "path": "Whisper/API/Readme.txt",
    "content": "﻿The headers in this folder define the complete public API of Whisper.dll.\n\nTo consume the library in your C++ software, include exactly one of the following headers.\n\n1. If you’re building a windows app, include whisperWindows.h header, and you'll get traditional Win32 COM projection of the API.\n\n2. If you’re porting to other OS, or porting to different C++ compiler, or already using ComLight support library, include whisperComLight.h header.\nIf you do that, in addition to this \"Whisper/API\" folder you also gonna need the \"ComLightLib\" dependency.\nThis will get you the ComLight flavor of these COM interfaces.\n\nInternally, the actual implementation uses the ComLight flavour of the interfaces, but that’s fine because they are binary compatible.\n\nThe reason for the difference between these flavors — Visual Studio’s CComPtr<T> and other related utilities expect interface IDs specified with __declspec(uuid) directive.\n\nThat language extension is specific to Visual C++, not supported in GCC nor Clang compilers."
  },
  {
    "path": "Whisper/API/SpecialTokens.h",
    "content": "#pragma once\n\nnamespace Whisper\n{\n\tstruct SpecialTokens\n\t{\n\t\t// The end of a transcription, token_eot\n\t\tint TranscriptionEnd;\n\t\t// Start of a transcription, token_sot\n\t\tint TranscriptionStart;\n\t\t// Represents the previous word in the transcription. It is used to help the model predict the current word based on the context of the words that came before it.\n\t\tint PreviousWord;   // token_prev\n\t\t// Start of a sentence\n\t\tint SentenceStart;   // token_solm\n\t\t//Represents the word \"not\" in the transcription\n\t\tint Not;    // token_not\n\t\t//New transcription\n\t\tint TranscriptionBegin;    // token_beg\n\n\t\t// token_translate\n\t\tint TaskTranslate;\n\t\t// token_transcribe\n\t\tint TaskTranscribe;\n\t};\n}"
  },
  {
    "path": "Whisper/API/TranscribeStructs.h",
    "content": "#pragma once\n#include <stdint.h>\n#include <assert.h>\n\nnamespace Whisper\n{\n\t// Timespan structure decomposed into fields\n\tstruct sTimeSpanFields\n\t{\n\t\tuint32_t days;\n\t\tuint8_t hours, minutes, seconds;\n\t\tuint32_t ticks;\n\n\t\tsTimeSpanFields( uint64_t tt )\n\t\t{\n\t\t\tticks = (uint32_t)( tt % 10'000'000 );\n\t\t\ttt /= 10'000'000;\n\t\t\tseconds = (uint8_t)( tt % 60 );\n\t\t\ttt /= 60;\n\t\t\tminutes = (uint8_t)( tt % 60 );\n\t\t\ttt /= 60;\n\t\t\thours = (uint8_t)( tt % 24 );\n\t\t\ttt /= 24;\n\t\t\tdays = (uint32_t)tt;\n\t\t}\n\t};\n\n\t// C++ equivalent of System.Timespan C# structure\n\tstruct sTimeSpan\n\t{\n\t\t// The value is expressed in 100-nanoseconds ticks: compatible with System.Timespan, FILETIME, and many other things\n\t\tuint64_t ticks;\n\n\t\toperator sTimeSpanFields() const\n\t\t{\n\t\t\treturn sTimeSpanFields{ ticks };\n\t\t}\n\t\tvoid operator=( uint64_t tt )\n\t\t{\n\t\t\tticks = tt;\n\t\t}\n\t\tvoid operator=( int64_t tt )\n\t\t{\n\t\t\tassert( tt >= 0 );\n\t\t\tticks = (uint64_t)tt;\n\t\t}\n\t};\n\n\t// Start and end times of the segment or token, expressed in 100-nanosecond ticks\n\tstruct sTimeInterval\n\t{\n\t\tsTimeSpan begin, end;\n\t};\n\n\t// Segment data\n\tstruct sSegment\n\t{\n\t\t// Segment text, null-terminated, and probably UTF-8 encoded\n\t\tconst char* text;\n\t\t// Start and end times of the segment\n\t\tsTimeInterval time;\n\t\t// These two integers define the slice of the tokens in this segment, in the array returned by iTranscribeResult.getTokens method\n\t\tuint32_t firstToken, countTokens;\n\t};\n\n\tenum eTokenFlags : uint32_t\n\t{\n\t\tNone = 0,\n\t\tSpecial = 1,\n\t};\n\tinline bool operator &( eTokenFlags a, eTokenFlags b )\n\t{\n\t\treturn 0 != ( (uint32_t)a & (uint32_t)b );\n\t}\n\n\t// Token data\n\tstruct sToken\n\t{\n\t\t// Token text, null-terminated, and usually UTF-8 encoded.\n\t\t// I think for Chinese language the models sometimes outputs invalid UTF8 strings here, Unicode code points can be split between adjacent tokens in the same segment\n\t\t// More info: https://github.com/ggerganov/whisper.cpp/issues/399\n\t\tconst char* text;\n\t\t// Start and end times of the token\n\t\tsTimeInterval time;\n\t\t// Probability of the token\n\t\tfloat probability;\n\t\t// Probability of the timestamp token\n\t\tfloat probabilityTimestamp;\n\t\t// Sum of probabilities of all timestamp tokens\n\t\tfloat ptsum;\n\t\t// Voice length of the token\n\t\tfloat vlen;\n\t\t// Token id\n\t\tint id;\n\t\teTokenFlags flags;\n\t};\n\n\tstruct sTranscribeLength\n\t{\n\t\tuint32_t countSegments, countTokens;\n\t};\n\n\tenum struct eResultFlags : uint32_t\n\t{\n\t\tNone = 0,\n\t\t// Return individual tokens in addition to the segments\n\t\tTokens = 1,\n\t\t// Return timestamps\n\t\tTimestamps = 2,\n\n\t\t// Create a new COM object for the results.\n\t\t// Without this flag, the context returns a pointer to the COM object stored in the context.\n\t\t// The content of that object is replaced every time you call iContext.getResults method\n\t\tNewObject = 0x100,\n\t};\n\n\tinline eResultFlags operator |( eResultFlags a, eResultFlags b )\n\t{\n\t\treturn (eResultFlags)( (uint32_t)a | (uint32_t)b );\n\t}\n\n\tinline bool operator &( eResultFlags a, eResultFlags b )\n\t{\n\t\treturn 0 != ( (uint32_t)a & (uint32_t)b );\n\t}\n\n\t// Output value for iContext.detectSpeaker method\n\tenum struct eSpeakerChannel : uint8_t\n\t{\n\t\tUnsure = 0,\n\t\tLeft = 1,\n\t\tRight = 2,\n\t\tNoStereoData = 0xFF,\n\t};\n}"
  },
  {
    "path": "Whisper/API/iContext.cl.h",
    "content": "﻿#pragma once\n#include \"../../ComLightLib/comLightCommon.h\"\n#include \"iTranscribeResult.cl.h\"\n#include \"SpecialTokens.h\"\n#include \"loggerApi.h\"\n#include \"sLanguageList.h\"\n#include \"sLoadModelCallbacks.h\"\n#include \"sModelSetup.h\"\n\nnamespace Whisper\n{\n\tstruct iModel;\n\tstruct iAudioBuffer;\n\tstruct iAudioReader;\n\tstruct iAudioCapture;\n\tstruct sCaptureCallbacks;\n\tstruct sFullParams;\n\tenum struct eModelImplementation : uint32_t;\n\tenum struct eSamplingStrategy : int;\n\tusing whisper_token = int;\n\tstruct sProgressSink;\n\n\tstruct DECLSPEC_NOVTABLE iContext : public ComLight::IUnknown\n\t{\n\t\tDEFINE_INTERFACE_ID( \"{b9956374-3b18-4943-90f2-2ab18a404537}\" );\n\n\t\t// Run the entire model: PCM -> log mel spectrogram -> encoder -> decoder -> text\n\t\t// Uses the specified decoding strategy to obtain the text.\n\t\tvirtual HRESULT COMLIGHTCALL runFull( const sFullParams& params, const iAudioBuffer* buffer ) = 0;\n\t\tvirtual HRESULT COMLIGHTCALL runStreamed( const sFullParams& params, const sProgressSink& progress, const iAudioReader* reader ) = 0;\n\t\tvirtual HRESULT COMLIGHTCALL runCapture( const sFullParams& params, const sCaptureCallbacks& callbacks, const iAudioCapture* reader ) = 0;\n\n\t\tvirtual HRESULT COMLIGHTCALL getResults( eResultFlags flags, iTranscribeResult** pp ) const = 0;\n\t\t// Try to detect speaker by comparing channels of the stereo PCM data\n\t\tvirtual HRESULT COMLIGHTCALL detectSpeaker( const sTimeInterval& time, eSpeakerChannel& result ) const = 0;\n\n\t\tvirtual HRESULT COMLIGHTCALL getModel( iModel** pp ) = 0;\n\n\t\tvirtual HRESULT COMLIGHTCALL fullDefaultParams( eSamplingStrategy strategy, sFullParams* rdi ) = 0;\n\n\t\t// Performance information\n\t\tvirtual HRESULT COMLIGHTCALL timingsPrint() = 0;\n\t\tvirtual HRESULT COMLIGHTCALL timingsReset() = 0;\n\t};\n\n\tstruct DECLSPEC_NOVTABLE iModel : public ComLight::IUnknown\n\t{\n\t\tDEFINE_INTERFACE_ID( \"{abefb4c9-e8d8-46a3-8747-5afbadef1adb}\" );\n\n\t\tvirtual HRESULT COMLIGHTCALL createContext( iContext** pp ) = 0;\n\n\t\tvirtual HRESULT COMLIGHTCALL tokenize( const char* text, pfnDecodedTokens pfn, void* pv ) = 0;\n\t\tvirtual HRESULT COMLIGHTCALL isMultilingual() = 0;\n\t\tvirtual HRESULT COMLIGHTCALL getSpecialTokens( SpecialTokens& rdi ) = 0;\n\n\t\t// Token Id -> String\n\t\tvirtual const char* COMLIGHTCALL stringFromToken( whisper_token token ) = 0;\n\n\t\tvirtual HRESULT COMLIGHTCALL clone( iModel** rdi ) = 0;\n\t};\n\n\tHRESULT COMLIGHTCALL setupLogger( const sLoggerSetup& setup );\n\tHRESULT COMLIGHTCALL loadModel( const wchar_t* path, const sModelSetup& setup, const sLoadModelCallbacks* callbacks, iModel** pp );\n\n\tuint32_t COMLIGHTCALL findLanguageKeyW( const wchar_t* lang );\n\tuint32_t COMLIGHTCALL findLanguageKeyA( const char* lang );\n\n\tHRESULT COMLIGHTCALL getSupportedLanguages( sLanguageList& rdi );\n\n\tHRESULT COMLIGHTCALL listGPUs( pfnListAdapters pfn, void* pv );\n}\n\n#include \"sFullParams.h\""
  },
  {
    "path": "Whisper/API/iContext.h",
    "content": "﻿#pragma once\n#include \"iTranscribeResult.h\"\n#include \"SpecialTokens.h\"\n#include \"loggerApi.h\"\n#include \"sLanguageList.h\"\n#include \"sLoadModelCallbacks.h\"\n#include \"sModelSetup.h\"\n\nnamespace Whisper\n{\n\t__interface iModel;\n\t__interface iAudioBuffer;\n\t__interface iAudioReader;\n\t__interface iAudioCapture;\n\tstruct sCaptureCallbacks;\n\tstruct sFullParams;\n\tenum struct eModelImplementation : uint32_t;\n\tenum struct eSamplingStrategy : int;\n\tusing whisper_token = int;\n\tstruct sProgressSink;\n\n\t__interface __declspec( novtable, uuid( \"b9956374-3b18-4943-90f2-2ab18a404537\" ) ) iContext : public IUnknown\n\t{\n\t\t// Run the entire model: PCM -> log mel spectrogram -> encoder -> decoder -> text\n\t\t// Uses the specified decoding strategy to obtain the text.\n\t\tHRESULT __stdcall runFull( const sFullParams& params, const iAudioBuffer* buffer );\n\t\tHRESULT __stdcall runStreamed( const sFullParams& params, const sProgressSink& progress, const iAudioReader* reader );\n\t\tHRESULT __stdcall runCapture( const sFullParams& params, const sCaptureCallbacks& callbacks, const iAudioCapture* reader );\n\n\t\tHRESULT __stdcall getResults( eResultFlags flags, iTranscribeResult** pp ) const;\n\t\t// Try to detect speaker by comparing channels of the stereo PCM data\n\t\tHRESULT __stdcall detectSpeaker( const sTimeInterval& time, eSpeakerChannel& result ) const;\n\n\t\tHRESULT __stdcall getModel( iModel** pp );\n\n\t\tHRESULT __stdcall fullDefaultParams( eSamplingStrategy strategy, sFullParams* rdi );\n\n\t\t// Performance information\n\t\tHRESULT __stdcall timingsPrint();\n\t\tHRESULT __stdcall timingsReset();\n\t};\n\n\t__interface __declspec( novtable, uuid( \"abefb4c9-e8d8-46a3-8747-5afbadef1adb\" ) ) iModel : public IUnknown\n\t{\n\t\tHRESULT __stdcall createContext( iContext** pp );\n\n\t\tHRESULT __stdcall tokenize( const char* text, pfnDecodedTokens pfn, void* pv );\n\n\t\tHRESULT __stdcall isMultilingual();\n\n\t\tHRESULT __stdcall getSpecialTokens( SpecialTokens& rdi );\n\n\t\t// Token Id -> String\n\t\tconst char* __stdcall stringFromToken( whisper_token token );\n\n\t\tHRESULT __stdcall clone( iModel** rdi );\n\t};\n\n\tHRESULT __stdcall setupLogger( const sLoggerSetup& setup );\n\tHRESULT __stdcall loadModel( const wchar_t* path, const sModelSetup& setup, const sLoadModelCallbacks* callbacks, iModel** pp );\n\n\tuint32_t __stdcall findLanguageKeyW( const wchar_t* lang );\n\tuint32_t __stdcall findLanguageKeyA( const char* lang );\n\n\tHRESULT __stdcall getSupportedLanguages( sLanguageList& rdi );\n\n\tHRESULT __stdcall listGPUs( pfnListAdapters pfn, void* pv );\n}\n\n#include \"sFullParams.h\""
  },
  {
    "path": "Whisper/API/iMediaFoundation.cl.h",
    "content": "#pragma once\n#include \"../../ComLightLib/comLightCommon.h\"\n#include \"MfStructs.h\"\n\nstruct IMFSourceReader;\n\nnamespace Whisper\n{\n\tstruct DECLSPEC_NOVTABLE iAudioBuffer : public ComLight::IUnknown\n\t{\n\t\tDEFINE_INTERFACE_ID( \"{013583aa-c9eb-42bc-83db-633c2c317051}\" );\n\n\t\tvirtual uint32_t COMLIGHTCALL countSamples() const = 0;\n\t\tvirtual const float* COMLIGHTCALL getPcmMono() const = 0;\n\t\tvirtual const float* COMLIGHTCALL getPcmStereo() const = 0;\n\t\tvirtual HRESULT COMLIGHTCALL getTime( int64_t& rdi ) const = 0;\n\t};\n\n\tstruct DECLSPEC_NOVTABLE iAudioReader : public ComLight::IUnknown\n\t{\n\t\tDEFINE_INTERFACE_ID( \"{35b988da-04a6-476a-a193-d8891d5dc390}\" );\n\n\t\tvirtual HRESULT COMLIGHTCALL getDuration( int64_t& rdi ) const = 0;\n\t\tvirtual HRESULT COMLIGHTCALL getReader( IMFSourceReader** pp ) const = 0;\n\t\tvirtual HRESULT COMLIGHTCALL requestedStereo() const = 0;\n\t};\n\n\tstruct DECLSPEC_NOVTABLE iAudioCapture : public ComLight::IUnknown\n\t{\n\t\tDEFINE_INTERFACE_ID( \"{747752c2-d9fd-40df-8847-583c781bf013}\" );\n\n\t\tvirtual HRESULT COMLIGHTCALL getReader( IMFSourceReader** pp ) const = 0;\n\t\tvirtual const sCaptureParams& COMLIGHTCALL getParams() const = 0;\n\t};\n\n\tstruct DECLSPEC_NOVTABLE iMediaFoundation : public ComLight::IUnknown\n\t{\n\t\tDEFINE_INTERFACE_ID( \"{fb9763a5-d77d-4b6e-aff8-f494813cebd8}\" );\n\n\t\tvirtual HRESULT COMLIGHTCALL loadAudioFile( LPCTSTR path, bool stereo, iAudioBuffer** pp ) const = 0;\n\t\tvirtual HRESULT COMLIGHTCALL openAudioFile( LPCTSTR path, bool stereo, iAudioReader** pp ) = 0;\n\t\tvirtual HRESULT COMLIGHTCALL loadAudioFileData( const void* data, uint64_t size, bool stereo, iAudioReader** pp ) = 0;\n\n\t\tvirtual HRESULT COMLIGHTCALL listCaptureDevices( pfnFoundCaptureDevices pfn, void* pv ) = 0;\n\t\tvirtual HRESULT COMLIGHTCALL openCaptureDevice( LPCTSTR endpoint, const sCaptureParams& captureParams, iAudioCapture** pp ) = 0;\n\t};\n\n\tHRESULT COMLIGHTCALL initMediaFoundation( iMediaFoundation** pp );\n}"
  },
  {
    "path": "Whisper/API/iMediaFoundation.h",
    "content": "#pragma once\n#include <stdint.h>\n#include \"MfStructs.h\"\nstruct IMFSourceReader;\n\nnamespace Whisper\n{\n\t__interface __declspec( novtable, uuid( \"013583aa-c9eb-42bc-83db-633c2c317051\" ) ) iAudioBuffer : public IUnknown\n\t{\n\t\tuint32_t __stdcall countSamples() const;\n\t\tconst float* __stdcall getPcmMono() const;\n\t\tconst float* __stdcall getPcmStereo() const;\n\t\tHRESULT __stdcall getTime( int64_t& rdi ) const;\n\t};\n\n\t__interface __declspec( novtable, uuid( \"35b988da-04a6-476a-a193-d8891d5dc390\" ) ) iAudioReader : public IUnknown\n\t{\n\t\tHRESULT __stdcall getDuration( int64_t& rdi ) const;\n\t\tHRESULT __stdcall getReader( IMFSourceReader** pp ) const;\n\t\tHRESULT __stdcall requestedStereo() const;\n\t};\n\n\t__interface __declspec( novtable, uuid( \"747752c2-d9fd-40df-8847-583c781bf013\" ) ) iAudioCapture : public IUnknown\n\t{\n\t\tHRESULT __stdcall getReader( IMFSourceReader** pp ) const;\n\t\tconst sCaptureParams& __stdcall getParams() const;\n\t};\n\n\t__interface __declspec( novtable, uuid( \"fb9763a5-d77d-4b6e-aff8-f494813cebd8\" ) ) iMediaFoundation : public IUnknown\n\t{\n\t\tHRESULT __stdcall loadAudioFile( LPCTSTR path, bool stereo, iAudioBuffer** pp ) const;\n\t\tHRESULT __stdcall openAudioFile( LPCTSTR path, bool stereo, iAudioReader** pp );\n\t\tHRESULT __stdcall loadAudioFileData( const void* data, uint64_t size, bool stereo, iAudioReader** pp );\n\n\t\tHRESULT __stdcall listCaptureDevices( pfnFoundCaptureDevices pfn, void* pv );\n\t\tHRESULT __stdcall openCaptureDevice( LPCTSTR endpoint, const sCaptureParams& captureParams, iAudioCapture** pp );\n\t};\n\n\tHRESULT __stdcall initMediaFoundation( iMediaFoundation** pp );\n}"
  },
  {
    "path": "Whisper/API/iTranscribeResult.cl.h",
    "content": "#pragma once\n#include \"TranscribeStructs.h\"\n#include \"../../ComLightLib/comLightCommon.h\"\n\nnamespace Whisper\n{\n\tstruct iTranscribeResult : public ComLight::IUnknown\n\t{\n\t\tDEFINE_INTERFACE_ID( \"{2871a73f-5ce3-48f8-8779-6582ee11935e}\" );\n\n\t\tvirtual HRESULT COMLIGHTCALL getSize( sTranscribeLength& rdi ) const = 0;\n\t\tvirtual const sSegment* COMLIGHTCALL getSegments() const = 0;\n\t\tvirtual const sToken* COMLIGHTCALL getTokens() const = 0;\n\t};\n}"
  },
  {
    "path": "Whisper/API/iTranscribeResult.h",
    "content": "#pragma once\n#include \"TranscribeStructs.h\"\n\nnamespace Whisper\n{\n\t__interface __declspec( novtable, uuid( \"2871a73f-5ce3-48f8-8779-6582ee11935e\" ) ) iTranscribeResult : public IUnknown\n\t{\n\t\tHRESULT __stdcall getSize( sTranscribeLength& rdi ) const;\n\t\tconst sSegment* __stdcall getSegments() const;\n\t\tconst sToken* __stdcall getTokens() const;\n\t};\n}"
  },
  {
    "path": "Whisper/API/loggerApi.h",
    "content": "#pragma once\n#include <stdint.h>\n\nnamespace Whisper\n{\n\t// Log level for messages\n\tenum struct eLogLevel : uint8_t\n\t{\n\t\tError = 0,\n\t\tWarning = 1,\n\t\tInfo = 2,\n\t\tDebug = 3\n\t};\n\tenum struct eLoggerFlags : uint8_t\n\t{\n\t\tUseStandardError = 1,\n\t\tSkipFormatMessage = 2,\n\t};\n\n\t// C function pointer to receive log messages from the library. The messages are encoded in UTF-8.\n\tusing pfnLoggerSink = void( __stdcall* )( void* context, eLogLevel lvl, const char* message );\n\n\t// A sink to receive log messages produced by MeshRepair.dll\n\tstruct sLoggerSetup\n\t{\n\t\t// C function pointer to receive log messages from the library\n\t\tpfnLoggerSink sink = nullptr;\n\t\t// Optional context parameter for the sink function; when consuming from C# you don't need that, pass IntPtr.Zero, delegates can capture things.\n\t\tvoid* context = nullptr;\n\t\t// Maximum log level to produce\n\t\teLogLevel level;\n\t\t// Flags about the logger\n\t\teLoggerFlags flags = (eLoggerFlags)0;\n\t};\n}"
  },
  {
    "path": "Whisper/API/sFullParams.h",
    "content": "﻿#pragma once\n#include <stdint.h>\n#include <assert.h>\n\nnamespace Whisper\n{\n\t// Available sampling strategies\n\tenum struct eSamplingStrategy : int\n\t{\n\t\t// Always select the most probable token\n\t\tGreedy,\n\t\t// TODO: not implemented yet!\n\t\tBeamSearch,\n\t};\n\n\tusing pfnNewSegment = HRESULT( __cdecl* )( iContext* ctx, uint32_t n_new, void* user_data ) noexcept;\n\n\t// Return S_OK to proceed, or S_FALSE to stop the process and return S_OK from runFull / runStreamed method\n\tusing pfnEncoderBegin = HRESULT( __cdecl* )( iContext* ctx, void* user_data ) noexcept;\n\n\tenum struct eFullParamsFlags : uint32_t\n\t{\n\t\tTranslate = 1,\n\t\tNoContext = 2,\n\t\tSingleSegment = 4,\n\t\tPrintSpecial = 8,\n\t\tPrintProgress = 0x10,\n\t\tPrintRealtime = 0x20,\n\t\tPrintTimestamps = 0x40,\n\n\t\t// Experimental\n\t\tTokenTimestamps = 0x100,\n\t\tSpeedupAudio = 0x200,\n\t};\n\n\tinline eFullParamsFlags operator | ( eFullParamsFlags a, eFullParamsFlags b )\n\t{\n\t\treturn (eFullParamsFlags)( (uint32_t)a | (uint32_t)b );\n\t}\n\tinline void operator |= ( eFullParamsFlags& a, eFullParamsFlags b )\n\t{\n\t\ta = a | b;\n\t}\n\n\tstruct sFullParams\n\t{\n\t\teSamplingStrategy strategy;\n\t\t// Count of CPU threads\n\t\tint cpuThreads;\n\t\tint n_max_text_ctx;\n\t\tint offset_ms;          // start offset in ms\n\t\tint duration_ms;        // audio duration to process in ms\n\t\teFullParamsFlags flags;\n\t\tuint32_t language;\n\n\t\t// [EXPERIMENTAL] token-level timestamps\n\t\tfloat thold_pt;         // timestamp token probability threshold (~0.01)\n\t\tfloat thold_ptsum;      // timestamp token sum probability threshold (~0.01)\n\t\tint   max_len;          // max segment length in characters\n\t\tint   max_tokens;       // max tokens per segment (0 = no limit)\n\n\t\tstruct\n\t\t{\n\t\t\tint n_past;\n\t\t} greedy;\n\n\t\tstruct\n\t\t{\n\t\t\tint n_past;\n\t\t\tint beam_width;\n\t\t\tint n_best;\n\t\t} beam_search;\n\n\t\t// [EXPERIMENTAL] speed-up techniques\n\t\tint  audio_ctx;         // overwrite the audio context size (0 = use default)\n\n\t\t// tokens to provide the whisper model as initial prompt\n\t\t// these are prepended to any existing text context from a previous call\n\t\tconst whisper_token* prompt_tokens;\n\t\tint prompt_n_tokens;\n\n\t\tpfnNewSegment new_segment_callback;\n\t\tvoid* new_segment_callback_user_data;\n\n\t\tpfnEncoderBegin encoder_begin_callback;\n\t\tvoid* encoder_begin_callback_user_data;\n\n\t\t// Couple utility methods, they workaround the lack of bit fields in C++\n\t\tinline bool flag( eFullParamsFlags f ) const\n\t\t{\n\t\t\treturn 0 != ( (uint32_t)flags & (uint32_t)f );\n\t\t}\n\t\tinline void resetFlag( eFullParamsFlags bit )\n\t\t{\n\t\t\tuint32_t f = (uint32_t)flags;\n\t\t\tf &= ~(uint32_t)bit;\n\t\t\tflags = (eFullParamsFlags)f;\n\t\t}\n\t\tinline void setFlag( eFullParamsFlags bit, bool set = true )\n\t\t{\n\t\t\tuint32_t f = (uint32_t)flags;\n\t\t\tif( set )\n\t\t\t\tf |= (uint32_t)bit;\n\t\t\telse\n\t\t\t\tf &= ~(uint32_t)bit;\n\t\t\tflags = (eFullParamsFlags)f;\n\t\t}\n\t};\n\n\tstruct sSegmentTime\n\t{\n\t\tint64_t begin, end;\n\t};\n\n\tinline uint32_t makeLanguageKey( const char* code )\n\t{\n\t\tassert( strlen( code ) <= 4 );\n\t\tuint32_t res = 0;\n\t\tuint32_t shift = 0;\n\t\tfor( size_t i = 0; i < 4; i++, code++, shift += 8 )\n\t\t{\n\t\t\tconst char c = *code;\n\t\t\tif( c == '\\0' )\n\t\t\t\treturn res;\n\t\t\tuint32_t u32 = (uint8_t)c;\n\t\t\tu32 = u32 << shift;\n\t\t\tres |= u32;\n\t\t}\n\t\treturn res;\n\t}\n\n\tusing pfnReportProgress = HRESULT( __stdcall* )( double val, iContext* ctx, void* pv ) noexcept;\n\tstruct sProgressSink\n\t{\n\t\tpfnReportProgress pfn;\n\t\tvoid* pv;\n\t};\n}"
  },
  {
    "path": "Whisper/API/sLanguageList.h",
    "content": "#pragma once\n#include <stdint.h>\n\nnamespace Whisper\n{\n\tstruct sLanguageEntry\n\t{\n\t\tuint32_t key;\n\t\tint id;\n\t\tconst char* name;\n\t};\n\n\tstruct sLanguageList\n\t{\n\t\tuint32_t length;\n\t\tconst sLanguageEntry* pointer;\n\t};\n}"
  },
  {
    "path": "Whisper/API/sLoadModelCallbacks.h",
    "content": "#pragma once\n\nnamespace Whisper\n{\n\tusing pfnLoadProgress = HRESULT( __stdcall* )( double val, void* pv ) noexcept;\n\t// Return S_OK to continue, or S_FALSE to fail with \"The operation was canceled by the user\" status code\n\tusing pfnCancel = HRESULT( __stdcall* )( void* pv ) noexcept;\n\t\n\tstruct sLoadModelCallbacks\n\t{\n\t\tpfnLoadProgress progress;\n\t\tpfnCancel cancel;\n\t\tvoid* pv;\n\t};\n}"
  },
  {
    "path": "Whisper/API/sModelSetup.h",
    "content": "#pragma once\n#include <stdint.h>\n\nnamespace Whisper\n{\n\tenum struct eModelImplementation : uint32_t\n\t{\n\t\t// GPGPU implementation based on Direct3D 11.0 compute shaders\n\t\tGPU = 1,\n\n\t\t// A hybrid implementation which uses DirectCompute for encode, and decodes on CPU\n\t\t// Not implemented in the published builds of the DLL. To enable, change BUILD_HYBRID_VERSION macro to 1\n\t\tHybrid = 2,\n\n\t\t// A reference implementation which uses the original GGML CPU-running code\n\t\t// Not implemented in the published builds of the DLL. To enable, change BUILD_BOTH_VERSIONS macro to 1\n\t\tReference = 3,\n\t};\n\n\tenum struct eGpuModelFlags : uint32_t\n\t{\n\t\tWave32 = 1,\n\t\tWave64 = 2,\n\t\tNoReshapedMatMul = 4,\n\t\tUseReshapedMatMul = 8,\n\t\tCloneable = 0x10,\n\t};\n\n\tstruct sModelSetup\n\t{\n\t\teModelImplementation impl = eModelImplementation::GPU;\n\t\tuint32_t flags = 0;\n\t\tconst wchar_t* adapter = nullptr;\n\t};\n\n\t// Function pointer to enumerate GPUs\n\tusing pfnListAdapters = void( __stdcall* )( const wchar_t* name, void* pv );\n\n\t// Function pointer to receive array of tokens from iModel.tokenize() API method\n\tusing pfnDecodedTokens = void( __stdcall* )( const int* tokens, int tokensLength, void* pv );\n}"
  },
  {
    "path": "Whisper/API/whisperComLight.h",
    "content": "#pragma once\n#include \"iMediaFoundation.cl.h\"\n#include \"iContext.cl.h\"\n#include \"iTranscribeResult.cl.h\""
  },
  {
    "path": "Whisper/API/whisperWindows.h",
    "content": "#pragma once\n#include \"iMediaFoundation.h\"\n#include \"iContext.h\"\n#include \"iTranscribeResult.h\""
  },
  {
    "path": "Whisper/CPU/BufferAllocator.cpp",
    "content": "#include <stdafx.h>\n#include \"BufferAllocator.h\"\n#include <immintrin.h>\n#include <ammintrin.h>\nusing namespace CpuCompute;\n\nHRESULT BufferAllocator::create( size_t cb )\n{\n\tCHECK( buffer.allocate( cb ) );\n\thead = 0;\n\tsize = cb;\n\tdbgMarkUninitializedMemory( buffer.pointer(), cb );\n\treturn S_OK;\n}\n\nnamespace\n{\n\t// Round up the integer by 32 bytes\n\t__forceinline size_t roundUpAlloc( size_t cb )\n\t{\n\t\tconst size_t mask = 31;\n\t\tcb += mask;\n\t\t// We require AVX1+FMA3 support, might as well use BMI1\n\t\treturn _andn_u64( mask, cb );\n\t}\n}\n\nvoid* BufferAllocator::allocate( size_t cb, size_t align ) noexcept\n{\n\tassert( align <= 32 );\n\tcb = roundUpAlloc( cb );\n\n\tuint8_t* pointer = buffer.pointer();\n\tif( head + cb > size || nullptr == pointer )\n\t{\n\t\tlogError( u8\"BufferAllocator.allocate, not enough capacity\" );\n\t\treturn nullptr;\n\t}\n\n\tvoid* const res = pointer + head;\n\thead += cb;\n\tassert( head <= size );\n\tdbgMarkUninitializedMemory( res, cb );\n\treturn res;\n}\n\nnamespace\n{\n\t// 2 MB of memory, we hope the OS kernel will then be smart enough to give us large pages.\n\tconstexpr size_t virtualAllocGranularityExp2 = 21;\n\n\tconstexpr size_t virtualAllocGranularityMask = ( ( (size_t)1 ) << virtualAllocGranularityExp2 ) - 1;\n\n\t// Round up the integer by 2 megabytes\n\t__forceinline size_t roundUpVirtualAlloc( size_t cb )\n\t{\n\t\tconst size_t mask = virtualAllocGranularityMask;\n\t\tcb += mask;\n\t\treturn _andn_u64( mask, cb );\n\t}\n}\n\nHRESULT VirtualAllocator::create( size_t cb )\n{\n\tif( nullptr != pointer )\n\t\treturn HRESULT_FROM_WIN32( ERROR_ALREADY_INITIALIZED );\n\tcb = roundUpVirtualAlloc( cb );\n\tpointer = (uint8_t*)VirtualAlloc( NULL, cb, MEM_RESERVE, PAGE_READWRITE );\n\tif( nullptr != pointer )\n\t{\n\t\thead = 0;\n\t\tsizeAllocated = 0;\n\t\tsizeVirtual = cb;\n\t\treturn S_OK;\n\t}\n\n\tconst HRESULT hr = getLastHr();\n\tlogErrorHr( hr, u8\"VirtualAlloc failed\" );\n\treturn hr;\n}\n\nvoid* VirtualAllocator::allocate( size_t cb, size_t align ) noexcept\n{\n\tassert( align <= 32 );\n\tcb = roundUpAlloc( cb );\n\n\tconst size_t newHead = head + cb;\n\tif( newHead <= sizeAllocated )\n\t{\n\t\tvoid* const res = pointer + head;\n\t\thead = newHead;\n\t\tdbgMarkUninitializedMemory( res, cb );\n\t\treturn res;\n\t}\n\n\tif( newHead <= sizeVirtual )\n\t{\n\t\tuint8_t* const ptrCommit = pointer + sizeAllocated;\n\t\tconst size_t cbCommit = roundUpVirtualAlloc( newHead ) - sizeAllocated;\n\t\tvoid* const res = VirtualAlloc( ptrCommit, cbCommit, MEM_COMMIT, PAGE_READWRITE );\n\t\tif( nullptr != res )\n\t\t{\n\t\t\tsizeAllocated += cbCommit;\n\t\t\tassert( sizeAllocated <= sizeVirtual );\n\t\t\tvoid* const res = pointer + head;\n\t\t\thead = newHead;\n\t\t\tdbgMarkUninitializedMemory( res, cb );\n\t\t\treturn res;\n\t\t}\n\n\t\tconst HRESULT hr = getLastHr();\n\t\tlogErrorHr( hr, u8\"VirtualAllocator.allocate, VirtualAlloc failed\" );\n\t\treturn nullptr;\n\t}\n\n\tlogError( u8\"VirtualAllocator.allocate, not enough arena capacity\" );\n\treturn nullptr;\n}\n\nVirtualAllocator::~VirtualAllocator()\n{\n\tif( nullptr == pointer )\n\t\treturn;\n\n\tif( VirtualFree( pointer, 0, MEM_RELEASE ) )\n\t{\n\t\tpointer = nullptr;\n\t\treturn;\n\t}\n\n\tconst HRESULT hr = getLastHr();\n\tlogErrorHr( hr, u8\"VirtualFree failed\" );\n}\n\n#ifndef NDEBUG\n// Reusing Microsoft's magic numbers: https://asawicki.info/news_1292_magic_numbers_in_visual_c\nvoid CpuCompute::dbgMarkUninitializedMemory( void* pv, size_t cb )\n{\n\t__stosb( (uint8_t*)pv, 0xCD, cb );\n}\nvoid CpuCompute::dbgMarkFreedMemory( void* pv, size_t cb )\n{\n\t__stosd( (DWORD*)pv, 0xFEEEFEEEu, cb / 4 );\n}\n#endif"
  },
  {
    "path": "Whisper/CPU/BufferAllocator.h",
    "content": "#pragma once\n#include \"LargeBuffer.h\"\n#include \"Tensor.h\"\n\nnamespace CpuCompute\n{\n#ifdef NDEBUG\n\tinline void dbgMarkUninitializedMemory( void* pv, size_t cb ) { }\n\tinline void dbgMarkFreedMemory( void* pv, size_t cb ) { }\n#else\n\tvoid dbgMarkUninitializedMemory( void* pv, size_t cb );\n\tvoid dbgMarkFreedMemory( void* pv, size_t cb );\n#endif\n\n\t// An implementation of arena allocator which slices pieces of a large buffer allocated in advance\n\tclass BufferAllocator : public iArenaAllocator\n\t{\n\t\tLargeBuffer buffer;\n\t\tsize_t head = 0;\n\t\tsize_t size = 0;\n\n\t\tvoid resetArena() noexcept override final\n\t\t{\n\t\t\thead = 0;\n\t\t\tdbgMarkFreedMemory( buffer.pointer(), size );\n\t\t}\n\n\t\tvoid* allocate( size_t cb, size_t align ) noexcept override final;\n\n\tpublic:\n\t\tBufferAllocator() = default;\n\t\tBufferAllocator( const BufferAllocator& ) = delete;\n\t\t~BufferAllocator() = default;\n\n\t\t// Allocate a large buffer with the specified count of bytes\n\t\tHRESULT create( size_t cb );\n\t};\n\n\t// An implementation of arena allocator which allocates a large chunk of virtual memory, and maps new physical pages into that memory region as needed.\n\tclass VirtualAllocator : public iArenaAllocator\n\t{\n\t\tuint8_t* pointer = nullptr;\n\t\tsize_t head = 0;\n\t\tsize_t sizeAllocated = 0;\n\t\tsize_t sizeVirtual = 0;\n\n\t\tvoid resetArena() noexcept override final\n\t\t{\n\t\t\thead = 0;\n\t\t\tdbgMarkFreedMemory( pointer, sizeAllocated );\n\t\t}\n\n\t\tvoid* allocate( size_t cb, size_t align ) noexcept override final;\n\n\tpublic:\n\n\t\tVirtualAllocator() = default;\n\t\tVirtualAllocator( const VirtualAllocator& ) = delete;\n\t\t~VirtualAllocator();\n\n\t\t// Reserve virtual memory space for the specified count of bytes in the arena, but don't allocate any pages\n\t\tHRESULT create( size_t cb );\n\t};\n}"
  },
  {
    "path": "Whisper/CPU/DecoderTensors.cpp",
    "content": "#include \"stdafx.h\"\n#include \"DecoderTensors.h\"\nusing namespace CpuCompute;\n\n#if TENSOR_GGML_COMPAT\nnamespace\n{\n\tclass CompatContext\n\t{\n\t\tstd::vector<ggml_tensor>& vec;\n\t\tsize_t index;\n\n\tpublic:\n\t\tCompatContext( std::vector<ggml_tensor>& dest, size_t layers ) :\n\t\t\tvec( dest )\n\t\t{\n\t\t\tconstexpr size_t tensorsPerLayer = 21;\n\t\t\tconst size_t count = tensorsPerLayer * layers + 4;\n\t\t\tvec.resize( count );\n\t\t\tindex = 0;\n\t\t}\n\n\t\tvoid add( const Tensor& rsi, ggml_tensor*& res )\n\t\t{\n\t\t\tggml_tensor& ten = vec[ index ];\n\t\t\tindex++;\n\t\t\tten = rsi.ggml();\n\t\t\tres = &ten;\n\t\t}\n\n\t\tvoid add2( const TensorPair& rsi, ggml_tensor*& w, ggml_tensor*& b )\n\t\t{\n\t\t\tadd( rsi.w, w );\n\t\t\tadd( rsi.b, b );\n\t\t}\n\n\t\tbool isComplete() const\n\t\t{\n\t\t\treturn index == vec.size();\n\t\t}\n\t};\n}\n\nvoid DecoderTensors::makeCompatTensors()\n{\n\tCompatContext ctx( ggml, layers.size() );\n\n\tctx.add( positionalEmbedding, d_pe );\n\tctx.add( tokenEmbedding, d_te );\n\tctx.add2( ln, d_ln_w, d_ln_b );\n\n\tfor( auto& i : layers )\n\t{\n\t\tctx.add2( i.attnLn0, i.attn_ln_0_w, i.attn_ln_0_b );\n\t\tctx.add2( i.attnLn1, i.attn_ln_1_w, i.attn_ln_1_b );\n\t\tctx.add2( i.attnQuery, i.attn_q_w, i.attn_q_b );\n\t\tctx.add( i.attnKey, i.attn_k_w );\n\t\tctx.add2( i.attnValue, i.attn_v_w, i.attn_v_b );\n\t\tctx.add2( i.crossAttnLn0, i.cross_attn_ln_0_w, i.cross_attn_ln_0_b );\n\t\tctx.add2( i.crossAttnLn1, i.cross_attn_ln_1_w, i.cross_attn_ln_1_b );\n\t\tctx.add2( i.crossAttnQuery, i.cross_attn_q_w, i.cross_attn_q_b );\n\t\tctx.add2( i.mlpLn, i.mlp_ln_w, i.mlp_ln_b );\n\t\tctx.add2( i.mlp0, i.mlp_0_w, i.mlp_0_b );\n\t\tctx.add2( i.mlp1, i.mlp_1_w, i.mlp_1_b );\n\t}\n\tassert( ctx.isComplete() );\n}\n#endif"
  },
  {
    "path": "Whisper/CPU/DecoderTensors.h",
    "content": "#pragma once\n#include <vector>\n#include \"Tensor.h\"\n#include \"LargeBuffer.h\"\n#if TENSOR_GGML_COMPAT\n#include \"../source/ggml.h\"\n#endif\n\nnamespace CpuCompute\n{\n\t// A set of tensors for one decoder's layer\n\tstruct LayerDecoder\n\t{\n\t\t// decoder.blocks.*.attn_ln\n\t\tTensorPair attnLn0;\n\t\t// decoder.blocks.*.attn.out\n\t\tTensorPair attnLn1;\n\t\t// decoder.blocks.*.attn.query\n\t\tTensorPair attnQuery;\n\t\t// decoder.blocks.*.attn.key\n\t\tTensor attnKey;\n\t\t// decoder.blocks.*.attn.value\n\t\tTensorPair attnValue;\n\t\t// decoder.blocks.*.cross_attn_ln\n\t\tTensorPair crossAttnLn0;\n\t\t// decoder.blocks.*.cross_attn.out\n\t\tTensorPair crossAttnLn1;\n\t\t// decoder.blocks.*.cross_attn.query\n\t\tTensorPair crossAttnQuery;\n\n\t\t// decoder.blocks.*.cross_attn.key\n\t\t// Tensor crossAttnKey;\n\t\t// decoder.blocks.*.cross_attn.value\n\t\t// TensorPair crossAttnValue;\n\n\t\t// decoder.blocks.*.mlp_ln\n\t\tTensorPair mlpLn;\n\t\t// decoder.blocks.*.mlp.0\n\t\tTensorPair mlp0;\n\t\t// decoder.blocks.*.mlp.2\n\t\tTensorPair mlp1;\n\n#if TENSOR_GGML_COMPAT\n\t\t// decoder.blocks.*.attn_ln\n\t\tggml_tensor* attn_ln_0_w;\n\t\tggml_tensor* attn_ln_0_b;\n\n\t\t// decoder.blocks.*.attn.out\n\t\tggml_tensor* attn_ln_1_w;\n\t\tggml_tensor* attn_ln_1_b;\n\n\t\t// decoder.blocks.*.attn.query\n\t\tggml_tensor* attn_q_w;\n\t\tggml_tensor* attn_q_b;\n\n\t\t// decoder.blocks.*.attn.key\n\t\tggml_tensor* attn_k_w;\n\n\t\t// decoder.blocks.*.attn.value\n\t\tggml_tensor* attn_v_w;\n\t\tggml_tensor* attn_v_b;\n\n\t\t// decoder.blocks.*.cross_attn_ln\n\t\tggml_tensor* cross_attn_ln_0_w;\n\t\tggml_tensor* cross_attn_ln_0_b;\n\n\t\t// decoder.blocks.*.cross_attn.out\n\t\tggml_tensor* cross_attn_ln_1_w;\n\t\tggml_tensor* cross_attn_ln_1_b;\n\n\t\t// decoder.blocks.*.cross_attn.query\n\t\tggml_tensor* cross_attn_q_w;\n\t\tggml_tensor* cross_attn_q_b;\n\n\t\t// decoder.blocks.*.mlp_ln\n\t\tggml_tensor* mlp_ln_w;\n\t\tggml_tensor* mlp_ln_b;\n\n\t\t// decoder.blocks.*.mlp.0\n\t\tggml_tensor* mlp_0_w;\n\t\tggml_tensor* mlp_0_b;\n\n\t\t// decoder.blocks.*.mlp.2\n\t\tggml_tensor* mlp_1_w;\n\t\tggml_tensor* mlp_1_b;\n#endif\n\t};\n\n\tstruct DecoderTensors\n\t{\n\t\t// decoder.positional_embedding\n\t\tTensor positionalEmbedding;\n\n\t\t// decoder.token_embedding\n\t\tTensor tokenEmbedding;\n\n\t\t// decoder.ln\n\t\tTensorPair ln;\n\t\t// A vector of layers\n\t\tstd::vector<LayerDecoder> layers;\n\n\t\tvoid setMemoryBuffer( LargeBuffer&& mem ) noexcept\n\t\t{\n\t\t\tmemory = std::move( mem );\n#if TENSOR_GGML_COMPAT\n\t\t\tmakeCompatTensors();\n#endif\n\t\t}\n\n#if TENSOR_GGML_COMPAT\n\t\tvoid makeCompatTensors();\n\n\t\t// decoder.positional_embedding\n\t\tggml_tensor* d_pe; // DD\n\n\t\t// decoder.token_embedding\n\t\tggml_tensor* d_te; // DD\n\n\t\t// decoder.ln\n\t\tggml_tensor* d_ln_w; // DD\n\t\tggml_tensor* d_ln_b; // DD\n#endif\n\n\tprivate:\n\t\t// A smart pointer which owns the memory for all the above tensors\n\t\tLargeBuffer memory;\n#if TENSOR_GGML_COMPAT\n\t\tstd::vector<ggml_tensor> ggml;\n#endif\n\t};\n}"
  },
  {
    "path": "Whisper/CPU/HybridLoader.cpp",
    "content": "#include \"stdafx.h\"\n#include \"HybridLoader.h\"\nusing namespace CpuCompute;\nusing namespace ComLight;\n\nstatic void populateDecodeTensorsMap( CAtlMap<CStringA, Tensor*>& map, int layersDec, DecoderTensors& dec )\n{\n\tdec.layers.resize( layersDec );\n\n\tmap[ \"decoder.positional_embedding\" ] = &dec.positionalEmbedding;\n\tmap[ \"decoder.token_embedding.weight\" ] = &dec.tokenEmbedding;\n\tmap[ \"decoder.ln.weight\" ] = &dec.ln.w;\n\tmap[ \"decoder.ln.bias\" ] = &dec.ln.b;\n\n\tCStringA tempString;\n\tauto add = [ & ]( const char* name, int i, Tensor& t )\n\t{\n\t\ttempString.Format( \"decoder.blocks.%i.%s\", i, name );\n\t\tmap[ tempString ] = &t;\n\t};\n\n\tauto add2 = [ & ]( const char* name, int i, TensorPair& tensors )\n\t{\n\t\ttempString.Format( \"decoder.blocks.%i.%s.weight\", i, name );\n\t\tmap[ tempString ] = &tensors.w;\n\t\ttempString.Format( \"decoder.blocks.%i.%s.bias\", i, name );\n\t\tmap[ tempString ] = &tensors.b;\n\t};\n\n\tfor( int i = 0; i < layersDec; i++ )\n\t{\n\t\tauto& gpu = dec.layers[ i ];\n\t\tadd2( \"mlp_ln\", i, gpu.mlpLn );\n\t\tadd2( \"mlp.0\", i, gpu.mlp0 );\n\t\tadd2( \"mlp.2\", i, gpu.mlp1 );\n\t\tadd2( \"attn_ln\", i, gpu.attnLn0 );\n\t\tadd2( \"attn.query\", i, gpu.attnQuery );\n\t\tadd( \"attn.key.weight\", i, gpu.attnKey );\n\n\t\tadd2( \"attn.value\", i, gpu.attnValue );\n\t\tadd2( \"attn.out\", i, gpu.attnLn1 );\n\n\t\tadd2( \"cross_attn_ln\", i, gpu.crossAttnLn0 );\n\t\tadd2( \"cross_attn.query\", i, gpu.crossAttnQuery );\n\n\t\t// These 3 tensors are used by the encode() method, to compute cross-attention buffers\n\t\t// Need them in VRAM even for the hybrid model\n\t\t// add( \"cross_attn.key.weight\", i, gpu.cross_attn_k_w );\n\t\t// add2( \"cross_attn.value\", i, gpu.cross_attn_v_w, gpu.cross_attn_v_b );\n\t\tadd2( \"cross_attn.out\", i, gpu.crossAttnLn1 );\n\t}\n}\n\nHybridLoader::HybridLoader( DecoderTensors& m, int countLayers ) :\n\tdestination( m )\n{\n\tpopulateDecodeTensorsMap( map, countLayers, destination );\n\tpending.reserve( map.GetCount() );\n}\n\nHRESULT HybridLoader::setupTensor( const CStringA& name, int n_dims, int ftype, const std::array<int, 4>& ne, ComLight::iReadStream* stream, int64_t& postponedBytes )\n{\n\tauto p = map.Lookup( name );\n\tif( nullptr == p )\n\t\treturn S_FALSE;\n\n\tTensor& rdi = *p->m_value;\n\tPendingTensor& pt = pending.emplace_back();\n\n\t__m128i vec = load16( ne.data() );\n\tvec = _mm_insert_epi32( vec, 1, 3 );\n\tstore16( &rdi.ne, vec );\n\trdi.setDenseStrides();\n\n\tpt.destPointer = p->m_value;\n\tCHECK( stream->getPosition( pt.streamOffset ) );\n\tpt.bufferOffset = bufferBytes;\n\n\tsize_t cbElement;\n\tif( ftype == 0 )\n\t{\n\t\trdi.setType( eDataType::FP32 );\n\t\tcbElement = 4;\n\t}\n\telse\n\t{\n\t\trdi.setType( eDataType::FP16 );\n\t\tcbElement = 2;\n\t}\n\n\tconst size_t totalElts = (size_t)(uint32_t)ne[ 0 ] * (uint32_t)ne[ 1 ] * (uint32_t)ne[ 2 ];\n\tif( totalElts * cbElement > UINT_MAX )\n\t\treturn DISP_E_OVERFLOW;\n\n\tsize_t payloadBytes = cbElement * totalElts;\n\tpt.payloadBytes = payloadBytes;\n\tCHECK( stream->seek( payloadBytes, eSeekOrigin::Current ) );\n\tpostponedBytes += (int64_t)payloadBytes;\n\n\tpayloadBytes = ( payloadBytes + 31 ) & ( ~( (size_t)31 ) );\n\tbufferBytes += payloadBytes;\n\treturn S_OK;\n}\n\nHRESULT HybridLoader::completeLoad( ComLight::iReadStream* stream, iLoaderProgressSink& progressSink )\n{\n\tif( pending.size() != map.GetCount() )\n\t{\n\t\tlogError( u8\"Not all tensors loaded from model file - expected %zu, got %zu\", map.GetCount(), pending.size() );\n\t\treturn E_INVALIDARG;\n\t}\n\n\tLargeBuffer buffer;\n\tCHECK( buffer.allocate( bufferBytes ) );\n\n\tuint8_t* rdi = buffer.pointer();\n\n\tfor( const auto& pt : pending )\n\t{\n\t\tif( pt.payloadBytes > INT_MAX )\n\t\t\treturn DISP_E_OVERFLOW;\n\t\tCHECK( stream->seek( pt.streamOffset, eSeekOrigin::Begin ) );\n\n\t\tint written = 0;\n\t\tCHECK( stream->read( rdi, (int)pt.payloadBytes, written ) );\n\t\tCHECK( progressSink.gotBytes( (int64_t)pt.payloadBytes ) );\n\n\t\tpt.destPointer->setDataPointer( rdi );\n\n\t\tconst size_t cb = ( pt.payloadBytes + 31 ) & ( ~( (size_t)31 ) );\n\t\trdi += cb;\n\t}\n\n\tCHECK( buffer.setReadOnly( bufferBytes ) );\n\tdestination.setMemoryBuffer( std::move( buffer ) );\n\n\tconstexpr double mulMb = 1.0 / ( 1 << 20 );\n\tlogDebug( u8\"Loaded %zu decoder tensors, %g MB RAM\", pending.size(), mulMb * (double)(int64_t)bufferBytes );\n\treturn S_OK;\n}"
  },
  {
    "path": "Whisper/CPU/HybridLoader.h",
    "content": "#pragma once\n#include \"DecoderTensors.h\"\n#include <atlstr.h>\n#include <atlcoll.h>\n#include \"../../ComLightLib/streams.h\"\n\nnamespace CpuCompute\n{\n\t__interface iLoaderProgressSink\n\t{\n\t\tHRESULT gotBytes( int64_t cb );\n\t};\n\n\tclass HybridLoader\n\t{\n\t\tDecoderTensors& destination;\n\t\tCAtlMap<CStringA, Tensor*> map;\n\t\tsize_t bufferBytes = 0;\n\n\t\tstruct alignas( 32 ) PendingTensor\n\t\t{\n\t\t\tTensor* destPointer = nullptr;\n\t\t\tint64_t streamOffset = 0;\n\t\t\tsize_t bufferOffset = 0;\n\t\t\tsize_t payloadBytes = 0;\n\t\t};\n\t\tstd::vector<PendingTensor> pending;\n\n\tpublic:\n\n\t\tHybridLoader( DecoderTensors& m, int countLayers );\n\n\t\tHRESULT setupTensor( const CStringA& name, int n_dims, int ftype, const std::array<int, 4>& ne, ComLight::iReadStream* stream, int64_t& postponedBytes );\n\n\t\tHRESULT completeLoad( ComLight::iReadStream* stream, iLoaderProgressSink& progressSink );\n\t};\n}"
  },
  {
    "path": "Whisper/CPU/KvTensors.h",
    "content": "#pragma once\n#include \"Tensor.h\"\n#include \"LargeBuffer.h\"\n#include \"../Whisper/sModelParams.h\"\n\nnamespace CpuCompute\n{\n\tclass KvTensors\n\t{\n\t\tuint16_t* keys = nullptr;\n\t\tuint16_t* values = nullptr;\n\t\tuint32_t size = 0;\n\n\t\tCpuCompute::LargeBuffer memory;\n\n\tpublic:\n\t\t// Create these two large tensors, FP16 precision\n\t\tHRESULT create( const Whisper::sModelParams& mp );\n\n\t\t// A slice of model.memory_cross_k tensor\n\t\tTensor keysView( uint32_t len, uint32_t off ) const\n\t\t{\n\t\t\tif( len + off <= size )\n\t\t\t\treturn Tensor::fromData( keys + off, eDataType::FP16, len );\n\t\t\tthrow E_BOUNDS;\n\t\t}\n\n\t\t// A slice of model.memory_cross_v tensor\n\t\tTensor valuesView( uint32_t len, uint32_t off ) const\n\t\t{\n\t\t\tif( len + off <= size )\n\t\t\t\treturn Tensor::fromData( values + off, eDataType::FP16, len );\n\t\t\tthrow E_BOUNDS;\n\t\t}\n\t};\n}"
  },
  {
    "path": "Whisper/CPU/KvTensorsCpu.cpp",
    "content": "#include \"stdafx.h\"\n#include \"KvTensors.h\"\nusing namespace CpuCompute;\n\n// Create these two large tensors, FP16 precision\nHRESULT KvTensors::create( const Whisper::sModelParams& mp )\n{\n\tconst uint32_t n_mem = mp.n_text_layer * mp.n_text_ctx;\n\tconst uint32_t n_elements = mp.n_text_state * n_mem;\n\n\tconst size_t cb = sizeof( uint16_t ) * (size_t)n_elements * 2;\n\tCHECK( memory.allocate( cb ) );\n\n\tuint16_t* pointer = (uint16_t*)memory.pointer();\n\tkeys = pointer;\n\tvalues = pointer + n_elements;\n\tsize = n_elements;\n\treturn S_OK;\n}"
  },
  {
    "path": "Whisper/CPU/LargeBuffer.cpp",
    "content": "#include \"stdafx.h\"\n#include \"LargeBuffer.h\"\nusing namespace CpuCompute;\n\nvoid LargeBuffer::deallocate()\n{\n\tif( nullptr == pv )\n\t\treturn;\n\tVirtualFree( pv, 0, MEM_RELEASE );\n\tpv = nullptr;\n}\n\nHRESULT LargeBuffer::allocate( size_t cb )\n{\n\tdeallocate();\n\t\n\tpv = VirtualAlloc( nullptr, cb, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE );\n\tif( nullptr != pv )\n\t\treturn S_OK;\n\treturn HRESULT_FROM_WIN32( GetLastError() );\n}\n\nHRESULT LargeBuffer::setReadOnly( size_t cb )\n{\n\tif( nullptr != pv )\n\t{\n\t\tDWORD op = 0;\n\t\tif( VirtualProtect( pv, cb, PAGE_READONLY, &op ) )\n\t\t\treturn S_OK;\n\t\treturn HRESULT_FROM_WIN32( GetLastError() );\n\t}\n\telse\n\t\treturn OLE_E_BLANK;\n}"
  },
  {
    "path": "Whisper/CPU/LargeBuffer.h",
    "content": "#pragma once\n\nnamespace CpuCompute\n{\n\t// A large memory buffer allocated with VirtualAlloc kernel API, bypassing the heap.\n\tclass LargeBuffer\n\t{\n\t\tvoid* pv = nullptr;\n\tpublic:\n\t\tLargeBuffer() = default;\n\t\tLargeBuffer( const LargeBuffer& ) = delete;\n\t\tLargeBuffer( LargeBuffer&& that ) noexcept\n\t\t{\n\t\t\tpv = that.pv;\n\t\t\tthat.pv = nullptr;\n\t\t}\n\t\t~LargeBuffer()\n\t\t{\n\t\t\tdeallocate();\n\t\t}\n\t\tvoid operator=( LargeBuffer&& that ) noexcept\n\t\t{\n\t\t\tstd::swap( pv, that.pv );\n\t\t}\n\t\tvoid operator=( const LargeBuffer& that ) = delete;\n\n\t\t// Allocate buffer with specified count of bytes, and read+write memory protection\n\t\t// The OS kernel guarantees zero-initialization of that memory.\n\t\tHRESULT allocate( size_t cb );\n\n\t\t// Change memory protection of the buffer to read only\n\t\tHRESULT setReadOnly( size_t cb );\n\n\t\t// Unless the pointer is nullptr, deallocate the buffer\n\t\tvoid deallocate();\n\n\t\t// Pointer to the start of the buffer, aligned by memory page = 4 kilobytes\n\t\tuint8_t* pointer() const\n\t\t{\n\t\t\tassert( nullptr != pv );\n\t\t\treturn (uint8_t*)pv;\n\t\t}\n\t};\n}"
  },
  {
    "path": "Whisper/CPU/MlContext.h",
    "content": "#pragma once\n#include \"Tensor.h\"\n#include \"ParallelForRunner.h\"\n\nnamespace CpuCompute\n{\n\tclass MlContext\n\t{\n\t\tParallelForRunner pfor;\n\t\tiMemoryAllocator* allocator = nullptr;\n\n\tpublic:\n\t\tMlContext( int threads );\n\t\tMlContext( const MlContext& ) = delete;\n\t\t~MlContext() = default;\n\n\t\tHRESULT setThreadsCount( int threads )\n\t\t{\n\t\t\treturn pfor.setThreadsCount( threads );\n\t\t}\n\n\t\tiMemoryAllocator* setAllocator( iMemoryAllocator* alloc )\n\t\t{\n\t\t\tiMemoryAllocator* const ret = allocator;\n\t\t\tallocator = alloc;\n\t\t\treturn ret;\n\t\t}\n\n\t\tTensor createTensor( eDataType type, const std::array<uint32_t, 4>& size );\n\t\tTensor createTensor( eDataType type, std::initializer_list<uint32_t> size );\n\n\t\tTensor addRows( const Tensor& d_te, const Tensor& d_pe, const int* tokens, const int n_tokens, const int n_past );\n\n\t\tTensor norm( const Tensor& arg );\n\n\t\t// cur = add( mul( repeat( w, cur ), cur ), repeat( b, cur ) );\n\t\tvoid fmaRepeat( Tensor& cur, const Tensor& w, const Tensor& b );\n\n\t\tinline void fmaRepeat( Tensor& cur, const TensorPair wb )\n\t\t{\n\t\t\tfmaRepeat( cur, wb.w, wb.b );\n\t\t}\n\n\t\t// Multiply two matrices\n\t\tTensor mulMat( const Tensor& a, const Tensor& b );\n\n\t\t// cur = add( repeat( b, cur ), cur ); cur = scale(cur, scaling)\n\t\tvoid addRepeatScale( Tensor& cur, const Tensor& b, float scaling );\n\n\t\tvoid addRepeat( Tensor& cur, const Tensor& b );\n\n\t\tTensor add( const Tensor& a, const Tensor& b );\n\t\tvoid addInPlace( Tensor& a, const Tensor& b );\n\t\tvoid addRepeatGelu( Tensor& cur, const Tensor& b );\n\n\t\t// cur = scale(cur, scaling)\n\t\tvoid scale( Tensor& cur, float scaling );\n\n\t\tvoid diagMaskInf( Tensor& cur, uint32_t n_past );\n\n\t\tvoid softMax( Tensor& cur, float inputScale = 1.0f );\n\n\t\tTensor copy( const Tensor& a, eDataType type, std::initializer_list<uint32_t> size );\n\n\t\tHRESULT copyImpl( Tensor& result, const Tensor& source );\n\n\t\tTensor permute( const Tensor& a, uint8_t axis0, uint8_t axis1, uint8_t axis2, uint8_t axis3 );\n\n\t\tvoid copyInPlace( Tensor& dest, const Tensor& a, eDataType type, std::initializer_list<uint32_t> size );\n\t};\n}"
  },
  {
    "path": "Whisper/CPU/MlContextCpu.cpp",
    "content": "#include \"stdafx.h\"\n#include \"MlContext.h\"\n#include \"simdUtils.h\"\n#include \"mulMat.h\"\nusing namespace CpuCompute;\n\nMlContext::MlContext( int threads ) : pfor( threads )\n{\n}\n\nTensor MlContext::createTensor( eDataType type, const std::array<uint32_t, 4>& size )\n{\n\tTensor res;\n\tcheck( res.create( type, size, allocator ) );\n\treturn res;\n}\n\nTensor MlContext::createTensor( eDataType type, std::initializer_list<uint32_t> size )\n{\n\tTensor res;\n\tcheck( res.create( type, size, allocator ) );\n\treturn res;\n}\n\nnamespace\n{\n\tinline const uint16_t* getRow16( const Tensor& t, size_t index )\n\t{\n\t\tconst uint16_t* rsi = t.fp16();\n\t\trsi += index * t.nb[ 1 ];\n\t\treturn rsi;\n\t}\n\tinline const float* getRow32( const Tensor& t, size_t index )\n\t{\n\t\tconst float* rsi = t.fp32();\n\t\trsi += index * t.nb[ 1 ];\n\t\treturn rsi;\n\t}\n}\n\nTensor MlContext::addRows( const Tensor& d_te, const Tensor& d_pe, const int* tokens, const int n_tokens, const int n_past )\n{\n\tif( d_te.type() != eDataType::FP16 || d_pe.type() != eDataType::FP32 )\n\t\tthrow E_INVALIDARG;\n\tif( d_te.ne[ 0 ] != d_pe.ne[ 0 ] )\n\t\tthrow E_INVALIDARG;\n\tif( n_tokens <= 0 )\n\t\tthrow E_BOUNDS;\n\n\tTensor res = createTensor( eDataType::FP32, { d_te.ne[ 0 ], (uint32_t)n_tokens } );\n\n\tconst size_t inner = (size_t)d_te.ne[ 0 ];\n\tconst size_t outer = (size_t)n_tokens;\n\tfloat* rdi = res.fp32();\n\tfor( size_t i = 0; i < outer; i++, rdi += inner, tokens++ )\n\t{\n\t\tconst uint16_t* const source1 = getRow16( d_te, *(const uint32_t*)tokens );\n\t\tconst float* const source2 = getRow32( d_pe, i + (size_t)n_past );\n\t\taddF16to32( rdi, source1, source2, inner );\n\t}\n\treturn res;\n}\n\nnamespace\n{\n\tclass DispatchHelper3\n\t{\n\t\tstd::array<uint32_t, 3> ne;\n\n\tpublic:\n\t\tDispatchHelper3() = default;\n\t\tDispatchHelper3( uint32_t x, uint32_t y, uint32_t z )\n\t\t{\n\t\t\tassert( x > 0 && y > 0 && z > 0 );\n\t\t\tne[ 0 ] = x;\n\t\t\tne[ 1 ] = y;\n\t\t\tne[ 2 ] = z;\n\t\t}\n\t\tsize_t groupsCount() const\n\t\t{\n\t\t\tsize_t res = ne[ 0 ];\n\t\t\tres *= ne[ 1 ];\n\t\t\tres *= ne[ 2 ];\n\t\t\treturn res;\n\t\t}\n\t\tstd::array<uint32_t, 3> unpack( size_t idx ) const\n\t\t{\n\t\t\tassert( idx < groupsCount() );\n\t\t\tstd::array<uint32_t, 3> res;\n\t\t\tres[ 0 ] = (uint32_t)( idx % ne[ 0 ] );\n\t\t\tidx = idx / ne[ 0 ];\n\t\t\tres[ 1 ] = (uint32_t)( idx % ne[ 1 ] );\n\t\t\tres[ 2 ] = (uint32_t)( idx / ne[ 1 ] );\n\t\t\treturn res;\n\t\t}\n\t\tvoid next( std::array<uint32_t, 3>& i ) const\n\t\t{\n\t\t\ti[ 0 ]++;\n\t\t\tif( i[ 0 ] < ne[ 0 ] )\n\t\t\t\treturn;\n\t\t\ti[ 0 ] = 0;\n\t\t\ti[ 1 ]++;\n\t\t\tif( i[ 1 ] < ne[ 1 ] )\n\t\t\t\treturn;\n\t\t\ti[ 1 ] = 0;\n\t\t\ti[ 2 ]++;\n\t\t}\n\t};\n\n\tinline const float* sourceRow( const float* rsi, const std::array<uint32_t, 3>& idx, size_t nb0, size_t nb1, size_t nb2 )\n\t{\n\t\tconst size_t r0 = idx[ 0 ] * nb0;\n\t\tconst size_t r1 = idx[ 1 ] * nb1;\n\t\tconst size_t r2 = idx[ 2 ] * nb2;\n\t\trsi = rsi + r0 + r1 + r2;\n\t\treturn rsi;\n\t}\n\n\tstruct NormContext : public iComputeRange\n\t{\n\t\tconst float* source;\n\t\tfloat* result;\n\t\tsize_t inner;\n\t\tDispatchHelper3 threads;\n\t\tstd::array<uint32_t, 3> nbInput;\n\n\t\tHRESULT __stdcall compute( size_t i, size_t end ) const override final\n\t\t{\n\t\t\tALIGNED_SPAN( temp, inner );\n\n\t\t\tstd::array<uint32_t, 3> idx = threads.unpack( i );\n\t\t\tfloat* rdi = result + i * inner;\n\t\t\tfor( ; i < end; i++, rdi += inner, threads.next( idx ) )\n\t\t\t{\n\t\t\t\tconst float* rsi = sourceRow( source, idx, nbInput[ 0 ], nbInput[ 1 ], nbInput[ 2 ] );\n\t\t\t\tnorm( rdi, temp, rsi, inner );\n\t\t\t}\n\t\t\treturn S_OK;\n\t\t}\n\t};\n}\n\nTensor MlContext::norm( const Tensor& arg )\n{\n\tif( arg.type() != eDataType::FP32 || arg.nb[ 0 ] != 1 )\n\t\tthrow E_INVALIDARG;\n\tTensor res = createTensor( eDataType::FP32, arg.ne );\n\n\tNormContext context;\n\tcontext.source = arg.fp32();\n\tcontext.result = res.fp32();\n\tcontext.inner = arg.ne[ 0 ];\n\tcontext.threads = DispatchHelper3( arg.ne[ 1 ], arg.ne[ 2 ], arg.ne[ 3 ] );\n\tcontext.nbInput = { arg.nb[ 1 ], arg.nb[ 2 ], arg.nb[ 3 ] };\n\n\tcheck( pfor.parallelFor( context, context.threads.groupsCount() ) );\n\treturn res;\n}\n\nvoid MlContext::fmaRepeat( Tensor& cur, const Tensor& w, const Tensor& b )\n{\n\tif( !( cur.isContinuous() && w.isContinuous() && b.isContinuous() ) )\n\t\tthrow E_INVALIDARG;\n\n\tif( !( cur.type() == eDataType::FP32 && w.type() == eDataType::FP32 && b.type() == eDataType::FP32 ) )\n\t\tthrow E_INVALIDARG;\n\n\tif( !isSameShape( w, b ) )\n\t\tthrow E_INVALIDARG;\n\n\tDispatchHelper3 helper{ cur.ne[ 1 ], cur.ne[ 2 ], cur.ne[ 3 ] };\n\tstd::array<uint32_t, 3> idx = { 0, 0, 0 };\n\tconst size_t countRows = helper.groupsCount();\n\n\tconst size_t innerRes = cur.ne[ 0 ];\n\tconst size_t innerPattern = w.ne[ 0 ];\n\n\tfloat* rdi = cur.fp32();\n\tfor( size_t i = 0; i < countRows; i++, helper.next( idx ), rdi += innerRes )\n\t{\n\t\tstd::array<uint32_t, 3> idxPattern;\n\t\tidxPattern[ 0 ] = idx[ 0 ] % w.ne[ 1 ];\n\t\tidxPattern[ 1 ] = idx[ 1 ] % w.ne[ 2 ];\n\t\tidxPattern[ 2 ] = idx[ 2 ] % w.ne[ 3 ];\n\n\t\tconst float* s1 = sourceRow( w.fp32(), idxPattern, w.nb[ 1 ], w.nb[ 2 ], w.nb[ 3 ] );\n\t\tconst float* s2 = sourceRow( b.fp32(), idxPattern, b.nb[ 1 ], b.nb[ 2 ], b.nb[ 3 ] );\n\t\tfmaRepeatRow( rdi, innerRes, s1, s2, innerPattern );\n\t}\n}\n\nTensor MlContext::mulMat( const Tensor& a, const Tensor& b )\n{\n\tif( !DirectCompute::canMulMat( a, b ) )\n\t\tthrow E_INVALIDARG;\n\n\tstd::array<uint32_t, 4> ne{ a.ne[ 1 ], b.ne[ 1 ], a.ne[ 2 ], b.ne[ 3 ] };\n\tTensor result = createTensor( eDataType::FP32, ne );\n\n\tcheck( CpuCompute::mulMat( result, a, b, pfor ) );\n\treturn result;\n}\n\n// cur = add( repeat( b, cur ), cur ); cur = scale(cur, scaling)\nvoid MlContext::addRepeatScale( Tensor& cur, const Tensor& b, float scaling )\n{\n\tif( !( cur.isContinuous() && b.isContinuous() ) )\n\t\tthrow E_INVALIDARG;\n\tif( !( cur.type() == eDataType::FP32 && b.type() == eDataType::FP32 ) )\n\t\tthrow E_INVALIDARG;\n\n\tDispatchHelper3 helper{ cur.ne[ 1 ], cur.ne[ 2 ], cur.ne[ 3 ] };\n\tstd::array<uint32_t, 3> idx = { 0, 0, 0 };\n\tconst size_t countRows = helper.groupsCount();\n\n\tconst size_t innerRes = (uint32_t)cur.ne[ 0 ];\n\tconst size_t innerPattern = (uint32_t)b.ne[ 0 ];\n\n\tfloat* rdi = cur.fp32();\n\tconst __m256 scale = _mm256_set1_ps( scaling );\n\tfor( size_t i = 0; i < countRows; i++, helper.next( idx ), rdi += innerRes )\n\t{\n\t\tstd::array<uint32_t, 3> idxPattern;\n\t\tidxPattern[ 0 ] = idx[ 0 ] % (uint32_t)b.ne[ 1 ];\n\t\tidxPattern[ 1 ] = idx[ 1 ] % (uint32_t)b.ne[ 2 ];\n\t\tidxPattern[ 2 ] = idx[ 2 ] % (uint32_t)b.ne[ 3 ];\n\n\t\tconst float* source = sourceRow( b.fp32(), idxPattern, b.nb[ 1 ], b.nb[ 2 ], b.nb[ 3 ] );\n\t\taddRepeatScaleRow( rdi, innerRes, source, innerPattern, scale );\n\t}\n}\n\nvoid MlContext::addRepeat( Tensor& cur, const Tensor& b )\n{\n\tif( !( cur.isContinuous() && b.isContinuous() ) )\n\t\tthrow E_INVALIDARG;\n\tif( !( cur.type() == eDataType::FP32 && b.type() == eDataType::FP32 ) )\n\t\tthrow E_INVALIDARG;\n\n\tDispatchHelper3 helper{ cur.ne[ 1 ], cur.ne[ 2 ], cur.ne[ 3 ] };\n\tstd::array<uint32_t, 3> idx = { 0, 0, 0 };\n\tconst size_t countRows = helper.groupsCount();\n\n\tconst size_t innerRes = (uint32_t)cur.ne[ 0 ];\n\tconst size_t innerPattern = (uint32_t)b.ne[ 0 ];\n\n\tfloat* rdi = cur.fp32();\n\tfor( size_t i = 0; i < countRows; i++, helper.next( idx ), rdi += innerRes )\n\t{\n\t\tstd::array<uint32_t, 3> idxPattern;\n\t\tidxPattern[ 0 ] = idx[ 0 ] % (uint32_t)b.ne[ 1 ];\n\t\tidxPattern[ 1 ] = idx[ 1 ] % (uint32_t)b.ne[ 2 ];\n\t\tidxPattern[ 2 ] = idx[ 2 ] % (uint32_t)b.ne[ 3 ];\n\n\t\tconst float* source = sourceRow( b.fp32(), idxPattern, b.nb[ 1 ], b.nb[ 2 ], b.nb[ 3 ] );\n\t\taddRepeatRow( rdi, innerRes, source, innerPattern );\n\t}\n}\n\n// cur = scale(cur, scaling)\nvoid MlContext::scale( Tensor& cur, float scaling )\n{\n\tif( !( cur.isContinuous() && cur.type() == eDataType::FP32 ) )\n\t\tthrow E_INVALIDARG;\n\n\tconst size_t len = cur.countElements();\n\tconst __m256 scale = _mm256_set1_ps( scaling );\n\tscaleRow( cur.fp32(), len, scale );\n}\n\nvoid MlContext::diagMaskInf( Tensor& cur, uint32_t n_past )\n{\n\tif( !( cur.isContinuous() && cur.type() == eDataType::FP32 ) )\n\t\tthrow E_INVALIDARG;\n\n\tconst size_t n = cur.countRows();\n\tconst size_t nc = cur.ne[ 0 ];\n\tconst size_t nr = cur.ne[ 1 ];\n\tconst size_t nz = n / nr;\n\n\tfor( size_t k = 0; k < nz; k++ )\n\t{\n\t\tfor( size_t j = 0; j < nr; j++ )\n\t\t{\n\t\t\tfloat* const rdi = cur.fp32() + k * cur.nb[ 2 ] + j * cur.nb[ 1 ];\n\t\t\t// +1 because the original code checked for `if( i > n_past + j )`\n\t\t\t// That's why the first index to write is ( n_past + j + 1 )\n\t\t\tconst size_t start = n_past + j + 1;\n\t\t\tconst ptrdiff_t len = (ptrdiff_t)nc - (ptrdiff_t)start;\n\t\t\tif( len <= 0 )\n\t\t\t\tcontinue;\n\n\t\t\t// Generates a store string instruction (rep stosd).\n\t\t\t// The magic number is negative infinity in FP32: https://www.h-schmidt.net/FloatConverter/IEEE754.html\n\t\t\t__stosd( (DWORD*)( rdi + start ), 0xff800000u, (size_t)len );\n\t\t}\n\t}\n}\n\nvoid MlContext::softMax( Tensor& cur, float inputScale )\n{\n\tif( !( cur.isContinuous() && cur.type() == eDataType::FP32 ) )\n\t\tthrow E_INVALIDARG;\n\n\tstruct SoftMaxContext : public iComputeRange\n\t{\n\t\tfloat* data;\n\t\tfloat inputScale;\n\t\tsize_t length, stride;\n\n\t\tHRESULT __stdcall compute( size_t i, size_t end ) const override final\n\t\t{\n\t\t\tfloat* rdi = data + stride * i;\n\t\t\tfor( ; i < end; i++, rdi += stride )\n\t\t\t\t::softMax( rdi, length, inputScale );\n\t\t\treturn S_OK;\n\t\t}\n\t};\n\n\tSoftMaxContext context;\n\tcontext.data = cur.fp32();\n\tcontext.inputScale = inputScale;\n\tcontext.length = cur.ne[ 0 ];\n\tcontext.stride = cur.nb[ 1 ];\n\n\tconst size_t n = cur.countRows();\n\tpfor.parallelFor( context, n );\n}\n\nnamespace\n{\n\ttemplate<class R, class S>\n\t__forceinline void copyElement( R* rdi, const S* rsi )\n\t{\n\t\tstatic_assert( std::is_same<R, S>() );\n\t\t*rdi = *rsi;\n\t}\n\ttemplate<>\n\t__forceinline void copyElement<float, uint16_t>( float* rdi, const uint16_t* rsi )\n\t{\n\t\t__m128i iv = _mm_cvtsi32_si128( *rsi );\n\t\t__m128 fv = _mm_cvtph_ps( iv );\n\t\t_mm_store_ss( rdi, fv );\n\t}\n\ttemplate<>\n\t__forceinline void copyElement<uint16_t, float>( uint16_t* rdi, const float* rsi )\n\t{\n\t\t__m128 fv = _mm_load_ss( rsi );\n\t\t__m128i iv = _mm_cvtps_ph( fv, 0 );\n\t\t*rdi = (uint16_t)(uint32_t)_mm_cvtsi128_si32( iv );\n\t}\n\n\ttemplate<class R, class S>\n\t__forceinline void copyRow( R* rdi, const S* rsi, size_t length )\n\t{\n\t\tstatic_assert( std::is_same<R, S>() );\n\t\tmemcpy( rdi, rsi, length * sizeof( R ) );\n\t}\n\ttemplate<>\n\t__forceinline void copyRow<uint16_t, float>( uint16_t* rdi, const float* rsi, size_t length )\n\t{\n\t\tfloatsDowncast( rdi, rsi, length );\n\t}\n\ttemplate<>\n\t__forceinline void copyRow<float, uint16_t>( float* rdi, const uint16_t* rsi, size_t length )\n\t{\n\t\tfloatsUpcast( rdi, rsi, length );\n\t}\n\n\ttemplate<class R, class S>\n\tstatic void __declspec( noinline ) copyImpl( R* rdi, const S* rsi, const TensorShape& shape )\n\t{\n\t\tconst bool continuousRows = shape.nb[ 0 ] == 1;\n\n\t\tfor( size_t i03 = 0; i03 < shape.ne[ 3 ]; i03++, rsi += shape.nb[ 3 ] )\n\t\t{\n\t\t\tconst S* source2 = rsi;\n\t\t\tfor( size_t i02 = 0; i02 < shape.ne[ 2 ]; i02++, source2 += shape.nb[ 2 ] )\n\t\t\t{\n\t\t\t\tconst S* source1 = source2;\n\t\t\t\tfor( size_t i01 = 0; i01 < shape.ne[ 1 ]; i01++, source1 += shape.nb[ 1 ] )\n\t\t\t\t{\n\t\t\t\t\t// Performance optimization here: when the rows are dense, we can copy them much faster with memcpy()\n\t\t\t\t\t// Or at least with AVX, when we need to convert between numeric types\n\t\t\t\t\tif( continuousRows )\n\t\t\t\t\t{\n\t\t\t\t\t\t// This branch is very predictable, same outcome for all loop iterations\n\t\t\t\t\t\tcopyRow( rdi, source1, shape.ne[ 0 ] );\n\t\t\t\t\t\trdi += shape.ne[ 0 ];\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tconst S* source0 = source1;\n\t\t\t\t\t\tfor( size_t i00 = 0; i00 < shape.ne[ 0 ]; i00++, source0 += shape.nb[ 0 ] )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcopyElement( rdi, source0 );\n\t\t\t\t\t\t\trdi++;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nHRESULT MlContext::copyImpl( Tensor& result, const Tensor& source )\n{\n\tif( !( result.isContinuous() && ( result.countElements() == source.countElements() ) ) )\n\t\treturn E_INVALIDARG;\n\n\tconst eDataType typeResult = result.type();\n\tconst eDataType typeSource = source.type();\n\tif( source.isContinuous() )\n\t{\n\t\tconst size_t elts = result.countElements();\n\t\tif( typeResult == typeSource )\n\t\t{\n\t\t\tconst size_t bytes = elts * elementSize( typeResult );\n\t\t\tmemcpy( result.data(), source.data(), bytes );\n\t\t\treturn S_OK;\n\t\t}\n\t\tif( typeSource == eDataType::FP16 && typeResult == eDataType::FP32 )\n\t\t{\n\t\t\tfloatsUpcast( result.fp32(), source.fp16(), elts );\n\t\t\treturn S_OK;\n\t\t}\n\t\tif( typeSource == eDataType::FP32 && typeResult == eDataType::FP16 )\n\t\t{\n\t\t\tfloatsDowncast( result.fp16(), source.fp32(), elts );\n\t\t\treturn S_OK;\n\t\t}\n\t\treturn E_UNEXPECTED;\n\t}\n\telse\n\t{\n\t\tif( typeSource == eDataType::FP16 && typeResult == eDataType::FP16 )\n\t\t{\n\t\t\t::copyImpl( result.fp16(), source.fp16(), source );\n\t\t\treturn S_OK;\n\t\t}\n\t\tif( typeSource == eDataType::FP32 && typeResult == eDataType::FP32 )\n\t\t{\n\t\t\t::copyImpl( result.fp32(), source.fp32(), source );\n\t\t\treturn S_OK;\n\t\t}\n\t\tif( typeSource == eDataType::FP16 && typeResult == eDataType::FP32 )\n\t\t{\n\t\t\t::copyImpl( result.fp32(), source.fp16(), source );\n\t\t\treturn S_OK;\n\t\t}\n\t\tif( typeSource == eDataType::FP32 && typeResult == eDataType::FP16 )\n\t\t{\n\t\t\t::copyImpl( result.fp16(), source.fp32(), source );\n\t\t\treturn S_OK;\n\t\t}\n\t\treturn E_UNEXPECTED;\n\t}\n}\n\nTensor MlContext::copy( const Tensor& a, eDataType type, std::initializer_list<uint32_t> size )\n{\n\tconst size_t dims = size.size();\n\tif( 0 == dims || dims > 4 )\n\t\tthrow E_BOUNDS;\n\n\tsize_t nRequested = 1;\n\tfor( size_t i = 0; i < dims; i++ )\n\t{\n\t\tuint32_t n = size.begin()[ i ];\n\t\tnRequested *= n;\n\t}\n\tif( nRequested != a.countElements() )\n\t\tthrow E_INVALIDARG;\n\n\tif( a.type() == type && a.isContinuous() )\n\t{\n\t\t// Same type, and it's dense - no need to move data, equal to reshape\n\t\tTensor res{ a };\n\t\tfor( size_t i = 0; i < dims; i++ )\n\t\t\tres.ne[ i ] = size.begin()[ i ];;\n\t\tfor( size_t i = dims; i < 4; i++ )\n\t\t\tres.ne[ i ] = 1;\n\t\tres.setDenseStrides();\n\t\treturn res;\n\t}\n\telse\n\t{\n\t\t// Need to convert types, and/or transpose the tensor. Make another tensor for the output\n\t\tTensor res = createTensor( type, size );\n\t\tcheck( copyImpl( res, a ) );\n\t\treturn res;\n\t}\n}\n\nTensor MlContext::permute( const Tensor& a, uint8_t axis0, uint8_t axis1, uint8_t axis2, uint8_t axis3 )\n{\n\tassert( axis0 < 4 );\n\tassert( axis1 < 4 );\n\tassert( axis2 < 4 );\n\tassert( axis3 < 4 );\n\n\tassert( axis0 != axis1 );\n\tassert( axis0 != axis2 );\n\tassert( axis0 != axis3 );\n\tassert( axis1 != axis2 );\n\tassert( axis1 != axis3 );\n\tassert( axis2 != axis3 );\n\n\tTensor res = a;\n\tres.ne[ axis0 ] = a.ne[ 0 ];\n\tres.ne[ axis1 ] = a.ne[ 1 ];\n\tres.ne[ axis2 ] = a.ne[ 2 ];\n\tres.ne[ axis3 ] = a.ne[ 3 ];\n\n\tres.nb[ axis0 ] = a.nb[ 0 ];\n\tres.nb[ axis1 ] = a.nb[ 1 ];\n\tres.nb[ axis2 ] = a.nb[ 2 ];\n\tres.nb[ axis3 ] = a.nb[ 3 ];\n\n\treturn res;\n}\n\nvoid MlContext::copyInPlace( Tensor& dest, const Tensor& a, eDataType type, std::initializer_list<uint32_t> size )\n{\n\tassert( type == dest.type() );\n\n\tconst size_t dims = size.size();\n\tif( 0 == dims || dims > 4 )\n\t\tthrow E_BOUNDS;\n\n\tsize_t nRequested = 1;\n\tfor( size_t i = 0; i < dims; i++ )\n\t{\n\t\tuint32_t n = size.begin()[ i ];\n\t\tnRequested *= n;\n\t}\n\tif( nRequested != a.countElements() || nRequested != dest.countElements() )\n\t\tthrow E_INVALIDARG;\n\n\t// Reshape the destination\n\tfor( size_t i = 0; i < dims; i++ )\n\t\tdest.ne[ i ] = size.begin()[ i ];\n\tfor( size_t i = dims; i < 4; i++ )\n\t\tdest.ne[ i ] = 1;\n\tdest.setDenseStrides();\n\n\t// Copy the data\n\tcheck( copyImpl( dest, a ) );\n}\n\nvoid MlContext::addInPlace( Tensor& a, const Tensor& b )\n{\n\tif( !( a.isContinuous() && b.isContinuous() && a.type() == eDataType::FP32 && b.type() == eDataType::FP32 ) )\n\t\tthrow E_NOTIMPL;\n\n\tconst size_t length = a.countElements();\n\taddRowInPlace( a.fp32(), b.fp32(), length );\n}\n\nTensor MlContext::add( const Tensor& a, const Tensor& b )\n{\n\tif( !( a.isContinuous() && b.isContinuous() && a.type() == eDataType::FP32 && b.type() == eDataType::FP32 ) )\n\t\tthrow E_NOTIMPL;\n\n\tTensor res = createTensor( eDataType::FP32, a.ne );\n\tconst size_t length = a.countElements();\n\taddRow( res.fp32(), a.fp32(), b.fp32(), length );\n\treturn res;\n}\n\nvoid MlContext::addRepeatGelu( Tensor& cur, const Tensor& b )\n{\n\tif( !( cur.isContinuous() && b.isContinuous() ) )\n\t\tthrow E_INVALIDARG;\n\tif( !( cur.type() == eDataType::FP32 && b.type() == eDataType::FP32 ) )\n\t\tthrow E_INVALIDARG;\n\n\tDispatchHelper3 helper{ cur.ne[ 1 ], cur.ne[ 2 ], cur.ne[ 3 ] };\n\tstd::array<uint32_t, 3> idx = { 0, 0, 0 };\n\tconst size_t countRows = helper.groupsCount();\n\n\tconst size_t innerRes = (uint32_t)cur.ne[ 0 ];\n\tconst size_t innerPattern = (uint32_t)b.ne[ 0 ];\n\tfloat* rdi = cur.fp32();\n\tauto& lookupTables = getLookupTables();\n\tfor( size_t i = 0; i < countRows; i++, helper.next( idx ), rdi += innerRes )\n\t{\n\t\tstd::array<uint32_t, 3> idxPattern;\n\t\tidxPattern[ 0 ] = idx[ 0 ] % (uint32_t)b.ne[ 1 ];\n\t\tidxPattern[ 1 ] = idx[ 1 ] % (uint32_t)b.ne[ 2 ];\n\t\tidxPattern[ 2 ] = idx[ 2 ] % (uint32_t)b.ne[ 3 ];\n\n\t\tconst float* source = sourceRow( b.fp32(), idxPattern, b.nb[ 1 ], b.nb[ 2 ], b.nb[ 3 ] );\n\t\taddRepeatGeluRow( rdi, innerRes, source, innerPattern, lookupTables );\n\t}\n\treturn;\n}"
  },
  {
    "path": "Whisper/CPU/ParallelForRunner.cpp",
    "content": "#include \"stdafx.h\"\n#include \"ParallelForRunner.h\"\nusing namespace CpuCompute;\n\nParallelForRunner::ParallelForRunner( int threads ) :\n\tmaxThreads( threads )\n{\n\tif( maxThreads <= 1 )\n\t{\n\t\tthreadBuffers.resize( 1 );\n\t\treturn;\n\t}\n\n\twork = CreateThreadpoolWork( &workCallbackStatic, this, nullptr );\n\tif( nullptr == work )\n\t\tthrow getLastHr();\n\tthreadBuffers.resize( maxThreads );\n}\n\nHRESULT ParallelForRunner::setThreadsCount( int threads )\n{\n\tmaxThreads = threads;\n\tif( threads <= 1 )\n\t{\n\t\tthreadBuffers.resize( 1 );\n\t\treturn S_OK;\n\t}\n\n\tthreadBuffers.resize( maxThreads );\n\tif( nullptr == work )\n\t{\n\t\twork = CreateThreadpoolWork( &workCallbackStatic, this, nullptr );\n\t\tif( nullptr == work )\n\t\t\treturn getLastHr();\n\t}\n\treturn S_OK;\n}\n\nParallelForRunner::~ParallelForRunner()\n{\n\tif( nullptr != work )\n\t{\n\t\tif( S_FALSE == status )\n\t\t\tWaitForThreadpoolWorkCallbacks( work, FALSE );\n\t\tCloseThreadpoolWork( work );\n\t}\n}\n\nnamespace\n{\n\tthread_local uint32_t currentThreadIndex = UINT_MAX;\n}\n\nvoid ParallelForRunner::runBatch( size_t ith ) noexcept\n{\n\tcurrentThreadIndex = (uint32_t)ith;\n\tconst size_t begin = ( ith * countItems ) / countThreads;\n\tconst size_t end = ( ( ith + 1 ) * countItems ) / countThreads;\n\n\tHRESULT hr = E_UNEXPECTED;\n\ttry\n\t{\n\t\thr = computeRange->compute( begin, end );\n\t}\n\tcatch( HRESULT code )\n\t{\n\t\thr = code;\n\t}\n\tcatch( const std::bad_alloc& )\n\t{\n\t\thr = E_OUTOFMEMORY;\n\t}\n\tcatch( const std::exception& )\n\t{\n\t\thr = E_FAIL;\n\t}\n\tcurrentThreadIndex = UINT_MAX;\n\tif( SUCCEEDED( hr ) )\n\t\treturn;\n\tInterlockedCompareExchange( &status, hr, S_FALSE );\n}\n\nvoid* ParallelForRunner::threadLocalBuffer( size_t cb )\n{\n\tconst uint32_t idx = currentThreadIndex;\n\tif( idx < threadBuffers.size() )\n\t{\n\t\tThreadBuffer& tb = threadBuffers[ idx ];\n\t\tif( tb.cb >= cb )\n\t\t{\n\t\t\t// We already have large enough buffer for the current thread\n\t\t\treturn tb.memory.pointer();\n\t\t}\n\t\ttb.memory.deallocate();\n\t\tcheck( tb.memory.allocate( cb ) );\n\t\ttb.cb = cb;\n\t\treturn tb.memory.pointer();\n\t}\n\tif( idx != UINT_MAX )\n\t\tthrow E_BOUNDS;\n\telse\n\t{\n\t\tlogError( u8\"threadLocalBuffer() method only works from inside a pool callback\" );\n\t\tthrow E_UNEXPECTED;\n\t}\n}\n\nvoid __stdcall ParallelForRunner::workCallbackStatic( PTP_CALLBACK_INSTANCE Instance, void* pv, PTP_WORK Work ) noexcept\n{\n\tParallelForRunner& context = *(ParallelForRunner*)pv;\n\tconst size_t ith = (uint32_t)( InterlockedIncrement( &context.threadIndex ) );\n\tcontext.runBatch( ith );\n}\n\nHRESULT ParallelForRunner::parallelFor( iComputeRange& compute, size_t length, size_t minBatch )\n{\n\tif( maxThreads <= 1 )\n\t{\n\t\tcurrentThreadIndex = 0;\n\t\tconst HRESULT hr1 = compute.compute( 0, length );\n\t\tcurrentThreadIndex = UINT_MAX;\n\t\treturn hr1;\n\t}\n\tassert( minBatch > 0 );\n\n\tsize_t nth = length / minBatch;\n\tnth = std::min( nth, (size_t)(uint32_t)maxThreads );\n\n\tcomputeRange = &compute;\n\tcountItems = length;\n\tcountThreads = nth;\n\tthreadIndex = 0;\n\tstatus = S_FALSE;\n\n\tfor( size_t i = 1; i < nth; i++ )\n\t\tSubmitThreadpoolWork( work );\n\trunBatch( 0 );\n\n\tif( nth > 1 )\n\t\tWaitForThreadpoolWorkCallbacks( work, FALSE );\n\n\tcomputeRange = nullptr;\n\tconst HRESULT hr = status;\n\tstatus = S_OK;\n\tif( SUCCEEDED( hr ) )\n\t\treturn S_OK;\n\n\treturn hr;\n}"
  },
  {
    "path": "Whisper/CPU/ParallelForRunner.h",
    "content": "#pragma once\n#include \"LargeBuffer.h\"\n\nnamespace CpuCompute\n{\n\t// Callback interface for the parallel `for`\n\t__interface iComputeRange\n\t{\n\t\t// The implementation calls this method on multiple thread pool threads in parallel, and aggregates status codes.\n\t\tHRESULT __stdcall compute( size_t begin, size_t end ) const;\n\t};\n\n\t// Similar to ThreadPoolWork in parallelFor.h, optimized to be used as a direct replacement of OpenMP pool.\n\tclass alignas( 64 ) ParallelForRunner\n\t{\n\tpublic:\n\t\tParallelForRunner( int threads );\n\t\t~ParallelForRunner();\n\n\t\tHRESULT setThreadsCount( int threads );\n\n\t\tHRESULT parallelFor( iComputeRange& compute, size_t length, size_t minBatch = 1 );\n\n\t\t// Allocate a temporary buffer for the calling thread.\n\t\t// The pointer is guaranteed to be aligned by page size = 4kb\n\t\tvoid* threadLocalBuffer( size_t cb );\n\n\tprivate:\n\n\t\tint maxThreads;\n\t\tPTP_WORK work = nullptr;\n\t\tiComputeRange* computeRange = nullptr;\n\t\tsize_t countItems = 0;\n\t\tsize_t countThreads = 0;\n\n\t\t// Aligning by cache lines.\n\t\t// Avoiding cache line sharing between CPU cores improves performance, despite wasting a few bytes of memory.\n\t\tstruct alignas( 64 ) ThreadBuffer\n\t\t{\n\t\t\tLargeBuffer memory;\n\t\t\tsize_t cb = 0;\n\t\t};\n\t\tstd::vector<ThreadBuffer> threadBuffers;\n\n\t\talignas( 64 ) volatile long threadIndex = 0;\n\t\tvolatile HRESULT status = S_OK;\n\n\t\tvoid runBatch( size_t ith ) noexcept;\n\n\t\tstatic void __stdcall workCallbackStatic( PTP_CALLBACK_INSTANCE Instance, void* pv, PTP_WORK Work ) noexcept;\n\t};\n}"
  },
  {
    "path": "Whisper/CPU/Readme.txt",
    "content": "﻿The code in this folder is dropped by the linker’s dead code elimination optimization pass, unless you change BUILD_HYBRID_VERSION macro in stdafx.h"
  },
  {
    "path": "Whisper/CPU/Tensor.h",
    "content": "#pragma once\n#include \"../D3D/enums.h\"\n#include \"../ML/TensorShape.h\"\n// 1 = new tensors can be allocated with `nullptr` iMemoryAllocator, by allocating memory internally and counting these references\n// 0 = memory allocator is mandatory, create() methods will fail with E_POINTER if the allocator is `nullptr`\n#define TENSOR_INTERNAL_ALLOC 0\n\n// 1 = expose compatibility API for GGML interop\n#define TENSOR_GGML_COMPAT 0\n\n#if TENSOR_GGML_COMPAT\n#include \"../source/ggml.h\"\n#endif\n\nnamespace CpuCompute\n{\n\tusing DirectCompute::TensorShape;\n\tusing DirectCompute::eDataType;\n\n\t__interface iMemoryAllocator\n\t{\n\t\tvoid* allocate( size_t cb, size_t align );\n\t};\n\t__interface iArenaAllocator : public iMemoryAllocator\n\t{\n\t\tvoid resetArena();\n\t};\n\n#if TENSOR_GGML_COMPAT\n\tclass Tensor;\n\tclass GgmlTensorView\n\t{\n\t\tggml_tensor tensor;\n\tpublic:\n\t\tGgmlTensorView( const Tensor& t );\n\t\toperator ggml_tensor* ( ) { return &tensor; }\n\t};\n#endif\n\n\t// A functional equivalent of ggml_tensor structure, designed for use from C++\n\tclass Tensor : public TensorShape\n\t{\n\t\tvoid* m_data = nullptr;\n\n\t\teDataType m_type = (eDataType)0xFF;\n\n#if TENSOR_INTERNAL_ALLOC\n\t\t// True when the memory block was allocated internally by this class\n\t\t// In this case, this class does reference counting to support cheap copies.\n\t\t// False when it's owned by someone else, such as iMemoryAllocator object, or a GGML's tensor\n\t\tbool ownsMemory = false;\n\t\tvoid deallocate();\n#endif\n\n\t\t// Private constructors for fromData() methods\n\t\tTensor( void* pointer, eDataType type, std::initializer_list<uint32_t> size );\n\t\tTensor( void* pointer, eDataType type, uint32_t length ) noexcept;\n\tpublic:\n\t\t// Trivial constructors\n\t\tTensor() = default;\n#if TENSOR_INTERNAL_ALLOC\n\t\t~Tensor()\n\t\t{\n\t\t\tdeallocate();\n\t\t}\n#else\n\t\t~Tensor() = default;\n#endif\n\t\tTensor( Tensor&& that ) noexcept;\n\t\tvoid operator=( Tensor&& that ) noexcept;\n\t\tTensor( const Tensor& that );\n\t\tvoid operator=( const Tensor& that );\n\n\t\t// Allocate a new tensor\n\t\tHRESULT create( eDataType type, const std::array<uint32_t, 4>& sizeElements, iMemoryAllocator* alloc = nullptr );\n\t\t// Allocate a new tensor\n\t\tHRESULT create( eDataType type, std::initializer_list<uint32_t> sizeElements, iMemoryAllocator* alloc = nullptr );\n\t\t// Attach to pre-existing block of memory, interpreting the data as a dense tensor of the specified type and size\n\t\tHRESULT attach( void* pointer, eDataType type, std::initializer_list<uint32_t> sizeElements );\n\t\t// Attach to pre-existing block of memory, interpret the data as a dense vector of the specified type and length\n\t\tstatic Tensor fromData( void* pointer, eDataType type, uint32_t length );\n\n\t\teDataType type() const { return m_type; }\n\t\tvoid* data() const { return m_data; }\n\n\t\tuint16_t* fp16()\n\t\t{\n\t\t\tassert( m_type == eDataType::FP16 );\n\t\t\tassert( nullptr != m_data );\n\t\t\treturn (uint16_t*)m_data;\n\t\t}\n\t\tconst uint16_t* fp16() const\n\t\t{\n\t\t\tassert( m_type == eDataType::FP16 );\n\t\t\tassert( nullptr != m_data );\n\t\t\treturn (uint16_t*)m_data;\n\t\t}\n\t\tfloat* fp32()\n\t\t{\n\t\t\tassert( m_type == eDataType::FP32 );\n\t\t\tassert( nullptr != m_data );\n\t\t\treturn (float*)m_data;\n\t\t}\n\t\tconst float* fp32() const\n\t\t{\n\t\t\tassert( m_type == eDataType::FP32 );\n\t\t\tassert( nullptr != m_data );\n\t\t\treturn (float*)m_data;\n\t\t}\n\n\t\tTensor reshape3d( uint32_t ne0, uint32_t ne1, uint32_t ne2 ) const;\n\n\t\tvoid setType( eDataType dt )\n\t\t{\n\t\t\tm_type = dt;\n\t\t}\n\t\tvoid setDataPointer( void* pv )\n\t\t{\n\t\t\tm_data = pv;\n\t\t}\n\n#if TENSOR_GGML_COMPAT\n\t\t// Compatibility with GGML's tensors, for testing and lulz\n\t\tTensor( const ggml_tensor* ggml );\n\t\tggml_tensor ggml() const;\n\n\t\toperator GgmlTensorView() const\n\t\t{\n\t\t\treturn GgmlTensorView( *this );\n\t\t}\n#endif\n\t};\n\n\t// A pair of tensors containing weights and biases; apparently, both tensors are of the same shape\n\tstruct TensorPair\n\t{\n\t\tTensor w, b;\n\t};\n}"
  },
  {
    "path": "Whisper/CPU/TensorCpu.cpp",
    "content": "#include <stdafx.h>\n#include <atomic>\n#include \"Tensor.h\"\nusing namespace CpuCompute;\n\n#if TENSOR_INTERNAL_ALLOC\nnamespace\n{\n\t// This structure is immediately before the payload of every tensor which has an internally-allocated memory buffer\n\tclass alignas( 32 ) sTensorMemoryHeader\n\t{\n\t\tstd::atomic_ptrdiff_t refCounter;\n\tpublic:\n\t\t// Reset the counter to the specified value\n\t\tvoid reset( ptrdiff_t rc )\n\t\t{\n\t\t\trefCounter = rc;\n\t\t}\n\t\t// Increment the ref.counter\n\t\tvoid increment()\n\t\t{\n\t\t\trefCounter++;\n\t\t}\n\t\t// Decrement the ref.counter, and return true if it reached zero as the result\n\t\tbool decrement()\n\t\t{\n\t\t\tptrdiff_t val = --refCounter;\n\t\t\tassert( val >= 0 );\n\t\t\treturn 0 == val;\n\t\t}\n\t};\n\n\tinline sTensorMemoryHeader* getMemBlockHeader( void* pv )\n\t{\n\t\tassert( nullptr != pv );\n\t\tuint8_t* pb = (uint8_t*)pv;\n\t\tstatic_assert( sizeof( sTensorMemoryHeader ) == 32 );\n\t\treturn (sTensorMemoryHeader*)( pb - sizeof( sTensorMemoryHeader ) );\n\t}\n\n\tinline void releaseBlock( sTensorMemoryHeader* pointer )\n\t{\n\t\tassert( nullptr != pointer );\n\t\t_aligned_free( pointer );\n\t}\n\n\tinline void* allocateBlock( size_t cb, ptrdiff_t initialRefCounter = 1 )\n\t{\n\t\tcb += sizeof( sTensorMemoryHeader );\n\t\tvoid* pv = _aligned_malloc( cb, 32 );\n\t\tif( nullptr == pv )\n\t\t\treturn nullptr;\n\n\t\tsTensorMemoryHeader* header = (sTensorMemoryHeader*)pv;\n\t\theader->reset( initialRefCounter );\n\t\treturn ( (uint8_t*)pv ) + sizeof( sTensorMemoryHeader );\n\t}\n}\n\nvoid Tensor::deallocate()\n{\n\tif( ownsMemory && nullptr != m_data )\n\t{\n\t\tsTensorMemoryHeader* const header = getMemBlockHeader( m_data );\n\t\tif( header->decrement() )\n\t\t{\n\t\t\t// This tensor is the last one which had a reference to that block of memory\n\t\t\t// Release the memory back to the heap\n\t\t\treleaseBlock( header );\n\t\t}\n\t}\n\townsMemory = false;\n\n\tTensorShape::setZero();\n\tm_data = nullptr;\n\tm_type = (eDataType)0xFF;\n}\n#endif\n\nTensor::Tensor( const Tensor& that )\n{\n\tstore( ne, that.sizeVec() );\n\tstore( nb, that.stridesVec() );\n\tm_data = that.m_data;\n\tm_type = that.m_type;\n#if TENSOR_INTERNAL_ALLOC\n\tif( that.ownsMemory && nullptr != m_data )\n\t{\n\t\tgetMemBlockHeader( m_data )->increment();\n\t\townsMemory = true;\n\t}\n\telse\n\t\townsMemory = false;\n#endif\n}\n\nTensor::Tensor( Tensor&& that ) noexcept\n{\n\tstore( ne, that.sizeVec() );\n\tstore( nb, that.stridesVec() );\n\tm_data = that.m_data;\n\tm_type = that.m_type;\n#if TENSOR_INTERNAL_ALLOC\n\townsMemory = that.ownsMemory;\n\tthat.ownsMemory = false;\n#endif\n\tthat.m_data = nullptr;\n}\n\nvoid Tensor::operator=( const Tensor& that )\n{\n\tassert( this != &that );\n#if TENSOR_INTERNAL_ALLOC\n\tdeallocate();\n#endif\n\n\tstore( ne, that.sizeVec() );\n\tstore( nb, that.stridesVec() );\n\tm_data = that.m_data;\n\tm_type = that.m_type;\n#if TENSOR_INTERNAL_ALLOC\n\tif( that.ownsMemory && nullptr != m_data )\n\t{\n\t\tgetMemBlockHeader( m_data )->increment();\n\t\townsMemory = true;\n\t}\n\telse\n\t\townsMemory = false;\n#endif\n}\n\nvoid Tensor::operator=( Tensor&& that ) noexcept\n{\n\tassert( this != &that );\n#if TENSOR_INTERNAL_ALLOC\n\tdeallocate();\n#endif\n\tstore( ne, that.sizeVec() );\n\tstore( nb, that.stridesVec() );\n\tm_data = that.m_data;\n\tm_type = that.m_type;\n\tthat.m_data = nullptr;\n#if TENSOR_INTERNAL_ALLOC\n\townsMemory = that.ownsMemory;\n\tthat.ownsMemory = false;\n#endif\n}\n\nHRESULT Tensor::create( eDataType type, const std::array<uint32_t, 4>& sizeElements, iMemoryAllocator* alloc )\n{\n\tconst size_t len = (size_t)sizeElements[ 0 ] * sizeElements[ 1 ] * sizeElements[ 2 ] * sizeElements[ 3 ];\n\tconst size_t cbElement = DirectCompute::elementSize( type );\n\tconst size_t cb = len * cbElement;\n\n#if TENSOR_INTERNAL_ALLOC\n\tdeallocate();\n#endif\n\n\tstore( ne, load( sizeElements ) );\n\tTensorShape::setDenseStrides();\n\tthis->m_type = type;\n\n\tif( nullptr != alloc )\n\t{\n#if TENSOR_INTERNAL_ALLOC\n\t\townsMemory = false;\n#endif\n\t\tm_data = alloc->allocate( cb, 32 );\n\t\tif( nullptr == m_data )\n\t\t\treturn E_OUTOFMEMORY;\n\t\treturn S_OK;\n\t}\n\telse\n\t{\n#if TENSOR_INTERNAL_ALLOC\n\t\tm_data = allocateBlock( cb, 1 );\n\t\tif( nullptr == m_data )\n\t\t\treturn E_OUTOFMEMORY;\n\t\townsMemory = true;\n\t\treturn S_OK;\n#else\n\t\treturn E_POINTER;\n#endif\n\t}\n}\n\nnamespace\n{\n\tstatic HRESULT arrayFromList( std::array<uint32_t, 4>& arr, std::initializer_list<uint32_t> list )\n\t{\n\t\tconst size_t dims = list.size();\n\t\tif( dims == 0 || dims > 4 )\n\t\t\treturn E_INVALIDARG;\n\n\t\tfor( size_t i = 0; i < dims; i++ )\n\t\t{\n\t\t\tuint32_t u = list.begin()[ i ];\n\t\t\tif( u == 0 )\n\t\t\t\treturn E_INVALIDARG;\n\t\t\tarr[ i ] = u;\n\t\t}\n\n\t\tfor( size_t i = dims; i < 4; i++ )\n\t\t\tarr[ i ] = 1;\n\n\t\treturn S_OK;\n\t}\n}\n\nHRESULT Tensor::create( eDataType type, std::initializer_list<uint32_t> sizeElements, iMemoryAllocator* alloc )\n{\n\tstd::array<uint32_t, 4> arr;\n\tCHECK( arrayFromList( arr, sizeElements ) );\n\n\treturn create( type, arr, alloc );\n}\n\nTensor::Tensor( void* pointer, eDataType type, std::initializer_list<uint32_t> size )\n{\n\tif( nullptr == pointer )\n\t\tthrow E_POINTER;\n\tcheck( arrayFromList( ne, size ) );\n\tTensorShape::setDenseStrides();\n\tm_data = pointer;\n\tm_type = type;\n#if TENSOR_INTERNAL_ALLOC\n\townsMemory = false;\n#endif\n}\n\nTensor::Tensor( void* pointer, eDataType type, uint32_t length ) noexcept\n{\n\t// size = [ length, 1, 1, 1 ]\n\tconst __m128i one = _mm_set1_epi32( 1 );\n\t__m128i v = _mm_insert_epi32( one, (int)length, 0 );\n\tstore( ne, v );\n\t// stride = [ 1, length, length, length ]\n\tv = _mm_shuffle_epi32( v, _MM_SHUFFLE( 0, 0, 0, 1 ) );\n\tstore( nb, v );\n\n\tm_data = pointer;\n\tm_type = type;\n#if TENSOR_INTERNAL_ALLOC\n\townsMemory = false;\n#endif\n}\n\nTensor Tensor::fromData( void* pointer, eDataType type, uint32_t length )\n{\n\tHRESULT hr = E_UNEXPECTED;\n\tif( nullptr != pointer )\n\t{\n\t\tif( 0 != length )\n\t\t\treturn Tensor{ pointer, type, length };\n\t\telse\n\t\t\thr = E_INVALIDARG;\n\t}\n\telse\n\t\thr = E_POINTER;\n\tthrow hr;\n}\n\nHRESULT Tensor::attach( void* pointer, eDataType type, std::initializer_list<uint32_t> sizeElements )\n{\n\tif( nullptr == pointer )\n\t\treturn E_POINTER;\n\n\tstd::array<uint32_t, 4> arr;\n\tCHECK( arrayFromList( arr, sizeElements ) );\n\n#if TENSOR_INTERNAL_ALLOC\n\tdeallocate();\n#endif\n\tstore( ne, load( arr ) );\n\tTensorShape::setDenseStrides();\n\n\tm_data = pointer;\n\tthis->m_type = type;\n#if TENSOR_INTERNAL_ALLOC\n\townsMemory = false;\n#endif\n\treturn S_OK;\n}\n\nTensor Tensor::reshape3d( uint32_t ne0, uint32_t ne1, uint32_t ne2 ) const\n{\n\tif( !isContinuous() )\n\t\tthrow E_NOTIMPL;\n\tif( countElements() != ne0 * ne1 * ne2 )\n\t\tthrow E_INVALIDARG;\n\n\tTensor res = *this;\n\tres.ne = { ne0, ne1, ne2, 1 };\n\tres.setDenseStrides();\n\treturn res;\n}\n\n#if TENSOR_GGML_COMPAT\nstatic const __m128i s_maskAlignment16 = _mm_set1_epi64x( 1 );\nstatic const __m128i s_maskAlignment32 = _mm_set1_epi64x( 3 );\n\nbool isAlignedProperly( __m128i r0, __m128i r1, __m128i mask )\n{\n\t__m128i test = _mm_or_si128( r0, r1 );\n\treturn (bool)_mm_testz_si128( test, mask );\n}\n\nTensor::Tensor( const ggml_tensor* ggml )\n{\n\tstore( ne, load16( ggml->ne ) );\n\n\t__m128i r0 = load16( (const int*)&ggml->nb[ 0 ] );\n\t__m128i r1 = load16( (const int*)&ggml->nb[ 2 ] );\n\t// Divide from bytes into elements by right-shifting the 64-bit integers in these vectors\n\tswitch( ggml->type )\n\t{\n\tcase GGML_TYPE_F16:\n\t\tassert( isAlignedProperly( r0, r1, s_maskAlignment16 ) );\n\t\tr0 = _mm_srli_epi64( r0, 1 );\n\t\tr1 = _mm_srli_epi64( r1, 1 );\n\t\tm_type = eDataType::FP16;\n\t\tbreak;\n\n\tcase GGML_TYPE_F32:\n\t\tassert( isAlignedProperly( r0, r1, s_maskAlignment32 ) );\n\t\tr0 = _mm_srli_epi64( r0, 2 );\n\t\tr1 = _mm_srli_epi64( r1, 2 );\n\t\tm_type = eDataType::FP32;\n\t\tbreak;\n\n\tcase GGML_TYPE_I32:\n\t\tassert( isAlignedProperly( r0, r1, s_maskAlignment32 ) );\n\t\tr0 = _mm_srli_epi64( r0, 2 );\n\t\tr1 = _mm_srli_epi64( r1, 2 );\n\t\tm_type = eDataType::U32;\n\t\tbreak;\n\n\tdefault:\n\t\tthrow E_INVALIDARG;\n\t}\n\t// downcast uint64_t into uint32_t in a single vector\n\tr0 = _mm_shuffle_epi32( r0, _MM_SHUFFLE( 3, 3, 2, 0 ) );\n\tr1 = _mm_shuffle_epi32( r1, _MM_SHUFFLE( 2, 0, 3, 3 ) );\n\tstore( nb, _mm_blend_epi16( r0, r1, 0b11110000 ) );\n\n\tm_data = ggml->data;\n}\n\nggml_tensor Tensor::ggml() const\n{\n\tggml_tensor res;\n\tmemset( &res, 0, sizeof( ggml_tensor ) );\n\n\tconst __m128i size = sizeVec();\n\tstore16( res.ne, size );\n\n\tconst __m128i one = _mm_set1_epi32( 1 );\n\tconst uint32_t maskOnes = (uint32_t)_mm_movemask_ps( _mm_castsi128_ps( _mm_cmpeq_epi32( size, one ) ) );\n\tconst uint32_t maskNotOnes = maskOnes ^ 0b1111;\n\tunsigned long idx;\n\tif( _BitScanReverse( &idx, maskNotOnes ) )\n\t\tres.n_dims = (int)idx + 1;\n\telse\n\t\tres.n_dims = 0;\n\n\tconst __m128i strides = stridesVec();\n\t// Upcast strides from u32 to u64\n\tconst __m128i zero = _mm_setzero_si128();\n\t__m128i r0 = _mm_unpacklo_epi32( strides, zero );\n\t__m128i r1 = _mm_unpackhi_epi32( strides, zero );\n\t// Scale from elements into bytes with left shift vector instructions\n\tswitch( m_type )\n\t{\n\tcase eDataType::FP16:\n\t\tr0 = _mm_slli_epi64( r0, 1 );\n\t\tr1 = _mm_slli_epi64( r1, 1 );\n\t\tres.type = GGML_TYPE_F16;\n\t\tbreak;\n\tcase eDataType::FP32:\n\t\tr0 = _mm_slli_epi64( r0, 2 );\n\t\tr1 = _mm_slli_epi64( r1, 2 );\n\t\tres.type = GGML_TYPE_F32;\n\t\tbreak;\n\tcase eDataType::U32:\n\t\tr0 = _mm_slli_epi64( r0, 2 );\n\t\tr1 = _mm_slli_epi64( r1, 2 );\n\t\tres.type = GGML_TYPE_I32;\n\t\tbreak;\n\tdefault:\n\t\tthrow OLE_E_BLANK;\n\t}\n\n\tstore16( &res.nb[ 0 ], r0 );\n\tstore16( &res.nb[ 2 ], r1 );\n\n\tres.data = m_data;\n\treturn res;\n}\n\nGgmlTensorView::GgmlTensorView( const Tensor& t ) : tensor( t.ggml() ) {}\n#endif"
  },
  {
    "path": "Whisper/CPU/mulMat.cpp",
    "content": "﻿#include \"stdafx.h\"\n#include \"mulMat.h\"\n#include \"mulMatImpl.h\"\nusing namespace CpuCompute;\n\nnamespace\n{\n\ttemplate<uint8_t panelHeightRegs, uint8_t tileWidthFloats>\n\tstatic HRESULT mulMatImpl( Tensor& result, const Tensor& a, const Tensor& b, ParallelForRunner& pfor )\n\t{\n\t\tMulMatImpl<panelHeightRegs, tileWidthFloats> impl{ result, a, b, pfor };\n\t\treturn impl.run( pfor );\n\t}\n}\n\nHRESULT CpuCompute::mulMat( Tensor& result, const Tensor& a, const Tensor& b, ParallelForRunner& pfor )\n{\n\tif( a.type() != eDataType::FP16 )\n\t\treturn E_NOTIMPL;\n\tif( b.type() != eDataType::FP32 )\n\t\treturn E_NOTIMPL;\n\n\t// return mulMatImpl<1, 1>( result, a, b, pfor );\n\n\tif( b.ne[ 1 ] == 1 )\n\t{\n\t\t// Multiplying by a single row\n\t\tif( a.ne[ 1 ] >= 32 )\n\t\t\treturn mulMatImpl<4, 1>( result, a, b, pfor );\n\t\telse\n\t\t\treturn mulMatImpl<1, 1>( result, a, b, pfor );\n\t}\n\telse if( b.ne[ 1 ] == 2 )\n\t{\n\t\tif( a.ne[ 1 ] >= 32 )\n\t\t\treturn mulMatImpl<4, 2>( result, a, b, pfor );\n\t\telse\n\t\t\treturn mulMatImpl<1, 2>( result, a, b, pfor );\n\t}\n\telse if( b.ne[ 1 ] == 3 )\n\t{\n\t\tif( a.ne[ 1 ] >= 16 )\n\t\t\treturn mulMatImpl<2, 3>( result, a, b, pfor );\n\t\telse\n\t\t\treturn mulMatImpl<1, 3>( result, a, b, pfor );\n\t}\n\telse\n\t{\n\t\tif( a.ne[ 1 ] >= 16 )\n\t\t\treturn mulMatImpl<2, 4>( result, a, b, pfor );\n\t\telse\n\t\t\treturn mulMatImpl<1, 4>( result, a, b, pfor );\n\t}\n}"
  },
  {
    "path": "Whisper/CPU/mulMat.h",
    "content": "#pragma once\n#include \"ParallelForRunner.h\"\n#include \"Tensor.h\"\n\nnamespace CpuCompute\n{\n\tHRESULT mulMat( Tensor& result, const Tensor& a, const Tensor& b, ParallelForRunner& pfor );\n}\n\n#if TENSOR_GGML_COMPAT\n#include \"../source/ggml.h\"\ninline HRESULT mulMat( ggml_tensor* result, const ggml_tensor* a, const ggml_tensor* b, CpuCompute::ParallelForRunner& pfor )\n{\n\tCpuCompute::Tensor r{ result }, lhs{ a }, rhs{ b };\n\treturn CpuCompute::mulMat( r, lhs, rhs, pfor );\n}\n#endif"
  },
  {
    "path": "Whisper/CPU/mulMat.kernel.hpp",
    "content": "#pragma once\n#include <stdint.h>\n#include <immintrin.h>\n#include \"simdUtils.h\"\n\ntemplate<uint8_t panelHeightRegs, uint8_t tileWidthFloats>\nstruct ResultTile\n{\n\tstatic constexpr size_t totalRegs = (size_t)(tileWidthFloats)*panelHeightRegs;\n\tstd::array<__m256, totalRegs> arr;\n\n\ttemplate<size_t idx>\n\t__forceinline void fmadd( __m256 a, __m256 b )\n\t{\n\t\tarr[ idx ] = _mm256_fmadd_ps( a, b, arr[ idx ] );\n\t}\n\t__forceinline void kernel( const std::array<__m256, panelHeightRegs>& panel, const float* rsi, size_t stride );\n\t__forceinline void kernelPartial( const std::array<__m256, panelHeightRegs>& panel, const float* rsi, size_t stride, size_t rem )\n\t{\n\t\tthrow E_UNEXPECTED;\n\t}\n\t__forceinline void store( float* rdi, size_t w, size_t h, size_t stride ) const;\n};\n\n#pragma region setZero functions\n__forceinline void setZero( std::array<__m256, 1>& dest )\n{\n\tdest[ 0 ] = _mm256_setzero_ps();\n}\n__forceinline void setZero( std::array<__m256, 2>& dest )\n{\n\tdest[ 0 ] = _mm256_setzero_ps();\n\tdest[ 1 ] = _mm256_setzero_ps();\n}\n__forceinline void setZero( std::array<__m256, 3>& dest )\n{\n\tdest[ 0 ] = _mm256_setzero_ps();\n\tdest[ 1 ] = _mm256_setzero_ps();\n\tdest[ 2 ] = _mm256_setzero_ps();\n}\n__forceinline void setZero( std::array<__m256, 4>& dest )\n{\n\tdest[ 0 ] = _mm256_setzero_ps();\n\tdest[ 1 ] = _mm256_setzero_ps();\n\tdest[ 2 ] = _mm256_setzero_ps();\n\tdest[ 3 ] = _mm256_setzero_ps();\n}\n__forceinline void setZero( std::array<__m256, 6>& dest )\n{\n\tdest[ 0 ] = _mm256_setzero_ps();\n\tdest[ 1 ] = _mm256_setzero_ps();\n\tdest[ 2 ] = _mm256_setzero_ps();\n\tdest[ 3 ] = _mm256_setzero_ps();\n\tdest[ 4 ] = _mm256_setzero_ps();\n\tdest[ 5 ] = _mm256_setzero_ps();\n}\n__forceinline void setZero( std::array<__m256, 8>& dest )\n{\n\tdest[ 0 ] = _mm256_setzero_ps();\n\tdest[ 1 ] = _mm256_setzero_ps();\n\tdest[ 2 ] = _mm256_setzero_ps();\n\tdest[ 3 ] = _mm256_setzero_ps();\n\tdest[ 4 ] = _mm256_setzero_ps();\n\tdest[ 5 ] = _mm256_setzero_ps();\n\tdest[ 6 ] = _mm256_setzero_ps();\n\tdest[ 7 ] = _mm256_setzero_ps();\n}\n#pragma endregion\n\n#pragma region Micro-kernels\n__forceinline void ResultTile<1, 1>::kernel( const std::array<__m256, 1>& panel, const float* rsi, size_t stride )\n{\n\t__m256 b = _mm256_broadcast_ss( rsi );\n\tfmadd<0>( panel[ 0 ], b );\n}\n__forceinline void ResultTile<1, 2>::kernel( const std::array<__m256, 1>& panel, const float* rsi, size_t stride )\n{\n\t__m256 b = _mm256_broadcast_ss( rsi );\n\tfmadd<0>( panel[ 0 ], b );\n\tb = _mm256_broadcast_ss( rsi + stride );\n\tfmadd<1>( panel[ 0 ], b );\n}\n__forceinline void ResultTile<1, 2>::kernelPartial( const std::array<__m256, 1>& panel, const float* rsi, size_t stride, size_t rem )\n{\n\tassert( 1 == rem );\n\t__m256 b = _mm256_broadcast_ss( rsi );\n\tfmadd<0>( panel[ 0 ], b );\n}\n__forceinline void ResultTile<1, 3>::kernel( const std::array<__m256, 1>& panel, const float* rsi, size_t stride )\n{\n\t__m256 b = _mm256_broadcast_ss( rsi );\n\tfmadd<0>( panel[ 0 ], b );\n\tb = _mm256_broadcast_ss( rsi + stride );\n\tfmadd<1>( panel[ 0 ], b );\n\tb = _mm256_broadcast_ss( rsi + stride * 2 );\n\tfmadd<2>( panel[ 0 ], b );\n}\n__forceinline void ResultTile<1, 3>::kernelPartial( const std::array<__m256, 1>& panel, const float* rsi, size_t stride, size_t rem )\n{\n\tassert( rem > 0 && rem < 3 );\n\t__m256 b = _mm256_broadcast_ss( rsi );\n\tfmadd<0>( panel[ 0 ], b );\n\tif( rem > 1 )\n\t{\n\t\tb = _mm256_broadcast_ss( rsi + stride );\n\t\tfmadd<1>( panel[ 0 ], b );\n\t}\n}\n\n__forceinline void ResultTile<1, 4>::kernel( const std::array<__m256, 1>& panel, const float* rsi, size_t stride )\n{\n\t__m256 b = _mm256_broadcast_ss( rsi );\n\tfmadd<0>( panel[ 0 ], b );\n\tb = _mm256_broadcast_ss( rsi + stride );\n\tfmadd<1>( panel[ 0 ], b );\n\tb = _mm256_broadcast_ss( rsi + stride * 2 );\n\tfmadd<2>( panel[ 0 ], b );\n\tb = _mm256_broadcast_ss( rsi + stride * 3 );\n\tfmadd<3>( panel[ 0 ], b );\n}\n__forceinline void ResultTile<1, 4>::kernelPartial( const std::array<__m256, 1>& panel, const float* rsi, size_t stride, size_t rem )\n{\n\tassert( rem > 0 && rem < 4 );\n\t__m256 b = _mm256_broadcast_ss( rsi );\n\tfmadd<0>( panel[ 0 ], b );\n\n\tswitch( rem )\n\t{\n\tcase 3:\n\t\tb = _mm256_broadcast_ss( rsi + stride * 2 );\n\t\tfmadd<2>( panel[ 0 ], b );\n\tcase 2:\n\t\tb = _mm256_broadcast_ss( rsi + stride );\n\t\tfmadd<1>( panel[ 0 ], b );\n\t}\n}\n__forceinline void ResultTile<4, 1>::kernel( const std::array<__m256, 4>& panel, const float* rsi, size_t stride )\n{\n\t__m256 b = _mm256_broadcast_ss( rsi );\n\tfmadd<0>( panel[ 0 ], b );\n\tfmadd<1>( panel[ 1 ], b );\n\tfmadd<2>( panel[ 2 ], b );\n\tfmadd<3>( panel[ 3 ], b );\n}\n__forceinline void ResultTile<2, 4>::kernel( const std::array<__m256, 2>& panel, const float* rsi, size_t stride )\n{\n\t__m256 b = _mm256_broadcast_ss( rsi );\n\tfmadd<0>( panel[ 0 ], b );\n\tfmadd<1>( panel[ 1 ], b );\n\n\tb = _mm256_broadcast_ss( rsi + stride );\n\tfmadd<2>( panel[ 0 ], b );\n\tfmadd<3>( panel[ 1 ], b );\n\n\tb = _mm256_broadcast_ss( rsi + stride * 2 );\n\tfmadd<4>( panel[ 0 ], b );\n\tfmadd<5>( panel[ 1 ], b );\n\n\tb = _mm256_broadcast_ss( rsi + stride * 3 );\n\tfmadd<6>( panel[ 0 ], b );\n\tfmadd<7>( panel[ 1 ], b );\n}\n\n__forceinline void ResultTile<2, 4>::kernelPartial( const std::array<__m256, 2>& panel, const float* rsi, size_t stride, size_t rem )\n{\n\tassert( rem > 0 && rem < 4 );\n\t__m256 b = _mm256_broadcast_ss( rsi );\n\tfmadd<0>( panel[ 0 ], b );\n\tfmadd<1>( panel[ 1 ], b );\n\n\tswitch( rem )\n\t{\n\tcase 3:\n\t\tb = _mm256_broadcast_ss( rsi + stride * 2 );\n\t\tfmadd<4>( panel[ 0 ], b );\n\t\tfmadd<5>( panel[ 1 ], b );\n\tcase 2:\n\t\tb = _mm256_broadcast_ss( rsi + stride );\n\t\tfmadd<2>( panel[ 0 ], b );\n\t\tfmadd<3>( panel[ 1 ], b );\n\t}\n}\n\n__forceinline void ResultTile<2, 3>::kernel( const std::array<__m256, 2>& panel, const float* rsi, size_t stride )\n{\n\t__m256 b = _mm256_broadcast_ss( rsi );\n\tfmadd<0>( panel[ 0 ], b );\n\tfmadd<1>( panel[ 1 ], b );\n\n\tb = _mm256_broadcast_ss( rsi + stride );\n\tfmadd<2>( panel[ 0 ], b );\n\tfmadd<3>( panel[ 1 ], b );\n\n\tb = _mm256_broadcast_ss( rsi + stride * 2 );\n\tfmadd<4>( panel[ 0 ], b );\n\tfmadd<5>( panel[ 1 ], b );\n}\n__forceinline void ResultTile<2, 3>::kernelPartial( const std::array<__m256, 2>& panel, const float* rsi, size_t stride, size_t rem )\n{\n\tassert( rem > 0 && rem < 3 );\n\t__m256 b = _mm256_broadcast_ss( rsi );\n\tfmadd<0>( panel[ 0 ], b );\n\tfmadd<1>( panel[ 1 ], b );\n\tif( rem > 1 )\n\t{\n\t\tb = _mm256_broadcast_ss( rsi + stride );\n\t\tfmadd<2>( panel[ 0 ], b );\n\t\tfmadd<3>( panel[ 1 ], b );\n\t}\n}\n\n__forceinline void ResultTile<4, 2>::kernel( const std::array<__m256, 4>& panel, const float* rsi, size_t stride )\n{\n\t__m256 b = _mm256_broadcast_ss( rsi );\n\tfmadd<0>( panel[ 0 ], b );\n\tfmadd<1>( panel[ 1 ], b );\n\tfmadd<2>( panel[ 2 ], b );\n\tfmadd<3>( panel[ 3 ], b );\n\n\tb = _mm256_broadcast_ss( rsi + stride );\n\tfmadd<4>( panel[ 0 ], b );\n\tfmadd<5>( panel[ 1 ], b );\n\tfmadd<6>( panel[ 2 ], b );\n\tfmadd<7>( panel[ 3 ], b );\n}\n__forceinline void ResultTile<4, 2>::kernelPartial( const std::array<__m256, 4>& panel, const float* rsi, size_t stride, size_t rem )\n{\n\tassert( 1 == rem );\n\t__m256 b = _mm256_broadcast_ss( rsi );\n\tfmadd<0>( panel[ 0 ], b );\n\tfmadd<1>( panel[ 1 ], b );\n\tfmadd<2>( panel[ 2 ], b );\n\tfmadd<3>( panel[ 3 ], b );\n}\n#pragma endregion\n\n#pragma region Loads\n// This function should compile into a single `vcvtph2ps` instruction, with memory operand\n__forceinline __m256 loadUpcasted( const uint16_t* rsi )\n{\n\t__m128i i = _mm_load_si128( ( const __m128i* )rsi );\n\treturn _mm256_cvtph_ps( i );\n}\n\n// We loading the panel from the temporary buffer.\n// For this reason, we don't need to handle remainders, the code which made the buffer wrote zeros into the remainder elements\n// We can even use aligned load instructions.\n__forceinline void loadPanel( const uint16_t* rsi, std::array<__m256, 1>& dest )\n{\n\tdest[ 0 ] = loadUpcasted( rsi );\n}\n__forceinline void loadPanel( const uint16_t* rsi, std::array<__m256, 2>& dest )\n{\n\tdest[ 0 ] = loadUpcasted( rsi );\n\tdest[ 1 ] = loadUpcasted( rsi + 8 );\n}\n__forceinline void loadPanel( const uint16_t* rsi, std::array<__m256, 3>& dest )\n{\n\tdest[ 0 ] = loadUpcasted( rsi );\n\tdest[ 1 ] = loadUpcasted( rsi + 8 );\n\tdest[ 2 ] = loadUpcasted( rsi + 8 * 2 );\n}\n__forceinline void loadPanel( const uint16_t* rsi, std::array<__m256, 4>& dest )\n{\n\tdest[ 0 ] = loadUpcasted( rsi );\n\tdest[ 1 ] = loadUpcasted( rsi + 8 );\n\tdest[ 2 ] = loadUpcasted( rsi + 8 * 2 );\n\tdest[ 3 ] = loadUpcasted( rsi + 8 * 3 );\n}\n#pragma endregion\n\n#pragma region Stores\n__forceinline void ResultTile<1, 1>::store( float* rdi, size_t w, size_t h, size_t stride ) const\n{\n\tassert( h == 1 && w > 0 && w <= 8 );\n\tif( w == 8 )\n\t\t_mm256_storeu_ps( rdi, arr[ 0 ] );\n\telse\n\t{\n\t\tconst __m256i mask = loadTailMaskInt( w );\n\t\t_mm256_maskstore_ps( rdi, mask, arr[ 0 ] );\n\t}\n}\n\n__forceinline void ResultTile<1, 2>::store( float* rdi, size_t w, size_t h, size_t stride ) const\n{\n\tassert( h > 0 && w > 0 && h <= 2 && w <= 8 );\n\tif( w == 8 )\n\t{\n\t\tswitch( h )\n\t\t{\n\t\tcase 2:\n\t\t\t_mm256_storeu_ps( rdi + stride, arr[ 1 ] );\n\t\tcase 1:\n\t\t\t_mm256_storeu_ps( rdi, arr[ 0 ] );\n\t\t}\n\t}\n\telse\n\t{\n\t\tconst __m256i mask = loadTailMaskInt( w );\n\t\tswitch( h )\n\t\t{\n\t\tcase 2:\n\t\t\t_mm256_maskstore_ps( rdi + stride, mask, arr[ 1 ] );\n\t\tcase 1:\n\t\t\t_mm256_maskstore_ps( rdi, mask, arr[ 0 ] );\n\t\t}\n\t}\n}\n\n__forceinline void ResultTile<1, 3>::store( float* rdi, size_t w, size_t h, size_t stride ) const\n{\n\tassert( h > 0 && w > 0 && h <= 3 && w <= 8 );\n\tif( w == 8 )\n\t{\n\t\tswitch( h )\n\t\t{\n\t\tcase 3:\n\t\t\t_mm256_storeu_ps( rdi + stride * 2, arr[ 2 ] );\n\t\tcase 2:\n\t\t\t_mm256_storeu_ps( rdi + stride, arr[ 1 ] );\n\t\tcase 1:\n\t\t\t_mm256_storeu_ps( rdi, arr[ 0 ] );\n\t\t}\n\t}\n\telse\n\t{\n\t\tconst __m256i mask = loadTailMaskInt( w );\n\t\tswitch( h )\n\t\t{\n\t\tcase 3:\n\t\t\t_mm256_maskstore_ps( rdi + stride * 2, mask, arr[ 2 ] );\n\t\tcase 2:\n\t\t\t_mm256_maskstore_ps( rdi + stride, mask, arr[ 1 ] );\n\t\tcase 1:\n\t\t\t_mm256_maskstore_ps( rdi, mask, arr[ 0 ] );\n\t\t}\n\t}\n}\n\n__forceinline void ResultTile<1, 4>::store( float* rdi, size_t w, size_t h, size_t stride ) const\n{\n\tassert( h > 0 && w > 0 && h <= 4 && w <= 8 );\n\n\tif( w == 8 )\n\t{\n\t\tswitch( h )\n\t\t{\n\t\tcase 4:\n\t\t\t_mm256_storeu_ps( rdi + stride * 3, arr[ 3 ] );\n\t\tcase 3:\n\t\t\t_mm256_storeu_ps( rdi + stride * 2, arr[ 2 ] );\n\t\tcase 2:\n\t\t\t_mm256_storeu_ps( rdi + stride, arr[ 1 ] );\n\t\tcase 1:\n\t\t\t_mm256_storeu_ps( rdi, arr[ 0 ] );\n\t\t}\n\t}\n\telse\n\t{\n\t\tconst __m256i mask = loadTailMaskInt( w );\n\t\tswitch( h )\n\t\t{\n\t\tcase 4:\n\t\t\t_mm256_maskstore_ps( rdi + stride * 3, mask, arr[ 3 ] );\n\t\tcase 3:\n\t\t\t_mm256_maskstore_ps( rdi + stride * 2, mask, arr[ 2 ] );\n\t\tcase 2:\n\t\t\t_mm256_maskstore_ps( rdi + stride, mask, arr[ 1 ] );\n\t\tcase 1:\n\t\t\t_mm256_maskstore_ps( rdi, mask, arr[ 0 ] );\n\t\t}\n\t}\n}\n\n__forceinline void ResultTile<4, 1>::store( float* rdi, size_t w, size_t h, size_t stride ) const\n{\n\tassert( h == 1 && w > 0 && w <= 32 );\n\tif( w == 32 )\n\t{\n\t\t// 4 complete vectors, this branch is very likely to be taken\n\t\t_mm256_storeu_ps( rdi, arr[ 0 ] );\n\t\t_mm256_storeu_ps( rdi + 8, arr[ 1 ] );\n\t\t_mm256_storeu_ps( rdi + 8 * 2, arr[ 2 ] );\n\t\t_mm256_storeu_ps( rdi + 8 * 3, arr[ 3 ] );\n\t}\n\telse\n\t{\n\t\tconst size_t rem = w % 8;\n\t\tconst __m256i mask = loadTailMaskInt<false>( rem );\n\t\tconst size_t completeVectors = w / 8;\n\t\tconst size_t key = ( completeVectors << 1 ) | ( ( 0 == rem ) ? 0 : 1 );\n\t\tswitch( key )\n\t\t{\n\t\tcase 1:\t// 0 complete vectors + remainder\n\t\t\t_mm256_maskstore_ps( rdi, mask, arr[ 0 ] );\n\t\t\tbreak;\n\t\tcase 2:\t// 1 complete vector\n\t\t\t_mm256_storeu_ps( rdi, arr[ 0 ] );\n\t\t\tbreak;\n\t\tcase 3:\t// 1 complete vector + remainder\n\t\t\t_mm256_storeu_ps( rdi, arr[ 0 ] );\n\t\t\t_mm256_maskstore_ps( rdi + 8, mask, arr[ 1 ] );\n\t\t\tbreak;\n\t\tcase 4:\t// 2 complete vectors\n\t\t\t_mm256_storeu_ps( rdi, arr[ 0 ] );\n\t\t\t_mm256_storeu_ps( rdi + 8, arr[ 1 ] );\n\t\t\tbreak;\n\t\tcase 5:\t// 2 complete vectors + remainder\n\t\t\t_mm256_storeu_ps( rdi, arr[ 0 ] );\n\t\t\t_mm256_storeu_ps( rdi + 8, arr[ 1 ] );\n\t\t\t_mm256_maskstore_ps( rdi + 8 * 2, mask, arr[ 2 ] );\n\t\t\tbreak;\n\t\tcase 6:\t// 3 complete vectors\n\t\t\t_mm256_storeu_ps( rdi, arr[ 0 ] );\n\t\t\t_mm256_storeu_ps( rdi + 8, arr[ 1 ] );\n\t\t\t_mm256_storeu_ps( rdi + 8 * 2, arr[ 2 ] );\n\t\t\tbreak;\n\t\tcase 7:\t// 3 complete vectors + remainder\n\t\t\t_mm256_storeu_ps( rdi, arr[ 0 ] );\n\t\t\t_mm256_storeu_ps( rdi + 8, arr[ 1 ] );\n\t\t\t_mm256_storeu_ps( rdi + 8 * 2, arr[ 2 ] );\n\t\t\t_mm256_maskstore_ps( rdi + 8 * 3, mask, arr[ 3 ] );\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tthrow E_UNEXPECTED;\n\t\t}\n\t}\n}\n__forceinline void ResultTile<4, 2>::store( float* rdi, size_t w, size_t h, size_t stride ) const\n{\n\tassert( h > 0 && w > 0 && h <= 2 && w <= 32 );\n\tconst bool twoRows = h == 2;\n\tfloat* const rdi1 = rdi + stride;\n\tif( w == 32 )\n\t{\n\t\t_mm256_storeu_ps( rdi, arr[ 0 ] );\n\t\t_mm256_storeu_ps( rdi + 8, arr[ 1 ] );\n\t\t_mm256_storeu_ps( rdi + 8 * 2, arr[ 2 ] );\n\t\t_mm256_storeu_ps( rdi + 8 * 3, arr[ 3 ] );\n\n\t\tif( twoRows )\n\t\t{\n\t\t\t_mm256_storeu_ps( rdi1, arr[ 4 ] );\n\t\t\t_mm256_storeu_ps( rdi1 + 8, arr[ 5 ] );\n\t\t\t_mm256_storeu_ps( rdi1 + 8 * 2, arr[ 6 ] );\n\t\t\t_mm256_storeu_ps( rdi1 + 8 * 3, arr[ 7 ] );\n\t\t}\n\t}\n\telse\n\t{\n\t\tconst size_t rem = w % 8;\n\t\tconst __m256i mask = loadTailMaskInt<false>( rem );\n\t\tconst size_t completeVectors = w / 8;\n\t\t// Lowest bit: remainder\n\t\t// Next bit: set when storing 2 rows\n\t\t// Next 2 bits: count of complete vectors in X direction, [ 0..3 ]\n\t\tconst size_t key = ( completeVectors << 2 ) | ( ( 0 == rem ) ? 0 : 1 ) | ( twoRows ? 2 : 0 );\n\t\tswitch( key )\n\t\t{\n\t\tcase 1:\t// 0 complete vectors + remainder, 1 row\n\t\t\t_mm256_maskstore_ps( rdi, mask, arr[ 0 ] );\n\t\t\tbreak;\n\t\tcase 3:\t// 0 complete vectors + remainder, 2 rows\n\t\t\t_mm256_maskstore_ps( rdi, mask, arr[ 0 ] );\n\t\t\t_mm256_maskstore_ps( rdi1, mask, arr[ 4 ] );\n\t\t\tbreak;\n\t\tcase 4:\t// 1 complete vector, 1 row\n\t\t\t_mm256_storeu_ps( rdi, arr[ 0 ] );\n\t\t\tbreak;\n\t\tcase 5:\t// 1 complete vector + remainder, 1 row\n\t\t\t_mm256_storeu_ps( rdi, arr[ 0 ] );\n\t\t\t_mm256_maskstore_ps( rdi + 8, mask, arr[ 1 ] );\n\t\t\tbreak;\n\t\tcase 6:\t// 1 complete vector, 2 rows\n\t\t\t_mm256_storeu_ps( rdi, arr[ 0 ] );\n\t\t\t_mm256_storeu_ps( rdi1, arr[ 4 ] );\n\t\t\tbreak;\n\t\tcase 7:\t// 1 complete vector + remainder, 2 rows\n\t\t\t_mm256_storeu_ps( rdi, arr[ 0 ] );\n\t\t\t_mm256_maskstore_ps( rdi + 8, mask, arr[ 1 ] );\n\n\t\t\t_mm256_storeu_ps( rdi1, arr[ 4 ] );\n\t\t\t_mm256_maskstore_ps( rdi1 + 8, mask, arr[ 5 ] );\n\t\t\tbreak;\n\t\tcase 8:\t// 2 complete vectors, 1 row\n\t\t\t_mm256_storeu_ps( rdi, arr[ 0 ] );\n\t\t\t_mm256_storeu_ps( rdi + 8, arr[ 1 ] );\n\t\t\tbreak;\n\t\tcase 9:\t// 2 complete vectors + remainder, 1 row\n\t\t\t_mm256_storeu_ps( rdi, arr[ 0 ] );\n\t\t\t_mm256_storeu_ps( rdi + 8, arr[ 1 ] );\n\t\t\t_mm256_maskstore_ps( rdi + 8 * 2, mask, arr[ 2 ] );\n\t\t\tbreak;\n\t\tcase 10:\t// 2 complete vectors, 2 rows\n\t\t\t_mm256_storeu_ps( rdi, arr[ 0 ] );\n\t\t\t_mm256_storeu_ps( rdi + 8, arr[ 1 ] );\n\n\t\t\t_mm256_storeu_ps( rdi1, arr[ 4 ] );\n\t\t\t_mm256_storeu_ps( rdi1 + 8, arr[ 5 ] );\n\t\t\tbreak;\n\t\tcase 11:\t// 2 complete vectors + remainder, 2 rows\n\t\t\t_mm256_storeu_ps( rdi, arr[ 0 ] );\n\t\t\t_mm256_storeu_ps( rdi + 8, arr[ 1 ] );\n\t\t\t_mm256_maskstore_ps( rdi + 8 * 2, mask, arr[ 2 ] );\n\n\t\t\t_mm256_storeu_ps( rdi1, arr[ 4 ] );\n\t\t\t_mm256_storeu_ps( rdi1 + 8, arr[ 5 ] );\n\t\t\t_mm256_maskstore_ps( rdi1 + 8 * 2, mask, arr[ 6 ] );\n\t\t\tbreak;\n\t\tcase 12:\t// 3 complete vectors, 1 row\n\t\t\t_mm256_storeu_ps( rdi, arr[ 0 ] );\n\t\t\t_mm256_storeu_ps( rdi + 8, arr[ 1 ] );\n\t\t\t_mm256_storeu_ps( rdi + 8 * 2, arr[ 2 ] );\n\t\t\tbreak;\n\t\tcase 13:\t// 3 complete vectors + remainder, 1 row\n\t\t\t_mm256_storeu_ps( rdi, arr[ 0 ] );\n\t\t\t_mm256_storeu_ps( rdi + 8, arr[ 1 ] );\n\t\t\t_mm256_storeu_ps( rdi + 8 * 2, arr[ 2 ] );\n\t\t\t_mm256_maskstore_ps( rdi + 8 * 3, mask, arr[ 3 ] );\n\t\t\tbreak;\n\t\tcase 14:\t// 3 complete vectors, 2 rows\n\t\t\t_mm256_storeu_ps( rdi, arr[ 0 ] );\n\t\t\t_mm256_storeu_ps( rdi + 8, arr[ 1 ] );\n\t\t\t_mm256_storeu_ps( rdi + 8 * 2, arr[ 2 ] );\n\n\t\t\t_mm256_storeu_ps( rdi1, arr[ 4 ] );\n\t\t\t_mm256_storeu_ps( rdi1 + 8, arr[ 5 ] );\n\t\t\t_mm256_storeu_ps( rdi1 + 8 * 2, arr[ 6 ] );\n\t\t\tbreak;\n\t\tcase 15:\t// 3 complete vectors + remainder, 2 rows\n\t\t\t_mm256_storeu_ps( rdi, arr[ 0 ] );\n\t\t\t_mm256_storeu_ps( rdi + 8, arr[ 1 ] );\n\t\t\t_mm256_storeu_ps( rdi + 8 * 2, arr[ 2 ] );\n\t\t\t_mm256_maskstore_ps( rdi + 8 * 3, mask, arr[ 3 ] );\n\n\t\t\t_mm256_storeu_ps( rdi1, arr[ 4 ] );\n\t\t\t_mm256_storeu_ps( rdi1 + 8, arr[ 5 ] );\n\t\t\t_mm256_storeu_ps( rdi1 + 8 * 2, arr[ 6 ] );\n\t\t\t_mm256_maskstore_ps( rdi1 + 8 * 3, mask, arr[ 7 ] );\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tthrow E_UNEXPECTED;\n\t\t}\n\t}\n}\n\n__forceinline void ResultTile<2, 4>::store( float* rdi, size_t w, size_t h, size_t stride ) const\n{\n\tassert( h > 0 && w > 0 && h <= 4 && w <= 16 );\n\th--;\n\tfloat* const rdi1 = rdi + stride;\n\tfloat* const rdi2 = rdi + stride * 2;\n\tfloat* const rdi3 = rdi + stride * 3;\n\n\tif( w == 16 )\n\t{\n\t\tswitch( h )\n\t\t{\n\t\tcase 3:\n\t\t\t_mm256_storeu_ps( rdi3, arr[ 6 ] );\n\t\t\t_mm256_storeu_ps( rdi3 + 8, arr[ 7 ] );\n\t\tcase 2:\n\t\t\t_mm256_storeu_ps( rdi2, arr[ 4 ] );\n\t\t\t_mm256_storeu_ps( rdi2 + 8, arr[ 5 ] );\n\t\tcase 1:\n\t\t\t_mm256_storeu_ps( rdi1, arr[ 2 ] );\n\t\t\t_mm256_storeu_ps( rdi1 + 8, arr[ 3 ] );\n\t\tcase 0:\n\t\t\t_mm256_storeu_ps( rdi, arr[ 0 ] );\n\t\t\t_mm256_storeu_ps( rdi + 8, arr[ 1 ] );\n\t\t}\n\t}\n\telse\n\t{\n\t\tconst size_t rem = w % 8;\n\t\tconst __m256i mask = loadTailMaskInt<false>( rem );\n\t\t// 0 for partial first vector, 1 for exactly 1 complete vector, 2 for 1 complete vector with remainder\n\t\tconst size_t partialCase = ( w < 8 ) ? 0 : ( ( w == 8 ) ? 1 : 2 );\n\t\t// Merge into a single integer for the switch statement\n\t\tconst size_t key = partialCase + h * 3;\n\n\t\tswitch( key )\n\t\t{\n\t\t\t// h = 1\n\t\tcase 0:\n\t\t\t_mm256_maskstore_ps( rdi, mask, arr[ 0 ] );\n\t\t\tbreak;\n\t\tcase 1:\n\t\t\t_mm256_storeu_ps( rdi, arr[ 0 ] );\n\t\t\tbreak;\n\t\tcase 2:\n\t\t\t_mm256_storeu_ps( rdi, arr[ 0 ] );\n\t\t\t_mm256_maskstore_ps( rdi + 8, mask, arr[ 1 ] );\n\t\t\tbreak;\n\t\t\t// h = 2\n\t\tcase 3:\n\t\t\t_mm256_maskstore_ps( rdi, mask, arr[ 0 ] );\n\t\t\t_mm256_maskstore_ps( rdi1, mask, arr[ 2 ] );\n\t\t\tbreak;\n\t\tcase 4:\n\t\t\t_mm256_storeu_ps( rdi, arr[ 0 ] );\n\t\t\t_mm256_storeu_ps( rdi1, arr[ 2 ] );\n\t\t\tbreak;\n\t\tcase 5:\n\t\t\t_mm256_storeu_ps( rdi, arr[ 0 ] );\n\t\t\t_mm256_maskstore_ps( rdi + 8, mask, arr[ 1 ] );\n\t\t\t_mm256_storeu_ps( rdi1, arr[ 2 ] );\n\t\t\t_mm256_maskstore_ps( rdi1 + 8, mask, arr[ 3 ] );\n\t\t\tbreak;\n\t\t\t// h = 3\n\t\tcase 6:\n\t\t\t_mm256_maskstore_ps( rdi, mask, arr[ 0 ] );\n\t\t\t_mm256_maskstore_ps( rdi1, mask, arr[ 2 ] );\n\t\t\t_mm256_maskstore_ps( rdi2, mask, arr[ 4 ] );\n\t\t\tbreak;\n\t\tcase 7:\n\t\t\t_mm256_storeu_ps( rdi, arr[ 0 ] );\n\t\t\t_mm256_storeu_ps( rdi1, arr[ 2 ] );\n\t\t\t_mm256_storeu_ps( rdi2, arr[ 4 ] );\n\t\t\tbreak;\n\t\tcase 8:\n\t\t\t_mm256_storeu_ps( rdi, arr[ 0 ] );\n\t\t\t_mm256_maskstore_ps( rdi + 8, mask, arr[ 1 ] );\n\t\t\t_mm256_storeu_ps( rdi1, arr[ 2 ] );\n\t\t\t_mm256_maskstore_ps( rdi1 + 8, mask, arr[ 3 ] );\n\t\t\t_mm256_storeu_ps( rdi2, arr[ 4 ] );\n\t\t\t_mm256_maskstore_ps( rdi2 + 8, mask, arr[ 5 ] );\n\t\t\tbreak;\n\t\t\t// h = 4\n\t\tcase 9:\n\t\t\t_mm256_maskstore_ps( rdi, mask, arr[ 0 ] );\n\t\t\t_mm256_maskstore_ps( rdi1, mask, arr[ 2 ] );\n\t\t\t_mm256_maskstore_ps( rdi2, mask, arr[ 4 ] );\n\t\t\t_mm256_maskstore_ps( rdi3, mask, arr[ 6 ] );\n\t\t\tbreak;\n\t\tcase 10:\n\t\t\t_mm256_storeu_ps( rdi, arr[ 0 ] );\n\t\t\t_mm256_storeu_ps( rdi1, arr[ 2 ] );\n\t\t\t_mm256_storeu_ps( rdi2, arr[ 4 ] );\n\t\t\t_mm256_storeu_ps( rdi3, arr[ 6 ] );\n\t\t\tbreak;\n\t\tcase 11:\n\t\t\t_mm256_storeu_ps( rdi, arr[ 0 ] );\n\t\t\t_mm256_maskstore_ps( rdi + 8, mask, arr[ 1 ] );\n\t\t\t_mm256_storeu_ps( rdi1, arr[ 2 ] );\n\t\t\t_mm256_maskstore_ps( rdi1 + 8, mask, arr[ 3 ] );\n\t\t\t_mm256_storeu_ps( rdi2, arr[ 4 ] );\n\t\t\t_mm256_maskstore_ps( rdi2 + 8, mask, arr[ 5 ] );\n\t\t\t_mm256_storeu_ps( rdi3, arr[ 6 ] );\n\t\t\t_mm256_maskstore_ps( rdi3 + 8, mask, arr[ 7 ] );\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tthrow E_UNEXPECTED;\n\t\t}\n\t}\n}\n\n__forceinline void ResultTile<2, 3>::store( float* rdi, size_t w, size_t h, size_t stride ) const\n{\n\tassert( h > 0 && w > 0 && h <= 3 && w <= 16 );\n\tfloat* const rdi1 = rdi + stride;\n\tfloat* const rdi2 = rdi + stride * 2;\n\th--;\n\n\tif( w == 16 )\n\t{\n\t\tswitch( h )\n\t\t{\n\t\tcase 2:\n\t\t\t_mm256_storeu_ps( rdi2, arr[ 4 ] );\n\t\t\t_mm256_storeu_ps( rdi2 + 8, arr[ 5 ] );\n\t\tcase 1:\n\t\t\t_mm256_storeu_ps( rdi1, arr[ 2 ] );\n\t\t\t_mm256_storeu_ps( rdi1 + 8, arr[ 3 ] );\n\t\tcase 0:\n\t\t\t_mm256_storeu_ps( rdi, arr[ 0 ] );\n\t\t\t_mm256_storeu_ps( rdi + 8, arr[ 1 ] );\n\t\t}\n\t}\n\telse\n\t{\n\t\tconst size_t rem = w % 8;\n\t\tconst __m256i mask = loadTailMaskInt<false>( rem );\n\t\t// 0 for partial first vector, 1 for exactly 1 complete vector, 2 for 1 complete vector with remainder\n\t\tconst size_t partialCase = ( w < 8 ) ? 0 : ( ( w == 8 ) ? 1 : 2 );\n\t\t// Merge into a single integer for the switch statement\n\t\tconst size_t key = partialCase + h * 3;\n\n\t\tswitch( key )\n\t\t{\n\t\t\t// h = 1\n\t\tcase 0:\n\t\t\t_mm256_maskstore_ps( rdi, mask, arr[ 0 ] );\n\t\t\tbreak;\n\t\tcase 1:\n\t\t\t_mm256_storeu_ps( rdi, arr[ 0 ] );\n\t\t\tbreak;\n\t\tcase 2:\n\t\t\t_mm256_storeu_ps( rdi, arr[ 0 ] );\n\t\t\t_mm256_maskstore_ps( rdi + 8, mask, arr[ 1 ] );\n\t\t\tbreak;\n\t\t\t// h = 2\n\t\tcase 3:\n\t\t\t_mm256_maskstore_ps( rdi, mask, arr[ 0 ] );\n\t\t\t_mm256_maskstore_ps( rdi1, mask, arr[ 2 ] );\n\t\t\tbreak;\n\t\tcase 4:\n\t\t\t_mm256_storeu_ps( rdi, arr[ 0 ] );\n\t\t\t_mm256_storeu_ps( rdi1, arr[ 2 ] );\n\t\t\tbreak;\n\t\tcase 5:\n\t\t\t_mm256_storeu_ps( rdi, arr[ 0 ] );\n\t\t\t_mm256_maskstore_ps( rdi + 8, mask, arr[ 1 ] );\n\t\t\t_mm256_storeu_ps( rdi1, arr[ 2 ] );\n\t\t\t_mm256_maskstore_ps( rdi1 + 8, mask, arr[ 3 ] );\n\t\t\tbreak;\n\t\t\t// h = 3\n\t\tcase 6:\n\t\t\t_mm256_maskstore_ps( rdi, mask, arr[ 0 ] );\n\t\t\t_mm256_maskstore_ps( rdi1, mask, arr[ 2 ] );\n\t\t\t_mm256_maskstore_ps( rdi2, mask, arr[ 4 ] );\n\t\t\tbreak;\n\t\tcase 7:\n\t\t\t_mm256_storeu_ps( rdi, arr[ 0 ] );\n\t\t\t_mm256_storeu_ps( rdi1, arr[ 2 ] );\n\t\t\t_mm256_storeu_ps( rdi2, arr[ 4 ] );\n\t\t\tbreak;\n\t\tcase 8:\n\t\t\t_mm256_storeu_ps( rdi, arr[ 0 ] );\n\t\t\t_mm256_maskstore_ps( rdi + 8, mask, arr[ 1 ] );\n\t\t\t_mm256_storeu_ps( rdi1, arr[ 2 ] );\n\t\t\t_mm256_maskstore_ps( rdi1 + 8, mask, arr[ 3 ] );\n\t\t\t_mm256_storeu_ps( rdi2, arr[ 4 ] );\n\t\t\t_mm256_maskstore_ps( rdi2 + 8, mask, arr[ 5 ] );\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tthrow E_UNEXPECTED;\n\t\t}\n\t}\n}\n#pragma endregion"
  },
  {
    "path": "Whisper/CPU/mulMatImpl.avx2.cpp",
    "content": "#include \"stdafx.h\"\n#include \"mulMatImpl.h\"\n#include <immintrin.h>\n#include \"mulMatUtils.hpp\"\nusing namespace CpuCompute;\n\nnamespace\n{\n\tconstexpr size_t prefetchBytes = 96;\n\tconstexpr int prefetchHint = _MM_HINT_T0;\n\n\tconstexpr size_t maskAlign16 = ~(size_t)15;\n\n\t__forceinline __m256i load( const void* rsi )\n\t{\n\t\treturn _mm256_loadu_si256( ( const __m256i* )rsi );\n\t}\n\n#define TRANSPOSE_8X16()                           \\\n                                                   \\\n\t__m256i t0 = _mm256_unpacklo_epi16( r0, r1 );  \\\n\t__m256i t1 = _mm256_unpackhi_epi16( r0, r1 );  \\\n\t__m256i t2 = _mm256_unpacklo_epi16( r2, r3 );  \\\n\t__m256i t3 = _mm256_unpackhi_epi16( r2, r3 );  \\\n\t__m256i t4 = _mm256_unpacklo_epi16( r4, r5 );  \\\n\t__m256i t5 = _mm256_unpackhi_epi16( r4, r5 );  \\\n\t__m256i t6 = _mm256_unpacklo_epi16( r6, r7 );  \\\n\t__m256i t7 = _mm256_unpackhi_epi16( r6, r7 );  \\\n                                                   \\\n\tr0 = _mm256_unpacklo_epi32( t0, t2 );          \\\n\tr1 = _mm256_unpackhi_epi32( t0, t2 );          \\\n\tr2 = _mm256_unpacklo_epi32( t1, t3 );          \\\n\tr3 = _mm256_unpackhi_epi32( t1, t3 );          \\\n\tr4 = _mm256_unpacklo_epi32( t4, t6 );          \\\n\tr5 = _mm256_unpackhi_epi32( t4, t6 );          \\\n\tr6 = _mm256_unpacklo_epi32( t5, t7 );          \\\n\tr7 = _mm256_unpackhi_epi32( t5, t7 );          \\\n                                                   \\\n\tt0 = _mm256_unpacklo_epi64( r0, r4 );          \\\n\tt1 = _mm256_unpackhi_epi64( r0, r4 );          \\\n\tt2 = _mm256_unpacklo_epi64( r1, r5 );          \\\n\tt3 = _mm256_unpackhi_epi64( r1, r5 );          \\\n\tt4 = _mm256_unpacklo_epi64( r2, r6 );          \\\n\tt5 = _mm256_unpackhi_epi64( r2, r6 );          \\\n\tt6 = _mm256_unpacklo_epi64( r3, r7 );          \\\n\tt7 = _mm256_unpackhi_epi64( r3, r7 )\n\n\t__forceinline void storeLow( void* rdi, __m256i v )\n\t{\n\t\t__m128i i = _mm256_castsi256_si128( v );\n\t\t_mm_store_si128( ( __m128i* )rdi, i );\n\t}\n\n#define STORE_8X16_LOW()                     \\\n\tstoreLow( rdi, t0 );                     \\\n\tstoreLow( rdi + destStride, t1 );        \\\n\tstoreLow( rdi + destStride * 2, t2 );    \\\n\trdi += destStride * 8;                   \\\n\tstoreLow( rdiMid, t3 );                  \\\n\tstoreLow( rdiMid + destStride, t4 );     \\\n\tstoreLow( rdiMid + destStride * 2, t5 ); \\\n\trdiMid += destStride * 8;                \\\n\tstoreLow( rdiLast, t6 );                 \\\n\tstoreLow( rdiLast + destStride, t7 );    \\\n\trdiLast += destStride * 8\n\n\t__forceinline void storeHigh( void* rdi, __m256i v )\n\t{\n\t\t__m128i i = _mm256_extracti128_si256( v, 1 );\n\t\t_mm_store_si128( ( __m128i* )rdi, i );\n\t}\n\n#define STORE_8X16_HIGH()                     \\\n\tstoreHigh( rdi, t0 );                     \\\n\tstoreHigh( rdi + destStride, t1 );        \\\n\tstoreHigh( rdi + destStride * 2, t2 );    \\\n\trdi += destStride * 8;                    \\\n\tstoreHigh( rdiMid, t3 );                  \\\n\tstoreHigh( rdiMid + destStride, t4 );     \\\n\tstoreHigh( rdiMid + destStride * 2, t5 ); \\\n\trdiMid += destStride * 8;                 \\\n\tstoreHigh( rdiLast, t6 );                 \\\n\tstoreHigh( rdiLast + destStride, t7 );    \\\n\trdiLast += destStride * 8\n\n\t__forceinline void prefetch( const uint8_t* p )\n\t{\n\t\t_mm_prefetch( (const char*)p, prefetchHint );\n\t}\n\n\t__forceinline void transpose8Avx2( uint16_t* rdiWords, size_t w, const uint16_t* rsiWords, size_t sourceStride, size_t destStride )\n\t{\n\t\tassert( 0 == ( (size_t)rdiWords ) % 16 );\n\t\tassert( 0 == destStride % 8 );\n\t\tassert( w <= sourceStride );\n\n\t\t// Scale strides to bytes, and cast the pointers\n\t\tsourceStride *= 2;\n\t\tdestStride *= 2;\n\t\tuint8_t* rdi = (uint8_t*)rdiWords;\n\t\tconst uint8_t* rsi = (const uint8_t*)rsiWords;\n\n\t\tconst uint8_t* const rsiEndAligned = rsi + ( w & maskAlign16 ) * 2;\n\t\tconst uint8_t* const rsiEnd = rsi + w * 2;\n\t\tconst uint8_t* rsiMid = rsi + sourceStride * 3;\n\t\tconst uint8_t* rsiLast = rsi + sourceStride * 6;\n\t\tuint8_t* rdiMid = rdi + destStride * 3;\n\t\tuint8_t* rdiLast = rdi + destStride * 6;\n\n\t\twhile( rsi < rsiEndAligned )\n\t\t{\n\t\t\t// Load 16x8 block into 8 registers\n\t\t\t__m256i r0 = load( rsi );\n\t\t\t__m256i r1 = load( rsi + sourceStride );\n\t\t\t__m256i r2 = load( rsi + sourceStride * 2 );\n\t\t\trsi += 32;\n\t\t\t__m256i r3 = load( rsiMid );\n\t\t\t__m256i r4 = load( rsiMid + sourceStride );\n\t\t\t__m256i r5 = load( rsiMid + sourceStride * 2 );\n\t\t\trsiMid += 32;\n\t\t\t__m256i r6 = load( rsiLast );\n\t\t\t__m256i r7 = load( rsiLast + sourceStride );\n\t\t\trsiLast += 32;\n\n\t\t\t// Transpose FP16 values in registers\n\t\t\tTRANSPOSE_8X16();\n\n\t\t\t// Store\n\t\t\tSTORE_8X16_LOW();\n\t\t\tSTORE_8X16_HIGH();\n\n\t\t\tif constexpr( prefetchBytes > 0 )\n\t\t\t{\n\t\t\t\tif( rsi + prefetchBytes < rsiEnd )\n\t\t\t\t{\n\t\t\t\t\tprefetch( rsi + prefetchBytes );\n\t\t\t\t\tprefetch( rsi + sourceStride + prefetchBytes );\n\t\t\t\t\tprefetch( rsi + sourceStride * 2 + prefetchBytes );\n\t\t\t\t\tprefetch( rsiMid + prefetchBytes );\n\t\t\t\t\tprefetch( rsiMid + sourceStride + prefetchBytes );\n\t\t\t\t\tprefetch( rsiMid + sourceStride * 2 + prefetchBytes );\n\t\t\t\t\tprefetch( rsiLast + prefetchBytes );\n\t\t\t\t\tprefetch( rsiLast + sourceStride + prefetchBytes );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif( rsi < rsiEnd )\n\t\t{\n\t\t\t// Loading 8 elements into corresponding lanes of 8 vectors\n\t\t\t// This way there's no data dependencies between these load instructions\n\t\t\t// Out of order execution should hopefully do it's magic in the CPU, running all these loads in parallel.\n\t\t\t__m128i r0;\n\t\t\t__m128i r1 = _mm_setzero_si128();\n\t\t\t__m128i r2 = _mm_setzero_si128();\n\t\t\t__m128i r3 = _mm_setzero_si128();\n\t\t\t__m128i r4 = _mm_setzero_si128();\n\t\t\t__m128i r5 = _mm_setzero_si128();\n\t\t\t__m128i r6 = _mm_setzero_si128();\n\t\t\t__m128i r7 = _mm_setzero_si128();\n\n\t\t\t__m128i t0, t1, t2, t3, t4, t5, t6;\n\n#pragma loop( no_vector )\n\t\t\twhile( rsi < rsiEnd )\n\t\t\t{\n\t\t\t\tr0 = _mm_cvtsi32_si128( *(const uint16_t*)rsi );\n\t\t\t\tr1 = _mm_insert_epi16( r1, *(const int16_t*)( rsi + sourceStride ), 1 );\n\t\t\t\tr2 = _mm_insert_epi16( r2, *(const int16_t*)( rsi + sourceStride * 2 ), 2 );\n\t\t\t\trsi += 2;\n\t\t\t\tr3 = _mm_insert_epi16( r3, *(const int16_t*)( rsiMid ), 3 );\n\t\t\t\tr4 = _mm_insert_epi16( r4, *(const int16_t*)( rsiMid + sourceStride ), 4 );\n\t\t\t\tr5 = _mm_insert_epi16( r5, *(const int16_t*)( rsiMid + sourceStride * 2 ), 5 );\n\t\t\t\trsiMid += 2;\n\t\t\t\tr6 = _mm_insert_epi16( r6, *(const int16_t*)( rsiLast ), 6 );\n\t\t\t\tr7 = _mm_insert_epi16( r7, *(const int16_t*)( rsiLast + sourceStride ), 7 );\n\t\t\t\trsiLast += 2;\n\n\t\t\t\t// Bitwise operations are pretty fast, AMD Zen3 CPU can run 4 of them every clock cycle\n\t\t\t\t// Combine 8 vectors into one\n\t\t\t\tt0 = _mm_or_si128( r0, r1 );\n\t\t\t\tt1 = _mm_or_si128( r2, r3 );\n\t\t\t\tt2 = _mm_or_si128( r4, r5 );\n\t\t\t\tt3 = _mm_or_si128( r6, r7 );\n\n\t\t\t\tt4 = _mm_or_si128( t0, t1 );\n\t\t\t\tt5 = _mm_or_si128( t2, t3 );\n\n\t\t\t\tt6 = _mm_or_si128( t4, t5 );\n\t\t\t\t// Store 8 FP16 values, the destination is aligned\n\t\t\t\t_mm_store_si128( ( __m128i* )rdi, t6 );\n\t\t\t\trdi += destStride;\n\t\t\t}\n\t\t}\n\t}\n\n\t__forceinline void transpose8PartialAvx2( uint16_t* rdiWords, size_t w, size_t h, const uint16_t* rsiWords, size_t sourceStride, size_t destStride )\n\t{\n\t\tassert( 0 == ( (size_t)rdiWords ) % 16 );\n\t\tassert( 0 == destStride % 8 );\n\t\tassert( w <= sourceStride );\n\t\tassert( h > 0 && h < 8 );\n\n\t\t// Scale strides to bytes, and cast the pointers\n\t\tsourceStride *= 2;\n\t\tdestStride *= 2;\n\t\tuint8_t* rdi = (uint8_t*)rdiWords;\n\t\tconst uint8_t* rsi = (const uint8_t*)rsiWords;\n\n\t\tconst uint8_t* const rsiEndAligned = rsi + ( w & maskAlign16 ) * 2;\n\t\tconst uint8_t* const rsiEnd = rsi + w * 2;\n\t\tconst uint8_t* rsiMid = rsi + sourceStride * 3;\n\t\tconst uint8_t* rsiLast = rsi + sourceStride * 6;\n\t\tuint8_t* rdiMid = rdi + destStride * 3;\n\t\tuint8_t* rdiLast = rdi + destStride * 6;\n\n\t\twhile( rsi < rsiEndAligned )\n\t\t{\n\t\t\t// Load the block into 8 registers, set unused rows to zero\n\t\t\t__m256i r0 = load( rsi );\n\t\t\t__m256i r1 = _mm256_setzero_si256();\n\t\t\t__m256i r2 = _mm256_setzero_si256();\n\t\t\t__m256i r3 = _mm256_setzero_si256();\n\t\t\t__m256i r4 = _mm256_setzero_si256();\n\t\t\t__m256i r5 = _mm256_setzero_si256();\n\t\t\t__m256i r6 = _mm256_setzero_si256();\n\t\t\t// These branches, whether direct or indirect, are very predictable: same outcome for all iterations of the outer loop\n\t\t\tswitch( h )\n\t\t\t{\n\t\t\tcase 7:\n\t\t\t\tr6 = load( rsiLast );\n\t\t\tcase 6:\n\t\t\t\tr5 = load( rsiMid + sourceStride * 2 );\n\t\t\tcase 5:\n\t\t\t\tr4 = load( rsiMid + sourceStride );\n\t\t\tcase 4:\n\t\t\t\tr3 = load( rsiMid );\n\t\t\tcase 3:\n\t\t\t\tr2 = load( rsi + sourceStride * 2 );\n\t\t\tcase 2:\n\t\t\t\tr1 = load( rsi + sourceStride );\n\t\t\t}\n\t\t\trsi += 32;\n\t\t\trsiMid += 32;\n\t\t\trsiLast += 32;\n\n\t\t\t__m256i r7 = _mm256_setzero_si256();\n\n\t\t\t// Transpose FP16 values in registers\n\t\t\tTRANSPOSE_8X16();\n\n\t\t\t// Store\n\t\t\tSTORE_8X16_LOW();\n\n\t\t\tSTORE_8X16_HIGH();\n\t\t}\n\n\t\tif( rsi < rsiEnd )\n\t\t{\n\t\t\t// Loading 8 elements into corresponding lanes of 8 vectors\n\t\t\t// This way there's no data dependencies between these load instructions\n\t\t\t// Out of order execution should hopefully do it's magic in the CPU, running all these loads in parallel.\n\t\t\t__m128i r0;\n\t\t\t__m128i r1 = _mm_setzero_si128();\n\t\t\t__m128i r2 = _mm_setzero_si128();\n\t\t\t__m128i r3 = _mm_setzero_si128();\n\t\t\t__m128i r4 = _mm_setzero_si128();\n\t\t\t__m128i r5 = _mm_setzero_si128();\n\t\t\t__m128i r6 = _mm_setzero_si128();\n\n\t\t\t__m128i t0, t1, t2, t3, t4, t5;\n\n#pragma loop( no_vector )\n\t\t\twhile( rsi < rsiEnd )\n\t\t\t{\n\t\t\t\tr0 = _mm_cvtsi32_si128( *(const uint16_t*)rsi );\n\n\t\t\t\tswitch( h )\n\t\t\t\t{\n\t\t\t\tcase 7:\n\t\t\t\t\tr6 = _mm_insert_epi16( r6, *(const int16_t*)( rsiLast ), 6 );\n\t\t\t\tcase 6:\n\t\t\t\t\tr5 = _mm_insert_epi16( r5, *(const int16_t*)( rsiMid + sourceStride * 2 ), 5 );\n\t\t\t\tcase 5:\n\t\t\t\t\tr4 = _mm_insert_epi16( r4, *(const int16_t*)( rsiMid + sourceStride ), 4 );\n\t\t\t\tcase 4:\n\t\t\t\t\tr3 = _mm_insert_epi16( r3, *(const int16_t*)( rsiMid ), 3 );\n\t\t\t\tcase 3:\n\t\t\t\t\tr2 = _mm_insert_epi16( r2, *(const int16_t*)( rsi + sourceStride * 2 ), 2 );\n\t\t\t\tcase 2:\n\t\t\t\t\tr1 = _mm_insert_epi16( r1, *(const int16_t*)( rsi + sourceStride ), 1 );\n\t\t\t\t}\n\t\t\t\trsi += 2;\n\t\t\t\trsiMid += 2;\n\t\t\t\trsiLast += 2;\n\n\t\t\t\t// Bitwise operations are pretty fast, AMD Zen3 CPU can run 4 of them every clock cycle\n\t\t\t\t// Combine 7 vectors into one\n\t\t\t\tt0 = _mm_or_si128( r0, r1 );\n\t\t\t\tt1 = _mm_or_si128( r2, r3 );\n\t\t\t\tt2 = _mm_or_si128( r4, r5 );\n\n\t\t\t\tt3 = _mm_or_si128( t0, t1 );\n\t\t\t\tt4 = _mm_or_si128( t2, r6 );\n\n\t\t\t\tt5 = _mm_or_si128( t3, t4 );\n\t\t\t\t// Store 8 FP16 values, the destination is aligned\n\t\t\t\t_mm_store_si128( ( __m128i* )rdi, t5 );\n\t\t\t\trdi += destStride;\n\t\t\t}\n\t\t}\n\t}\n}\n\n// At least for the hybrid decoder, this method absolutely dominates the CPU time.\n// And not due to the integer shuffles - the bottleneck is loading data from the source matrix.\nHRESULT MulMatBase::transposePanelAvx2( uint16_t* rdi, size_t i, size_t m2, size_t m3 ) const\n{\n\tassert( stridesA[ 0 ] == 1 );\n\n\tconst size_t heightFloats = (size_t)panelHeightRegisters * 8;\n\ti *= heightFloats;\n\n\tconst uint16_t* rsi = (const uint16_t*)pa;\n\trsi += m3 * stridesA[ 3 ];\n\trsi += m2 * stridesA[ 2 ];\n\trsi += i * stridesA[ 1 ];\n\n\tconst size_t resultStride = heightFloats;\n\n\tif( i + heightFloats <= resultSize[ 0 ] )\n\t{\n\t\t// A complete panel\n\t\tfor( size_t i = 0; i < panelHeightRegisters; i++ )\n\t\t{\n\t\t\ttranspose8Avx2( rdi, length, rsi, stridesA[ 1 ], resultStride );\n\t\t\t// Advance by 8 floats in the output buffer\n\t\t\trdi += 8;\n\t\t\t// Advance by 8 rows in the source matrix\n\t\t\trsi += 8 * stridesA[ 1 ];\n\t\t}\n\t}\n\telse\n\t{\n\t\t// A partial panel, at the bottom of the first argument matrix\n\t\tconst size_t remainder = resultSize[ 0 ] - i;\n\t\tassert( remainder > 0 && remainder < heightFloats );\n\t\tzeroAlignedMemory( rdi, resultStride * length * sizeof( uint16_t ) );\n\n\t\tconst size_t completePanels = remainder / 8;\n\t\tfor( size_t i = 0; i < completePanels; i++ )\n\t\t{\n\t\t\ttranspose8Avx2( rdi, length, rsi, stridesA[ 1 ], resultStride );\n\t\t\trdi += 8;\n\t\t\trsi += 8 * stridesA[ 1 ];\n\t\t}\n\t\tconst size_t lastPanel = remainder % 8;\n\t\tif( 0 != lastPanel )\n\t\t\ttranspose8PartialAvx2( rdi, length, lastPanel, rsi, stridesA[ 1 ], resultStride );\n\t}\n\treturn S_OK;\n}"
  },
  {
    "path": "Whisper/CPU/mulMatImpl.cpp",
    "content": "#include \"stdafx.h\"\n#include <intrin.h>\n#include \"mulMatImpl.h\"\n#include \"mulMat.kernel.hpp\"\n\n#define DBG_TRACK_TEMPLATE_INSTANTIATION 0\n\n#if DBG_TRACK_TEMPLATE_INSTANTIATION\n#include <unordered_set>\nstatic std::unordered_set<uint16_t> g_mulMatTemplates;\n#endif\n\nnamespace\n{\n\tusing namespace CpuCompute;\n\n\tbool checkAvx2Support()\n\t{\n\t\tint cpuInfo[ 4 ];\n\t\t__cpuid( cpuInfo, 7 );\n\t\treturn ( cpuInfo[ 1 ] & ( 1 << 5 ) ) != 0;\n\t}\n\n\t// a / b, rounded up to the next integer\n\tinline uint32_t divRoundUp( uint32_t a, uint32_t b )\n\t{\n\t\tassert( b != 0 );\n\t\treturn ( a + ( b - 1 ) ) / b;\n\t}\n}\n\nconst bool MulMatBase::haveAvx2 = checkAvx2Support();\n\nMulMatBase::MulMatBase( Tensor& result, const Tensor& a, const Tensor& b, ParallelForRunner& pfor, uint8_t panelHeightRegs, uint8_t tileWidthFloats ) :\n\tresultPointer( result.fp32() ),\n\tpa( a.data() ),\n\tpb( b.data() ),\n\trunner( pfor )\n{\n\tlength = a.ne[ 0 ];\n\tresultStrides[ 0 ] = result.nb[ 1 ];\n\tresultStrides[ 1 ] = result.nb[ 2 ];\n\tresultStrides[ 2 ] = result.nb[ 3 ];\n\tstore( resultSize, result.sizeVec() );\n\tstore( stridesA, a.stridesVec() );\n\tstore( stridesB, b.stridesVec() );\n\n\tcountPanels = divRoundUp( resultSize[ 0 ], panelHeightRegs * 8 );\n\tcompleteTilesPerPanel = resultSize[ 1 ] / tileWidthFloats;\n\tlastColumnsInPanel = (uint8_t)( resultSize[ 1 ] % tileWidthFloats );\n\tthis->panelHeightRegisters = panelHeightRegs;\n\tthis->tileWidth = tileWidthFloats;\n\n\t// Pick a method which reshapes a panel of the matrix A into the shape we need to compute the product\n\t// Store the pointer to that method in the field of this class\n\tif( a.nb[ 0 ] == 1 )\n\t{\n\t\tif( haveAvx2 )\n\t\t\tpfnMakePanel = &MulMatBase::transposePanelAvx2;\n\t\telse\n\t\t\tpfnMakePanel = &MulMatBase::transposePanel;\n\t}\n\telse if( a.nb[ 1 ] == 1 )\n\t{\n\t\tswitch( panelHeightRegs )\n\t\t{\n\t\tcase 1:\n\t\t\tpfnMakePanel = &MulMatBase::copyPanelColumnMajor8;\n\t\t\tbreak;\n\t\tcase 2:\n\t\t\tpfnMakePanel = &MulMatBase::copyPanelColumnMajor16;\n\t\t\tbreak;\n\t\tcase 4:\n\t\t\tpfnMakePanel = &MulMatBase::copyPanelColumnMajor32;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tthrow E_NOTIMPL;\n\t\t}\n\t}\n\telse\n\t\tpfnMakePanel = &MulMatBase::gatherPanel;\n\n\t// That last version is generic and very simple, unlikely to have weird bugs\n\t// pfnMakePanel = &MulMatBase::gatherPanel;\n\n#if DBG_TRACK_TEMPLATE_INSTANTIATION\n\tuint16_t key = panelHeightRegs;\n\tkey = key << 8;\n\tkey |= tileWidthFloats;\n\tif( !g_mulMatTemplates.emplace( key ).second )\n\t\treturn;\n\tlogDebug( u8\"MulMatImpl<panelHeightRegs = %i, tileWidthFloats = %i>\", (int)panelHeightRegs, (int)tileWidthFloats );\n#endif\n}\n\nHRESULT MulMatBase::run( ParallelForRunner& pfor )\n{\n\tsize_t length = (size_t)countPanels * resultSize[ 2 ] * resultSize[ 3 ];\n\treturn pfor.parallelFor( *this, length );\n}\n\nconst float* MulMatBase::getLayerB( size_t m2, size_t m3 ) const\n{\n\tconst float* rsi = (const float*)this->pb;\n\trsi += m2 * stridesB[ 2 ];\n\trsi += m3 * stridesB[ 3 ];\n\treturn rsi;\n}\n\n// This method is the main one, its called by the thread pool\ntemplate<uint8_t panelHeightRegs, uint8_t tileWidthFloats>\nHRESULT __stdcall MulMatImpl<panelHeightRegs, tileWidthFloats>::compute( size_t i, size_t end ) const noexcept\n{\n\t// Allocate a thread-local buffer for the transposed panel\n\tconstexpr size_t panelHeightFloats = panelHeightRegs * 8;\n\tuint16_t* const panel = (uint16_t*)runner.threadLocalBuffer( floatsPerPanel() * 2 );\n\tconst size_t resultStride = resultStrides[ 0 ];\n\n\t// Load a few numbers from this class into local variables, while upcasting from DWORD into size_t\n\tconst size_t length = this->length;\n\tconst std::array<size_t, 2> stridesB{ this->stridesB[ 0 ], this->stridesB[ 1 ] };\n\n\t// This outer loop iterates over the panels assigned to the current thread\n\t// For example, matrix A of size [ 1024, 1024 ] may be split into panels of size [ 1024, 16 ]\n\t// Each iteration of that loop computes matrix product of that panel, with the complete matrix B\n\tfor( ; i < end; i++ )\n\t{\n\t\tconst size_t iPanel = i % countPanels;\n\t\tsize_t j = i / countPanels;\n\t\tconst size_t m2 = j % (size_t)resultSize[ 2 ];\n\t\tconst size_t m3 = j / (size_t)resultSize[ 2 ];\n\n\t\tCHECK( ( this->*pfnMakePanel )( panel, iPanel, m2, m3 ) );\n\t\t// We got a column-major panel in the thread local buffer, of size [ length, panelHeightRegs * 8 ]\n\t\t// Hopefully, these buffers should all fit at least in L3 cache\n\t\t// The longest matrix I saw in the debugger had 4096 elements, with panelHeightRegs = 4 that's 256 kb of data in the panel\n\t\tconst float* pb = getLayerB( m2, m3 );\n\t\tfloat* rdi = getPanelDest( iPanel, m2, m3 );\n\n\t\tconst size_t storeWidth = std::min( panelHeightFloats, (size_t)resultSize[ 0 ] - iPanel * panelHeightFloats );\n\t\tstd::array<__m256, panelHeightRegs> vecPanel;\n#if 1\n\t\tResultTile<panelHeightRegs, tileWidthFloats> tile;\n\n\t\t// This loop iterates over tiles within the panel.\n\t\t// Each iteration of the loop computes an output tile of the result matrix.\n\t\tfor( j = 0; j < completeTilesPerPanel; j++, pb += tileWidthFloats * stridesB[ 1 ], rdi += resultStride * tileWidthFloats )\n\t\t{\n\t\t\tsetZero( tile.arr );\n\t\t\tconst uint16_t* rsiA = panel;\n\t\t\tconst uint16_t* const rsiAEnd = panel + length * panelHeightFloats;\n\t\t\tconst float* rsiB = pb;\n\t\t\t// This loop runs for `length` iterations, iterates over the first dimensions of both matrices, accumulating these dot products we're after\n\t\t\tfor( ; rsiA < rsiAEnd; rsiA += panelHeightFloats, rsiB += stridesB[ 0 ] )\n\t\t\t{\n\t\t\t\tloadPanel( rsiA, vecPanel );\n\t\t\t\ttile.kernel( vecPanel, rsiB, stridesB[ 1 ] );\n\t\t\t}\n\t\t\ttile.store( rdi, storeWidth, tileWidthFloats, resultStride );\n\t\t}\n\n\t\tif( 0 != lastColumnsInPanel )\n\t\t{\n\t\t\tsetZero( tile.arr );\n\t\t\tconst uint16_t* rsiA = panel;\n\t\t\tconst uint16_t* rsiAEnd = panel + length * panelHeightFloats;\n\t\t\tconst float* rsiB = pb;\n\t\t\tfor( ; rsiA < rsiAEnd; rsiA += panelHeightFloats, rsiB += stridesB[ 0 ] )\n\t\t\t{\n\t\t\t\tloadPanel( rsiA, vecPanel );\n\t\t\t\ttile.kernelPartial( vecPanel, rsiB, stridesB[ 1 ], lastColumnsInPanel );\n\t\t\t}\n\t\t\ttile.store( rdi, storeWidth, lastColumnsInPanel, resultStride );\n\t\t}\n#else\n\t\t// This version bypasses horizontal tiling, instead implements a brute force algorithm to multiply the current panel by the complete B matrix\n\t\t// Not terribly efficient, only implemented for debugging purposes\n\t\tconst size_t resHeight = resultSize[ 1 ];\n\t\tstd::array<__m256, panelHeightRegs> tile;\n\t\tfor( size_t j = 0; j < resHeight; j++, pb += stridesB[ 1 ], rdi += resultStride )\n\t\t{\n\t\t\tsetZero( tile );\n\n\t\t\tconst uint16_t* rsiA = panel;\n\t\t\tconst uint16_t* const rsiAEnd = panel + length * panelHeightFloats;\n\t\t\tconst float* rsiB = pb;\n\t\t\tfor( size_t k = 0; k < length; k++, rsiA += panelHeightFloats, rsiB += stridesB[ 0 ] )\n\t\t\t{\n\t\t\t\tloadPanel( rsiA, vecPanel );\n\t\t\t\tconst __m256 b = _mm256_broadcast_ss( rsiB );\n\t\t\t\tfor( size_t r = 0; r < panelHeightRegs; r++ )\n\t\t\t\t\ttile[ r ] = _mm256_fmadd_ps( vecPanel[ r ], b, tile[ r ] );\n\t\t\t}\n\n\t\t\talignas( 32 ) std::array<float, panelHeightFloats> arr;\n\t\t\tfor( size_t k = 0; k < panelHeightRegs; k++ )\n\t\t\t\t_mm256_store_ps( &arr[ k * 8 ], tile[ k ] );\n\t\t\tmemcpy( rdi, arr.data(), storeWidth * 4 );\n\t\t}\n#endif\n\t}\n\treturn S_OK;\n}\n\n// Instantiate the templates we need\ntemplate class MulMatImpl<4, 1>;\ntemplate class MulMatImpl<1, 1>;\ntemplate class MulMatImpl<4, 2>;\ntemplate class MulMatImpl<1, 2>;\ntemplate class MulMatImpl<2, 3>;\ntemplate class MulMatImpl<1, 3>;\ntemplate class MulMatImpl<2, 4>;\ntemplate class MulMatImpl<1, 4>;"
  },
  {
    "path": "Whisper/CPU/mulMatImpl.h",
    "content": "#pragma once\n// Matrix*matrix multiplication is the most expensive algorithm in the model, by far.\n// For this reason, the code in this source file, and in the mulMat.kernel.hpp header, is optimized for performance. Readability suffers.\n// The implementation is inspired by following two articles:\n// https://gist.github.com/nadavrot/5b35d44e8ba3dd718e595e40184d03f0\n// https://link.springer.com/article/10.1007/s11227-022-05003-3\n#include \"ParallelForRunner.h\"\n#include \"Tensor.h\"\n\nnamespace CpuCompute\n{\n\t// Abstract base class for all implementations, to reduce binary size\n\tclass MulMatBase : public iComputeRange\n\t{\n\tprotected:\n\t\t// Pointers to the payload of the output matrix\n\t\tfloat* const resultPointer;\n\n\t\t// Lengths of the dot products to compute, equal to width of both source matrices\n\t\tuint32_t length;\n\n\t\t// Last 3 strides of the output matrix, expressed as count of elements. The first one is always 1 because the output matrix is continuous.\n\t\tstd::array<uint32_t, 3> resultStrides;\n\n\t\t// Size of the output matrix\n\t\tstd::array<uint32_t, 4> resultSize;\n\n\t\t// Pointers to the payload of the source matrices\n\t\tconst void* const pa;\n\t\tconst void* const pb;\n\n\t\t// Matrix strides, expressed as count of elements\n\t\tstd::array<uint32_t, 4> stridesA, stridesB;\n\n\t\t// Total count of panels in the layer of the output matrix.\n\t\t// The last panel might be incomplete, with smaller height.\n\t\t// The thread-local buffer however is always complete, unused elements will be zeros.\n\t\tuint32_t countPanels;\n\n\t\t// Complete tiles in the length of the panel\n\t\tuint32_t completeTilesPerPanel;\n\n\t\t// Count of the last remainder columns in the panel, can be 0\n\t\tuint8_t lastColumnsInPanel;\n\n\t\t// Same as panelHeightRegs template argument - height of the panels, in AVX vectors\n\t\tuint8_t panelHeightRegisters;\n\n\t\t// Same as tileWidthFloats template argument - width of the tile, in floats\n\t\tuint8_t tileWidth;\n\n\t\t// Method pointer to reshape a panel from the source matrix into a thread-local buffer\n\t\tusing pfnTransposePanel = HRESULT( MulMatBase::* )( uint16_t* rdi, size_t i, size_t m2, size_t m3 ) const;\n\t\tpfnTransposePanel pfnMakePanel;\n\t\t// The object which implements multithreading for this job, and supplies memory for thread-local buffers\n\t\tParallelForRunner& runner;\n\n\t\t// Count of FP16 values in the thread-local panel buffer\n\t\tuint32_t floatsPerPanel() const\n\t\t{\n\t\t\treturn length * panelHeightRegisters * 8;\n\t\t}\n\n\t\t// Transpose a horizontal panel of the first matrix, when the rows are continuous in that matrix\n\t\tHRESULT transposePanel( uint16_t* rdi, size_t i, size_t m2, size_t m3 ) const;\n\t\tHRESULT transposePanelAvx2( uint16_t* rdi, size_t i, size_t m2, size_t m3 ) const;\n\t\t// Copy a horizontal panel of the first matrix without transpose, for column major layout of that matrix\n\t\tHRESULT copyPanelColumnMajor8( uint16_t* rdi, size_t i, size_t m2, size_t m3 ) const;\n\t\tHRESULT copyPanelColumnMajor16( uint16_t* rdi, size_t i, size_t m2, size_t m3 ) const;\n\t\tHRESULT copyPanelColumnMajor32( uint16_t* rdi, size_t i, size_t m2, size_t m3 ) const;\n\t\t// Transpose a panel of the first matrix for irregular layout of that matrix, when neither rows nor columns are at sequential addresses.\n\t\t// This one ain't implemented yet.\n\t\tHRESULT gatherPanel( uint16_t* rdi, size_t i, size_t m2, size_t m3 ) const;\n\n\t\tconst uint16_t* getPanelA( size_t i, size_t m2, size_t m3 ) const;\n\t\t// Pointer to the first element of the second source matrix in the specified layer\n\t\tconst float* getLayerB( size_t m2, size_t m3 ) const;\n\n\t\t// Pointer to the first element of the output tile of the result matrix\n\t\tfloat* getPanelDest( size_t i, size_t m2, size_t m3 ) const\n\t\t{\n\t\t\tfloat* rdi = resultPointer;\n\t\t\trdi += m2 * resultStrides[ 1 ];\n\t\t\trdi += m3 * resultStrides[ 2 ];\n\t\t\trdi += i * panelHeightRegisters * 8;\n\t\t\treturn rdi;\n\t\t}\n\n\t\tstatic const bool haveAvx2;\n\tpublic:\n\t\tMulMatBase( Tensor& result, const Tensor& a, const Tensor& b, ParallelForRunner& pfor, uint8_t panelHeightRegs, uint8_t tileWidthFloats );\n\t\tHRESULT run( ParallelForRunner& pfor );\n\t};\n\n\t// This class actually contains the kernels implementations\n\ttemplate<uint8_t panelHeightRegs, uint8_t tileWidthFloats>\n\tclass MulMatImpl : public MulMatBase\n\t{\n\t\tHRESULT __stdcall compute( size_t i, size_t end ) const noexcept override final;\n\n\tpublic:\n\t\tMulMatImpl( Tensor& result, const Tensor& a, const Tensor& b, ParallelForRunner& pfor ) :\n\t\t\tMulMatBase( result, a, b, pfor, panelHeightRegs, tileWidthFloats )\n\t\t{ }\n\t};\n}"
  },
  {
    "path": "Whisper/CPU/mulMatImpl.panel.cpp",
    "content": "#include \"stdafx.h\"\n#include <intrin.h>\n#include \"mulMatImpl.h\"\n#include \"mulMatUtils.hpp\"\nusing namespace CpuCompute;\n\n// We want to keep code size reasonable, that's why these panel reshaping methods are in the base class\nHRESULT MulMatBase::transposePanel( uint16_t* rdi, size_t i, size_t m2, size_t m3 ) const\n{\n\tassert( stridesA[ 0 ] == 1 );\n\n\tconst size_t heightFloats = (size_t)panelHeightRegisters * 8;\n\ti *= heightFloats;\n\n\tconst uint16_t* rsi = (const uint16_t*)pa;\n\trsi += m3 * stridesA[ 3 ];\n\trsi += m2 * stridesA[ 2 ];\n\trsi += i * stridesA[ 1 ];\n\n\tconst size_t resultStride = heightFloats;\n\n\tif( i + heightFloats <= resultSize[ 0 ] )\n\t{\n\t\t// A complete panel\n\t\tfor( size_t i = 0; i < panelHeightRegisters; i++ )\n\t\t{\n\t\t\ttranspose8( rdi, length, rsi, stridesA[ 1 ], resultStride );\n\t\t\t// Advance by 8 floats in the output buffer\n\t\t\trdi += 8;\n\t\t\t// Advance by 8 rows in the source matrix\n\t\t\trsi += 8 * stridesA[ 1 ];\n\t\t}\n\t}\n\telse\n\t{\n\t\t// A partial panel, at the bottom of the first argument matrix\n\t\tconst size_t remainder = resultSize[ 0 ] - i;\n\t\tassert( remainder > 0 && remainder < heightFloats );\n\t\tzeroAlignedMemory( rdi, resultStride * length * sizeof( uint16_t ) );\n\n\t\tconst size_t completePanels = remainder / 8;\n\t\tfor( size_t i = 0; i < completePanels; i++ )\n\t\t{\n\t\t\ttranspose8( rdi, length, rsi, stridesA[ 1 ], resultStride );\n\t\t\trdi += 8;\n\t\t\trsi += 8 * stridesA[ 1 ];\n\t\t}\n\t\tconst size_t lastPanel = remainder % 8;\n\t\tif( 0 != lastPanel )\n\t\t\ttranspose8Partial( rdi, length, lastPanel, rsi, stridesA[ 1 ], resultStride );\n\t}\n\treturn S_OK;\n}\n\ninline const uint16_t* MulMatBase::getPanelA( size_t i, size_t m2, size_t m3 ) const\n{\n\tconst uint16_t* rsi = (const uint16_t*)pa;\n\trsi += m3 * stridesA[ 3 ];\n\trsi += m2 * stridesA[ 2 ];\n\trsi += i * stridesA[ 1 ];\n\treturn rsi;\n}\n\nHRESULT MulMatBase::copyPanelColumnMajor8( uint16_t* rdi, size_t i, size_t m2, size_t m3 ) const\n{\n\tassert( stridesA[ 1 ] == 1 );\n\tassert( panelHeightRegisters == 1 );\n\n\tconstexpr size_t heightFloats = 8;\n\ti *= heightFloats;\n\tconst uint16_t* rsi = getPanelA( i, m2, m3 );\n\n\tconstexpr size_t resultStride = heightFloats;\n\n\tif( i + heightFloats <= resultSize[ 0 ] )\n\t{\n\t\t// A complete panel, height = 8 elements\n\t\tcopyColumnMajor( rdi, length, rsi, stridesA[ 0 ], resultStride );\n\t}\n\telse\n\t{\n\t\t// A partial panel, at the bottom of the first argument matrix\n\t\tconst size_t remainder = resultSize[ 0 ] - i;\n\t\tassert( remainder > 0 && remainder < heightFloats );\n\t\tcopyColumnMajorPartial( rdi, length, remainder, rsi, stridesA[ 0 ], resultStride );\n\t}\n\treturn S_OK;\n}\n\n__forceinline __m128i load8Partial( const uint16_t* x, size_t len )\n{\n\tassert( len > 0 && len < 8 );\n\t__m128i ix = _mm_setzero_si128();\n\tswitch( len )\n\t{\n\tcase 1: // load 2 bytes\n\t\tix = _mm_cvtsi32_si128( *x );\n\t\tbreak;\n\tcase 2: // load 4 bytes\n\t\tix = _mm_cvtsi32_si128( *(const int*)x );\n\t\tbreak;\n\tcase 3: // load 6 bytes\n\t\tix = _mm_cvtsi32_si128( *(const int*)x );\n\t\tix = _mm_insert_epi16( ix, x[ 2 ], 2 );\n\t\tbreak;\n\tcase 4: // load 8 bytes\n\t\tix = _mm_cvtsi64_si128( *(const int64_t*)x );\n\t\tbreak;\n\tcase 5: // load 10 bytes\n\t\tix = _mm_cvtsi64_si128( *(const int64_t*)x );\n\t\tix = _mm_insert_epi16( ix, x[ 4 ], 4 );\n\t\tbreak;\n\tcase 6: // load 12 bytes\n\t\tix = _mm_cvtsi64_si128( *(const int64_t*)x );\n\t\tix = _mm_insert_epi32( ix, *(const int*)( x + 4 ), 2 );\n\t\tbreak;\n\tcase 7: // load 14 bytes\n\t\tix = _mm_cvtsi64_si128( *(const int64_t*)x );\n\t\tix = _mm_insert_epi32( ix, *(const int*)( x + 4 ), 2 );\n\t\tix = _mm_insert_epi16( ix, x[ 6 ], 6 );\n\t\tbreak;\n\t}\n\treturn ix;\n}\n\n__forceinline __m256i load16Partial( const uint16_t* rsi, size_t len )\n{\n\tassert( len > 0 && len < 16 );\n\n\tif( len < 8 )\n\t{\n\t\t__m128i low = load8Partial( rsi, len );\n\t\treturn _mm256_setr_m128i( low, _mm_setzero_si128() );\n\t}\n\telse if( len > 8 )\n\t{\n\t\t__m128i low = load16( (const int*)rsi );\n\t\t__m128i high = load8Partial( rsi + 8, len - 8 );\n\t\treturn _mm256_setr_m128i( low, high );\n\t}\n\telse\n\t{\n\t\t__m128i low = load16( (const int*)rsi );\n\t\treturn _mm256_setr_m128i( low, _mm_setzero_si128() );\n\t}\n}\n\nHRESULT MulMatBase::copyPanelColumnMajor16( uint16_t* rdi, size_t i, size_t m2, size_t m3 ) const\n{\n\tassert( stridesA[ 1 ] == 1 );\n\tassert( panelHeightRegisters == 2 );\n\n\tconstexpr size_t heightFloats = 16;\n\ti *= heightFloats;\n\n\tconst uint16_t* rsi = getPanelA( i, m2, m3 );\n\tuint16_t* const rdiEnd = rdi + 16 * length;\n\n\tif( i + heightFloats <= resultSize[ 0 ] )\n\t{\n\t\t// A complete panel, height = 16 elements\n\t\tfor( ; rdi < rdiEnd; rdi += 16, rsi += stridesA[ 0 ] )\n\t\t{\n\t\t\t__m256i v = _mm256_loadu_si256( ( const __m256i* )rsi );\n\t\t\t_mm256_store_si256( ( __m256i* )rdi, v );\n\t\t}\n\t}\n\telse\n\t{\n\t\t// A partial panel, at the bottom of the first argument matrix\n\t\tconst size_t remainder = resultSize[ 0 ] - i;\n\t\tassert( remainder > 0 && remainder < heightFloats );\n\n\t\tfor( ; rdi < rdiEnd; rdi += 16, rsi += stridesA[ 0 ] )\n\t\t{\n\t\t\t__m256i v = load16Partial( rsi, remainder );\n\t\t\t_mm256_store_si256( ( __m256i* )rdi, v );\n\t\t}\n\t}\n\treturn S_OK;\n}\n\nHRESULT MulMatBase::copyPanelColumnMajor32( uint16_t* rdi, size_t i, size_t m2, size_t m3 ) const\n{\n\tassert( stridesA[ 1 ] == 1 );\n\tassert( panelHeightRegisters == 4 );\n\n\tconstexpr size_t heightFloats = 32;\n\ti *= heightFloats;\n\n\tconst uint16_t* rsi = getPanelA( i, m2, m3 );\n\tuint16_t* const rdiEnd = rdi + 32 * length;\n\n\tif( i + heightFloats <= resultSize[ 0 ] )\n\t{\n\t\t// A complete panel, height = 32 elements\n\t\tfor( ; rdi < rdiEnd; rdi += 32, rsi += stridesA[ 0 ] )\n\t\t{\n\t\t\t__m256i v = _mm256_loadu_si256( ( const __m256i* )rsi );\n\t\t\t_mm256_store_si256( ( __m256i* )rdi, v );\n\t\t\tv = _mm256_loadu_si256( ( const __m256i* )( rsi + 16 ) );\n\t\t\t_mm256_store_si256( ( __m256i* )( rdi + 16 ), v );\n\t\t}\n\t}\n\telse\n\t{\n\t\t// A partial panel, at the bottom of the first argument matrix\n\t\tconst size_t remainder = resultSize[ 0 ] - i;\n\t\tassert( remainder > 0 && remainder < heightFloats );\n\n\t\t// _mm256_setzero_si256 probably compiles into vpxor, that's AVX2, we don't want that here\n\t\tconst __m256 zero = _mm256_setzero_ps();\n\n\t\tfor( ; rdi < rdiEnd; rdi += 32, rsi += stridesA[ 0 ] )\n\t\t{\n\t\t\tif( remainder < 16 )\n\t\t\t{\n\t\t\t\t__m256i v = load16Partial( rsi, remainder );\n\t\t\t\t_mm256_store_si256( ( __m256i* )rdi, v );\n\t\t\t\t_mm256_store_ps( (float*)( rdi + 16 ), zero );\n\t\t\t}\n\t\t\telse if( remainder > 16 )\n\t\t\t{\n\t\t\t\t__m256i v = _mm256_loadu_si256( ( const __m256i* )rsi );\n\t\t\t\t_mm256_store_si256( ( __m256i* )rdi, v );\n\t\t\t\tv = load16Partial( rsi + 16, remainder - 16 );\n\t\t\t\t_mm256_store_si256( ( __m256i* )( rdi + 16 ), v );\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t__m256i v = _mm256_loadu_si256( ( const __m256i* )rsi );\n\t\t\t\t_mm256_store_si256( ( __m256i* )rdi, v );\n\t\t\t\t_mm256_store_ps( (float*)( rdi + 16 ), zero );\n\t\t\t}\n\t\t}\n\t}\n\treturn S_OK;\n}\n\nHRESULT MulMatBase::gatherPanel( uint16_t* rdi, size_t i, size_t m2, size_t m3 ) const\n{\n\t// BTW, I never saw this method called.\n\tconst size_t heightFloats = (size_t)panelHeightRegisters * 8;\n\tconst size_t length = this->length;\n\n\tzeroAlignedMemory( rdi, length * heightFloats * sizeof( uint16_t ) );\n\n\tconst size_t height = std::min( heightFloats, resultSize[ 0 ] - i );\n\tconst size_t strideElement = stridesA[ 0 ];\n\tconst size_t strideRow = stridesA[ 1 ];\n\tconst uint16_t* rsi = getPanelA( i * heightFloats, m2, m3 );\n\n\tif( strideElement < strideRow )\n\t{\n\t\tfor( size_t r = 0; r < height; r++, rsi += strideRow, rdi++ )\n\t\t{\n\t\t\tconst uint16_t* sourceRow = rsi;\n\t\t\tuint16_t* destRow = rdi;\n\t\t\tfor( size_t c = 0; c < length; c++, sourceRow += strideElement, destRow += heightFloats )\n\t\t\t\t*destRow = *sourceRow;\n\t\t}\n\t}\n\telse\n\t{\n\t\tfor( size_t c = 0; c < length; c++, rsi += strideElement, rdi += heightFloats )\n\t\t{\n\t\t\tconst uint16_t* sourceCol = rsi;\n\t\t\tuint16_t* destCol = rdi;\n\t\t\tfor( size_t r = 0; r < height; r++, sourceCol += strideRow, destCol++ )\n\t\t\t\t*destCol = *sourceCol;\n\t\t}\n\t}\n\treturn S_OK;\n}"
  },
  {
    "path": "Whisper/CPU/mulMatUtils.hpp",
    "content": "#pragma once\n#include <immintrin.h>\n#include <stdint.h>\n#include <assert.h>\n\n__forceinline __m128i f16Load( const uint16_t* rsi )\n{\n\treturn _mm_loadu_si128( ( const __m128i* )rsi );\n}\n\nconstexpr size_t maskAlign8 = ~(size_t)7;\n\n__forceinline void transpose8( uint16_t* rdi, size_t w, const uint16_t* rsi, size_t sourceStride, size_t destStride )\n{\n\tassert( 0 == ( (size_t)rdi ) % 16 );\n\tassert( 0 == destStride % 8 );\n\tassert( w <= sourceStride );\n\n\tconst uint16_t* const rsiEndAligned = rsi + ( w & maskAlign8 );\n\tconst uint16_t* rsi5 = rsi + sourceStride * 5;\n\tuint16_t* rdi5 = rdi + destStride * 5;\n\tconst size_t rem = w % 8;\n\tfor( ; rsi < rsiEndAligned; rsi += 8, rsi5 += 8, rdi += 8 * destStride, rdi5 += 8 * destStride )\n\t{\n\t\t// Load 8x8 block into 8 registers\n\t\t__m128i r0 = f16Load( rsi );                     // 00, 01, 02, 03, 04, 05, 06, 07\n\t\t__m128i r1 = f16Load( rsi + sourceStride );      // 10, 11, 12, 13, 14, 15, 16, 17\n\t\t__m128i r2 = f16Load( rsi + sourceStride * 2 );  // 20, 21, 22, 23, 24, 25, 26, 27\n\t\t__m128i r3 = f16Load( rsi5 - sourceStride * 2 ); // 30, 31, 32, 33, 34, 35, 36, 37\n\t\t__m128i r4 = f16Load( rsi5 - sourceStride );     // 40, 41, 42, 43, 44, 45, 46, 47\n\t\t__m128i r5 = f16Load( rsi5 );                    // 50, 51, 52, 53, 54, 55, 56, 57\n\t\t__m128i r6 = f16Load( rsi5 + sourceStride );     // 60, 61, 62, 63, 64, 65, 66, 67\n\t\t__m128i r7 = f16Load( rsi5 + sourceStride * 2 ); // 70, 71, 72, 73, 74, 75, 76, 77\n\n\t\t// Transpose FP16 values in registers\n\t\t__m128i t0 = _mm_unpacklo_epi16( r0, r1 ); // 00, 10, 01, 11, 02, 12, 03, 13\n\t\t__m128i t1 = _mm_unpackhi_epi16( r0, r1 ); // 04, 14, 05, 15, 06, 16, 07, 17\n\t\t__m128i t2 = _mm_unpacklo_epi16( r2, r3 ); // 20, 30, 21, 31, 22, 32, 23, 33\n\t\t__m128i t3 = _mm_unpackhi_epi16( r2, r3 ); // 24, 34, 25, 35, 26, 36, 27, 37\n\t\t__m128i t4 = _mm_unpacklo_epi16( r4, r5 ); // 40, 50, 41, 52, 42, 52, 43, 53\n\t\t__m128i t5 = _mm_unpackhi_epi16( r4, r5 ); // 44, 54, 45, 55, 46, 56, 47, 57\n\t\t__m128i t6 = _mm_unpacklo_epi16( r6, r7 ); // 60, 70, 61, 71, 62, 72, 63, 73\n\t\t__m128i t7 = _mm_unpackhi_epi16( r6, r7 ); // 64, 74, 65, 75, 66, 76, 67, 77\n\n\t\tr0 = _mm_unpacklo_epi32( t0, t2 ); // 00, 10, 20, 30, 01, 11, 21, 31\n\t\tr1 = _mm_unpackhi_epi32( t0, t2 ); // 02, 12, 22, 32, 03, 13, 23, 33\n\t\tr2 = _mm_unpacklo_epi32( t1, t3 ); // 04, 14, 24, 34, 05, 15, 25, 35\n\t\tr3 = _mm_unpackhi_epi32( t1, t3 ); // 06, 16, 26, 36, 07, 17, 27, 37\n\t\tr4 = _mm_unpacklo_epi32( t4, t6 ); // 40, 50, 60, 70, 41, 51, 61, 71\n\t\tr5 = _mm_unpackhi_epi32( t4, t6 ); // 42, 52, 62, 72, 43, 53, 63, 73\n\t\tr6 = _mm_unpacklo_epi32( t5, t7 ); // 44, 54, 64, 74, 45, 55, 65, 75\n\t\tr7 = _mm_unpackhi_epi32( t5, t7 ); // 46, 56, 66, 76, 47, 57, 67, 77\n\n\t\tt0 = _mm_unpacklo_epi64( r0, r4 ); // 00, 10, 20, 30, 40, 50, 60, 70\n\t\tt1 = _mm_unpackhi_epi64( r0, r4 ); // 01, 11, 21, 31, 41, 52, 61, 71\n\t\tt2 = _mm_unpacklo_epi64( r1, r5 ); // 02, 12, 22, 32, 42, 52, 62, 72\n\t\tt3 = _mm_unpackhi_epi64( r1, r5 ); // 03, 13, 23, 33, 43, 53, 63, 73\n\t\tt4 = _mm_unpacklo_epi64( r2, r6 );\n\t\tt5 = _mm_unpackhi_epi64( r2, r6 );\n\t\tt6 = _mm_unpacklo_epi64( r3, r7 );\n\t\tt7 = _mm_unpackhi_epi64( r3, r7 );\n\n\t\t// Store\n\t\tstore16( rdi, t0 );\n\t\tstore16( rdi + destStride, t1 );\n\t\tstore16( rdi + destStride * 2, t2 );\n\t\tstore16( rdi5 - destStride * 2, t3 );\n\t\tstore16( rdi5 - destStride, t4 );\n\t\tstore16( rdi5, t5 );\n\t\tstore16( rdi5 + destStride, t6 );\n\t\tstore16( rdi5 + destStride * 2, t7 );\n\t}\n\n#pragma loop( no_vector )\n\tfor( size_t i = 0; i < rem; rsi++, rsi5++, rdi += destStride )\n\t{\n\t\tconst int16_t* p0 = (const int16_t*)rsi;\n\t\tconst int16_t* p5 = (const int16_t*)rsi5;\n\t\t// Load a complete column into a vector\n\t\t__m128i v = _mm_cvtsi32_si128( *rsi );\n\t\tv = _mm_insert_epi16( v, *( p0 + sourceStride ), 1 );\n\t\tv = _mm_insert_epi16( v, *( p0 + sourceStride * 2 ), 2 );\n\t\tv = _mm_insert_epi16( v, *( p5 - sourceStride * 2 ), 3 );\n\t\tv = _mm_insert_epi16( v, *( p5 - sourceStride ), 4 );\n\t\tv = _mm_insert_epi16( v, *( p5 ), 5 );\n\t\tv = _mm_insert_epi16( v, *( p5 + sourceStride ), 6 );\n\t\tv = _mm_insert_epi16( v, *( p5 + sourceStride * 2 ), 7 );\n\t\t// Store 8 FP16 values\n\t\tstore16( rdi, v );\n\t}\n}\n\ninline void transpose8Partial( uint16_t* rdi, size_t w, size_t h, const uint16_t* rsi, size_t sourceStride, size_t destStride )\n{\n\tassert( 0 == ( (size_t)rdi ) % 16 );\n\tassert( 0 == destStride % 8 );\n\tassert( w <= sourceStride );\n\tassert( h > 0 && h < 8 );\n\n\tconst uint16_t* const rsiEndAligned = rsi + ( w & maskAlign8 );\n\tconst uint16_t* rsi5 = rsi + sourceStride * 5;\n\tuint16_t* rdi5 = rdi + destStride * 5;\n\tconst size_t rem = w % 8;\n\tfor( ; rsi < rsiEndAligned; rsi += 8, rsi5 += 8, rdi += 8 * destStride, rdi5 += 8 * destStride )\n\t{\n\t\t// Load the block into 8 registers, set unused rows to zero\n\t\t__m128i r0 = f16Load( rsi );\n\t\t__m128i r1 = _mm_setzero_si128();\n\t\t__m128i r2 = _mm_setzero_si128();\n\t\t__m128i r3 = _mm_setzero_si128();\n\t\t__m128i r4 = _mm_setzero_si128();\n\t\t__m128i r5 = _mm_setzero_si128();\n\t\t__m128i r6 = _mm_setzero_si128();\n\t\t// These branches, whether direct or indirect, are very predictable: same outcome for all iterations of the outer loop\n\t\tswitch( h )\n\t\t{\n\t\tcase 7:\n\t\t\tr6 = f16Load( rsi5 + sourceStride );\n\t\tcase 6:\n\t\t\tr5 = f16Load( rsi5 );\n\t\tcase 5:\n\t\t\tr4 = f16Load( rsi5 - sourceStride );\n\t\tcase 4:\n\t\t\tr3 = f16Load( rsi5 - sourceStride * 2 );\n\t\tcase 3:\n\t\t\tr2 = f16Load( rsi + sourceStride * 2 );\n\t\tcase 2:\n\t\t\tr1 = f16Load( rsi + sourceStride );\n\t\t}\n\t\t__m128i r7 = _mm_setzero_si128();\n\n\t\t// Transpose FP16 values in registers\n\t\t__m128i t0 = _mm_unpacklo_epi16( r0, r1 ); // 00, 10, 01, 11, 02, 12, 03, 13\n\t\t__m128i t1 = _mm_unpackhi_epi16( r0, r1 ); // 04, 14, 05, 15, 06, 16, 07, 17\n\t\t__m128i t2 = _mm_unpacklo_epi16( r2, r3 ); // 20, 30, 21, 31, 22, 32, 23, 33\n\t\t__m128i t3 = _mm_unpackhi_epi16( r2, r3 ); // 24, 34, 25, 35, 26, 36, 27, 37\n\t\t__m128i t4 = _mm_unpacklo_epi16( r4, r5 ); // 40, 50, 41, 52, 42, 52, 43, 53\n\t\t__m128i t5 = _mm_unpackhi_epi16( r4, r5 ); // 44, 54, 45, 55, 46, 56, 47, 57\n\t\t__m128i t6 = _mm_unpacklo_epi16( r6, r7 ); // 60, 70, 61, 71, 62, 72, 63, 73\n\t\t__m128i t7 = _mm_unpackhi_epi16( r6, r7 ); // 64, 74, 65, 75, 66, 76, 67, 77\n\n\t\tr0 = _mm_unpacklo_epi32( t0, t2 ); // 00, 10, 20, 30, 01, 11, 21, 31\n\t\tr1 = _mm_unpackhi_epi32( t0, t2 ); // 02, 12, 22, 32, 03, 13, 23, 33\n\t\tr2 = _mm_unpacklo_epi32( t1, t3 ); // 04, 14, 24, 34, 05, 15, 25, 35\n\t\tr3 = _mm_unpackhi_epi32( t1, t3 ); // 06, 16, 26, 36, 07, 17, 27, 37\n\t\tr4 = _mm_unpacklo_epi32( t4, t6 ); // 40, 50, 60, 70, 41, 51, 61, 71\n\t\tr5 = _mm_unpackhi_epi32( t4, t6 ); // 42, 52, 62, 72, 43, 53, 63, 73\n\t\tr6 = _mm_unpacklo_epi32( t5, t7 ); // 44, 54, 64, 74, 45, 55, 65, 75\n\t\tr7 = _mm_unpackhi_epi32( t5, t7 ); // 46, 56, 66, 76, 47, 57, 67, 77\n\n\t\tt0 = _mm_unpacklo_epi64( r0, r4 ); // 00, 10, 20, 30, 40, 50, 60, 70\n\t\tt1 = _mm_unpackhi_epi64( r0, r4 ); // 01, 11, 21, 31, 41, 52, 61, 71\n\t\tt2 = _mm_unpacklo_epi64( r1, r5 ); // 02, 12, 22, 32, 42, 52, 62, 72\n\t\tt3 = _mm_unpackhi_epi64( r1, r5 ); // 03, 13, 23, 33, 43, 53, 63, 73\n\t\tt4 = _mm_unpacklo_epi64( r2, r6 );\n\t\tt5 = _mm_unpackhi_epi64( r2, r6 );\n\t\tt6 = _mm_unpacklo_epi64( r3, r7 );\n\t\tt7 = _mm_unpackhi_epi64( r3, r7 );\n\n\t\t// Store\n\t\tstore16( rdi, t0 );\n\t\tstore16( rdi + destStride, t1 );\n\t\tstore16( rdi + destStride * 2, t2 );\n\t\tstore16( rdi5 - destStride * 2, t3 );\n\t\tstore16( rdi5 - destStride, t4 );\n\t\tstore16( rdi5, t5 );\n\t\tstore16( rdi5 + destStride, t6 );\n\t\tstore16( rdi5 + destStride * 2, t7 );\n\t}\n\n#pragma loop( no_vector )\n\tfor( size_t i = 0; i < rem; rsi++, rsi5++, rdi += destStride )\n\t{\n\t\tconst int16_t* p0 = (const int16_t*)rsi;\n\t\tconst int16_t* p5 = (const int16_t*)rsi5;\n\t\t// Load a partial column into vector\n\t\t__m128i v = _mm_cvtsi32_si128( *rsi );\n\t\tswitch( h )\n\t\t{\n\t\tcase 7:\n\t\t\tv = _mm_insert_epi16( v, *( p5 + sourceStride ), 6 );\n\t\tcase 6:\n\t\t\tv = _mm_insert_epi16( v, *( p5 ), 5 );\n\t\tcase 5:\n\t\t\tv = _mm_insert_epi16( v, *( p5 - sourceStride ), 4 );\n\t\tcase 4:\n\t\t\tv = _mm_insert_epi16( v, *( p5 - sourceStride * 2 ), 3 );\n\t\tcase 3:\n\t\t\tv = _mm_insert_epi16( v, *( p0 + sourceStride * 2 ), 2 );\n\t\tcase 2:\n\t\t\tv = _mm_insert_epi16( v, *( p0 + sourceStride ), 1 );\n\t\t}\n\t\t// Store 8 FP16 values\n\t\tstore16( rdi, v );\n\t}\n}\n\n// Same as above, but skip the transpose. The source stride is distance between columns of the matrix.\n__forceinline void copyColumnMajor( uint16_t* rdi, size_t w, const uint16_t* rsi, size_t sourceStride, size_t destStride )\n{\n\tassert( 0 == ( (size_t)rdi ) % 16 );\n\tassert( 0 == destStride % 8 );\n\n\tconstexpr size_t maskAlign4 = ~(size_t)3;\n\n\tconst uint16_t* const rsiEndAligned = rsi + sourceStride * ( w & maskAlign4 );\n\tconst uint16_t* const rsiEnd = rsi + sourceStride * w;\n\tfor( ; rsi < rsiEndAligned; rsi += sourceStride * 4, rdi += destStride * 4 )\n\t{\n\t\t__m128i c = f16Load( rsi );\n\t\tstore16( rdi, c );\n\n\t\tc = f16Load( rsi + sourceStride );\n\t\tstore16( rdi + destStride, c );\n\n\t\tc = f16Load( rsi + sourceStride * 2 );\n\t\tstore16( rdi + destStride * 2, c );\n\n\t\tc = f16Load( rsi + sourceStride * 3 );\n\t\tstore16( rdi + destStride * 3, c );\n\t}\n\n\tfor( ; rsi < rsiEnd; rsi += sourceStride, rdi += destStride )\n\t{\n\t\t__m128i c = f16Load( rsi );\n\t\tstore16( rdi, c );\n\t}\n}\n\n__forceinline __m128i loadPartial( const uint16_t* x, size_t count )\n{\n\tassert( count < 8 );\n\t__m128i ix;\n\tswitch( count )\n\t{\n\tcase 1: // load 2 bytes\n\t\tix = _mm_cvtsi32_si128( *x );\n\t\tbreak;\n\tcase 2: // load 4 bytes\n\t\tix = _mm_cvtsi32_si128( *(const int*)x );\n\t\tbreak;\n\tcase 3: // load 6 bytes\n\t\tix = _mm_cvtsi32_si128( *(const int*)x );\n\t\tix = _mm_insert_epi16( ix, x[ 2 ], 2 );\n\t\tbreak;\n\tcase 4: // load 8 bytes\n\t\tix = _mm_cvtsi64_si128( *(const int64_t*)x );\n\t\tbreak;\n\tcase 5: // load 10 bytes\n\t\tix = _mm_cvtsi64_si128( *(const int64_t*)x );\n\t\tix = _mm_insert_epi16( ix, x[ 4 ], 4 );\n\t\tbreak;\n\tcase 6: // load 12 bytes\n\t\tix = _mm_cvtsi64_si128( *(const int64_t*)x );\n\t\tix = _mm_insert_epi32( ix, *(const int*)( x + 4 ), 2 );\n\t\tbreak;\n\tcase 7: // load 14 bytes\n\t\tix = _mm_cvtsi64_si128( *(const int64_t*)x );\n\t\tix = _mm_insert_epi32( ix, *(const int*)( x + 4 ), 2 );\n\t\tix = _mm_insert_epi16( ix, x[ 6 ], 6 );\n\t\tbreak;\n\tdefault:\n\t\treturn _mm_setzero_si128();\n\t}\n\treturn ix;\n}\n\ninline void copyColumnMajorPartial( uint16_t* rdi, size_t w, size_t h, const uint16_t* rsi, size_t sourceStride, size_t destStride )\n{\n\tassert( 0 == ( (size_t)rdi ) % 32 );\n\tassert( 0 == destStride % 8 );\n\tassert( h > 0 && h < 8 );\n\n\tconst uint16_t* const rsiEnd = rsi + sourceStride * w;\n\tfor( ; rsi < rsiEnd; rsi += sourceStride, rdi += destStride )\n\t{\n\t\t// Can't use mask loads because loading 2-byte elements\n\t\t// Still, that switch() in loadPartial makes a very predictable branch, same outcome for all iterations of this loop.\n\t\t__m128i c = loadPartial( rsi, h );\n\t\tstore16( rdi, c );\n\t}\n}\n\n// Store zeros into block of memory, with aligned AVX store instructions\n__forceinline void zeroAlignedMemory( void* pv, size_t cb )\n{\n\tassert( 0 == cb % 16 );\n\tassert( 0 == ( (size_t)pv % 32 ) );\n\n\tuint8_t* rdi = (uint8_t*)pv;\n\tconstexpr size_t maskAlign32 = ~(size_t)31;\n\tuint8_t* const rdiEndAligned = rdi + ( cb & maskAlign32 );\n\tuint8_t* const rdiEnd = rdi + cb;\n\n\tconst __m256 zero = _mm256_setzero_ps();\n\tfor( ; rdi < rdiEndAligned; rdi += 32 )\n\t\t_mm256_store_ps( (float*)rdi, zero );\n\n\tif( rdi < rdiEnd )\n\t\t_mm_store_ps( (float*)rdi, _mm_setzero_ps() );\n}"
  },
  {
    "path": "Whisper/CPU/simdUtils.cpp",
    "content": "#include \"stdafx.h\"\n#include \"simdUtils.h\"\n#include \"../ML/LookupTablesData.h\"\n#include <cmath>\n#include <memory>\n\nnamespace\n{\n\tconstexpr size_t maskAlign8 = ~(size_t)7;\n\n\t__forceinline __m256 load8( const uint16_t* rsi )\n\t{\n\t\t__m128i i = _mm_loadu_si128( ( const __m128i* )rsi );\n\t\treturn _mm256_cvtph_ps( i );\n\t}\n\n\t__forceinline void loadPartial( const uint16_t* x, const uint16_t* y, size_t count, __m256& fx, __m256& fy )\n\t{\n\t\tassert( count < 8 );\n\n\t\t__m128i ix, iy;\n\t\tswitch( count )\n\t\t{\n\t\tcase 1: // load 2 bytes\n\t\t\tix = _mm_cvtsi32_si128( *x );\n\t\t\tiy = _mm_cvtsi32_si128( *y );\n\t\t\tbreak;\n\t\tcase 2: // load 4 bytes\n\t\t\tix = _mm_cvtsi32_si128( *(const int*)x );\n\t\t\tiy = _mm_cvtsi32_si128( *(const int*)y );\n\t\t\tbreak;\n\t\tcase 3: // load 6 bytes\n\t\t\tix = _mm_cvtsi32_si128( *(const int*)x );\n\t\t\tiy = _mm_cvtsi32_si128( *(const int*)y );\n\t\t\tix = _mm_insert_epi16( ix, x[ 2 ], 2 );\n\t\t\tiy = _mm_insert_epi16( iy, y[ 2 ], 2 );\n\t\t\tbreak;\n\t\tcase 4: // load 8 bytes\n\t\t\tix = _mm_cvtsi64_si128( *(const int64_t*)x );\n\t\t\tiy = _mm_cvtsi64_si128( *(const int64_t*)y );\n\t\t\tbreak;\n\t\tcase 5: // load 10 bytes\n\t\t\tix = _mm_cvtsi64_si128( *(const int64_t*)x );\n\t\t\tiy = _mm_cvtsi64_si128( *(const int64_t*)y );\n\t\t\tix = _mm_insert_epi16( ix, x[ 4 ], 4 );\n\t\t\tiy = _mm_insert_epi16( iy, y[ 4 ], 4 );\n\t\t\tbreak;\n\t\tcase 6: // load 12 bytes\n\t\t\tix = _mm_cvtsi64_si128( *(const int64_t*)x );\n\t\t\tiy = _mm_cvtsi64_si128( *(const int64_t*)y );\n\t\t\tix = _mm_insert_epi32( ix, *(const int*)( x + 4 ), 2 );\n\t\t\tiy = _mm_insert_epi32( iy, *(const int*)( y + 4 ), 2 );\n\t\t\tbreak;\n\t\tcase 7: // load 14 bytes\n\t\t\tix = _mm_cvtsi64_si128( *(const int64_t*)x );\n\t\t\tiy = _mm_cvtsi64_si128( *(const int64_t*)y );\n\t\t\tix = _mm_insert_epi32( ix, *(const int*)( x + 4 ), 2 );\n\t\t\tiy = _mm_insert_epi32( iy, *(const int*)( y + 4 ), 2 );\n\t\t\tix = _mm_insert_epi16( ix, x[ 6 ], 6 );\n\t\t\tiy = _mm_insert_epi16( iy, y[ 6 ], 6 );\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tfx = fy = _mm256_setzero_ps();\n\t\t\treturn;\n\t\t}\n\n\t\tfx = _mm256_cvtph_ps( ix );\n\t\tfy = _mm256_cvtph_ps( iy );\n\t}\n\n\t__forceinline __m256 loadPartial( const uint16_t* x, size_t count )\n\t{\n\t\tassert( count < 8 );\n\t\t__m128i ix;\n\t\tswitch( count )\n\t\t{\n\t\tcase 1: // load 2 bytes\n\t\t\tix = _mm_cvtsi32_si128( *x );\n\t\t\tbreak;\n\t\tcase 2: // load 4 bytes\n\t\t\tix = _mm_cvtsi32_si128( *(const int*)x );\n\t\t\tbreak;\n\t\tcase 3: // load 6 bytes\n\t\t\tix = _mm_cvtsi32_si128( *(const int*)x );\n\t\t\tix = _mm_insert_epi16( ix, x[ 2 ], 2 );\n\t\t\tbreak;\n\t\tcase 4: // load 8 bytes\n\t\t\tix = _mm_cvtsi64_si128( *(const int64_t*)x );\n\t\t\tbreak;\n\t\tcase 5: // load 10 bytes\n\t\t\tix = _mm_cvtsi64_si128( *(const int64_t*)x );\n\t\t\tix = _mm_insert_epi16( ix, x[ 4 ], 4 );\n\t\t\tbreak;\n\t\tcase 6: // load 12 bytes\n\t\t\tix = _mm_cvtsi64_si128( *(const int64_t*)x );\n\t\t\tix = _mm_insert_epi32( ix, *(const int*)( x + 4 ), 2 );\n\t\t\tbreak;\n\t\tcase 7: // load 14 bytes\n\t\t\tix = _mm_cvtsi64_si128( *(const int64_t*)x );\n\t\t\tix = _mm_insert_epi32( ix, *(const int*)( x + 4 ), 2 );\n\t\t\tix = _mm_insert_epi16( ix, x[ 6 ], 6 );\n\t\t\tbreak;\n\t\tdefault:\n\t\t\treturn _mm256_setzero_ps();\n\t\t}\n\t\treturn  _mm256_cvtph_ps( ix );\n\t}\n\n\t__forceinline __m128 loadFloat2( const float* rsi )\n\t{\n\t\treturn _mm_castpd_ps( _mm_load_sd( (const double*)rsi ) );\n\t}\n\t__forceinline __m128 loadFloat3( const float* rsi )\n\t{\n\t\t__m128 f = loadFloat2( rsi );\n\t\tf = _mm_insert_ps( f, _mm_load_ss( rsi + 2 ), 0x20 );\n\t\treturn f;\n\t}\n\n\t__forceinline __m256 loadPartial( const float* rsi, size_t count )\n\t{\n\t\tassert( count < 8 );\n\t\t__m128 low = _mm_setzero_ps();\n\t\t__m128 high = _mm_setzero_ps();\n\t\tswitch( count )\n\t\t{\n\t\tcase 1:\n\t\t\tlow = _mm_load_ss( rsi );\n\t\t\tbreak;\n\t\tcase 2:\n\t\t\tlow = loadFloat2( rsi );\n\t\t\tbreak;\n\t\tcase 3:\n\t\t\tlow = loadFloat3( rsi );\n\t\t\tbreak;\n\t\tcase 4:\n\t\t\tlow = _mm_loadu_ps( rsi );\n\t\t\tbreak;\n\t\tcase 5:\n\t\t\tlow = _mm_loadu_ps( rsi );\n\t\t\thigh = _mm_load_ss( rsi + 4 );\n\t\t\tbreak;\n\t\tcase 6:\n\t\t\tlow = _mm_loadu_ps( rsi );\n\t\t\thigh = loadFloat2( rsi + 4 );\n\t\t\tbreak;\n\t\tcase 7:\n\t\t\tlow = _mm_loadu_ps( rsi );\n\t\t\thigh = loadFloat3( rsi + 4 );\n\t\t\tbreak;\n\t\t}\n\t\treturn _mm256_setr_m128( low, high );\n\t}\n\n\t__forceinline void storeFloat2( float* rdi, __m128 vec )\n\t{\n\t\t_mm_store_sd( (double*)rdi, _mm_castps_pd( vec ) );\n\t}\n\n\t__forceinline void storePartial( float* rdi, __m256 vec, size_t count )\n\t{\n\t\tassert( count < 8 );\n\n\t\t__m128 tmp = _mm256_castps256_ps128( vec );\n\t\tif( count >= 4 )\n\t\t{\n\t\t\t_mm_storeu_ps( rdi, tmp );\n\t\t\tif( count == 4 )\n\t\t\t\treturn;\n\t\t\tcount -= 4;\n\t\t\trdi += 4;\n\t\t\ttmp = _mm256_extractf128_ps( vec, 1 );\n\t\t}\n\n\t\tswitch( count )\n\t\t{\n\t\tcase 1:\n\t\t\t_mm_store_ss( rdi, tmp );\n\t\t\treturn;\n\t\tcase 2:\n\t\t\tstoreFloat2( rdi, tmp );\n\t\t\treturn;\n\t\tcase 3:\n\t\t\tstoreFloat2( rdi, tmp );\n\t\t\t( (int*)rdi )[ 2 ] = _mm_extract_ps( tmp, 2 );\n\t\t\treturn;\n\t\t}\n\t}\n}\n\nvoid addF16to32( float* rdi, const uint16_t* a, const uint16_t* b, size_t length )\n{\n\tconst uint16_t* const endAligned = a + ( length & maskAlign8 );\n\tconst size_t rem = length % 8;\n\n\tfor( ; a < endAligned; a += 8, b += 8, rdi += 8 )\n\t{\n\t\t__m256 f1 = load8( a );\n\t\t__m256 f2 = load8( b );\n\t\t__m256 res = _mm256_add_ps( f1, f2 );\n\t\t_mm256_storeu_ps( rdi, res );\n\t}\n\n\tif( rem != 0 )\n\t{\n\t\t__m256 f1, f2;\n\t\tloadPartial( a, b, rem, f1, f2 );\n\t\t__m256 res = _mm256_add_ps( f1, f2 );\n\t\tstorePartial( rdi, res, rem );\n\t}\n}\n\nvoid addF16to32( float* rdi, const uint16_t* a, const float* b, size_t length )\n{\n\tconst uint16_t* const endAligned = a + ( length & maskAlign8 );\n\tconst size_t rem = length % 8;\n\n\tfor( ; a < endAligned; a += 8, b += 8, rdi += 8 )\n\t{\n\t\t__m256 f1 = load8( a );\n\t\t__m256 f2 = _mm256_loadu_ps( b );\n\t\t__m256 res = _mm256_add_ps( f1, f2 );\n\t\t_mm256_storeu_ps( rdi, res );\n\t}\n\n\tif( rem != 0 )\n\t{\n\t\t__m256 f1 = loadPartial( a, rem );\n\t\t__m256 f2 = loadPartial( b, rem );\n\t\t__m256 res = _mm256_add_ps( f1, f2 );\n\t\tstorePartial( rdi, res, rem );\n\t}\n}\n\nalignas( 64 ) const std::array<int, 16> s_zeroTailMask =\n{\n\t-1,-1,-1,-1,-1,-1,-1,-1,\n\t0, 0, 0, 0, 0, 0, 0, 0,\n};\n\nnamespace\n{\n\t__forceinline float horizontalSum( __m256 vec )\n\t{\n\t\t__m128 v = _mm256_extractf128_ps( vec, 1 );\n\t\tv = _mm_add_ps( v, _mm256_castps256_ps128( vec ) );\n\t\tv = _mm_add_ps( v, _mm_movehl_ps( v, v ) );\n\t\tv = _mm_add_ss( v, _mm_movehdup_ps( v ) );\n\t\treturn _mm_cvtss_f32( v );\n\t}\n}\n\nvoid norm( float* rdi, float* temp, const float* rsi, size_t length )\n{\n\tassert( (size_t)temp % 32 == 0 );\n\tconst float* rsiEndAligned = rsi + ( length & maskAlign8 );\n\tconst size_t rem = length % 8;\n\n\t// First pass: copy to temp buffer, and compute the sum; computeVectorSum() in HLSL\n\t__m256 sum = _mm256_setzero_ps();\n\tfloat* t;\n\tfor( t = temp; rsi < rsiEndAligned; rsi += 8, t += 8 )\n\t{\n\t\t__m256 v = _mm256_loadu_ps( rsi );\n\t\tsum = _mm256_add_ps( sum, v );\n\t\t_mm256_store_ps( t, v );\n\t}\n\tfloat* const tEndAligned = t;\n\tif( 0 != rem )\n\t{\n\t\t__m256 v = loadPartial( rsi, rem );\n\t\tsum = _mm256_add_ps( sum, v );\n\t\t_mm256_store_ps( t, v );\n\t\tt += 8;\n\t}\n\n\tconst float lengthFloat = (float)(int)length;\n\tconst float meanScalar = horizontalSum( sum ) / lengthFloat;\n\tconst __m256 mean = _mm256_set1_ps( meanScalar );\n\n\t// Second pass, offsetAndComputeSumSquares() in HLSL\n\tsum = _mm256_setzero_ps();\n\tfor( t = temp; t < tEndAligned; t += 8 )\n\t{\n\t\t__m256 v = _mm256_load_ps( t );\n\t\tv = _mm256_sub_ps( v, mean );\n\t\t_mm256_store_ps( t, v );\n\t\tsum = _mm256_fmadd_ps( v, v, sum );\n\t}\n\tif( 0 != rem )\n\t{\n\t\t__m256 v = _mm256_load_ps( t );\n\t\tv = _mm256_sub_ps( v, mean );\n\t\tv = _mm256_and_ps( v, loadTailMaskFloats( rem ) );\n\t\t_mm256_store_ps( t, v );\n\t\tsum = _mm256_fmadd_ps( v, v, sum );\n\t}\n\n\t// Final pass: scale, and copy from temporary buffer into the destination row\n\n\tconstexpr float eps = 1e-5f; // TODO: make this a parameter\n\tconst float scaleScalar = 1.0f / std::sqrtf( horizontalSum( sum ) / lengthFloat + eps );\n\tconst __m256 scale = _mm256_set1_ps( scaleScalar );\n\n\tfor( t = temp; t < tEndAligned; t += 8, rdi += 8 )\n\t{\n\t\t__m256 v = _mm256_load_ps( t );\n\t\tv = _mm256_mul_ps( v, scale );\n\t\t_mm256_storeu_ps( rdi, v );\n\t}\n\tif( 0 != rem )\n\t{\n\t\t__m256 v = _mm256_load_ps( t );\n\t\tv = _mm256_mul_ps( v, scale );\n\t\tstorePartial( rdi, v, rem );\n\t}\n}\n\nvoid fmaRepeatRow( float* rdi, size_t len, const float* w, const float* b, size_t lenPattern )\n{\n\tfloat* rdiEndAligned = rdi + ( len & maskAlign8 );\n\tconst size_t rem = len % 8;\n\n\tif( 1 == lenPattern )\n\t{\n\t\tconst __m256 v1 = _mm256_broadcast_ss( w );\n\t\tconst __m256 v2 = _mm256_broadcast_ss( b );\n\t\tfor( ; rdi < rdiEndAligned; rdi += 8 )\n\t\t{\n\t\t\t__m256 v = _mm256_loadu_ps( rdi );\n\t\t\tv = _mm256_fmadd_ps( v, v1, v2 );\n\t\t\t_mm256_storeu_ps( rdi, v );\n\t\t}\n\t\tif( 0 != rem )\n\t\t{\n\t\t\tconst __m256i mask = loadTailMaskInt( rem );\n\t\t\t__m256 v = _mm256_maskload_ps( rdi, mask );\n\t\t\tv = _mm256_fmadd_ps( v, v1, v2 );\n\t\t\t_mm256_maskstore_ps( rdi, mask, v );\n\t\t}\n\t}\n\telse if( len == lenPattern )\n\t{\n\t\tfor( ; rdi < rdiEndAligned; rdi += 8, w += 8, b += 8 )\n\t\t{\n\t\t\t__m256 v = _mm256_loadu_ps( rdi );\n\t\t\t__m256 v1 = _mm256_loadu_ps( w );\n\t\t\t__m256 v2 = _mm256_loadu_ps( b );\n\t\t\tv = _mm256_fmadd_ps( v, v1, v2 );\n\t\t\t_mm256_storeu_ps( rdi, v );\n\t\t}\n\t\tif( 0 != rem )\n\t\t{\n\t\t\tconst __m256i mask = loadTailMaskInt( rem );\n\t\t\t__m256 v = _mm256_maskload_ps( rdi, mask );\n\t\t\t__m256 v1 = _mm256_maskload_ps( w, mask );\n\t\t\t__m256 v2 = _mm256_maskload_ps( b, mask );\n\t\t\tv = _mm256_fmadd_ps( v, v1, v2 );\n\t\t\t_mm256_maskstore_ps( rdi, mask, v );\n\t\t}\n\t}\n\telse\n\t{\n\t\t// TODO: implement if this actually happens\n\t\tthrow E_NOTIMPL;\n\t}\n}\n\nvoid __vectorcall addRepeatScaleRow( float* rdi, size_t len, const float* b, size_t lenPattern, const __m256 scale )\n{\n\tfloat* rdiEndAligned = rdi + ( len & maskAlign8 );\n\tconst size_t rem = len % 8;\n\n\tif( 1 == lenPattern )\n\t{\n\t\tconst __m256 v2 = _mm256_broadcast_ss( b );\n\t\tfor( ; rdi < rdiEndAligned; rdi += 8 )\n\t\t{\n\t\t\t__m256 v = _mm256_loadu_ps( rdi );\n\t\t\tv = _mm256_add_ps( v, v2 );\n\t\t\tv = _mm256_mul_ps( v, scale );\n\t\t\t_mm256_storeu_ps( rdi, v );\n\t\t}\n\t\tif( 0 != rem )\n\t\t{\n\t\t\tconst __m256i mask = loadTailMaskInt( rem );\n\t\t\t__m256 v = _mm256_maskload_ps( rdi, mask );\n\t\t\tv = _mm256_add_ps( v, v2 );\n\t\t\tv = _mm256_mul_ps( v, scale );\n\t\t\t_mm256_maskstore_ps( rdi, mask, v );\n\t\t}\n\t\treturn;\n\t}\n\telse if( len == lenPattern )\n\t{\n\t\tfor( ; rdi < rdiEndAligned; rdi += 8, b += 8 )\n\t\t{\n\t\t\t__m256 v = _mm256_loadu_ps( rdi );\n\t\t\t__m256 v2 = _mm256_loadu_ps( b );\n\t\t\tv = _mm256_add_ps( v, v2 );\n\t\t\tv = _mm256_mul_ps( v, scale );\n\t\t\t_mm256_storeu_ps( rdi, v );\n\t\t}\n\t\tif( 0 != rem )\n\t\t{\n\t\t\tconst __m256i mask = loadTailMaskInt( rem );\n\t\t\t__m256 v = _mm256_maskload_ps( rdi, mask );\n\t\t\t__m256 v2 = _mm256_maskload_ps( b, mask );\n\t\t\tv = _mm256_add_ps( v, v2 );\n\t\t\tv = _mm256_mul_ps( v, scale );\n\t\t\t_mm256_maskstore_ps( rdi, mask, v );\n\t\t}\n\t\treturn;\n\t}\n\telse\n\t{\n\t\t// TODO: implement if this actually happens\n\t\tthrow E_NOTIMPL;\n\t}\n}\n\nvoid addRepeatRow( float* rdi, size_t len, const float* b, size_t lenPattern )\n{\n\tfloat* rdiEndAligned = rdi + ( len & maskAlign8 );\n\tconst size_t rem = len % 8;\n\n\tif( 1 == lenPattern )\n\t{\n\t\tconst __m256 v2 = _mm256_broadcast_ss( b );\n\t\tfor( ; rdi < rdiEndAligned; rdi += 8 )\n\t\t{\n\t\t\t__m256 v = _mm256_loadu_ps( rdi );\n\t\t\tv = _mm256_add_ps( v, v2 );\n\t\t\t_mm256_storeu_ps( rdi, v );\n\t\t}\n\t\tif( 0 != rem )\n\t\t{\n\t\t\tconst __m256i mask = loadTailMaskInt( rem );\n\t\t\t__m256 v = _mm256_maskload_ps( rdi, mask );\n\t\t\tv = _mm256_add_ps( v, v2 );\n\t\t\t_mm256_maskstore_ps( rdi, mask, v );\n\t\t}\n\t\treturn;\n\t}\n\telse if( len == lenPattern )\n\t{\n\t\tfor( ; rdi < rdiEndAligned; rdi += 8, b += 8 )\n\t\t{\n\t\t\t__m256 v = _mm256_loadu_ps( rdi );\n\t\t\t__m256 v2 = _mm256_loadu_ps( b );\n\t\t\tv = _mm256_add_ps( v, v2 );\n\t\t\t_mm256_storeu_ps( rdi, v );\n\t\t}\n\t\tif( 0 != rem )\n\t\t{\n\t\t\tconst __m256i mask = loadTailMaskInt( rem );\n\t\t\t__m256 v = _mm256_maskload_ps( rdi, mask );\n\t\t\t__m256 v2 = _mm256_maskload_ps( b, mask );\n\t\t\tv = _mm256_add_ps( v, v2 );\n\t\t\t_mm256_maskstore_ps( rdi, mask, v );\n\t\t}\n\t\treturn;\n\t}\n\telse\n\t{\n\t\t// TODO: implement if this actually happens\n\t\tthrow E_NOTIMPL;\n\t}\n}\n\nnamespace\n{\n\t__forceinline __m256 gelu( __m256 x, const DirectCompute::LookupTablesData& lookup )\n\t{\n\t\t__m128i iv = _mm256_cvtps_ph( x, 0 );\n\t\talignas( 16 ) std::array<uint16_t, 8> arr;\n\t\t_mm_store_si128( ( __m128i* )arr.data(), iv );\n\t\tfor( uint16_t& a : arr )\n\t\t\ta = lookup.gelu[ a ];\n\t\tiv = _mm_load_si128( ( __m128i* )arr.data() );\n\t\treturn _mm256_cvtph_ps( iv );\n\t}\n}\n\nvoid addRepeatGeluRow( float* rdi, size_t len, const float* b, size_t lenPattern, const DirectCompute::LookupTablesData& lookup )\n{\n\tfloat* rdiEndAligned = rdi + ( len & maskAlign8 );\n\tconst size_t rem = len % 8;\n\n\tif( 1 == lenPattern )\n\t{\n\t\tconst __m256 v2 = _mm256_broadcast_ss( b );\n\t\tfor( ; rdi < rdiEndAligned; rdi += 8 )\n\t\t{\n\t\t\t__m256 v = _mm256_loadu_ps( rdi );\n\t\t\tv = _mm256_add_ps( v, v2 );\n\t\t\tv = gelu( v, lookup );\n\t\t\t_mm256_storeu_ps( rdi, v );\n\t\t}\n\t\tif( 0 != rem )\n\t\t{\n\t\t\tconst __m256i mask = loadTailMaskInt( rem );\n\t\t\t__m256 v = _mm256_maskload_ps( rdi, mask );\n\t\t\tv = _mm256_add_ps( v, v2 );\n\t\t\tv = gelu( v, lookup );\n\t\t\t_mm256_maskstore_ps( rdi, mask, v );\n\t\t}\n\t\treturn;\n\t}\n\telse if( len == lenPattern )\n\t{\n\t\tfor( ; rdi < rdiEndAligned; rdi += 8, b += 8 )\n\t\t{\n\t\t\t__m256 v = _mm256_loadu_ps( rdi );\n\t\t\t__m256 v2 = _mm256_loadu_ps( b );\n\t\t\tv = _mm256_add_ps( v, v2 );\n\t\t\tv = gelu( v, lookup );\n\t\t\t_mm256_storeu_ps( rdi, v );\n\t\t}\n\t\tif( 0 != rem )\n\t\t{\n\t\t\tconst __m256i mask = loadTailMaskInt( rem );\n\t\t\t__m256 v = _mm256_maskload_ps( rdi, mask );\n\t\t\t__m256 v2 = _mm256_maskload_ps( b, mask );\n\t\t\tv = _mm256_add_ps( v, v2 );\n\t\t\tv = gelu( v, lookup );\n\t\t\t_mm256_maskstore_ps( rdi, mask, v );\n\t\t}\n\t\treturn;\n\t}\n\telse\n\t{\n\t\t// TODO: implement if this actually happens\n\t\tthrow E_NOTIMPL;\n\t}\n}\n\nvoid __vectorcall scaleRow( float* rdi, size_t len, const __m256 scale )\n{\n\tfloat* rdiEndAligned = rdi + ( len & maskAlign8 );\n\tconst size_t rem = len % 8;\n\tfor( ; rdi < rdiEndAligned; rdi += 8 )\n\t{\n\t\t__m256 v = _mm256_loadu_ps( rdi );\n\t\tv = _mm256_mul_ps( v, scale );\n\t\t_mm256_storeu_ps( rdi, v );\n\t}\n\tif( 0 != rem )\n\t{\n\t\tconst __m256i mask = loadTailMaskInt( rem );\n\t\t__m256 v = _mm256_maskload_ps( rdi, mask );\n\t\tv = _mm256_mul_ps( v, scale );\n\t\t_mm256_maskstore_ps( rdi, mask, v );\n\t}\n}\n\nnamespace\n{\n\tusing DirectCompute::LookupTablesData;\n\n\t__forceinline float horizontalMax( __m256 vec )\n\t{\n\t\t__m128 v = _mm256_extractf128_ps( vec, 1 );\n\t\tv = _mm_max_ps( v, _mm256_castps256_ps128( vec ) );\n\t\tv = _mm_max_ps( v, _mm_movehl_ps( v, v ) );\n\t\tv = _mm_max_ss( v, _mm_movehdup_ps( v ) );\n\t\treturn _mm_cvtss_f32( v );\n\t}\n\n\t__forceinline float _cvtsh_ss( uint16_t f16 )\n\t{\n\t\t__m128i i = _mm_cvtsi32_si128( f16 );\n\t\t__m128 f = _mm_cvtph_ps( i );\n\t\treturn _mm_cvtss_f32( f );\n\t}\n\n\t__forceinline uint16_t _cvtss_sh( float f, int rounding )\n\t{\n\t\tassert( 0 == rounding );\n\t\t__m128 v = _mm_set_ss( f );\n\t\t__m128i i = _mm_cvtps_ph( v, 0 );\n\t\treturn (uint16_t)(uint32_t)_mm_cvtsi128_si32( i );\n\t}\n}\n\nconst LookupTablesData& getLookupTables()\n{\n\tstatic const std::unique_ptr<LookupTablesData> res = std::make_unique<LookupTablesData>();\n\treturn *res;\n}\n\nvoid softMax( float* rdi, size_t length, const float inputScale )\n{\n\tfloat* const rdiBegin = rdi;\n\tfloat* const rdiEndAligned = rdi + ( length & maskAlign8 );\n\tconst size_t remainder = length % 8;\n\t// First pass, compute maximum\n\t__m256 max = _mm256_set1_ps( -INFINITY );\n\tfor( rdi = rdiBegin; rdi < rdiEndAligned; rdi += 8 )\n\t{\n\t\t__m256 v = _mm256_loadu_ps( rdi );\n\t\tmax = _mm256_max_ps( max, v );\n\t}\n\t__m256i tailMask;\n\tif( 0 != remainder )\n\t{\n\t\ttailMask = loadTailMaskInt( remainder );\n\t\t__m256 v = _mm256_maskload_ps( rdi, tailMask );\n\t\tv = _mm256_max_ps( max, v );\n\t\tmax = _mm256_blendv_ps( max, v, _mm256_castsi256_ps( tailMask ) );\n\t}\n\n\t// Second pass: apply initial scale, compute the exponent, and compute total sum over the row\n\tconst LookupTablesData& lookup = getLookupTables();\n\tconst float maxScalar = horizontalMax( max );\n\n\tfloat* const rdiEnd = rdiBegin + length;\n\tdouble sum = 0;\n\tfor( rdi = rdiBegin; rdi < rdiEnd; rdi++ )\n\t{\n\t\t// Possible to vectorize, but relatively hard\n\t\t// An easy way is upcast the complete lookup table to FP32 and then use two _mm256_i32gather_ps instructions per iteration\n\t\t// However, that instruction is from AVX2 set. Let's hope this loop won't be a bottleneck.\n\t\tfloat f = *rdi;\n\t\tif( f != -INFINITY )\n\t\t{\n\t\t\tf = ( f - maxScalar ) * inputScale;\n\t\t\tuint16_t f16 = _cvtss_sh( f, 0 );\n\t\t\tf16 = lookup.exponent[ f16 ];\n\t\t\tf = _cvtsh_ss( f16 );\n\t\t\tsum += f;\n\t\t}\n\t\telse\n\t\t\tf = 0;\n\n\t\t*rdi = f;\n\t}\n\n\t// Final pass: apply the final scale\n\tconst __m256 finalScale = _mm256_set1_ps( (float)( 1.0 / sum ) );\n\tfor( rdi = rdiBegin; rdi < rdiEndAligned; rdi += 8 )\n\t{\n\t\t__m256 v = _mm256_loadu_ps( rdi );\n\t\tv = _mm256_mul_ps( v, finalScale );\n\t\t_mm256_storeu_ps( rdi, v );\n\t}\n\tif( 0 != remainder )\n\t{\n\t\t__m256 v = _mm256_maskload_ps( rdi, tailMask );\n\t\tv = _mm256_mul_ps( v, finalScale );\n\t\t_mm256_maskstore_ps( rdi, tailMask, v );\n\t}\n}\n\nvoid floatsUpcast( float* rdi, const uint16_t* rsi, size_t length )\n{\n\tconst uint16_t* rsiEndAligned = rsi + ( length & maskAlign8 );\n\tconst size_t rem = length % 8;\n\n\tfor( ; rsi < rsiEndAligned; rsi += 8, rdi += 8 )\n\t\t_mm256_storeu_ps( rdi, load8( rsi ) );\n\n\tif( 0 != rem )\n\t{\n\t\t__m256 v = loadPartial( rsi, rem );\n\t\t_mm256_maskstore_ps( rdi, loadTailMaskInt( rem ), v );\n\t}\n}\n\nvoid floatsDowncast( uint16_t* rdi, const float* rsi, size_t length )\n{\n\tconst float* rsiEndAligned = rsi + ( length & maskAlign8 );\n\tsize_t rem = length % 8;\n\n\tfor( ; rsi < rsiEndAligned; rsi += 8, rdi += 8 )\n\t{\n\t\t__m256 vf = _mm256_loadu_ps( rsi );\n\t\t__m128i vi = _mm256_cvtps_ph( vf, 0 );\n\t\tstore16( rdi, vi );\n\t}\n\n\tif( 0 != rem )\n\t{\n\t\t__m256 vf = _mm256_maskload_ps( rsi, loadTailMaskInt( rem ) );\n\t\t__m128i vi = _mm256_cvtps_ph( vf, 0 );\n\t\tfor( size_t i = 0; i < rem; i++, rdi++ )\n\t\t{\n\t\t\t*rdi = (uint16_t)(uint32_t)_mm_cvtsi128_si32( vi );\n\t\t\tvi = _mm_srli_si128( vi, 2 );\n\t\t}\n\t}\n}\n\nvoid addRowInPlace( float* rdi, const float* rsi, size_t length )\n{\n\tconst float* rdiEndAligned = rdi + ( length & maskAlign8 );\n\tsize_t rem = length % 8;\n\n\tfor( ; rdi < rdiEndAligned; rdi += 8, rsi += 8 )\n\t{\n\t\t__m256 a = _mm256_loadu_ps( rdi );\n\t\t__m256 b = _mm256_loadu_ps( rsi );\n\t\ta = _mm256_add_ps( a, b );\n\t\t_mm256_storeu_ps( rdi, a );\n\t}\n\n\tif( 0 != rem )\n\t{\n\t\tconst __m256i mask = loadTailMaskInt( rem );\n\t\t__m256 a = _mm256_maskload_ps( rdi, mask );\n\t\t__m256 b = _mm256_maskload_ps( rsi, mask );\n\t\ta = _mm256_add_ps( a, b );\n\t\t_mm256_maskstore_ps( rdi, mask, a );\n\t}\n}\n\nvoid addRow( float* rdi, const float* a, const float* b, size_t length )\n{\n\tconst float* aEndAligned = a + ( length & maskAlign8 );\n\tsize_t rem = length % 8;\n\n\tfor( ; a < aEndAligned; a += 8, b += 8, rdi += 8 )\n\t{\n\t\t__m256 x = _mm256_loadu_ps( a );\n\t\t__m256 y = _mm256_loadu_ps( b );\n\t\tx = _mm256_add_ps( x, y );\n\t\t_mm256_storeu_ps( rdi, x );\n\t}\n\n\tif( 0 != rem )\n\t{\n\t\tconst __m256i mask = loadTailMaskInt( rem );\n\t\t__m256 x = _mm256_maskload_ps( a, mask );\n\t\t__m256 y = _mm256_maskload_ps( b, mask );\n\t\tx = _mm256_add_ps( x, y );\n\t\t_mm256_maskstore_ps( rdi, mask, x );\n\t}\n}"
  },
  {
    "path": "Whisper/CPU/simdUtils.h",
    "content": "#pragma once\n#include <immintrin.h>\n\nvoid addF16to32( float* rdi, const uint16_t* a, const uint16_t* b, size_t length );\nvoid addF16to32( float* rdi, const uint16_t* a, const float* b, size_t length );\n\nclass AlignedSpan\n{\n\tfloat* pointer;\n\npublic:\n\tAlignedSpan( void* data )\n\t{\n\t\tsize_t i = (size_t)data;\n\t\tconstexpr size_t mask32 = ~(size_t)31;\n\t\ti = ( i + 31 ) & mask32;\n\t\tpointer = (float*)i;\n\t}\n\n\toperator float* ( ) { return pointer; }\n};\n\ninline size_t tempBufferForFloats( size_t count )\n{\n\t// Round up by 8 to be able to use full-vector loads and stores\n\tconstexpr size_t mask8 = ~(size_t)7;\n\tcount = ( count + 7 ) & mask8;\n\n\t// Add 32 more bytes to align the temporary buffer\n\treturn ( count * 4 ) + 32;\n}\n\n#define ALIGNED_SPAN( name, countFloats ) AlignedSpan name{ _alloca( tempBufferForFloats( countFloats ) ) }\n\nvoid norm( float* rdi, float* temp, const float* rsi, size_t length );\n\nvoid fmaRepeatRow( float* rdi, size_t len, const float* w, const float* b, size_t lenPattern );\nvoid __vectorcall addRepeatScaleRow( float* rdi, size_t len, const float* b, size_t lenPattern, const __m256 scale );\nvoid addRepeatRow( float* rdi, size_t len, const float* b, size_t lenPattern );\nvoid __vectorcall scaleRow( float* rdi, size_t len, const __m256 scale );\n\nnamespace DirectCompute\n{\n\tstruct LookupTablesData;\n}\nconst DirectCompute::LookupTablesData& getLookupTables();\nvoid addRepeatGeluRow( float* rdi, size_t len, const float* b, size_t lenPattern, const DirectCompute::LookupTablesData& lookup );\n\nvoid softMax( float* rdi, size_t length, const float inputScale );\n\n// A cache line-aligned array where first 8 elements have all bits set, last 8 elements are zeros\nextern const std::array<int, 16> s_zeroTailMask;\n\n// Load a tail mask as FP32 vector, for use with _mm256_and_ps or _mm256_blendv_ps instructions\n__forceinline __m256 loadTailMaskFloats( size_t remainder )\n{\n\tassert( remainder > 0 && remainder < 8 );\n\tconst float* rsi = (const float*)&s_zeroTailMask;\n\trsi += 8;\n\treturn _mm256_loadu_ps( rsi - remainder );\n}\n\n// Load a tail mask as int32 vector, for use with _mm256_maskstore_ps instruction\ntemplate<bool assertIncomplete = true>\n__forceinline __m256i loadTailMaskInt( size_t remainder )\n{\n\tif constexpr( assertIncomplete )\n\t\tassert( remainder > 0 && remainder < 8 );\n\telse\n\t\tassert( remainder >= 0 && remainder <= 8 );\n\n\tconst int* rsi = (const int*)&s_zeroTailMask;\n\trsi += 8;\n\treturn _mm256_loadu_si256( ( const __m256i* )( rsi - remainder ) );\n}\n\nvoid floatsUpcast( float* rdi, const uint16_t* rsi, size_t length );\n\nvoid floatsDowncast( uint16_t* rdi, const float* rsi, size_t length );\n\nvoid addRowInPlace( float* rdi, const float* rsi, size_t length );\nvoid addRow( float* rdi, const float* a, const float* b, size_t length );"
  },
  {
    "path": "Whisper/D3D/Binder.cpp",
    "content": "#include \"stdafx.h\"\n#include \"Binder.h\"\n#include <algorithm>\nusing namespace DirectCompute;\n\nvoid Binder::bind( ID3D11ShaderResourceView* srv0, ID3D11UnorderedAccessView* uav0 )\n{\n\tID3D11DeviceContext* const ctx = context();\n\tctx->CSSetUnorderedAccessViews( 0, 1, &uav0, nullptr );\n\n\tctx->CSSetShaderResources( 0, 1, &srv0 );\n\n\tmaxSrv = std::max( maxSrv, (uint8_t)1 );\n\tmaxUav = std::max( maxUav, (uint8_t)1 );\n}\n\nvoid Binder::bind( ID3D11UnorderedAccessView* uav0 )\n{\n\tcontext()->CSSetUnorderedAccessViews( 0, 1, &uav0, nullptr );\n\tmaxUav = std::max( maxUav, (uint8_t)1 );\n}\n\nvoid Binder::bind( ID3D11ShaderResourceView* srv0, ID3D11ShaderResourceView* srv1, ID3D11UnorderedAccessView* uav0 )\n{\n\tID3D11DeviceContext* const ctx = context();\n\tctx->CSSetUnorderedAccessViews( 0, 1, &uav0, nullptr );\n\n\tstd::array< ID3D11ShaderResourceView*, 2> arr = { srv0, srv1 };\n\tctx->CSSetShaderResources( 0, 2, arr.data() );\n\n\tmaxSrv = std::max( maxSrv, (uint8_t)2 );\n\tmaxUav = std::max( maxUav, (uint8_t)1 );\n}\n\nvoid Binder::bind( std::initializer_list<ID3D11ShaderResourceView*> srvs, std::initializer_list<ID3D11UnorderedAccessView*> uavs )\n{\n\tID3D11DeviceContext* const ctx = context();\n\n\tconst size_t lengthResources = srvs.size();\n\tconst size_t lengthUnordered = uavs.size();\n\tassert( lengthResources > 0 && lengthResources < D3D11_COMMONSHADER_INPUT_RESOURCE_REGISTER_COUNT );\n\tassert( lengthUnordered > 0 && lengthUnordered < D3D11_PS_CS_UAV_REGISTER_COUNT );\n\n\tctx->CSSetUnorderedAccessViews( 0, (UINT)lengthUnordered, uavs.begin(), nullptr );\n\tctx->CSSetShaderResources( 0, (UINT)lengthResources, srvs.begin() );\n\n\tmaxSrv = std::max( maxSrv, (uint8_t)lengthResources );\n\tmaxUav = std::max( maxUav, (uint8_t)lengthUnordered );\n}\n\nBinder::~Binder()\n{\n\tuint8_t count = std::max( maxSrv, maxUav );\n\tif( 0 == count )\n\t\treturn;\n#pragma warning (disable: 6255) // Compiler doesn't know we have very few of these things\n\tsize_t* arr = (size_t*)_alloca( count * sizeof( size_t ) );\n\tmemset( arr, 0, count * sizeof( size_t ) );\n\n\tID3D11DeviceContext* const ctx = context();\n\tctx->CSSetShaderResources( 0, maxSrv, (ID3D11ShaderResourceView**)arr );\n\tctx->CSSetUnorderedAccessViews( 0, maxUav, (ID3D11UnorderedAccessView**)arr, nullptr );\n}"
  },
  {
    "path": "Whisper/D3D/Binder.h",
    "content": "#pragma once\n#include \"device.h\"\n\nnamespace DirectCompute\n{\n\tclass Binder\n\t{\n\t\tuint8_t maxSrv = 0;\n\t\tuint8_t maxUav = 0;\n\n\tpublic:\n\t\tBinder() = default;\n\t\tBinder( const Binder& ) = delete;\n\n\t\tvoid bind( ID3D11ShaderResourceView* srv0, ID3D11UnorderedAccessView* uav0 );\n\t\tvoid bind( ID3D11ShaderResourceView* srv0, ID3D11ShaderResourceView* srv1, ID3D11UnorderedAccessView* uav0 );\n\t\tvoid bind( std::initializer_list<ID3D11ShaderResourceView*> srvs, std::initializer_list<ID3D11UnorderedAccessView*> uavs );\n\t\tvoid bind( ID3D11UnorderedAccessView* uav0 );\n\t\t~Binder();\n\t};\n}"
  },
  {
    "path": "Whisper/D3D/MappedResource.cpp",
    "content": "#include \"stdafx.h\"\n#include \"MappedResource.h\"\nusing namespace DirectCompute;\n#define CHECK( hr ) { const HRESULT __hr = ( hr ); if( FAILED( __hr ) ) return __hr; }\n\nMappedResource::MappedResource()\n{\n\tmapped.pData = nullptr;\n\tmapped.RowPitch = mapped.DepthPitch = 0;\n\tresource = nullptr;\n}\n\nHRESULT MappedResource::map( ID3D11Resource* res, bool reading )\n{\n\tif( nullptr == resource )\n\t{\n\t\tD3D11_MAP mt = reading ? D3D11_MAP_READ : D3D11_MAP_WRITE_DISCARD;\n\t\tCHECK( context()->Map( res, 0, mt, 0, &mapped ) );\n\t\tresource = res;\n\t\treturn S_OK;\n\t}\n\treturn HRESULT_FROM_WIN32( ERROR_ALREADY_INITIALIZED );\n}\n\nMappedResource::~MappedResource()\n{\n\tif( nullptr != resource )\n\t{\n\t\tcontext()->Unmap( resource, 0 );\n\t\tresource = nullptr;\n\t\tmapped.pData = nullptr;\n\t}\n}"
  },
  {
    "path": "Whisper/D3D/MappedResource.h",
    "content": "#pragma once\n#include \"device.h\"\n#include <assert.h>\n\nnamespace DirectCompute\n{\n\tclass MappedResource\n\t{\n\t\tD3D11_MAPPED_SUBRESOURCE mapped;\n\t\tID3D11Resource* resource;\n\tpublic:\n\t\tMappedResource();\n\t\tHRESULT map( ID3D11Resource* res, bool reading );\n\t\t~MappedResource();\n\n\t\tvoid* data() const\n\t\t{\n\t\t\tassert( nullptr != mapped.pData );\n\t\t\treturn mapped.pData;\n\t\t}\n\t};\n}"
  },
  {
    "path": "Whisper/D3D/RenderDoc/renderDoc.cpp",
    "content": "#include \"stdafx.h\"\n#include \"renderDoc.h\"\n#include \"renderdoc_app.h\"\n#include \"../device.h\"\n\n#define ENABLE_RENDERDOC_DEBUGGER 1\n\n#if ENABLE_RENDERDOC_DEBUGGER\nnamespace\n{\n\tstatic HMODULE hmRenderDoc = nullptr;\n\tstatic RENDERDOC_API_1_6_0* api = nullptr;\n}\n\nbool DirectCompute::initializeRenderDoc()\n{\n\thmRenderDoc = GetModuleHandleW( L\"renderdoc.dll\" );\n\tif( nullptr == hmRenderDoc )\n\t\treturn false;\n\n\tpRENDERDOC_GetAPI getApi = (pRENDERDOC_GetAPI)GetProcAddress( hmRenderDoc, \"RENDERDOC_GetAPI\" );\n\tif( nullptr == getApi )\n\t\treturn false;\n\tif( 1 != getApi( eRENDERDOC_API_Version_1_6_0, (void**)&api ) )\n\t\treturn false;\n\tif( nullptr == api )\n\t\treturn false;\n\n\treturn true;\n}\n\nnamespace\n{\n\tusing namespace DirectCompute;\n\tinline bool isKeyPressed( int vKey )\n\t{\n\t\treturn 0 != ( GetAsyncKeyState( vKey ) & 0x8000 );\n\t}\n}\n\nCaptureRaii::CaptureRaii() : capturing( false )\n{\n\tif( nullptr == api )\n\t\treturn;\n\tif( !isKeyPressed( VK_F12 ) )\n\t\treturn;\n\tID3D11Device* const dev = device();\n\tif( nullptr == dev )\n\t\treturn;\n\n\tapi->StartFrameCapture( dev, nullptr );\n\tcapturing = true;\n}\n\nCaptureRaii::~CaptureRaii()\n{\n\tif( !capturing )\n\t\treturn;\n\tapi->EndFrameCapture( device(), nullptr );\n}\n#else\t// !ENABLE_RENDERDOC_DEBUGGER\nbool DirectCompute::initializeRenderDoc()\n{\n\treturn false;\n}\nDirectCompute::CaptureRaii::CaptureRaii() : capturing( false )\n{\n}\nDirectCompute::CaptureRaii::~CaptureRaii()\n{\n}\n#endif"
  },
  {
    "path": "Whisper/D3D/RenderDoc/renderDoc.h",
    "content": "#pragma once\n\nnamespace DirectCompute\n{\n\tbool initializeRenderDoc();\n\n\tclass CaptureRaii\n\t{\n\t\tbool capturing;\n\tpublic:\n\t\tCaptureRaii();\n\t\tCaptureRaii( const CaptureRaii& ) = delete;\n\t\t~CaptureRaii();\n\t};\n}"
  },
  {
    "path": "Whisper/D3D/RenderDoc/renderdoc_app.h",
    "content": "/******************************************************************************\n * The MIT License (MIT)\n *\n * Copyright (c) 2019-2022 Baldur Karlsson\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n ******************************************************************************/\n\n#pragma once\n\n//////////////////////////////////////////////////////////////////////////////////////////////////\n//\n// Documentation for the API is available at https://renderdoc.org/docs/in_application_api.html\n//\n\n#if !defined(RENDERDOC_NO_STDINT)\n#include <stdint.h>\n#endif\n\n#if defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER)\n#define RENDERDOC_CC __cdecl\n#elif defined(__linux__)\n#define RENDERDOC_CC\n#elif defined(__APPLE__)\n#define RENDERDOC_CC\n#else\n#error \"Unknown platform\"\n#endif\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n//////////////////////////////////////////////////////////////////////////////////////////////////\n// Constants not used directly in below API\n\n// This is a GUID/magic value used for when applications pass a path where shader debug\n// information can be found to match up with a stripped shader.\n// the define can be used like so: const GUID RENDERDOC_ShaderDebugMagicValue =\n// RENDERDOC_ShaderDebugMagicValue_value\n#define RENDERDOC_ShaderDebugMagicValue_struct                                \\\n  {                                                                           \\\n    0xeab25520, 0x6670, 0x4865, 0x84, 0x29, 0x6c, 0x8, 0x51, 0x54, 0x00, 0xff \\\n  }\n\n// as an alternative when you want a byte array (assuming x86 endianness):\n#define RENDERDOC_ShaderDebugMagicValue_bytearray                                                 \\\n  {                                                                                               \\\n    0x20, 0x55, 0xb2, 0xea, 0x70, 0x66, 0x65, 0x48, 0x84, 0x29, 0x6c, 0x8, 0x51, 0x54, 0x00, 0xff \\\n  }\n\n// truncated version when only a uint64_t is available (e.g. Vulkan tags):\n#define RENDERDOC_ShaderDebugMagicValue_truncated 0x48656670eab25520ULL\n\n//////////////////////////////////////////////////////////////////////////////////////////////////\n// RenderDoc capture options\n//\n\ntypedef enum RENDERDOC_CaptureOption {\n  // Allow the application to enable vsync\n  //\n  // Default - enabled\n  //\n  // 1 - The application can enable or disable vsync at will\n  // 0 - vsync is force disabled\n  eRENDERDOC_Option_AllowVSync = 0,\n\n  // Allow the application to enable fullscreen\n  //\n  // Default - enabled\n  //\n  // 1 - The application can enable or disable fullscreen at will\n  // 0 - fullscreen is force disabled\n  eRENDERDOC_Option_AllowFullscreen = 1,\n\n  // Record API debugging events and messages\n  //\n  // Default - disabled\n  //\n  // 1 - Enable built-in API debugging features and records the results into\n  //     the capture, which is matched up with events on replay\n  // 0 - no API debugging is forcibly enabled\n  eRENDERDOC_Option_APIValidation = 2,\n  eRENDERDOC_Option_DebugDeviceMode = 2,    // deprecated name of this enum\n\n  // Capture CPU callstacks for API events\n  //\n  // Default - disabled\n  //\n  // 1 - Enables capturing of callstacks\n  // 0 - no callstacks are captured\n  eRENDERDOC_Option_CaptureCallstacks = 3,\n\n  // When capturing CPU callstacks, only capture them from actions.\n  // This option does nothing without the above option being enabled\n  //\n  // Default - disabled\n  //\n  // 1 - Only captures callstacks for actions.\n  //     Ignored if CaptureCallstacks is disabled\n  // 0 - Callstacks, if enabled, are captured for every event.\n  eRENDERDOC_Option_CaptureCallstacksOnlyDraws = 4,\n  eRENDERDOC_Option_CaptureCallstacksOnlyActions = 4,\n\n  // Specify a delay in seconds to wait for a debugger to attach, after\n  // creating or injecting into a process, before continuing to allow it to run.\n  //\n  // 0 indicates no delay, and the process will run immediately after injection\n  //\n  // Default - 0 seconds\n  //\n  eRENDERDOC_Option_DelayForDebugger = 5,\n\n  // Verify buffer access. This includes checking the memory returned by a Map() call to\n  // detect any out-of-bounds modification, as well as initialising buffers with undefined contents\n  // to a marker value to catch use of uninitialised memory.\n  //\n  // NOTE: This option is only valid for OpenGL and D3D11. Explicit APIs such as D3D12 and Vulkan do\n  // not do the same kind of interception & checking and undefined contents are really undefined.\n  //\n  // Default - disabled\n  //\n  // 1 - Verify buffer access\n  // 0 - No verification is performed, and overwriting bounds may cause crashes or corruption in\n  //     RenderDoc.\n  eRENDERDOC_Option_VerifyBufferAccess = 6,\n\n  // The old name for eRENDERDOC_Option_VerifyBufferAccess was eRENDERDOC_Option_VerifyMapWrites.\n  // This option now controls the filling of uninitialised buffers with 0xdddddddd which was\n  // previously always enabled\n  eRENDERDOC_Option_VerifyMapWrites = eRENDERDOC_Option_VerifyBufferAccess,\n\n  // Hooks any system API calls that create child processes, and injects\n  // RenderDoc into them recursively with the same options.\n  //\n  // Default - disabled\n  //\n  // 1 - Hooks into spawned child processes\n  // 0 - Child processes are not hooked by RenderDoc\n  eRENDERDOC_Option_HookIntoChildren = 7,\n\n  // By default RenderDoc only includes resources in the final capture necessary\n  // for that frame, this allows you to override that behaviour.\n  //\n  // Default - disabled\n  //\n  // 1 - all live resources at the time of capture are included in the capture\n  //     and available for inspection\n  // 0 - only the resources referenced by the captured frame are included\n  eRENDERDOC_Option_RefAllResources = 8,\n\n  // **NOTE**: As of RenderDoc v1.1 this option has been deprecated. Setting or\n  // getting it will be ignored, to allow compatibility with older versions.\n  // In v1.1 the option acts as if it's always enabled.\n  //\n  // By default RenderDoc skips saving initial states for resources where the\n  // previous contents don't appear to be used, assuming that writes before\n  // reads indicate previous contents aren't used.\n  //\n  // Default - disabled\n  //\n  // 1 - initial contents at the start of each captured frame are saved, even if\n  //     they are later overwritten or cleared before being used.\n  // 0 - unless a read is detected, initial contents will not be saved and will\n  //     appear as black or empty data.\n  eRENDERDOC_Option_SaveAllInitials = 9,\n\n  // In APIs that allow for the recording of command lists to be replayed later,\n  // RenderDoc may choose to not capture command lists before a frame capture is\n  // triggered, to reduce overheads. This means any command lists recorded once\n  // and replayed many times will not be available and may cause a failure to\n  // capture.\n  //\n  // NOTE: This is only true for APIs where multithreading is difficult or\n  // discouraged. Newer APIs like Vulkan and D3D12 will ignore this option\n  // and always capture all command lists since the API is heavily oriented\n  // around it and the overheads have been reduced by API design.\n  //\n  // 1 - All command lists are captured from the start of the application\n  // 0 - Command lists are only captured if their recording begins during\n  //     the period when a frame capture is in progress.\n  eRENDERDOC_Option_CaptureAllCmdLists = 10,\n\n  // Mute API debugging output when the API validation mode option is enabled\n  //\n  // Default - enabled\n  //\n  // 1 - Mute any API debug messages from being displayed or passed through\n  // 0 - API debugging is displayed as normal\n  eRENDERDOC_Option_DebugOutputMute = 11,\n\n  // Option to allow vendor extensions to be used even when they may be\n  // incompatible with RenderDoc and cause corrupted replays or crashes.\n  //\n  // Default - inactive\n  //\n  // No values are documented, this option should only be used when absolutely\n  // necessary as directed by a RenderDoc developer.\n  eRENDERDOC_Option_AllowUnsupportedVendorExtensions = 12,\n\n} RENDERDOC_CaptureOption;\n\n// Sets an option that controls how RenderDoc behaves on capture.\n//\n// Returns 1 if the option and value are valid\n// Returns 0 if either is invalid and the option is unchanged\ntypedef int(RENDERDOC_CC *pRENDERDOC_SetCaptureOptionU32)(RENDERDOC_CaptureOption opt, uint32_t val);\ntypedef int(RENDERDOC_CC *pRENDERDOC_SetCaptureOptionF32)(RENDERDOC_CaptureOption opt, float val);\n\n// Gets the current value of an option as a uint32_t\n//\n// If the option is invalid, 0xffffffff is returned\ntypedef uint32_t(RENDERDOC_CC *pRENDERDOC_GetCaptureOptionU32)(RENDERDOC_CaptureOption opt);\n\n// Gets the current value of an option as a float\n//\n// If the option is invalid, -FLT_MAX is returned\ntypedef float(RENDERDOC_CC *pRENDERDOC_GetCaptureOptionF32)(RENDERDOC_CaptureOption opt);\n\ntypedef enum RENDERDOC_InputButton {\n  // '0' - '9' matches ASCII values\n  eRENDERDOC_Key_0 = 0x30,\n  eRENDERDOC_Key_1 = 0x31,\n  eRENDERDOC_Key_2 = 0x32,\n  eRENDERDOC_Key_3 = 0x33,\n  eRENDERDOC_Key_4 = 0x34,\n  eRENDERDOC_Key_5 = 0x35,\n  eRENDERDOC_Key_6 = 0x36,\n  eRENDERDOC_Key_7 = 0x37,\n  eRENDERDOC_Key_8 = 0x38,\n  eRENDERDOC_Key_9 = 0x39,\n\n  // 'A' - 'Z' matches ASCII values\n  eRENDERDOC_Key_A = 0x41,\n  eRENDERDOC_Key_B = 0x42,\n  eRENDERDOC_Key_C = 0x43,\n  eRENDERDOC_Key_D = 0x44,\n  eRENDERDOC_Key_E = 0x45,\n  eRENDERDOC_Key_F = 0x46,\n  eRENDERDOC_Key_G = 0x47,\n  eRENDERDOC_Key_H = 0x48,\n  eRENDERDOC_Key_I = 0x49,\n  eRENDERDOC_Key_J = 0x4A,\n  eRENDERDOC_Key_K = 0x4B,\n  eRENDERDOC_Key_L = 0x4C,\n  eRENDERDOC_Key_M = 0x4D,\n  eRENDERDOC_Key_N = 0x4E,\n  eRENDERDOC_Key_O = 0x4F,\n  eRENDERDOC_Key_P = 0x50,\n  eRENDERDOC_Key_Q = 0x51,\n  eRENDERDOC_Key_R = 0x52,\n  eRENDERDOC_Key_S = 0x53,\n  eRENDERDOC_Key_T = 0x54,\n  eRENDERDOC_Key_U = 0x55,\n  eRENDERDOC_Key_V = 0x56,\n  eRENDERDOC_Key_W = 0x57,\n  eRENDERDOC_Key_X = 0x58,\n  eRENDERDOC_Key_Y = 0x59,\n  eRENDERDOC_Key_Z = 0x5A,\n\n  // leave the rest of the ASCII range free\n  // in case we want to use it later\n  eRENDERDOC_Key_NonPrintable = 0x100,\n\n  eRENDERDOC_Key_Divide,\n  eRENDERDOC_Key_Multiply,\n  eRENDERDOC_Key_Subtract,\n  eRENDERDOC_Key_Plus,\n\n  eRENDERDOC_Key_F1,\n  eRENDERDOC_Key_F2,\n  eRENDERDOC_Key_F3,\n  eRENDERDOC_Key_F4,\n  eRENDERDOC_Key_F5,\n  eRENDERDOC_Key_F6,\n  eRENDERDOC_Key_F7,\n  eRENDERDOC_Key_F8,\n  eRENDERDOC_Key_F9,\n  eRENDERDOC_Key_F10,\n  eRENDERDOC_Key_F11,\n  eRENDERDOC_Key_F12,\n\n  eRENDERDOC_Key_Home,\n  eRENDERDOC_Key_End,\n  eRENDERDOC_Key_Insert,\n  eRENDERDOC_Key_Delete,\n  eRENDERDOC_Key_PageUp,\n  eRENDERDOC_Key_PageDn,\n\n  eRENDERDOC_Key_Backspace,\n  eRENDERDOC_Key_Tab,\n  eRENDERDOC_Key_PrtScrn,\n  eRENDERDOC_Key_Pause,\n\n  eRENDERDOC_Key_Max,\n} RENDERDOC_InputButton;\n\n// Sets which key or keys can be used to toggle focus between multiple windows\n//\n// If keys is NULL or num is 0, toggle keys will be disabled\ntypedef void(RENDERDOC_CC *pRENDERDOC_SetFocusToggleKeys)(RENDERDOC_InputButton *keys, int num);\n\n// Sets which key or keys can be used to capture the next frame\n//\n// If keys is NULL or num is 0, captures keys will be disabled\ntypedef void(RENDERDOC_CC *pRENDERDOC_SetCaptureKeys)(RENDERDOC_InputButton *keys, int num);\n\ntypedef enum RENDERDOC_OverlayBits {\n  // This single bit controls whether the overlay is enabled or disabled globally\n  eRENDERDOC_Overlay_Enabled = 0x1,\n\n  // Show the average framerate over several seconds as well as min/max\n  eRENDERDOC_Overlay_FrameRate = 0x2,\n\n  // Show the current frame number\n  eRENDERDOC_Overlay_FrameNumber = 0x4,\n\n  // Show a list of recent captures, and how many captures have been made\n  eRENDERDOC_Overlay_CaptureList = 0x8,\n\n  // Default values for the overlay mask\n  eRENDERDOC_Overlay_Default = (eRENDERDOC_Overlay_Enabled | eRENDERDOC_Overlay_FrameRate |\n                                eRENDERDOC_Overlay_FrameNumber | eRENDERDOC_Overlay_CaptureList),\n\n  // Enable all bits\n  eRENDERDOC_Overlay_All = ~0U,\n\n  // Disable all bits\n  eRENDERDOC_Overlay_None = 0,\n} RENDERDOC_OverlayBits;\n\n// returns the overlay bits that have been set\ntypedef uint32_t(RENDERDOC_CC *pRENDERDOC_GetOverlayBits)();\n// sets the overlay bits with an and & or mask\ntypedef void(RENDERDOC_CC *pRENDERDOC_MaskOverlayBits)(uint32_t And, uint32_t Or);\n\n// this function will attempt to remove RenderDoc's hooks in the application.\n//\n// Note: that this can only work correctly if done immediately after\n// the module is loaded, before any API work happens. RenderDoc will remove its\n// injected hooks and shut down. Behaviour is undefined if this is called\n// after any API functions have been called, and there is still no guarantee of\n// success.\ntypedef void(RENDERDOC_CC *pRENDERDOC_RemoveHooks)();\n\n// DEPRECATED: compatibility for code compiled against pre-1.4.1 headers.\ntypedef pRENDERDOC_RemoveHooks pRENDERDOC_Shutdown;\n\n// This function will unload RenderDoc's crash handler.\n//\n// If you use your own crash handler and don't want RenderDoc's handler to\n// intercede, you can call this function to unload it and any unhandled\n// exceptions will pass to the next handler.\ntypedef void(RENDERDOC_CC *pRENDERDOC_UnloadCrashHandler)();\n\n// Sets the capture file path template\n//\n// pathtemplate is a UTF-8 string that gives a template for how captures will be named\n// and where they will be saved.\n//\n// Any extension is stripped off the path, and captures are saved in the directory\n// specified, and named with the filename and the frame number appended. If the\n// directory does not exist it will be created, including any parent directories.\n//\n// If pathtemplate is NULL, the template will remain unchanged\n//\n// Example:\n//\n// SetCaptureFilePathTemplate(\"my_captures/example\");\n//\n// Capture #1 -> my_captures/example_frame123.rdc\n// Capture #2 -> my_captures/example_frame456.rdc\ntypedef void(RENDERDOC_CC *pRENDERDOC_SetCaptureFilePathTemplate)(const char *pathtemplate);\n\n// returns the current capture path template, see SetCaptureFileTemplate above, as a UTF-8 string\ntypedef const char *(RENDERDOC_CC *pRENDERDOC_GetCaptureFilePathTemplate)();\n\n// DEPRECATED: compatibility for code compiled against pre-1.1.2 headers.\ntypedef pRENDERDOC_SetCaptureFilePathTemplate pRENDERDOC_SetLogFilePathTemplate;\ntypedef pRENDERDOC_GetCaptureFilePathTemplate pRENDERDOC_GetLogFilePathTemplate;\n\n// returns the number of captures that have been made\ntypedef uint32_t(RENDERDOC_CC *pRENDERDOC_GetNumCaptures)();\n\n// This function returns the details of a capture, by index. New captures are added\n// to the end of the list.\n//\n// filename will be filled with the absolute path to the capture file, as a UTF-8 string\n// pathlength will be written with the length in bytes of the filename string\n// timestamp will be written with the time of the capture, in seconds since the Unix epoch\n//\n// Any of the parameters can be NULL and they'll be skipped.\n//\n// The function will return 1 if the capture index is valid, or 0 if the index is invalid\n// If the index is invalid, the values will be unchanged\n//\n// Note: when captures are deleted in the UI they will remain in this list, so the\n// capture path may not exist anymore.\ntypedef uint32_t(RENDERDOC_CC *pRENDERDOC_GetCapture)(uint32_t idx, char *filename,\n                                                      uint32_t *pathlength, uint64_t *timestamp);\n\n// Sets the comments associated with a capture file. These comments are displayed in the\n// UI program when opening.\n//\n// filePath should be a path to the capture file to add comments to. If set to NULL or \"\"\n// the most recent capture file created made will be used instead.\n// comments should be a NULL-terminated UTF-8 string to add as comments.\n//\n// Any existing comments will be overwritten.\ntypedef void(RENDERDOC_CC *pRENDERDOC_SetCaptureFileComments)(const char *filePath,\n                                                              const char *comments);\n\n// returns 1 if the RenderDoc UI is connected to this application, 0 otherwise\ntypedef uint32_t(RENDERDOC_CC *pRENDERDOC_IsTargetControlConnected)();\n\n// DEPRECATED: compatibility for code compiled against pre-1.1.1 headers.\n// This was renamed to IsTargetControlConnected in API 1.1.1, the old typedef is kept here for\n// backwards compatibility with old code, it is castable either way since it's ABI compatible\n// as the same function pointer type.\ntypedef pRENDERDOC_IsTargetControlConnected pRENDERDOC_IsRemoteAccessConnected;\n\n// This function will launch the Replay UI associated with the RenderDoc library injected\n// into the running application.\n//\n// if connectTargetControl is 1, the Replay UI will be launched with a command line parameter\n// to connect to this application\n// cmdline is the rest of the command line, as a UTF-8 string. E.g. a captures to open\n// if cmdline is NULL, the command line will be empty.\n//\n// returns the PID of the replay UI if successful, 0 if not successful.\ntypedef uint32_t(RENDERDOC_CC *pRENDERDOC_LaunchReplayUI)(uint32_t connectTargetControl,\n                                                          const char *cmdline);\n\n// RenderDoc can return a higher version than requested if it's backwards compatible,\n// this function returns the actual version returned. If a parameter is NULL, it will be\n// ignored and the others will be filled out.\ntypedef void(RENDERDOC_CC *pRENDERDOC_GetAPIVersion)(int *major, int *minor, int *patch);\n\n// Requests that the replay UI show itself (if hidden or not the current top window). This can be\n// used in conjunction with IsTargetControlConnected and LaunchReplayUI to intelligently handle\n// showing the UI after making a capture.\n//\n// This will return 1 if the request was successfully passed on, though it's not guaranteed that\n// the UI will be on top in all cases depending on OS rules. It will return 0 if there is no current\n// target control connection to make such a request, or if there was another error\ntypedef uint32_t(RENDERDOC_CC *pRENDERDOC_ShowReplayUI)();\n\n//////////////////////////////////////////////////////////////////////////\n// Capturing functions\n//\n\n// A device pointer is a pointer to the API's root handle.\n//\n// This would be an ID3D11Device, HGLRC/GLXContext, ID3D12Device, etc\ntypedef void *RENDERDOC_DevicePointer;\n\n// A window handle is the OS's native window handle\n//\n// This would be an HWND, GLXDrawable, etc\ntypedef void *RENDERDOC_WindowHandle;\n\n// A helper macro for Vulkan, where the device handle cannot be used directly.\n//\n// Passing the VkInstance to this macro will return the RENDERDOC_DevicePointer to use.\n//\n// Specifically, the value needed is the dispatch table pointer, which sits as the first\n// pointer-sized object in the memory pointed to by the VkInstance. Thus we cast to a void** and\n// indirect once.\n#define RENDERDOC_DEVICEPOINTER_FROM_VKINSTANCE(inst) (*((void **)(inst)))\n\n// This sets the RenderDoc in-app overlay in the API/window pair as 'active' and it will\n// respond to keypresses. Neither parameter can be NULL\ntypedef void(RENDERDOC_CC *pRENDERDOC_SetActiveWindow)(RENDERDOC_DevicePointer device,\n                                                       RENDERDOC_WindowHandle wndHandle);\n\n// capture the next frame on whichever window and API is currently considered active\ntypedef void(RENDERDOC_CC *pRENDERDOC_TriggerCapture)();\n\n// capture the next N frames on whichever window and API is currently considered active\ntypedef void(RENDERDOC_CC *pRENDERDOC_TriggerMultiFrameCapture)(uint32_t numFrames);\n\n// When choosing either a device pointer or a window handle to capture, you can pass NULL.\n// Passing NULL specifies a 'wildcard' match against anything. This allows you to specify\n// any API rendering to a specific window, or a specific API instance rendering to any window,\n// or in the simplest case of one window and one API, you can just pass NULL for both.\n//\n// In either case, if there are two or more possible matching (device,window) pairs it\n// is undefined which one will be captured.\n//\n// Note: for headless rendering you can pass NULL for the window handle and either specify\n// a device pointer or leave it NULL as above.\n\n// Immediately starts capturing API calls on the specified device pointer and window handle.\n//\n// If there is no matching thing to capture (e.g. no supported API has been initialised),\n// this will do nothing.\n//\n// The results are undefined (including crashes) if two captures are started overlapping,\n// even on separate devices and/oror windows.\ntypedef void(RENDERDOC_CC *pRENDERDOC_StartFrameCapture)(RENDERDOC_DevicePointer device,\n                                                         RENDERDOC_WindowHandle wndHandle);\n\n// Returns whether or not a frame capture is currently ongoing anywhere.\n//\n// This will return 1 if a capture is ongoing, and 0 if there is no capture running\ntypedef uint32_t(RENDERDOC_CC *pRENDERDOC_IsFrameCapturing)();\n\n// Ends capturing immediately.\n//\n// This will return 1 if the capture succeeded, and 0 if there was an error capturing.\ntypedef uint32_t(RENDERDOC_CC *pRENDERDOC_EndFrameCapture)(RENDERDOC_DevicePointer device,\n                                                           RENDERDOC_WindowHandle wndHandle);\n\n// Ends capturing immediately and discard any data stored without saving to disk.\n//\n// This will return 1 if the capture was discarded, and 0 if there was an error or no capture\n// was in progress\ntypedef uint32_t(RENDERDOC_CC *pRENDERDOC_DiscardFrameCapture)(RENDERDOC_DevicePointer device,\n                                                               RENDERDOC_WindowHandle wndHandle);\n\n// Only valid to be called between a call to StartFrameCapture and EndFrameCapture. Gives a custom\n// title to the capture produced which will be displayed in the UI.\n//\n// If multiple captures are ongoing, this title will be applied to the first capture to end after\n// this call. The second capture to end will have no title, unless this function is called again.\n//\n// Calling this function has no effect if no capture is currently running, and if it is called\n// multiple times only the last title will be used.\ntypedef void(RENDERDOC_CC *pRENDERDOC_SetCaptureTitle)(const char *title);\n\n//////////////////////////////////////////////////////////////////////////////////////////////////\n// RenderDoc API versions\n//\n\n// RenderDoc uses semantic versioning (http://semver.org/).\n//\n// MAJOR version is incremented when incompatible API changes happen.\n// MINOR version is incremented when functionality is added in a backwards-compatible manner.\n// PATCH version is incremented when backwards-compatible bug fixes happen.\n//\n// Note that this means the API returned can be higher than the one you might have requested.\n// e.g. if you are running against a newer RenderDoc that supports 1.0.1, it will be returned\n// instead of 1.0.0. You can check this with the GetAPIVersion entry point\ntypedef enum RENDERDOC_Version {\n  eRENDERDOC_API_Version_1_0_0 = 10000,    // RENDERDOC_API_1_0_0 = 1 00 00\n  eRENDERDOC_API_Version_1_0_1 = 10001,    // RENDERDOC_API_1_0_1 = 1 00 01\n  eRENDERDOC_API_Version_1_0_2 = 10002,    // RENDERDOC_API_1_0_2 = 1 00 02\n  eRENDERDOC_API_Version_1_1_0 = 10100,    // RENDERDOC_API_1_1_0 = 1 01 00\n  eRENDERDOC_API_Version_1_1_1 = 10101,    // RENDERDOC_API_1_1_1 = 1 01 01\n  eRENDERDOC_API_Version_1_1_2 = 10102,    // RENDERDOC_API_1_1_2 = 1 01 02\n  eRENDERDOC_API_Version_1_2_0 = 10200,    // RENDERDOC_API_1_2_0 = 1 02 00\n  eRENDERDOC_API_Version_1_3_0 = 10300,    // RENDERDOC_API_1_3_0 = 1 03 00\n  eRENDERDOC_API_Version_1_4_0 = 10400,    // RENDERDOC_API_1_4_0 = 1 04 00\n  eRENDERDOC_API_Version_1_4_1 = 10401,    // RENDERDOC_API_1_4_1 = 1 04 01\n  eRENDERDOC_API_Version_1_4_2 = 10402,    // RENDERDOC_API_1_4_2 = 1 04 02\n  eRENDERDOC_API_Version_1_5_0 = 10500,    // RENDERDOC_API_1_5_0 = 1 05 00\n  eRENDERDOC_API_Version_1_6_0 = 10600,    // RENDERDOC_API_1_6_0 = 1 06 00\n} RENDERDOC_Version;\n\n// API version changelog:\n//\n// 1.0.0 - initial release\n// 1.0.1 - Bugfix: IsFrameCapturing() was returning false for captures that were triggered\n//         by keypress or TriggerCapture, instead of Start/EndFrameCapture.\n// 1.0.2 - Refactor: Renamed eRENDERDOC_Option_DebugDeviceMode to eRENDERDOC_Option_APIValidation\n// 1.1.0 - Add feature: TriggerMultiFrameCapture(). Backwards compatible with 1.0.x since the new\n//         function pointer is added to the end of the struct, the original layout is identical\n// 1.1.1 - Refactor: Renamed remote access to target control (to better disambiguate from remote\n//         replay/remote server concept in replay UI)\n// 1.1.2 - Refactor: Renamed \"log file\" in function names to just capture, to clarify that these\n//         are captures and not debug logging files. This is the first API version in the v1.0\n//         branch.\n// 1.2.0 - Added feature: SetCaptureFileComments() to add comments to a capture file that will be\n//         displayed in the UI program on load.\n// 1.3.0 - Added feature: New capture option eRENDERDOC_Option_AllowUnsupportedVendorExtensions\n//         which allows users to opt-in to allowing unsupported vendor extensions to function.\n//         Should be used at the user's own risk.\n//         Refactor: Renamed eRENDERDOC_Option_VerifyMapWrites to\n//         eRENDERDOC_Option_VerifyBufferAccess, which now also controls initialisation to\n//         0xdddddddd of uninitialised buffer contents.\n// 1.4.0 - Added feature: DiscardFrameCapture() to discard a frame capture in progress and stop\n//         capturing without saving anything to disk.\n// 1.4.1 - Refactor: Renamed Shutdown to RemoveHooks to better clarify what is happening\n// 1.4.2 - Refactor: Renamed 'draws' to 'actions' in callstack capture option.\n// 1.5.0 - Added feature: ShowReplayUI() to request that the replay UI show itself if connected\n// 1.6.0 - Added feature: SetCaptureTitle() which can be used to set a title for a\n//         capture made with StartFrameCapture() or EndFrameCapture()\n\ntypedef struct RENDERDOC_API_1_6_0\n{\n  pRENDERDOC_GetAPIVersion GetAPIVersion;\n\n  pRENDERDOC_SetCaptureOptionU32 SetCaptureOptionU32;\n  pRENDERDOC_SetCaptureOptionF32 SetCaptureOptionF32;\n\n  pRENDERDOC_GetCaptureOptionU32 GetCaptureOptionU32;\n  pRENDERDOC_GetCaptureOptionF32 GetCaptureOptionF32;\n\n  pRENDERDOC_SetFocusToggleKeys SetFocusToggleKeys;\n  pRENDERDOC_SetCaptureKeys SetCaptureKeys;\n\n  pRENDERDOC_GetOverlayBits GetOverlayBits;\n  pRENDERDOC_MaskOverlayBits MaskOverlayBits;\n\n  // Shutdown was renamed to RemoveHooks in 1.4.1.\n  // These unions allow old code to continue compiling without changes\n  union\n  {\n    pRENDERDOC_Shutdown Shutdown;\n    pRENDERDOC_RemoveHooks RemoveHooks;\n  };\n  pRENDERDOC_UnloadCrashHandler UnloadCrashHandler;\n\n  // Get/SetLogFilePathTemplate was renamed to Get/SetCaptureFilePathTemplate in 1.1.2.\n  // These unions allow old code to continue compiling without changes\n  union\n  {\n    // deprecated name\n    pRENDERDOC_SetLogFilePathTemplate SetLogFilePathTemplate;\n    // current name\n    pRENDERDOC_SetCaptureFilePathTemplate SetCaptureFilePathTemplate;\n  };\n  union\n  {\n    // deprecated name\n    pRENDERDOC_GetLogFilePathTemplate GetLogFilePathTemplate;\n    // current name\n    pRENDERDOC_GetCaptureFilePathTemplate GetCaptureFilePathTemplate;\n  };\n\n  pRENDERDOC_GetNumCaptures GetNumCaptures;\n  pRENDERDOC_GetCapture GetCapture;\n\n  pRENDERDOC_TriggerCapture TriggerCapture;\n\n  // IsRemoteAccessConnected was renamed to IsTargetControlConnected in 1.1.1.\n  // This union allows old code to continue compiling without changes\n  union\n  {\n    // deprecated name\n    pRENDERDOC_IsRemoteAccessConnected IsRemoteAccessConnected;\n    // current name\n    pRENDERDOC_IsTargetControlConnected IsTargetControlConnected;\n  };\n  pRENDERDOC_LaunchReplayUI LaunchReplayUI;\n\n  pRENDERDOC_SetActiveWindow SetActiveWindow;\n\n  pRENDERDOC_StartFrameCapture StartFrameCapture;\n  pRENDERDOC_IsFrameCapturing IsFrameCapturing;\n  pRENDERDOC_EndFrameCapture EndFrameCapture;\n\n  // new function in 1.1.0\n  pRENDERDOC_TriggerMultiFrameCapture TriggerMultiFrameCapture;\n\n  // new function in 1.2.0\n  pRENDERDOC_SetCaptureFileComments SetCaptureFileComments;\n\n  // new function in 1.4.0\n  pRENDERDOC_DiscardFrameCapture DiscardFrameCapture;\n\n  // new function in 1.5.0\n  pRENDERDOC_ShowReplayUI ShowReplayUI;\n\n  // new function in 1.6.0\n  pRENDERDOC_SetCaptureTitle SetCaptureTitle;\n} RENDERDOC_API_1_6_0;\n\ntypedef RENDERDOC_API_1_6_0 RENDERDOC_API_1_0_0;\ntypedef RENDERDOC_API_1_6_0 RENDERDOC_API_1_0_1;\ntypedef RENDERDOC_API_1_6_0 RENDERDOC_API_1_0_2;\ntypedef RENDERDOC_API_1_6_0 RENDERDOC_API_1_1_0;\ntypedef RENDERDOC_API_1_6_0 RENDERDOC_API_1_1_1;\ntypedef RENDERDOC_API_1_6_0 RENDERDOC_API_1_1_2;\ntypedef RENDERDOC_API_1_6_0 RENDERDOC_API_1_2_0;\ntypedef RENDERDOC_API_1_6_0 RENDERDOC_API_1_3_0;\ntypedef RENDERDOC_API_1_6_0 RENDERDOC_API_1_4_0;\ntypedef RENDERDOC_API_1_6_0 RENDERDOC_API_1_4_1;\ntypedef RENDERDOC_API_1_6_0 RENDERDOC_API_1_4_2;\ntypedef RENDERDOC_API_1_6_0 RENDERDOC_API_1_5_0;\n\n//////////////////////////////////////////////////////////////////////////////////////////////////\n// RenderDoc API entry point\n//\n// This entry point can be obtained via GetProcAddress/dlsym if RenderDoc is available.\n//\n// The name is the same as the typedef - \"RENDERDOC_GetAPI\"\n//\n// This function is not thread safe, and should not be called on multiple threads at once.\n// Ideally, call this once as early as possible in your application's startup, before doing\n// any API work, since some configuration functionality etc has to be done also before\n// initialising any APIs.\n//\n// Parameters:\n//   version is a single value from the RENDERDOC_Version above.\n//\n//   outAPIPointers will be filled out with a pointer to the corresponding struct of function\n//   pointers.\n//\n// Returns:\n//   1 - if the outAPIPointers has been filled with a pointer to the API struct requested\n//   0 - if the requested version is not supported or the arguments are invalid.\n//\ntypedef int(RENDERDOC_CC *pRENDERDOC_GetAPI)(RENDERDOC_Version version, void **outAPIPointers);\n\n#ifdef __cplusplus\n}    // extern \"C\"\n#endif\n"
  },
  {
    "path": "Whisper/D3D/createBuffer.cpp",
    "content": "#include \"stdafx.h\"\n#include \"createBuffer.h\"\n\n#define CHECK( hr ) { const HRESULT __hr = ( hr ); if( FAILED( __hr ) ) return __hr; }\n\nHRESULT DirectCompute::createBuffer( eBufferUse use, size_t totalBytes, ID3D11Buffer** ppGpuBuffer, const void* rsi, ID3D11Buffer** ppStagingBuffer, bool shared )\n{\n\tif( totalBytes > INT_MAX )\n\t\treturn DISP_E_OVERFLOW;\n\tif( nullptr == ppGpuBuffer )\n\t\treturn E_POINTER;\n\n\tCD3D11_BUFFER_DESC bufferDesc{ (UINT)totalBytes, D3D11_BIND_SHADER_RESOURCE };\n\tswitch( use )\n\t{\n\tcase eBufferUse::Immutable:\n\t\tif( nullptr == rsi )\n\t\t\treturn E_INVALIDARG;\n\t\tbufferDesc.Usage = D3D11_USAGE_IMMUTABLE;\n\t\tif( gpuInfo().cloneableModel() )\n\t\t{\n\t\t\t// According to D3D11 documentation, the only resources that can be shared are 2D non-mipmapped textures.\n\t\t\t// https://learn.microsoft.com/en-us/windows/win32/api/d3d11/ne-d3d11-d3d11_resource_misc_flag\n\t\t\t// However, D3D9 documentation says all resource types are supported, as long as they are in the default pool.\n\t\t\t// https://learn.microsoft.com/en-us/windows/win32/direct3d9/dx9lh?redirectedfrom=MSDN#sharing-resources\n\t\t\t// Appears to work on my computer\n\t\t\tbufferDesc.Usage = D3D11_USAGE_DEFAULT;\n\t\t\tbufferDesc.MiscFlags |= D3D11_RESOURCE_MISC_SHARED;\n\t\t}\n\t\tbreak;\n\tcase eBufferUse::ReadWrite:\n\tcase eBufferUse::ReadWriteDownload:\n\t\tbufferDesc.BindFlags |= D3D11_BIND_UNORDERED_ACCESS;\n\t\tif( shared && gpuInfo().cloneableModel() )\n\t\t\tbufferDesc.MiscFlags |= D3D11_RESOURCE_MISC_SHARED;\n\t\tbreak;\n\tcase eBufferUse::Dynamic:\n\t\tbufferDesc.Usage = D3D11_USAGE_DYNAMIC;\n\t\tbufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;\n\t\tbreak;\n\t}\n\n\tD3D11_SUBRESOURCE_DATA srd;\n\tD3D11_SUBRESOURCE_DATA* pSrd = nullptr;\n\tif( nullptr != rsi )\n\t{\n\t\tsrd.pSysMem = rsi;\n\t\tsrd.SysMemPitch = srd.SysMemSlicePitch = 0;\n\t\tpSrd = &srd;\n\t}\n\n\tCHECK( device()->CreateBuffer( &bufferDesc, pSrd, ppGpuBuffer ) );\n\n\tif( nullptr != ppStagingBuffer && use == eBufferUse::ReadWriteDownload )\n\t{\n\t\tbufferDesc.Usage = D3D11_USAGE_STAGING;\n\t\tbufferDesc.BindFlags = 0;\n\t\tbufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;\n\t\tCHECK( device()->CreateBuffer( &bufferDesc, nullptr, ppStagingBuffer ) );\n\t}\n\n\treturn S_OK;\n}"
  },
  {
    "path": "Whisper/D3D/createBuffer.h",
    "content": "#pragma once\n#include \"enums.h\"\n#include \"device.h\"\n\nnamespace DirectCompute\n{\n\tHRESULT createBuffer( eBufferUse use, size_t totalBytes, ID3D11Buffer** ppGpuBuffer, const void* rsi, ID3D11Buffer** ppStagingBuffer, bool shared = false );\n}"
  },
  {
    "path": "Whisper/D3D/createDevice.cpp",
    "content": "#include \"stdafx.h\"\n#include \"createDevice.h\"\n#include \"listGPUs.h\"\n#include \"RenderDoc/renderDoc.h\"\n#pragma comment(lib, \"D3D11.lib\")\n#include <atlstr.h>\n\nHRESULT DirectCompute::createDevice( const std::wstring& adapterName, ID3D11Device** dev, ID3D11DeviceContext** context )\n{\n\tCComPtr<IDXGIAdapter1> adapter = selectAdapter( adapterName );\n\tconst D3D_DRIVER_TYPE driverType = adapter ? D3D_DRIVER_TYPE_UNKNOWN : D3D_DRIVER_TYPE_HARDWARE;\n\n\tconst std::array<D3D_FEATURE_LEVEL, 4> levels = { D3D_FEATURE_LEVEL_12_1 , D3D_FEATURE_LEVEL_12_0 , D3D_FEATURE_LEVEL_11_1 , D3D_FEATURE_LEVEL_11_0 };\n\tUINT flags = D3D11_CREATE_DEVICE_DISABLE_GPU_TIMEOUT | D3D11_CREATE_DEVICE_SINGLETHREADED;\n\tbool renderDoc = initializeRenderDoc();\n#ifdef _DEBUG\n\tif( !renderDoc )\n\t{\n\t\t// Last time I checked, RenderDoc crashed with debug version of D3D11 runtime\n\t\t// Only setting this flag unless renderdoc.dll is loaded to the current process\n\t\tflags |= D3D11_CREATE_DEVICE_DEBUG;\n\t}\n#endif\n\tconstexpr UINT levelsCount = (UINT)levels.size();\n\tHRESULT hr = D3D11CreateDevice( adapter, driverType, nullptr, flags, levels.data(), levelsCount, D3D11_SDK_VERSION, dev, nullptr, context );\n\tif( SUCCEEDED( hr ) )\n\t\treturn S_OK;\n\t// D3D11_CREATE_DEVICE_DISABLE_GPU_TIMEOUT: This value is not supported until Direct3D 11.1\n\t// https://learn.microsoft.com/en-us/windows/win32/api/d3d11/ne-d3d11-d3d11_create_device_flag\n\tflags = _andn_u32( D3D11_CREATE_DEVICE_DISABLE_GPU_TIMEOUT, flags );\n\n\thr = D3D11CreateDevice( adapter, driverType, nullptr, flags, levels.data(), levelsCount, D3D11_SDK_VERSION, dev, nullptr, context );\n\tif( SUCCEEDED( hr ) )\n\t\treturn S_OK;\n\treturn hr;\n}\n\nHRESULT DirectCompute::cloneDevice( ID3D11Device* source, ID3D11Device** dev, ID3D11DeviceContext** context )\n{\n\tCComPtr<IDXGIDevice> dxgiDev;\n\tCHECK( source->QueryInterface( &dxgiDev ) );\n\n\tCComPtr<IDXGIAdapter> adapter;\n\tCHECK( dxgiDev->GetAdapter( &adapter ) );\n\n\tconst uint32_t flags = source->GetCreationFlags();\n\tconst D3D_FEATURE_LEVEL level = source->GetFeatureLevel();\n\treturn D3D11CreateDevice( adapter, D3D_DRIVER_TYPE_UNKNOWN, nullptr, flags, &level, 1,\n\t\tD3D11_SDK_VERSION, dev, nullptr, context );\n}\n\nnamespace\n{\n\tusing Whisper::eGpuModelFlags;\n\tinline constexpr uint32_t operator|( eGpuModelFlags a, eGpuModelFlags b )\n\t{\n\t\treturn (uint32_t)a | (uint32_t)b;\n\t}\n\tinline bool operator&( uint32_t flags, eGpuModelFlags bit )\n\t{\n\t\treturn 0 != ( flags & (uint32_t)bit );\n\t}\n\tinline bool merge3( uint32_t flags, eGpuModelFlags enabled, eGpuModelFlags disabled, bool def )\n\t{\n\t\tif( flags & enabled )\n\t\t\treturn true;\n\t\tif( flags & disabled )\n\t\t\treturn false;\n\t\treturn def;\n\t}\n}\n\nHRESULT DirectCompute::validateFlags( uint32_t flags )\n{\n\tconstexpr uint32_t waveBoth = eGpuModelFlags::Wave32 | eGpuModelFlags::Wave64;\n\tif( ( flags & waveBoth ) == waveBoth )\n\t{\n\t\tlogError( u8\"eGpuModelFlags.%s and eGpuModelFlags.%s are mutually exclusive\", \"Wave32\", \"Wave64\" );\n\t\treturn E_INVALIDARG;\n\t}\n\n\tconstexpr uint32_t reshapedBoth = eGpuModelFlags::NoReshapedMatMul | eGpuModelFlags::UseReshapedMatMul;\n\tif( ( flags & reshapedBoth ) == reshapedBoth )\n\t{\n\t\tlogError( u8\"eGpuModelFlags.%s and eGpuModelFlags.%s are mutually exclusive\", \"NoReshapedMatMul\", \"UseReshapedMatMul\" );\n\t\treturn E_INVALIDARG;\n\t}\n\treturn S_OK;\n}\n\nHRESULT DirectCompute::queryDeviceInfo( sGpuInfo& rdi, ID3D11Device* dev, uint32_t flags )\n{\n\tif( nullptr == dev )\n\t\treturn OLE_E_BLANK;\n\n\tCComPtr<IDXGIDevice> dd;\n\tCHECK( dev->QueryInterface( &dd ) );\n\n\tCComPtr<IDXGIAdapter> adapter;\n\tCHECK( dd->GetAdapter( &adapter ) );\n\n\tDXGI_ADAPTER_DESC desc;\n\tadapter->GetDesc( &desc );\n\n\tconst size_t descLen = wcsnlen_s( desc.Description, 128 );\n\tconst wchar_t* rsi = &desc.Description[ 0 ];\n\trdi.description.assign( rsi, rsi + descLen );\n\trdi.vendor = (eGpuVendor)desc.VendorId;\n\trdi.device = (uint16_t)desc.DeviceId;\n\trdi.revision = (uint16_t)desc.Revision;\n\trdi.subsystem = desc.SubSysId;\n\trdi.vramDedicated = desc.DedicatedVideoMemory;\n\trdi.ramDedicated = desc.DedicatedSystemMemory;\n\trdi.ramShared = desc.SharedSystemMemory;\n\n\t// Set up these flags\n\tuint8_t ef = 0;\n\tconst bool amd = ( rdi.vendor == eGpuVendor::AMD );\n\tif( merge3( flags, eGpuModelFlags::Wave64, eGpuModelFlags::Wave32, amd ) )\n\t\tef |= (uint8_t)eGpuEffectiveFlags::Wave64;\n\tif( merge3( flags, eGpuModelFlags::UseReshapedMatMul, eGpuModelFlags::NoReshapedMatMul, amd ) )\n\t\tef |= (uint8_t)eGpuEffectiveFlags::ReshapedMatMul;\n\tif( 0 != ( flags & eGpuModelFlags::Cloneable ) )\n\t\tef |= (uint8_t)eGpuEffectiveFlags::Cloneable;\n\trdi.flags = (eGpuEffectiveFlags)ef;\n\n\tif( willLogMessage( Whisper::eLogLevel::Debug ) )\n\t{\n\t\tconst int fl = dev->GetFeatureLevel();\n\t\tconst int flMajor = ( fl >> 12 ) & 0xF;\n\t\tconst int flMinor = ( fl >> 8 ) & 0xF;\n\n\t\tCStringA flagsString;\n\t\tflagsString.Format( \"%s | %s\", rdi.wave64() ? \"Wave64\" : \"Wave32\",\n\t\t\trdi.useReshapedMatMul() ? \"UseReshapedMatMul\" : \"NoReshapedMatMul\" );\n\t\tif( rdi.cloneableModel() )\n\t\t\tflagsString += \" | Cloneable\";\n\n\t\tlogDebug16( L\"Using GPU \\\"%s\\\", feature level %i.%i, effective flags %S\",\n\t\t\trdi.description.c_str(), flMajor, flMinor,\n\t\t\tflagsString.operator const char* ( ) );\n\t}\n\treturn S_OK;\n}"
  },
  {
    "path": "Whisper/D3D/createDevice.h",
    "content": "// Low-level functions to create and initialize D3D11 device\n#pragma once\n#include <string>\n#include \"sGpuInfo.h\"\n\nnamespace DirectCompute\n{\n\tHRESULT createDevice( const std::wstring& adapter, ID3D11Device** dev, ID3D11DeviceContext** context );\n\n\tHRESULT validateFlags( uint32_t flags );\n\n\tHRESULT queryDeviceInfo( sGpuInfo& rdi, ID3D11Device* dev, uint32_t flags );\n\n\t// Create another device and context, on the same hardware GPU\n\tHRESULT cloneDevice( ID3D11Device* source, ID3D11Device** dev, ID3D11DeviceContext** context );\n}"
  },
  {
    "path": "Whisper/D3D/device.h",
    "content": "#pragma once\n#include <atlcomcli.h>\n#include <string>\n#include \"sGpuInfo.h\"\n\nnamespace DirectCompute\n{\n\tID3D11Device* device();\n\tID3D11DeviceContext* context();\n\tconst sGpuInfo& gpuInfo();\n\n\tinline void csSetCB( ID3D11Buffer* cb )\n\t{\n\t\tcontext()->CSSetConstantBuffers( 0, 1, &cb );\n\t}\n\n\t__m128i bufferMemoryUsage( ID3D11Buffer* buffer );\n\n\t__m128i resourceMemoryUsage( ID3D11ShaderResourceView* srv );\n}"
  },
  {
    "path": "Whisper/D3D/downloadBuffer.cpp",
    "content": "#include \"stdafx.h\"\n#include \"downloadBuffer.h\"\n#include \"device.h\"\n#include \"MappedResource.h\"\n\nnamespace\n{\n\tstruct BufferInfo\n\t{\n\t\tD3D11_SHADER_RESOURCE_VIEW_DESC viewDesc;\n\t\tD3D11_BUFFER_DESC bufferDesc;\n\t\tCComPtr<ID3D11Buffer> source;\n\n\t\tHRESULT create( ID3D11ShaderResourceView* srv )\n\t\t{\n\t\t\tsrv->GetDesc( &viewDesc );\n\t\t\tif( viewDesc.ViewDimension != D3D_SRV_DIMENSION_BUFFER )\n\t\t\t\treturn E_INVALIDARG;\n\n\t\t\tCComPtr<ID3D11Resource> res;\n\t\t\tsrv->GetResource( &res );\n\t\t\tCHECK( res.QueryInterface( &source ) );\n\n\t\t\tsource->GetDesc( &bufferDesc );\n\t\t\treturn S_OK;\n\t\t}\n\n\t\tHRESULT download( void* rdi )\n\t\t{\n\t\t\tbufferDesc.BindFlags = 0;\n\t\t\tbufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;\n\t\t\tbufferDesc.Usage = D3D11_USAGE_STAGING;\n\t\t\tCComPtr<ID3D11Buffer> staging;\n\t\t\tusing namespace DirectCompute;\n\t\t\tCHECK( device()->CreateBuffer( &bufferDesc, nullptr, &staging ) );\n\n\t\t\tcontext()->CopyResource( staging, source );\n\n\t\t\tMappedResource mapped;\n\t\t\tmapped.map( staging, true );\n\t\t\tmemcpy( rdi, mapped.data(), bufferDesc.ByteWidth );\n\t\t\treturn S_OK;\n\t\t}\n\t};\n\n\tsize_t dxgiSizeof( DXGI_FORMAT fmt )\n\t{\n\t\tswitch( fmt )\n\t\t{\n\t\tcase DXGI_FORMAT_R16_FLOAT: return 2;\n\t\tcase DXGI_FORMAT_R32_FLOAT: return 4;\n\t\t}\n\t\treturn 0;\n\t}\n}\n\ntemplate<class E>\nHRESULT DirectCompute::downloadBuffer( ID3D11ShaderResourceView* srv, std::vector<E>& vec )\n{\n\tBufferInfo bi;\n\tCHECK( bi.create( srv ) );\n\n\tconst size_t cb = dxgiSizeof( bi.viewDesc.Format );\n\tif( cb != sizeof( E ) )\n\t\treturn E_INVALIDARG;\n\n\tvec.resize( bi.bufferDesc.ByteWidth / cb );\n\treturn bi.download( vec.data() );\n}\n\ntemplate HRESULT DirectCompute::downloadBuffer( ID3D11ShaderResourceView* srv, std::vector<uint16_t>& vec );\ntemplate HRESULT DirectCompute::downloadBuffer( ID3D11ShaderResourceView* srv, std::vector<float>& vec );"
  },
  {
    "path": "Whisper/D3D/downloadBuffer.h",
    "content": "#pragma once\n\nnamespace DirectCompute\n{\n\t// Download a buffer from VRAM into std::vector\n\t// The function is relatively expensive, creates a temporary staging buffer on each call, and only used to test things.\n\ttemplate<class E>\n\tHRESULT downloadBuffer( ID3D11ShaderResourceView* srv, std::vector<E>& vec );\n}"
  },
  {
    "path": "Whisper/D3D/enums.cpp",
    "content": "#include \"stdafx.h\"\n#include \"enums.h\"\n\nstatic const alignas( 16 ) std::array<DXGI_FORMAT, 3> s_tensorViewFormats = { DXGI_FORMAT_R16_FLOAT, DXGI_FORMAT_R32_FLOAT, DXGI_FORMAT_R32_UINT };\n\nDXGI_FORMAT DirectCompute::viewFormat( eDataType dt )\n{\n\treturn s_tensorViewFormats[ (uint8_t)dt ];\n}"
  },
  {
    "path": "Whisper/D3D/enums.h",
    "content": "#pragma once\n#include <stdint.h>\n#include <assert.h>\n\nnamespace DirectCompute\n{\n\tenum struct eDataType : uint8_t\n\t{\n\t\tFP16,\n\t\tFP32,\n\t\tU32,\n\t};\n\n\tinline size_t elementSize( eDataType dt )\n\t{\n\t\tassert( dt == eDataType::FP16 || dt == eDataType::FP32 || dt == eDataType::U32 );\n\n\t\treturn ( dt == eDataType::FP16 ) ? 2 : 4;\n\t}\n\n\tDXGI_FORMAT viewFormat( eDataType dt );\n\n\tenum struct eBufferUse : uint8_t\n\t{\n\t\t// Immutable tensor, readable from GPU\n\t\tImmutable,\n\t\t// Read+write tensor, readable and writable on GPU\n\t\tReadWrite,\n\t\t// Read+write tensor, readable and writable on GPU, which supports downloads from GPU\n\t\tReadWriteDownload,\n\t\t// The tensor is accessible by both GPU (read only) and CPU (write only). Optimized for resources frequently updated from CPU.\n\t\tDynamic,\n\t};\n}"
  },
  {
    "path": "Whisper/D3D/listGPUs.cpp",
    "content": "#include \"stdafx.h\"\n#include \"listGPUs.h\"\n#pragma comment(lib, \"DXGI.lib\")\n#include <charconv>\n#include <optional>\n\nnamespace DirectCompute\n{\n\tstatic HRESULT createFactory( CComPtr<IDXGIFactory1>& rdi )\n\t{\n\t\tHRESULT hr = CreateDXGIFactory1( IID_PPV_ARGS( &rdi ) );\n\t\tif( SUCCEEDED( hr ) )\n\t\t\treturn S_OK;\n\t\treturn hr;\n\t}\n\n\tinline void setName( std::wstring& rdi, const DXGI_ADAPTER_DESC1& desc )\n\t{\n\t\tconst size_t descLen = wcsnlen_s( desc.Description, 128 );\n\t\tconst wchar_t* rsi = &desc.Description[ 0 ];\n\t\trdi.assign( rsi, rsi + descLen );\n\t}\n\n\t// If the UTF16 string contains a small non-negative number, return that number.\n\tstd::optional<uint32_t> parseGpuIndex( const std::wstring& requestedName )\n\t{\n\t\tif( requestedName.length() > 3 )\n\t\t\treturn {};\n\n\t\tchar buffer[ 4 ];\n\t\t*(uint32_t*)( &buffer[ 0 ] ) = 0;\n\t\tfor( size_t i = 0; i < requestedName.length(); i++ )\n\t\t{\n\t\t\tconst wchar_t wc = requestedName[ i ];\n\t\t\tif( wc < L'0' || wc > L'9' )\n\t\t\t\treturn {};\n\t\t\tbuffer[ i ] = (char)(uint8_t)wc;\n\t\t}\n\n\t\tuint32_t result;\n\t\tauto res = std::from_chars( buffer, &buffer[ 0 ] + requestedName.length(), result );\n\t\tif( res.ec == std::errc{} )\n\t\t\treturn result;\n\n\t\treturn {};\n\t}\n\n\tCComPtr<IDXGIAdapter1> selectAdapter( const std::wstring& requestedName )\n\t{\n\t\tif( requestedName.empty() )\n\t\t\treturn nullptr;\n\n\t\tCComPtr<IDXGIFactory1> dxgi;\n\t\tHRESULT hr = createFactory( dxgi );\n\t\tif( FAILED( hr ) )\n\t\t{\n\t\t\tlogWarningHr( hr, u8\"CreateDXGIFactory1 failed\" );\n\t\t\treturn nullptr;\n\t\t}\n\n\t\tconst auto idx = parseGpuIndex( requestedName );\n\t\tif( idx.has_value() )\n\t\t{\n\t\t\t// User has specified 0-based GPU index instead of the name\n\t\t\t// https://github.com/Const-me/Whisper/issues/72\n\n\t\t\tCComPtr<IDXGIAdapter1> adapter;\n\t\t\thr = dxgi->EnumAdapters1( idx.value(), &adapter );\n\t\t\tif( hr == DXGI_ERROR_NOT_FOUND )\n\t\t\t{\n\t\t\t\tlogWarning( u8\"Requested GPU #%i not found\", (int)idx.value() );\n\t\t\t\treturn nullptr;\n\t\t\t}\n\n\t\t\tif( FAILED( hr ) )\n\t\t\t{\n\t\t\t\tlogWarningHr( hr, u8\"IDXGIFactory1.EnumAdapters1 failed\" );\n\t\t\t\treturn nullptr;\n\t\t\t}\n\n\t\t\treturn adapter;\n\t\t}\n\n\t\tstd::wstring name;\n\t\tfor( UINT i = 0; true; i++ )\n\t\t{\n\t\t\tCComPtr<IDXGIAdapter1> adapter;\n\t\t\thr = dxgi->EnumAdapters1( i, &adapter );\n\t\t\tif( hr == DXGI_ERROR_NOT_FOUND )\n\t\t\t{\n\t\t\t\tlogWarning16( L\"Requested GPU not found: \\\"%s\\\"\", requestedName.c_str() );\n\t\t\t\treturn nullptr;\n\t\t\t}\n\n\t\t\tif( FAILED( hr ) )\n\t\t\t{\n\t\t\t\tlogWarningHr( hr, u8\"IDXGIFactory1.EnumAdapters1 failed\" );\n\t\t\t\treturn nullptr;\n\t\t\t}\n\n\t\t\tDXGI_ADAPTER_DESC1 desc;\n\t\t\tadapter->GetDesc1( &desc );\n\t\t\tsetName( name, desc );\n\t\t\tif( name == requestedName )\n\t\t\t\treturn adapter;\n\t\t}\n\t}\n}\n\nHRESULT COMLIGHTCALL Whisper::listGPUs( pfnListAdapters pfn, void* pv )\n{\n\tusing namespace DirectCompute;\n\n\tCComPtr<IDXGIFactory1> dxgi;\n\tHRESULT hr = createFactory( dxgi );\n\tif( FAILED( hr ) )\n\t{\n\t\tlogErrorHr( hr, u8\"CreateDXGIFactory1 failed\" );\n\t\treturn hr;\n\t}\n\n\tstd::wstring name;\n\tfor( UINT i = 0; true; i++ )\n\t{\n\t\tCComPtr<IDXGIAdapter1> adapter;\n\t\thr = dxgi->EnumAdapters1( i, &adapter );\n\t\tif( hr == DXGI_ERROR_NOT_FOUND )\n\t\t\treturn S_OK;\n\n\t\tif( FAILED( hr ) )\n\t\t{\n\t\t\tlogErrorHr( hr, u8\"IDXGIFactory1.EnumAdapters1 failed\" );\n\t\t\treturn hr;\n\t\t}\n\n\t\tDXGI_ADAPTER_DESC1 desc;\n\t\tadapter->GetDesc1( &desc );\n\t\tsetName( name, desc );\n\t\tpfn( name.c_str(), pv );\n\t}\n}"
  },
  {
    "path": "Whisper/D3D/listGPUs.h",
    "content": "#pragma once\n#include <API/iContext.cl.h>\n\nnamespace DirectCompute\n{\n\tCComPtr<IDXGIAdapter1> selectAdapter( const std::wstring& requestedName );\n}"
  },
  {
    "path": "Whisper/D3D/sGpuInfo.h",
    "content": "#pragma once\n#include <stdint.h>\n\nnamespace DirectCompute\n{\n\t// DXGI_ADAPTER_DESC.VendorId magic numbers; they come from that database: https://pcisig.com/membership/member-companies\n\tenum struct eGpuVendor : uint16_t\n\t{\n\t\tAMD = 0x1002,\n\t\tNVidia = 0x10de,\n\t\tIntel = 0x8086,\n\t\tVMWare = 0x15ad,\n\t};\n\n\tenum struct eGpuEffectiveFlags : uint8_t\n\t{\n\t\tWave64 = 1,\n\t\tReshapedMatMul = 2,\n\t\tCloneable = 4,\n\t};\n\n\tstruct sGpuInfo\n\t{\n\t\teGpuEffectiveFlags flags;\n\t\teGpuVendor vendor;\n\t\tuint16_t device, revision;\n\t\tuint32_t subsystem;\n\t\tsize_t vramDedicated, ramDedicated, ramShared;\n\t\tstd::wstring description;\n\n\t\tinline bool wave64() const\n\t\t{\n\t\t\treturn 0 != ( (uint8_t)flags & (uint8_t)eGpuEffectiveFlags::Wave64 );\n\t\t}\n\n\t\t// On nVidia 1080Ti that approach is much slower, by a factor of 2.4\n\t\t// On AMD Cezanne that approach is faster by a factor of 0.69, i.e. 30% faster.\n\t\t// Dunno why that is, maybe 'coz on that AMD complete panels fit in L3 cache.\n\t\t// Anyway, we do want extra 30% perf on AMD Cezanne, so only using that code on AMD GPUs.\n\t\t// Dunno how it gonna behave on other GPUs, need to test.\n\t\tinline bool useReshapedMatMul() const\n\t\t{\n\t\t\treturn 0 != ( (uint8_t)flags & (uint8_t)eGpuEffectiveFlags::ReshapedMatMul );\n\t\t}\n\n\t\tinline bool cloneableModel() const\n\t\t{\n\t\t\treturn 0 != ( (uint8_t)flags & (uint8_t)eGpuEffectiveFlags::Cloneable );\n\t\t}\n\t};\n}"
  },
  {
    "path": "Whisper/D3D/shaderNames.cpp",
    "content": "// This source file is generated by a tool\n#include \"stdafx.h\"\n#include \"shaderNames.h\"\n\nstatic const std::array<const char*, 41> s_shaderNames = \n{\n\t\"add\",\n\t\"addInPlace\",\n\t\"addRepeat\",\n\t\"addRepeatEx\",\n\t\"addRepeatGelu\",\n\t\"addRepeatScale\",\n\t\"addRows\",\n\t\"convolutionMain\",\n\t\"convolutionMain2\",\n\t\"convolutionMain2Fixed\",\n\t\"convolutionPrep1\",\n\t\"convolutionPrep2\",\n\t\"copyConvert\",\n\t\"copyTranspose\",\n\t\"dbgFindNaN\",\n\t\"diagMaskInf\",\n\t\"flashAttention\",\n\t\"flashAttentionCompat1\",\n\t\"flashAttentionCompat2\",\n\t\"flashAttentionCompat3\",\n\t\"fmaRepeat1\",\n\t\"fmaRepeat2\",\n\t\"matReshapePanels\",\n\t\"mulMatByRow\",\n\t\"mulMatByRowTiled\",\n\t\"mulMatByRowTiledEx\",\n\t\"mulMatByScalar\",\n\t\"mulMatDotMain\",\n\t\"mulMatDotReshape\",\n\t\"mulMatMadMain\",\n\t\"mulMatTiled\",\n\t\"mulMatTiledEx\",\n\t\"norm\",\n\t\"normCompat\",\n\t\"normFixed\",\n\t\"scaleInPlace\",\n\t\"softMax\",\n\t\"softMaxCompat\",\n\t\"softMaxFixed\",\n\t\"softMaxLong\",\n\t\"zeroMemory\",\n};\n\nconst char* DirectCompute::computeShaderName( eComputeShader cs )\n{\n\tconst uint16_t i = (uint16_t)cs;\n\tif( i < s_shaderNames.size() )\n\t\treturn s_shaderNames[ i ];\n\treturn nullptr;\n}"
  },
  {
    "path": "Whisper/D3D/shaderNames.h",
    "content": "// This header is generated by a tool\n#pragma once\n#include <stdint.h>\n\nnamespace DirectCompute\n{\n\tenum struct eComputeShader: uint16_t\n\t{\n\t\tadd = 0,\n\t\taddInPlace = 1,\n\t\taddRepeat = 2,\n\t\taddRepeatEx = 3,\n\t\taddRepeatGelu = 4,\n\t\taddRepeatScale = 5,\n\t\taddRows = 6,\n\t\tconvolutionMain = 7,\n\t\tconvolutionMain2 = 8,\n\t\tconvolutionMain2Fixed = 9,\n\t\tconvolutionPrep1 = 10,\n\t\tconvolutionPrep2 = 11,\n\t\tcopyConvert = 12,\n\t\tcopyTranspose = 13,\n\t\tdbgFindNaN = 14,\n\t\tdiagMaskInf = 15,\n\t\tflashAttention = 16,\n\t\tflashAttentionCompat1 = 17,\n\t\tflashAttentionCompat2 = 18,\n\t\tflashAttentionCompat3 = 19,\n\t\tfmaRepeat1 = 20,\n\t\tfmaRepeat2 = 21,\n\t\tmatReshapePanels = 22,\n\t\tmulMatByRow = 23,\n\t\tmulMatByRowTiled = 24,\n\t\tmulMatByRowTiledEx = 25,\n\t\tmulMatByScalar = 26,\n\t\tmulMatDotMain = 27,\n\t\tmulMatDotReshape = 28,\n\t\tmulMatMadMain = 29,\n\t\tmulMatTiled = 30,\n\t\tmulMatTiledEx = 31,\n\t\tnorm = 32,\n\t\tnormCompat = 33,\n\t\tnormFixed = 34,\n\t\tscaleInPlace = 35,\n\t\tsoftMax = 36,\n\t\tsoftMaxCompat = 37,\n\t\tsoftMaxFixed = 38,\n\t\tsoftMaxLong = 39,\n\t\tzeroMemory = 40,\n\t};\n\n\tconst char* computeShaderName( eComputeShader cs );\n}"
  },
  {
    "path": "Whisper/D3D/shaders.cpp",
    "content": "#include \"stdafx.h\"\n#include \"shaders.h\"\n#include \"device.h\"\n#include \"../Utils/LZ4/lz4.h\"\n\nnamespace\n{\n#ifdef _DEBUG\n#include \"shaderData-Debug.inl\"\n#else\n#include \"shaderData-Release.inl\"\n#endif\n\n\t// static std::vector<CComPtr<ID3D11ComputeShader>> s_shaders;\n}\n\nHRESULT DirectCompute::createComputeShaders( std::vector<CComPtr<ID3D11ComputeShader>>& shaders )\n{\n\tconstexpr size_t countBinaries = s_shaderOffsets.size() - 1;\n\tconst size_t cbDecompressedLength = s_shaderOffsets[ countBinaries ];\n\tconstexpr size_t countShaders = s_shaderBlobs32.size();\n\n\tstd::vector<uint8_t> dxbc;\n\ttry\n\t{\n\t\tshaders.resize( countShaders );\n\t\tdxbc.resize( cbDecompressedLength );\n\t}\n\tcatch( const std::bad_alloc& )\n\t{\n\t\treturn E_OUTOFMEMORY;\n\t}\n\n\tconst int lz4Status = LZ4_decompress_safe( (const char*)s_compressedShaders.data(), (char*)dxbc.data(), (int)s_compressedShaders.size(), (int)cbDecompressedLength );\n\tif( lz4Status != (int)cbDecompressedLength )\n\t{\n\t\tlogError( u8\"LZ4_decompress_safe failed with status %i\", lz4Status );\n\t\treturn PLA_E_CABAPI_FAILURE;\n\t}\n\tID3D11Device* const dev = device();\n\n\tconst auto& blobs = gpuInfo().wave64() ? s_shaderBlobs64 : s_shaderBlobs32;\n\n\tfor( size_t i = 0; i < countShaders; i++ )\n\t{\n\t\tconst size_t idxBinary = blobs[ i ];\n\t\tconst uint32_t offThis = s_shaderOffsets[ idxBinary ];\n\t\tconst uint8_t* rsi = &dxbc[ offThis ];\n\t\tconst size_t len = s_shaderOffsets[ idxBinary + 1 ] - offThis;\n\t\tconst HRESULT hr = dev->CreateComputeShader( rsi, len, nullptr, &shaders[ i ] );\n\t\tif( SUCCEEDED( hr ) )\n\t\t\tcontinue;\n\n\t\tconst uint64_t binaryBit = ( 1ull << idxBinary );\n\t\tif( 0 != ( binaryBit & fp64ShadersBitmap ) )\n\t\t\tcontinue;\t// This shader uses FP64 math, the support for that is optional. When not supported, CreateComputeShader method is expected to fail.\n\t\t// TODO [low]: ideally, query for the support when creating the device, and don't even try creating these compute shaders\n\t\treturn hr;\n\t}\n\n\treturn S_OK;\n}"
  },
  {
    "path": "Whisper/D3D/shaders.h",
    "content": "#pragma once\n#include \"shaderNames.h\"\n\nnamespace DirectCompute\n{\n\tHRESULT createComputeShaders( std::vector<CComPtr<ID3D11ComputeShader>>& shaders );\n\n\tvoid bindShader( eComputeShader shader );\n}"
  },
  {
    "path": "Whisper/DllMain.cpp",
    "content": "#include \"stdafx.h\"\n\nBOOL __stdcall DllMain( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved )\n{\n\t// Perform actions based on the reason for calling.\n\tswitch( fdwReason )\n\t{\n\tcase DLL_PROCESS_ATTACH:\n\t\t// Initialize once for each new process. Return FALSE to fail DLL load.\n\t\tDisableThreadLibraryCalls( (HMODULE)hinstDLL );\n\t\tbreak;\n\tcase DLL_THREAD_ATTACH:\n\t\t// Do thread-specific initialization.\n\t\tbreak;\n\tcase DLL_THREAD_DETACH:\n\t\t// Do thread-specific cleanup.\n\t\tbreak;\n\tcase DLL_PROCESS_DETACH:\n\t\tif( lpvReserved != nullptr )\n\t\t{\n\t\t\tbreak; // do not do cleanup if process termination scenario\n\t\t}\n\t\t// Perform any necessary cleanup\n\t\tbreak;\n\t}\n\treturn TRUE;  // Successful DLL_PROCESS_ATTACH.\n}"
  },
  {
    "path": "Whisper/Hybrid/HybridContext.cpp",
    "content": "#include \"stdafx.h\"\n#include <immintrin.h>\n#include <optional>\n#include \"HybridContext.h\"\n#include \"../Utils/Trace/tracing.h\"\n\n#if BUILD_HYBRID_VERSION\n#ifndef __AVX__\n#error Hybrid version requires AVX build, and ideally AVX2 CPU\n#endif // !__AVX__\n\nnamespace\n{\n\tint threadsCount( int t )\n\t{\n#ifdef NDEBUG\n\t\tif( t == 0 )\n\t\t{\n\t\t\tSYSTEM_INFO si;\n\t\t\tGetSystemInfo( &si );\n\t\t\treturn (int)si.dwNumberOfProcessors;\n\t\t}\n\t\tif( t <= 1 )\n\t\t\treturn 1;\n\t\treturn t;\n#else\n\t\treturn 1;\n#endif\n\t}\n\n\tconstexpr size_t MB = 1u << 20;\n}\n\nHybridContext::HybridContext( const Whisper::WhisperModel& wm ) :\n\tml( threadsCount( 0 ) ),\n\tmodel( wm.shared->hybridTensors ),\n\twhisperModel( wm )\n{ }\n\nnamespace\n{\n\tenum struct eModelType : uint8_t\n\t{\n\t\tTiny = 0,\n\t\tBase = 1,\n\t\tSmall = 2,\n\t\tMedium = 3,\n\t\tLarge = 4,\n\t};\n\n\tstatic HRESULT detectModelType( const Whisper::sModelParams& modelParams, eModelType& mt )\n\t{\n\t\tswitch( modelParams.n_audio_layer )\n\t\t{\n\t\tcase 4:\n\t\t\tmt = eModelType::Tiny;\n\t\t\treturn S_OK;\n\t\tcase 6:\n\t\t\tmt = eModelType::Base;\n\t\t\treturn S_OK;\n\t\tcase 12:\n\t\t\tmt = eModelType::Small;\n\t\t\treturn S_OK;\n\t\tcase 24:\n\t\t\tmt = eModelType::Medium;\n\t\t\treturn S_OK;\n\t\tcase 32:\n\t\t\tmt = eModelType::Large;\n\t\t\treturn S_OK;\n\t\t}\n\t\tlogError( u8\"Unrecognized model\" );\n\t\treturn E_INVALIDARG;\n\t}\n\n\tstruct alignas( 2 ) RamMB\n\t{\n\t\tuint8_t dec, decLayer;\n\t\tconstexpr RamMB( uint8_t d, uint8_t dl ) : dec( d ), decLayer( dl ) { }\n\n\t\t__m128i loadBytes() const\n\t\t{\n\t\t\t__m128i v = _mm_loadu_si16( this );\n\t\t\t// Upcast bytes to int64_t. That instruction can load directly from memory, too bad VC++ optimized doesn't care\n\t\t\tv = _mm_cvtepu8_epi64( v );\n\t\t\t// Scale from megabytes into bytes, the multiplier is obviously 2^20\n\t\t\tv = _mm_slli_epi64( v, 20 );\n\t\t\treturn v;\n\t\t}\n\t};\n\n\t// The magic numbers are from MEM_REQ_DECODE and MEM_REQ_DECODE_LAYER red/black maps in the reference version,\n\t// near the top of whisper.cpp source file\n\tstatic const std::array<RamMB, 5> s_memRequirements =\n\t{\n\t\tRamMB{ 200, 32 },\t// Tiny\n\t\tRamMB{ 202, 44 },\t// Base\n\t\tRamMB{ 204, 64 },\t// Small\n\t\tRamMB{ 206, 84 },\t// Medium\n\t\tRamMB{ 208, 110 },\t// Large\n\t};\n}\n\nHRESULT HybridContext::create()\n{\n\t// Allocate buffers for compute\n\t// We know they're large, so bypassing the heap\n\teModelType modelType;\n\tCHECK( detectModelType( whisperModel.parameters, modelType ) );\n\n\tconst __m128i bytes = s_memRequirements.at( (uint8_t)modelType ).loadBytes();\n\tCHECK( allocCompute.create( _mm_cvtsi128_si64( bytes ) ) );\n\tCHECK( allocComputeLayer.create( _mm_extract_epi64( bytes, 1 ) ) );\n\n\t// Create staging buffers to download output from encoder stage,\n\t// in the reference version they're named memory_cross_k / memory_cross_v\n\tCHECK( kvCross.create( whisperModel.parameters ) );\n\n\t// Create RAM buffers for memory_k / memory_v\n\tCHECK( kv.create( whisperModel.parameters ) );\n\n\treturn S_OK;\n}\n\nclass HybridContext::SetAllocatorRaii\n{\n\tHybridContext& context;\n\tCpuCompute::iMemoryAllocator* prevAlloc;\n\tCpuCompute::iArenaAllocator* newAlloc;\npublic:\n\n\tSetAllocatorRaii( HybridContext* owner, CpuCompute::iArenaAllocator& a ) :\n\t\tcontext( *owner )\n\t{\n\t\tprevAlloc = context.ml.setAllocator( &a );\n\t\tnewAlloc = &a;\n\t}\n\t~SetAllocatorRaii()\n\t{\n\t\tcontext.ml.setAllocator( prevAlloc );\n\t\tnewAlloc->resetArena();\n\t}\n};\n\nHRESULT HybridContext::decode( const int* tokens, const int n_tokens, const int n_past, const sDecParams& dp, std::vector<float>& probs )\n{\n\tCHECK( ml.setThreadsCount( dp.n_threads ) );\n\n\t// whisper_decode\n\tconst auto& hparams = whisperModel.parameters;\n\tconst uint32_t n_vocab = hparams.n_vocab;\n\n\tconst uint32_t n_ctx = hparams.n_text_ctx;\n\tconst uint32_t n_state = hparams.n_text_state;\n\tconst uint32_t n_head = hparams.n_text_head;\n\tconst uint32_t n_layer = hparams.n_text_layer;\n\n\tconst uint32_t N = n_tokens;\n\tconst uint32_t M = dp.M;\n\n\tSetAllocatorRaii ac{ this, allocCompute };\n\tusing namespace CpuCompute;\n\tTensor cur = ml.addRows( model.tokenEmbedding, model.positionalEmbedding, tokens, n_tokens, n_past );\n\tTracing::tensor( \"dec-rows\", cur );\n\n\tTensor inpL = cur;\n\tauto kvCross = this->kvCross.map();\n\n\tfor( uint32_t il = 0; il < n_layer; il++ )\n\t{\n\t\tif( 0 == il ) Tracing::tensor( \"dec-inpL\", inpL );\n\t\tconst auto& layer = model.layers[ il ];\n\t\tSetAllocatorRaii acLayer{ this, allocComputeLayer };\n\n\t\t// norm\n\t\tTensor cur = ml.norm( inpL );\n\t\tml.fmaRepeat( cur, layer.attnLn0 );\n\t\tif( 0 == il ) Tracing::tensor( \"dec-norm\", cur );\n\n\t\t// self-attention\n\t\t{\n\t\t\tTensor Qcur = ml.mulMat( layer.attnQuery.w, cur );\n\t\t\tif( 0 == il ) Tracing::tensor( \"dec-Qcur-0\", Qcur );\n\t\t\tconst float scaling = computeScaling( (int)n_state, (int)n_head );\n\t\t\tml.addRepeatScale( Qcur, layer.attnQuery.b, scaling );\n\t\t\tif( 0 == il ) Tracing::tensor( \"dec-Qcur-1\", Qcur );\n\n\t\t\t// note: no bias for Key\n\t\t\tTensor Kcur = ml.mulMat( layer.attnKey, cur );\n\t\t\tml.scale( Kcur, scaling );\n\t\t\tif( 0 == il ) Tracing::tensor( \"dec-Kcur\", Kcur );\n\n\t\t\tTensor Vcur = ml.mulMat( layer.attnValue.w, cur );\n\t\t\tml.addRepeat( Vcur, layer.attnValue.b );\n\t\t\tif( 0 == il ) Tracing::tensor( \"dec-Vcur\", Vcur );\n\n\t\t\t// store key and value to memory\n\t\t\t{\n\t\t\t\tconst uint32_t len = N * n_state;\n\t\t\t\tconst uint32_t off = n_state * ( (uint32_t)il * n_ctx + n_past );\n\t\t\t\tTensor k = kv.keysView( len, off );\n\t\t\t\tTensor v = kv.valuesView( len, off );\n\n\t\t\t\tCHECK( ml.copyImpl( k, Kcur ) );\n\t\t\t\tCHECK( ml.copyImpl( v, Vcur ) );\n\t\t\t}\n\n\t\t\t// ------\n\t\t\tTensor Q = ml.permute( ml.copy( Qcur, eDataType::FP32, { n_state / n_head, n_head, N } ), 0, 2, 1, 3 );\n\t\t\tTensor K = ml.permute( kv.keysView( ( n_past + N ) * n_state, (uint32_t)il * n_ctx * n_state )\n\t\t\t\t.reshape3d( n_state / n_head, n_head, n_past + N ),\n\t\t\t\t0, 2, 1, 3 );\n\t\t\tTensor KQ = ml.mulMat( K, Q );\n\t\t\tif( 0 == il ) Tracing::tensor( \"dec-KQ-0\", KQ );\n\t\t\tml.diagMaskInf( KQ, n_past );\n\t\t\tif( 0 == il ) Tracing::tensor( \"dec-KQ-1\", KQ );\n\t\t\tml.softMax( KQ );\n\t\t\tif( 0 == il ) Tracing::tensor( \"dec-KQ-2\", KQ );\n\n\t\t\tTensor V_trans = ml.permute(\n\t\t\t\tkv.valuesView( ( n_past + N ) * n_state, (uint32_t)il * n_ctx * n_state )\n\t\t\t\t.reshape3d( n_state / n_head, n_head, n_past + N ),\n\t\t\t\t1, 2, 0, 3 );\n\n\t\t\tTensor KQV = ml.mulMat( V_trans, KQ );\n\t\t\tif( 0 == il ) Tracing::tensor( \"dec-KQV\", KQV );\n\n\t\t\tTensor KQV_merged = ml.permute( KQV, 0, 2, 1, 3 );\n\t\t\tml.copyInPlace( cur, KQV_merged, eDataType::FP32, { n_state, N } );\n\t\t}\n\n\t\t{\n\t\t\tcur = ml.mulMat( layer.attnLn1.w, cur );\n\t\t\tml.addRepeat( cur, layer.attnLn1.b );\n\t\t}\n\n\t\t// add the input\n\t\tTensor inpCA = ml.add( cur, inpL );\n\n\t\t// norm\n\t\t{\n\t\t\tcur = ml.norm( inpCA );\n\t\t\tml.fmaRepeat( cur, layer.crossAttnLn0 );\n\t\t}\n\n\t\t// cross-attention\n\t\t{\n\t\t\tTensor Qcur = ml.mulMat( layer.crossAttnQuery.w, cur );\n\t\t\tml.addRepeatScale( Qcur, layer.crossAttnQuery.b, computeScaling( (int)n_state, (int)n_head ) );\n\n\t\t\t// Kcross is already scaled\n\t\t\tconst uint32_t len = M * n_state;\n\t\t\tconst uint32_t off = (uint32_t)il * len;\n\t\t\tTensor Kcross = kvCross.keysView( len, off ).reshape3d( n_state / n_head, n_head, M );\n\t\t\tTensor Vcross = kvCross.valuesView( len, off ).reshape3d( n_state / n_head, n_head, M );\n\n\t\t\t// ------\n\t\t\tTensor Q = ml.permute( ml.copy( Qcur, eDataType::FP32, { n_state / n_head, n_head, N } ), 0, 2, 1, 3 );\n\t\t\tTensor K = ml.permute( Kcross, 0, 2, 1, 3 );\n\t\t\tTensor KQ = ml.mulMat( K, Q );\n\t\t\tml.softMax( KQ );\n\t\t\tTensor V_trans = ml.permute( Vcross, 1, 2, 0, 3 );\n\t\t\tTensor KQV = ml.mulMat( V_trans, KQ );\n\t\t\tif( 0 == il ) Tracing::tensor( \"dec-KQV\", KQV );\n\t\t\tTensor KQV_merged = ml.permute( KQV, 0, 2, 1, 3 );\n\n\t\t\tml.copyInPlace( cur, KQV_merged, eDataType::FP32, { n_state, N } );\n\t\t}\n\n\t\t// projection\n\t\t{\n\t\t\tcur = ml.mulMat( layer.crossAttnLn1.w, cur );\n\t\t\tml.addRepeat( cur, layer.crossAttnLn1.b );\n\t\t}\n\t\t// add the input\n\t\tml.addInPlace( cur, inpCA );\n\t\tTensor inpFF = cur;\n\n\t\t// feed-forward network\n\t\t{\n\t\t\t// norm\n\t\t\tcur = ml.norm( inpFF );\n\t\t\tml.fmaRepeat( cur, layer.mlpLn );\n\n\t\t\tcur = ml.mulMat( layer.mlp0.w, cur );\n\t\t\tml.addRepeatGelu( cur, layer.mlp0.b );\n\n\t\t\t// The mulMat() below creates a tensor for the output of this layer.\n\t\t\t// We have a special memory storage for these tensors, that's how they survive resets of per-layer arenas\n\t\t\tallocLayerOutput.resetArena();\n\t\t\tml.setAllocator( &allocLayerOutput );\n\n\t\t\t// projection\n\t\t\tcur = ml.mulMat( layer.mlp1.w, cur );\n\t\t\tml.addRepeat( cur, layer.mlp1.b );\n\t\t}\n\n\t\t// output from this layer\n\t\tml.addInPlace( cur, inpFF );\n\t\tinpL = cur;\n\t}\n\n\t// norm\n\tcur = ml.norm( inpL );\n\tml.fmaRepeat( cur, model.ln );\n\n\tcur = ml.mulMat( model.tokenEmbedding, cur );\n\n\t// logits -> probs\n\tml.softMax( cur );\n\n\tconst float* rsi = cur.fp32();\n\tprobs.assign( rsi, rsi + cur.countElements() );\n\tTracing::vector( \"probs\", probs );\n\treturn S_OK;\n}\n\nvoid* HybridContext::AllocSingle::allocate( size_t cb, size_t align )\n{\n\tif( !allocated )\n\t{\n\t\tallocated = true;\n\t\tif( cb <= capacity )\n\t\t{\n\t\t\tCpuCompute::dbgMarkUninitializedMemory( buffer.pointer(), capacity );\n\t\t\treturn buffer.pointer();\n\t\t}\n\t\telse\n\t\t{\n\t\t\tHRESULT hr = buffer.allocate( cb );\n\t\t\tif( SUCCEEDED( hr ) )\n\t\t\t{\n\t\t\t\tcapacity = cb;\n\t\t\t\tCpuCompute::dbgMarkUninitializedMemory( buffer.pointer(), capacity );\n\t\t\t\treturn buffer.pointer();\n\t\t\t}\n\t\t\tlogErrorHr( hr, u8\"HybridContext.AllocSingle.allocate\" );\n\t\t\tthrow hr;\n\t\t}\n\t}\n\telse\n\t{\n\t\tlogError( u8\"HybridContext.AllocSingle only supports 1 tensor\" );\n\t\tthrow E_UNEXPECTED;\n\t}\n}\n\nvoid HybridContext::AllocSingle::resetArena()\n{\n\tallocated = false;\n\tif( capacity > 0 )\n\t\tCpuCompute::dbgMarkFreedMemory( buffer.pointer(), capacity );\n}\n#endif"
  },
  {
    "path": "Whisper/Hybrid/HybridContext.h",
    "content": "#pragma once\n#include \"../Whisper/WhisperModel.h\"\n#include \"../CPU/MlContext.h\"\n#include \"../CPU/BufferAllocator.h\"\n#include \"KeyValueDownloader.h\"\n#include \"../CPU/KvTensors.h\"\n\n// This version of the hybrid context uses the new, custom-built kernels\nclass HybridContext\n{\n\tCpuCompute::MlContext ml;\n\tCpuCompute::VirtualAllocator allocCompute, allocComputeLayer;\n\t\n\tclass AllocSingle : public CpuCompute::iArenaAllocator\n\t{\n\t\tCpuCompute::LargeBuffer buffer;\n\t\tsize_t capacity = 0;\n\t\tbool allocated = false;\n\t\t// Inherited via iArenaAllocator\n\t\tvirtual void* allocate( size_t cb, size_t align ) override final;\n\n\tpublic:\n\t\tvirtual void resetArena() override final;\n\t};\n\tAllocSingle allocLayerOutput;\n\n\tconst CpuCompute::DecoderTensors& model;\n\tconst Whisper::WhisperModel& whisperModel;\n\tKeyValueDownloader kvCross;\n\tCpuCompute::KvTensors kv;\n\n\tclass SetAllocatorRaii;\n\npublic:\n\n\tHybridContext( const Whisper::WhisperModel& wm );\n\n\tHRESULT create();\n\n\tHRESULT downloadKeyValues( const DirectCompute::KeyValueBuffers& source )\n\t{\n\t\treturn kvCross.download( source );\n\t}\n\n\tstruct sDecParams\n\t{\n\t\tint n_threads;\n\t\tint M;\n\t};\n\n\tHRESULT decode( const int* tokens, const int n_tokens, const int n_past, const sDecParams& dp, std::vector<float>& probs_out );\n};"
  },
  {
    "path": "Whisper/Hybrid/KeyValueDownloader.cpp",
    "content": "#include \"stdafx.h\"\n#include \"KeyValueDownloader.h\"\n\nHRESULT KeyValueDownloader::create( const Whisper::sModelParams& mp )\n{\n\tconst uint32_t n_audio_ctx = mp.n_audio_ctx;\n\tconst uint32_t n_mem = mp.n_text_layer * mp.n_audio_ctx;\n\tconst uint32_t n_elements = mp.n_text_state * n_mem;\n\n\tCD3D11_BUFFER_DESC desc{ n_elements * 2, 0, D3D11_USAGE_STAGING, D3D11_CPU_ACCESS_READ };\n\tID3D11Device* dev = DirectCompute::device();\n\tCHECK( dev->CreateBuffer( &desc, nullptr, &keys ) );\n\tCHECK( dev->CreateBuffer( &desc, nullptr, &values ) );\n\n\tlength = n_elements;\n\treturn S_OK;\n}\n\nHRESULT KeyValueDownloader::download( const DirectCompute::KeyValueBuffers& source )\n{\n\tID3D11DeviceContext* ctx = DirectCompute::context();\n\tctx->CopyResource( keys, source.keys.getBuffer() );\n\tctx->CopyResource( values, source.values.getBuffer() );\n\treturn S_OK;\n}\n\nKeyValueDownloader::ReadMap::ReadMap( KeyValueDownloader& owner ) :\n\tlength( owner.length )\n{\n\tcheck( mappedKeys.map( owner.keys, true ) );\n\tcheck( mappedValues.map( owner.values, true ) );\n}"
  },
  {
    "path": "Whisper/Hybrid/KeyValueDownloader.h",
    "content": "#pragma once\n#include \"../Whisper/sModelParams.h\"\n#include \"../Whisper/KeyValueBuffers.h\"\n#include \"../D3D/MappedResource.h\"\n#include \"../CPU/Tensor.h\"\n\nclass KeyValueDownloader\n{\n\tCComPtr<ID3D11Buffer> keys, values;\n\tuint32_t length = 0;\n\n\tusing E = uint16_t;\n\tstatic constexpr DirectCompute::eDataType dataType = DirectCompute::eDataType::FP16;\n\npublic:\n\t// Create the staging resources to download kvCross tensors produced by the GPGPU encoder\n\tHRESULT create( const Whisper::sModelParams& mp );\n\n\t// Download these two tensors from VRAM to the staging buffers in system RAM\n\tHRESULT download( const DirectCompute::KeyValueBuffers& source );\n\n\tclass ReadMap\n\t{\n\t\tconst uint32_t length;\n\t\tDirectCompute::MappedResource mappedKeys, mappedValues;\n\n\tpublic:\n\t\tReadMap( KeyValueDownloader& owner );\n\t\t~ReadMap() = default;\n\t\tReadMap( const ReadMap& ) = delete;\n\n\t\t// A slice of model.memory_k tensor\n\t\tCpuCompute::Tensor keysView( uint32_t len, uint32_t off ) const\n\t\t{\n\t\t\tif( len + off <= length )\n\t\t\t{\n\t\t\t\tE* rsi = (E*)mappedKeys.data();\n\t\t\t\trsi += off;\n\t\t\t\treturn CpuCompute::Tensor::fromData( rsi, dataType, len );\n\t\t\t}\n\t\t\tthrow E_BOUNDS;\n\t\t}\n\n\t\t// A slice of model.memory_v tensor\n\t\tCpuCompute::Tensor valuesView( uint32_t len, uint32_t off ) const\n\t\t{\n\t\t\tif( len + off <= length )\n\t\t\t{\n\t\t\t\tE* rsi = (E*)mappedValues.data();\n\t\t\t\trsi += off;\n\t\t\t\treturn CpuCompute::Tensor::fromData( rsi, dataType, len );\n\t\t\t}\n\t\t\tthrow E_BOUNDS;\n\t\t}\n\t};\n\n\t// Map both staging buffers, return RAII object which unmaps when destroyed,\n\t// which can supply the data in the shape of CpuCompute::Tensor vector\n\tdecltype( auto ) map()\n\t{\n\t\treturn ReadMap( *this );\n\t}\n};"
  },
  {
    "path": "Whisper/Hybrid/Readme.txt",
    "content": "﻿The code in this folder is dropped by the linker’s dead code elimination optimization pass, unless you change BUILD_HYBRID_VERSION macro in stdafx.h"
  },
  {
    "path": "Whisper/MF/AudioBuffer.cpp",
    "content": "#include \"stdafx.h\"\n#include \"AudioBuffer.h\"\nusing namespace Whisper;\n\nvoid AudioBuffer::appendMono( const float* rsi, size_t countFloats )\n{\n\tmono.insert( mono.end(), rsi, rsi + countFloats );\n}\n\nvoid AudioBuffer::appendStereo( const float* rsi, size_t countFloats )\n{\n\tassert( 0 == ( countFloats % 2 ) );\n\tconst size_t countSamples = countFloats / 2;\n\n\tconst size_t oldLength = mono.size();\n\tassert( oldLength * 2 == stereo.size() );\n\tmono.resize( oldLength + countSamples );\n\tstereo.resize( ( oldLength + countSamples ) * 2 );\n\n\tconst float* const rsiEnd = rsi + countSamples * 2;\n\tconst float* const rsiEndAligned = rsiEnd - ( countSamples * 2 ) % 8;\n\n\tfloat* rdiStereo = &stereo[ oldLength * 2 ];\n\tfloat* rdiMono = &mono[ oldLength ];\n\n\tconst __m128 half = _mm_set1_ps( 0.5f );\n\tfor( ; rsi < rsiEndAligned; rsi += 8, rdiStereo += 8, rdiMono += 4 )\n\t{\n\t\t// Load 4 samples = 8 floats \n\t\t__m128 v0 = _mm_loadu_ps( rsi );\t// L0, R0, L1, R1\n\t\t__m128 v1 = _mm_loadu_ps( rsi + 4 );// L2, R2, L3, R3\n\n\t\t// Store into the stereo PCM vector\n\t\t_mm_storeu_ps( rdiStereo, v0 );\n\t\t_mm_storeu_ps( rdiStereo + 4, v1 );\n\n\t\t// Compute and store the average of these channels\n\t\t__m128 left = _mm_shuffle_ps( v0, v1, _MM_SHUFFLE( 2, 0, 2, 0 ) );\n\t\t__m128 right = _mm_shuffle_ps( v0, v1, _MM_SHUFFLE( 3, 1, 3, 1 ) );\n\t\t__m128 sum = _mm_add_ps( left, right );\n\t\tsum = _mm_mul_ps( sum, half );\n\t\t_mm_storeu_ps( rdiMono, sum );\n\t}\n\n#pragma loop (no_vector)\n\tfor( ; rsi < rsiEnd; rsi += 2, rdiStereo += 2, rdiMono++ )\n\t{\n\t\t__m128 vec = _mm_castpd_ps( _mm_load_sd( (const double*)rsi ) );\n\t\t_mm_store_sd( (double*)rdiStereo, _mm_castps_pd( vec ) );\n\n\t\tvec = _mm_add_ss( vec, _mm_movehdup_ps( vec ) );\n\t\tvec = _mm_mul_ss( vec, half );\n\t\t_mm_store_ss( rdiMono, vec );\n\t}\n}\n\nvoid AudioBuffer::appendDownmixedStereo( const float* rsi, size_t countFloats )\n{\n\tassert( 0 == ( countFloats % 2 ) );\n\tconst size_t countSamples = countFloats / 2;\n\n\tconst size_t oldLength = mono.size();\n\tmono.resize( oldLength + countSamples );\n\n\tconst float* const rsiEnd = rsi + countSamples * 2;\n\tconst float* const rsiEndAligned = rsiEnd - ( countSamples * 2 ) % 8;\n\n\tfloat* rdiMono = &mono[ oldLength ];\n\n\tconst __m128 half = _mm_set1_ps( 0.5f );\n\tfor( ; rsi < rsiEndAligned; rsi += 8, rdiMono += 4 )\n\t{\n\t\t// Load 4 samples = 8 floats \n\t\t__m128 v0 = _mm_loadu_ps( rsi );\t// L0, R0, L1, R1\n\t\t__m128 v1 = _mm_loadu_ps( rsi + 4 );// L2, R2, L3, R3\n\n\t\t// Compute and store the average of these channels\n\t\t__m128 left = _mm_shuffle_ps( v0, v1, _MM_SHUFFLE( 2, 0, 2, 0 ) );\n\t\t__m128 right = _mm_shuffle_ps( v0, v1, _MM_SHUFFLE( 3, 1, 3, 1 ) );\n\t\t__m128 sum = _mm_add_ps( left, right );\n\t\tsum = _mm_mul_ps( sum, half );\n\t\t_mm_storeu_ps( rdiMono, sum );\n\t}\n\n#pragma loop (no_vector)\n\tfor( ; rsi < rsiEnd; rsi += 2, rdiMono++ )\n\t{\n\t\t__m128 vec = _mm_castpd_ps( _mm_load_sd( (const double*)rsi ) );\n\t\tvec = _mm_add_ss( vec, _mm_movehdup_ps( vec ) );\n\t\tvec = _mm_mul_ss( vec, half );\n\t\t_mm_store_ss( rdiMono, vec );\n\t}\n}"
  },
  {
    "path": "Whisper/MF/AudioBuffer.h",
    "content": "#pragma once\n#include <vector>\n\nnamespace Whisper\n{\n\tstruct AudioBuffer\n\t{\n\t\tstd::vector<float> mono;\n\t\tstd::vector<float> stereo;\n\n\t\tvoid appendMono( const float* rsi, size_t countFloats );\n\t\tvoid appendDownmixedStereo( const float* rsi, size_t countFloats );\n\t\tvoid appendStereo( const float* rsi, size_t countFloats );\n\n\t\tusing pfnAppendSamples = void( AudioBuffer::* )( const float* rsi, size_t countFloats );\n\n\t\tinline static pfnAppendSamples appendSamplesFunc( bool sourceMono, bool wantStereo )\n\t\t{\n\t\t\tif( sourceMono )\n\t\t\t\treturn &AudioBuffer::appendMono;\n\t\t\telse if( !wantStereo )\n\t\t\t\treturn &AudioBuffer::appendDownmixedStereo;\n\t\t\telse\n\t\t\t\treturn &AudioBuffer::appendStereo;\n\t\t}\n\n\t\tvoid clear()\n\t\t{\n\t\t\tmono.clear();\n\t\t\tstereo.clear();\n\t\t}\n\n\t\tvoid swap( AudioBuffer& that )\n\t\t{\n\t\t\tmono.swap( that.mono );\n\t\t\tstereo.swap( that.stereo );\n\t\t}\n\n\t\tvoid resize( size_t len )\n\t\t{\n\t\t\tassert( len <= mono.size() );\n\t\t\tmono.resize( len );\n\t\t\tif( !stereo.empty() )\n\t\t\t\tstereo.resize( len * 2 );\n\t\t}\n\t};\n}"
  },
  {
    "path": "Whisper/MF/AudioCapture.cpp",
    "content": "#include \"stdafx.h\"\n#include <atlstr.h>\n#include <mfapi.h>\n#include <mfidl.h>\n#include <mfreadwrite.h>\n#include \"AudioCapture.h\"\n#include \"../API/iMediaFoundation.cl.h\"\n#include \"../ComLightLib/comLightServer.h\"\n#pragma comment(lib, \"Mf.lib\")\n\nnamespace\n{\n\tstruct Strings\n\t{\n\t\tCString displayName, endpoint;\n\t};\n\n\tHRESULT getAllocString( IMFActivate* activate, const GUID& id, CString& rdi )\n\t{\n\t\twchar_t* pointer = nullptr;\n\t\tUINT32 cchName;\n\t\tHRESULT hr = activate->GetAllocatedString( id, &pointer, &cchName );\n\t\tif( SUCCEEDED( hr ) )\n\t\t\trdi.SetString( pointer, cchName );\n\t\tCoTaskMemFree( pointer );\n\t\treturn hr;\n\t}\n\n\tHRESULT getInfo( IMFActivate* activate, Strings& rdi )\n\t{\n\t\tCHECK( getAllocString( activate, MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME, rdi.displayName ) );\n\t\tCHECK( getAllocString( activate, MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_AUDCAP_ENDPOINT_ID, rdi.endpoint ) );\n\t\treturn S_OK;\n\t}\n\n\tHRESULT __stdcall supplyDevices( Whisper::pfnFoundCaptureDevices pfn, void* pv, IMFActivate** ppDevices, UINT32 count )\n\t{\n\t\tif( ppDevices == nullptr || count == 0 )\n\t\t\treturn pfn( 0, nullptr, pv );\n\n\t\tstd::vector<Strings> strings;\n\t\tstrings.reserve( count );\n\n\t\tfor( UINT i = 0; i < count; i++ )\n\t\t{\n\t\t\tIMFActivate* const activate = ppDevices[ i ];\n\t\t\tif( nullptr == activate )\n\t\t\t\tcontinue;\n\t\t\tStrings info;\n\t\t\tHRESULT hr = getInfo( activate, info );\n\t\t\tif( FAILED( hr ) )\n\t\t\t\tcontinue;\n\n\t\t\tstrings.emplace_back( std::move( info ) );\n\t\t}\n\n\t\tconst size_t len = strings.size();\n\t\tif( 0 == len )\n\t\t\treturn pfn( 0, nullptr, pv );\n\n\t\tstd::vector<Whisper::sCaptureDevice> pointers;\n\t\tpointers.resize( len );\n\t\tfor( size_t i = 0; i < len; i++ )\n\t\t{\n\t\t\tconst auto& src = strings[ i ];\n\t\t\tauto& dest = pointers[ i ];\n\t\t\tdest.displayName = src.displayName;\n\t\t\tdest.endpoint = src.endpoint;\n\t\t}\n\t\treturn pfn( (int)len, pointers.data(), pv );\n\t}\n}\n\nHRESULT __stdcall Whisper::captureDeviceList( pfnFoundCaptureDevices pfn, void* pv )\n{\n\t// Create an attribute store to hold the search criteria.\n\tCComPtr<IMFAttributes> attrs;\n\tCHECK( MFCreateAttributes( &attrs, 1 ) );\n\t// Request audio capture devices\n\tCHECK( attrs->SetGUID( MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_AUDCAP_GUID ) );\n\n\t// Enumerate the devices\n\tIMFActivate** ppDevices = nullptr;\n\tUINT32 count = 0;\n\tCHECK( MFEnumDeviceSources( attrs, &ppDevices, &count ) );\n\n\t// Feed the data to the caller\n\tHRESULT hr = supplyDevices( pfn, pv, ppDevices, count );\n\n\t// Free the memory\n\tfor( DWORD i = 0; i < count; i++ )\n\t\tppDevices[ i ]->Release();\n\tCoTaskMemFree( ppDevices );\n\n\treturn hr;\n}\n\nnamespace\n{\n\tusing namespace Whisper;\n\n\tclass Capture : public ComLight::ObjectRoot<iAudioCapture>\n\t{\n\t\tCComPtr<IMFSourceReader> reader;\n\t\tCComPtr<iMediaFoundation> mediaFoundation;\n\t\tsCaptureParams captureParams;\n\n\t\tHRESULT COMLIGHTCALL getReader( IMFSourceReader** pp ) const noexcept override final\n\t\t{\n\t\t\tif( pp == nullptr )\n\t\t\t\treturn E_POINTER;\n\t\t\tCComPtr<IMFSourceReader> res = reader;\n\t\t\t*pp = res.Detach();;\n\t\t\treturn S_OK;\n\t\t}\n\t\tconst sCaptureParams& COMLIGHTCALL getParams() const noexcept override final\n\t\t{\n\t\t\treturn captureParams;\n\t\t}\n\tpublic:\n\t\tHRESULT open( iMediaFoundation* owner, const wchar_t* endpoint, const sCaptureParams& cp );\n\t};\n\n\tHRESULT Capture::open( iMediaFoundation* owner, const wchar_t* endpoint, const sCaptureParams& cp )\n\t{\n\t\t// Create an attribute store to hold the search criteria.\n\t\tCComPtr<IMFAttributes> attrs;\n\t\tCHECK( MFCreateAttributes( &attrs, 2 ) );\n\t\t// Request audio capture devices\n\t\tCHECK( attrs->SetGUID( MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_AUDCAP_GUID ) );\n\t\tCHECK( attrs->SetString( MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_AUDCAP_ENDPOINT_ID, endpoint ) );\n\n\t\tCComPtr<IMFMediaSource> source;\n\t\tHRESULT hr = MFCreateDeviceSource( attrs, &source );\n\t\tif( FAILED( hr ) )\n\t\t{\n\t\t\tlogErrorHr( hr, u8\"MFCreateDeviceSource\" );\n\t\t\treturn hr;\n\t\t}\n\n\t\t// TODO: implement IMFSourceReaderCallback, pass into MF_SOURCE_READER_ASYNC_CALLBACK attribute\n\t\t// This is to support cancellation\n\t\thr = MFCreateSourceReaderFromMediaSource( source, nullptr, &reader );\n\t\tif( FAILED( hr ) )\n\t\t{\n\t\t\tlogErrorHr( hr, u8\"MFCreateSourceReaderFromMediaSource\" );\n\t\t\treturn hr;\n\t\t}\n\n\t\tcaptureParams = cp;\n\t\tmediaFoundation = owner;\n\t\treturn S_OK;\n\t}\n}\n\nHRESULT __stdcall Whisper::captureOpen( iMediaFoundation* owner, const wchar_t* endpoint, const sCaptureParams& captureParams, iAudioCapture** pp ) noexcept\n{\n\tif( nullptr == endpoint || nullptr == pp )\n\t\treturn E_POINTER;\n\n\tComLight::CComPtr<ComLight::Object<Capture>> res;\n\tCHECK( ComLight::Object<Capture>::create( res ) );\n\tCHECK( res->open( owner, endpoint, captureParams ) );\n\n\tres.detach( pp );\n\treturn S_OK;\n}"
  },
  {
    "path": "Whisper/MF/AudioCapture.h",
    "content": "#pragma once\n#include \"../API/MfStructs.h\"\n\nnamespace Whisper\n{\n\tstruct iAudioCapture;\n\tstruct iMediaFoundation;\n\n\tHRESULT __stdcall captureDeviceList( pfnFoundCaptureDevices pfn, void* pv );\n\n\tHRESULT __stdcall captureOpen( iMediaFoundation* owner, const wchar_t* endpoint, const sCaptureParams& captureParams, iAudioCapture** pp ) noexcept;\n}"
  },
  {
    "path": "Whisper/MF/MediaFoundation.cpp",
    "content": "#include \"stdafx.h\"\n#include \"../API/iMediaFoundation.cl.h\"\n#include \"mfStartup.h\"\n#include \"../ComLightLib/comLightServer.h\"\n#include \"loadAudioFile.h\"\n#include <mfidl.h>\n#include <mfreadwrite.h>\n#include \"mfUtils.h\"\n#include \"AudioCapture.h\"\n#include <mfapi.h>\n#include <shlwapi.h>\n\nnamespace Whisper\n{\n\tclass AudioReader : public ComLight::ObjectRoot<iAudioReader>\n\t{\n\t\tCComPtr<IMFSourceReader> reader;\n\t\tbool wantStereo;\n\t\tCComPtr<iMediaFoundation> mediaFoundation;\n\t\tmutable int64_t preciseSamplesCount = 0;\n\n\t\tHRESULT COMLIGHTCALL getReader( IMFSourceReader** pp ) const noexcept override final\n\t\t{\n\t\t\tif( pp == nullptr )\n\t\t\t\treturn E_POINTER;\n\t\t\tCComPtr<IMFSourceReader> res = reader;\n\t\t\t*pp = res.Detach();;\n\t\t\treturn S_OK;\n\t\t}\n\t\tHRESULT COMLIGHTCALL requestedStereo() const noexcept override final\n\t\t{\n\t\t\treturn wantStereo ? S_OK : S_FALSE;\n\t\t}\n\t\tHRESULT COMLIGHTCALL getDuration( int64_t& rdi ) const noexcept override final\n\t\t{\n\t\t\tif( reader )\n\t\t\t{\n\t\t\t\tif( 0 == preciseSamplesCount )\n\t\t\t\t\treturn getStreamDuration( reader, rdi );\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\trdi = MFllMulDiv( preciseSamplesCount, 10'000'000, SAMPLE_RATE, 0 );\n\t\t\t\t\treturn S_OK;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn OLE_E_BLANK;\n\t\t}\n\tpublic:\n\n\t\tHRESULT open( iMediaFoundation* owner, LPCTSTR path, bool stereo )\n\t\t{\n\t\t\tHRESULT hr = MFCreateSourceReaderFromURL( path, nullptr, &reader );\n\t\t\tif( FAILED( hr ) )\n\t\t\t{\n\t\t\t\tlogError16( L\"Unable to decode audio file \\\"%s\\\", MFCreateSourceReaderFromURL failed\", path );\n\t\t\t\treturn hr;\n\t\t\t}\n\t\t\twantStereo = stereo;\n\t\t\tmediaFoundation = owner;\n\t\t\tlogDebug16( L\"Created source reader from the file \\\"%s\\\"\", path );\n\t\t\treturn S_OK;\n\t\t}\n\n\t\tHRESULT open( iMediaFoundation* owner, IMFByteStream* stream, bool stereo )\n\t\t{\n\t\t\tHRESULT hr = MFCreateSourceReaderFromByteStream( stream, nullptr, &reader );\n\t\t\tif( FAILED( hr ) )\n\t\t\t{\n\t\t\t\tlogErrorHr( hr, u8\"MFCreateSourceReaderFromByteStream failed\" );\n\t\t\t\treturn hr;\n\t\t\t}\n\t\t\twantStereo = stereo;\n\t\t\tmediaFoundation = owner;\n\t\t\tlogDebug( u8\"Created source reader from the byte stream\" );\n\t\t\treturn S_OK;\n\t\t}\n\t\tvoid setPreciseSamplesCount( int64_t count ) const\n\t\t{\n\t\t\tpreciseSamplesCount = count;\n\t\t}\n\t};\n\n\tvoid setPreciseSamplesCount( const iAudioReader* ar, int64_t count )\n\t{\n\t\tconst AudioReader* r = static_cast<const AudioReader*>( ar );\n\t\tr->setPreciseSamplesCount( count );\n\t}\n\n\tclass MediaFoundation : public ComLight::ObjectRoot<iMediaFoundation>\n\t{\n\t\tMfStartupRaii raii;\n\t\tDWORD tid = ~(DWORD)0;\n\n\t\tHRESULT COMLIGHTCALL loadAudioFile( LPCTSTR path, bool stereo, iAudioBuffer** pp ) const noexcept override final\n\t\t{\n\t\t\treturn Whisper::loadAudioFile( path, stereo, pp );\n\t\t}\n\t\tHRESULT COMLIGHTCALL openAudioFile( LPCTSTR path, bool stereo, iAudioReader** pp ) noexcept override final\n\t\t{\n\t\t\tif( nullptr == path || nullptr == pp )\n\t\t\t\treturn E_POINTER;\n\n\t\t\tComLight::CComPtr<ComLight::Object<AudioReader>> res;\n\t\t\tCHECK( ComLight::Object<AudioReader>::create( res ) );\n\t\t\tCHECK( res->open( this, path, stereo ) );\n\n\t\t\tres.detach( pp );\n\t\t\treturn S_OK;\n\t\t}\n\t\tHRESULT COMLIGHTCALL loadAudioFileData( const void* data, uint64_t size, bool stereo, iAudioReader** pp ) noexcept override final\n\t\t{\n\t\t\tif( nullptr == data || nullptr == pp )\n\t\t\t\treturn E_POINTER;\n\t\t\tif( 0 != ( size >> 32 ) )\n\t\t\t\treturn DISP_E_OVERFLOW;\t// SHCreateMemStream is limited to 4GB, it seems\n\n\t\t\tCComPtr<IStream> comStream;\n\t\t\t// Microsoft neglected to document their API, but Wine returns a new stream with reference counter = 1\n\t\t\t// See shstream_create() function there https://source.winehq.org/source/dlls/shcore/main.c#0832\n\t\t\t// That's why we need the Attach(), as opposed to an assignment\n\t\t\tcomStream.Attach( SHCreateMemStream( (const BYTE*)data, (UINT)size ) );\n\t\t\tif( !comStream )\n\t\t\t{\n\t\t\t\tlogError( u8\"SHCreateMemStream failed\" );\n\t\t\t\treturn E_FAIL;\n\t\t\t}\n\n\t\t\tCComPtr<IMFByteStream> mfStream;\n\t\t\tCHECK( MFCreateMFByteStreamOnStream( comStream, &mfStream ) );\n\n\t\t\tComLight::CComPtr<ComLight::Object<AudioReader>> res;\n\t\t\tCHECK( ComLight::Object<AudioReader>::create( res ) );\n\n\t\t\tCHECK( res->open( this, mfStream, stereo ) );\n\n\t\t\tres.detach( pp );\n\t\t\treturn S_OK;\n\t\t}\n\t\tHRESULT COMLIGHTCALL listCaptureDevices( pfnFoundCaptureDevices pfn, void* pv ) noexcept override final\n\t\t{\n\t\t\treturn captureDeviceList( pfn, pv );\n\t\t}\n\t\tHRESULT COMLIGHTCALL openCaptureDevice( LPCTSTR endpoint, const sCaptureParams& captureParams, iAudioCapture** pp ) noexcept override final\n\t\t{\n\t\t\treturn captureOpen( this, endpoint, captureParams, pp );\n\t\t}\n\tprotected:\n\n\t\tHRESULT FinalConstruct()\n\t\t{\n\t\t\tCHECK( raii.startup() );\n\t\t\ttid = GetCurrentThreadId();\n\t\t\treturn S_OK;\n\t\t}\n\n\tpublic:\n\n\t\t~MediaFoundation() override\n\t\t{\n\t\t\tassert( tid == GetCurrentThreadId() );\n\t\t}\n\t};\n}\n\nHRESULT COMLIGHTCALL Whisper::initMediaFoundation( iMediaFoundation** pp )\n{\n\tif( nullptr == pp )\n\t\treturn E_POINTER;\n\n\tComLight::CComPtr<ComLight::Object<MediaFoundation>> obj;\n\tCHECK( ComLight::Object<MediaFoundation>::create( obj ) );\n\tobj.detach( pp );\n\treturn S_OK;\n}"
  },
  {
    "path": "Whisper/MF/PcmReader.cpp",
    "content": "﻿#include \"stdafx.h\"\n#include \"PcmReader.h\"\n#include <mfapi.h>\n#include <Mferror.h>\n#include \"mfUtils.h\"\n\nnamespace Whisper\n{\n\t__interface iSampleHandler\n\t{\n\t\tvoid copyChunk( PcmMonoChunk* pMono, const AudioBuffer& rsi, size_t sourceOffset, PcmStereoChunk* pStereo ) const;\n\t\tvoid moveBufferData( AudioBuffer& rdi, size_t amount ) const;\n\t\tvoid appendPcm( AudioBuffer& rdi, const float* rsi, size_t countFloats ) const;\n\t\tvoid copyChunk( PcmMonoChunk* pMono, const AudioBuffer& rsi, size_t sourceOffset, size_t samples, PcmStereoChunk* pStereo ) const;\n\t\tuint32_t readerChannelsCount() const;\n\t};\n}\n\nnamespace\n{\n\tusing namespace Whisper;\n\n\t__forceinline void copyMono( PcmMonoChunk* rdi, const AudioBuffer& rsi, size_t sourceOffset, size_t samples )\n\t{\n\t\tassert( sourceOffset + samples <= rsi.mono.size() );\n\t\tmemcpy( rdi->mono.data(), &rsi.mono[ sourceOffset ], samples * 4 );\n\t\tif( samples < FFT_STEP )\n\t\t\tmemset( rdi->mono.data() + samples, 0, ( FFT_STEP - samples ) * 4 );\n\t}\n\n\t__forceinline void copyStereo( PcmStereoChunk* rdi, const AudioBuffer& rsi, size_t sourceOffset, size_t samples )\n\t{\n\t\tmemcpy( rdi->stereo.data(), &rsi.stereo[ sourceOffset * 2 ], samples * 8 );\n\t\tif( samples < FFT_STEP )\n\t\t\tmemset( rdi->stereo.data() + samples * 2, 0, ( FFT_STEP - samples ) * 8 );\n\t}\n\n\tstruct HandlerMono : iSampleHandler\n\t{\n\t\tvoid appendPcm( AudioBuffer& rdi, const float* rsi, size_t countFloats ) const override\n\t\t{\n\t\t\trdi.appendMono( rsi, countFloats );\n\t\t}\n\t\tvoid copyChunk( PcmMonoChunk* pMono, const AudioBuffer& rsi, size_t sourceOffset, PcmStereoChunk* pStereo ) const override final\n\t\t{\n\t\t\tcopyMono( pMono, rsi, sourceOffset, FFT_STEP );\n\t\t}\n\t\tvoid copyChunk( PcmMonoChunk* pMono, const AudioBuffer& rsi, size_t sourceOffset, size_t samples, PcmStereoChunk* pStereo ) const override final\n\t\t{\n\t\t\tcopyMono( pMono, rsi, sourceOffset, samples );\n\t\t}\n\t\tvoid moveBufferData( AudioBuffer& rdi, size_t amount ) const override final\n\t\t{\n\t\t\tconst size_t len = rdi.mono.size();\n\t\t\tassert( amount <= len );\n\t\t\tif( amount < len )\n\t\t\t{\n\t\t\t\tconst size_t block = len - amount;\n\t\t\t\tmemmove( rdi.mono.data(), rdi.mono.data() + amount, block * 4 );\n\t\t\t\trdi.mono.resize( block );\n\t\t\t}\n\t\t\telse\n\t\t\t\trdi.mono.clear();\n\t\t}\n\t\tuint32_t readerChannelsCount() const override { return 1; }\n\t};\n\tstruct HandlerDownmixedStereo : HandlerMono\n\t{\n\t\tvoid appendPcm( AudioBuffer& rdi, const float* rsi, size_t countFloats ) const override final\n\t\t{\n\t\t\trdi.appendDownmixedStereo( rsi, countFloats );\n\t\t}\n\t\tuint32_t readerChannelsCount() const override final { return 2; }\n\t};\n\tstruct HandlerStereo : iSampleHandler\n\t{\n\t\tvoid appendPcm( AudioBuffer& rdi, const float* rsi, size_t countFloats ) const override final\n\t\t{\n\t\t\trdi.appendStereo( rsi, countFloats );\n\t\t}\n\t\tvoid copyChunk( PcmMonoChunk* pMono, const AudioBuffer& rsi, size_t sourceOffset, PcmStereoChunk* pStereo ) const override final\n\t\t{\n\t\t\tcopyMono( pMono, rsi, sourceOffset, FFT_STEP );\n\t\t\tcopyStereo( pStereo, rsi, sourceOffset, FFT_STEP );\n\t\t}\n\t\tvoid copyChunk( PcmMonoChunk* pMono, const AudioBuffer& rsi, size_t sourceOffset, size_t samples, PcmStereoChunk* pStereo ) const override final\n\t\t{\n\t\t\tcopyMono( pMono, rsi, sourceOffset, samples );\n\t\t\tcopyStereo( pStereo, rsi, sourceOffset, samples );\n\t\t}\n\t\tvoid moveBufferData( AudioBuffer& rdi, size_t amount ) const override final\n\t\t{\n\t\t\tconst size_t len = rdi.mono.size();\n\t\t\tassert( amount <= len );\n\t\t\tif( amount < len )\n\t\t\t{\n\t\t\t\tconst size_t block = len - amount;\n\t\t\t\tmemmove( rdi.mono.data(), rdi.mono.data() + amount, block * 4 );\n\t\t\t\trdi.mono.resize( block );\n\t\t\t\tmemmove( rdi.stereo.data(), rdi.stereo.data() + amount * 2, block * 8 );\n\t\t\t\trdi.stereo.resize( block * 2 );\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\trdi.mono.clear();\n\t\t\t\trdi.stereo.clear();\n\t\t\t}\n\t\t}\n\t\tuint32_t readerChannelsCount() const override final { return 2; }\n\t};\n\tstatic const HandlerMono s_mono;\n\tstatic const HandlerDownmixedStereo s_downmix;\n\tstatic const HandlerStereo s_stereo;\n\n\t__forceinline __m128i load( const GUID& guid )\n\t{\n\t\treturn _mm_loadu_si128( ( const __m128i* )( &guid ) );\n\t}\n\n\t// Find audio decoder MFT, query MF_MT_SUBTYPE attribute of the current input media type of that MFT\n\tHRESULT getDecoderInputSubtype( IMFSourceReader* reader, __m128i& rdi )\n\t{\n\t\tstore16( &rdi, _mm_setzero_si128() );\n\n\t\tCComPtr<IMFSourceReaderEx> readerEx;\n\t\tCHECK( reader->QueryInterface( &readerEx ) );\n\t\tconstexpr uint32_t stream = MF_SOURCE_READER_FIRST_AUDIO_STREAM;\n\t\tconst __m128i decGuid = load( MFT_CATEGORY_AUDIO_DECODER );\n\t\talignas( 16 ) GUID category;\n\t\tfor( DWORD i = 0; true; i++ )\n\t\t{\n\t\t\tCComPtr<IMFTransform> mft;\n\t\t\tHRESULT hr = readerEx->GetTransformForStream( stream, i, &category, &mft );\n\t\t\tif( hr == MF_E_INVALIDINDEX )\n\t\t\t{\n\t\t\t\t// This happens for *.wav input files\n\t\t\t\t// They don't have any MFT_CATEGORY_AUDIO_DECODER MFTs in the source reader, and it's not an error\n\t\t\t\treturn S_FALSE;\n\t\t\t}\n\t\t\tif( FAILED( hr ) )\n\t\t\t\treturn hr;\n\t\t\tconst __m128i cat = _mm_load_si128( ( const __m128i* ) & category );\n\t\t\tif( !vectorEqual( decGuid, cat ) )\n\t\t\t\tcontinue;\n\n\t\t\tCComPtr<IMFMediaType> mt;\n\t\t\tCHECK( mft->GetInputCurrentType( 0, &mt ) );\n\t\t\tCHECK( mt->GetGUID( MF_MT_SUBTYPE, (GUID*)&rdi ) );\n\t\t\treturn S_OK;\n\t\t}\n\t}\n\n\t// S_OK when the reader has an MP3 decoder for the first audio stream, S_FALSE otherwise\n\tHRESULT isMp3Decoder( IMFSourceReader* reader )\n\t{\n\t\t__m128i subtype;\n\t\tCHECK( getDecoderInputSubtype( reader, subtype ) );\n\t\tconst bool res = vectorEqual( subtype, load( MFAudioFormat_MP3 ) );\n\t\treturn res ? S_OK : S_FALSE;\n\t}\n\n\t// Workaround for a Microsoft's bug in Media Foundation MP3 decoder: https://github.com/Const-me/Whisper/issues/4\n\t// Media Foundation is reporting incorrect media duration = 12.54. Windows Media Player does the same.\n\t// Winamp and Media Player Classic are reporting 12:35, VLC reports 12:36.\n\tHRESULT getPreciseDuration( IMFSourceReader* reader, size_t& length, bool mono, const iAudioReader* iar )\n\t{\n\t\tsize_t samples = 0;\n\n\t\t// Decode the complete stream, counting samples\n\t\twhile( true )\n\t\t{\n\t\t\tDWORD dwFlags = 0;\n\t\t\tCComPtr<IMFSample> sample;\n\n\t\t\t// Read the next sample\n\t\t\tHRESULT hr = reader->ReadSample( (DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM, 0, nullptr, &dwFlags, nullptr, &sample );\n\t\t\tif( FAILED( hr ) )\n\t\t\t{\n\t\t\t\tlogErrorHr( hr, u8\"IMFSourceReader.ReadSample\" );\n\t\t\t\treturn hr;\n\t\t\t}\n\n\t\t\tif( dwFlags & MF_SOURCE_READERF_CURRENTMEDIATYPECHANGED )\n\t\t\t{\n\t\t\t\t// logError( u8\"Media type changes ain’t supported by the library.\" );\n\t\t\t\t// return E_UNEXPECTED;\n\n\t\t\t\t// This happens for some video files at the very start of the reading, with Dolby AC3 audio track.\n\t\t\t\t// Instead of failing the transcribe process, verify the important attributes (FP32 samples, sample rate, count of channels) haven’t changed.\n\t\t\t\tCHECK( validateCurrentMediaType( reader, mono ? 1 : 2 ) );\n\t\t\t}\n\n\t\t\tif( dwFlags & MF_SOURCE_READERF_ENDOFSTREAM )\n\t\t\t\tbreak;\n\n\t\t\tif( !sample )\n\t\t\t{\n\t\t\t\t// printf( \"No sample\\n\" );\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Get a pointer to the audio data in the sample.\n\t\t\tCComPtr<IMFMediaBuffer> buffer;\n\t\t\thr = sample->ConvertToContiguousBuffer( &buffer );\n\t\t\tif( FAILED( hr ) )\n\t\t\t\treturn hr;\n\n\t\t\tconst float* pAudioData = nullptr;\n\t\t\tDWORD cbBuffer;\n\t\t\thr = buffer->Lock( (BYTE**)&pAudioData, nullptr, &cbBuffer );\n\t\t\tif( FAILED( hr ) )\n\t\t\t\treturn hr;\n\n\t\t\tassert( 0 == ( cbBuffer % sizeof( float ) ) );\n\t\t\tconst size_t countFloats = cbBuffer / sizeof( float );\n\t\t\tif( mono )\n\t\t\t\tsamples += countFloats;\n\t\t\telse\n\t\t\t{\n\t\t\t\tassert( 0 == countFloats % 2 );\n\t\t\t\tsamples += countFloats / 2;\n\t\t\t}\n\n\t\t\t// Unlock the buffer\n\t\t\thr = buffer->Unlock();\n\t\t\tif( FAILED( hr ) )\n\t\t\t\treturn hr;\n\t\t}\n\n\t\t// Rewind the stream to beginning\n\t\tPROPVARIANT pv;\n\t\tPropVariantInit( &pv );\n\t\tpv.vt = VT_I8;\n\t\tpv.hVal.QuadPart = 0;\n\t\tCHECK( reader->SetCurrentPosition( GUID_NULL, pv ) );\n\n\t\t// Make the output value\n\t\tlength = samples / FFT_STEP;\n\n\t\t// Store the actual samples count in the reader\n\t\t// This way the iAudioReader.getDuration() API returns correct value to the user\n\t\tsetPreciseSamplesCount( iar, samples );\n\n\t\treturn S_OK;\n\t}\n\n\tHRESULT getDuration( IMFSourceReader* reader, size_t& length, bool mono, const iAudioReader* iar )\n\t{\n\t\tHRESULT hr = isMp3Decoder( reader );\n\t\tif( SUCCEEDED( hr ) )\n\t\t{\n\t\t\tif( S_OK == hr )\n\t\t\t{\n\t\t\t\treturn getPreciseDuration( reader, length, mono, iar );\n\t\t\t}\n\t\t}\n\t\telse\n\t\t\tlogWarningHr( hr, u8\"isMp3Decoder\" );\n\n\t\t// Find out the length\n\t\tint64_t durationTicks;\n\t\tCHECK( getStreamDuration( reader, durationTicks ) );\n\n\t\t// Convert length to chunks\n\t\t// Seconds = Ticks / 10^7\n\t\t// Samples = Seconds * SAMPLE_RATE = Ticks * SAMPLE_RATE / 10^7\n\t\t// Chunks = Samples / FFT_STEP = Ticks * SAMPLE_RATE / ( FFT_STEP * 10^7 ), and we want that integer rounded down\n\t\tconstexpr __int64 mul = SAMPLE_RATE;\n\t\tconstexpr __int64 div = (__int64)FFT_STEP * 10'000'000;\n\t\tlength = (size_t)MFllMulDiv( durationTicks, mul, div, 0 );\n\t\treturn S_OK;\n\t}\n}\n\nPcmReader::PcmReader( const iAudioReader* iar )\n{\n\tif( nullptr == iar )\n\t\tthrow E_POINTER;\n\n\tcheck( iar->getReader( &reader ) );\n\tconst bool stereo = iar->requestedStereo() == S_OK;\n\n\t// Set up media type, and figure out sample handler\n\tcheck( reader->SetStreamSelection( MF_SOURCE_READER_ALL_STREAMS, FALSE ) );\n\tcheck( reader->SetStreamSelection( MF_SOURCE_READER_FIRST_AUDIO_STREAM, TRUE ) );\n\n\tCComPtr<IMFMediaType> mtNative;\n\tcheck( reader->GetNativeMediaType( MF_SOURCE_READER_FIRST_AUDIO_STREAM, MF_SOURCE_READER_CURRENT_TYPE_INDEX, &mtNative ) );\n\tUINT32 numChannels;\n\tcheck( mtNative->GetUINT32( MF_MT_AUDIO_NUM_CHANNELS, &numChannels ) );\n\n\tconst bool sourceMono = numChannels < 2;\n\tif( sourceMono )\n\t\tsampleHandler = &s_mono;\n\telse if( !stereo )\n\t\tsampleHandler = &s_downmix;\n\telse\n\t{\n\t\tsampleHandler = &s_stereo;\n\t\tm_stereoOutput = true;\n\t}\n\n\tCComPtr<IMFMediaType> mt;\n\tcheck( createMediaType( !sourceMono, &mt ) );\n\tcheck( reader->SetCurrentMediaType( MF_SOURCE_READER_FIRST_AUDIO_STREAM, nullptr, mt ) );\n\n\t// Find out the length.\n\t// Sadly, broken Microsoft's MP3 decoder MFT made this much harder than necessary:\n\t// https://github.com/Const-me/Whisper/issues/4\n\tcheck( getDuration( reader, m_length, sourceMono, iar ) );\n}\n\nHRESULT PcmReader::readNextSample()\n{\n\tconst size_t off = bufferReadOffset;\n\tconst size_t availableSamples = pcm.mono.size() - off;\n\n\t// If needed, move the remaining PCM data to the start of these vectors\n\tif( availableSamples > 0 )\n\t{\n\t\tif( 0 != off )\n\t\t\tsampleHandler->moveBufferData( pcm, off );\n\t}\n\telse\n\t\tpcm.clear();\n\tbufferReadOffset = 0;\n\n\twhile( true )\n\t{\n\t\tDWORD dwFlags = 0;\n\t\tCComPtr<IMFSample> sample;\n\n\t\t// Read the next sample\n\t\tHRESULT hr = reader->ReadSample( (DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM, 0, nullptr, &dwFlags, nullptr, &sample );\n\t\tif( FAILED( hr ) )\n\t\t{\n\t\t\tlogErrorHr( hr, u8\"IMFSourceReader.ReadSample\" );\n\t\t\treturn hr;\n\t\t}\n\n\t\tif( dwFlags & MF_SOURCE_READERF_CURRENTMEDIATYPECHANGED )\n\t\t{\n\t\t\t// logError( u8\"Media type changes ain’t supported by the library.\" );\n\t\t\t// return E_UNEXPECTED;\n\n\t\t\t// This happens for some video files at the very start of the reading, with Dolby AC3 audio track.\n\t\t\t// Instead of failing the transcribe process, verify the important attributes (FP32 samples, sample rate, count of channels) haven’t changed.\n\t\t\tCHECK( validateCurrentMediaType( reader, sampleHandler->readerChannelsCount() ) );\n\t\t}\n\n\t\tif( dwFlags & MF_SOURCE_READERF_ENDOFSTREAM )\n\t\t\treturn E_EOF;\n\n\t\tif( !sample )\n\t\t{\n\t\t\t// printf( \"No sample\\n\" );\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Get a pointer to the audio data in the sample.\n\t\tCComPtr<IMFMediaBuffer> buffer;\n\t\thr = sample->ConvertToContiguousBuffer( &buffer );\n\t\tif( FAILED( hr ) )\n\t\t\treturn hr;\n\n\t\tconst float* pAudioData = nullptr;\n\t\tDWORD cbBuffer;\n\t\thr = buffer->Lock( (BYTE**)&pAudioData, nullptr, &cbBuffer );\n\t\tif( FAILED( hr ) )\n\t\t\treturn hr;\n\n\t\ttry\n\t\t{\n\t\t\tassert( 0 == ( cbBuffer % sizeof( float ) ) );\n\t\t\tconst size_t countFloats = cbBuffer / sizeof( float );\n\t\t\tsampleHandler->appendPcm( pcm, pAudioData, countFloats );\n\t\t}\n\t\tcatch( const std::bad_alloc& )\n\t\t{\n\t\t\tbuffer->Unlock();\n\t\t\treturn E_OUTOFMEMORY;\n\t\t}\n\n\t\t// Unlock the buffer\n\t\thr = buffer->Unlock();\n\t\tif( FAILED( hr ) )\n\t\t\treturn hr;\n\n\t\treturn S_OK;\n\t}\n}\n\nHRESULT PcmReader::readChunk( PcmMonoChunk& mono, PcmStereoChunk* stereo )\n{\n\twhile( true )\n\t{\n\t\tconst size_t off = bufferReadOffset;\n\t\tconst size_t availableSamples = pcm.mono.size() - off;\n\t\tif( availableSamples >= FFT_STEP )\n\t\t{\n\t\t\t// We have enough data in the buffer\n\t\t\tsampleHandler->copyChunk( &mono, pcm, off, stereo );\n\t\t\tbufferReadOffset = off + FFT_STEP;\n\t\t\treturn S_OK;\n\t\t}\n\n\t\tif( !m_readerEndOfFile )\n\t\t{\n\t\t\t// We don't have enough data, but the stream has not ended yet, can load moar samples from the reader\n\t\t\tHRESULT hr = readNextSample();\n\t\t\tif( SUCCEEDED( hr ) )\n\t\t\t\tcontinue;\n\t\t\tif( hr != E_EOF )\n\t\t\t\treturn hr;\n\t\t\tm_readerEndOfFile = true;\n\t\t}\n\n\t\tif( availableSamples > 0 )\n\t\t{\n\t\t\t// We have reached the end of stream of the reader, but the buffer still has a few samples.\n\t\t\t// Return the final incomplete chunk padded with zeros\n\t\t\tsampleHandler->copyChunk( &mono, pcm, off, availableSamples, stereo );\n\t\t\tbufferReadOffset = off + availableSamples;\n\t\t\treturn S_OK;\n\t\t}\n\n\t\treturn E_EOF;\n\t}\n}"
  },
  {
    "path": "Whisper/MF/PcmReader.h",
    "content": "#pragma once\n#include \"../Whisper/audioConstants.h\"\n#include <mfidl.h>\n#include <mfreadwrite.h>\n#include \"AudioBuffer.h\"\n#include \"../API/iMediaFoundation.cl.h\"\n\nnamespace Whisper\n{\n\t// PCM buffer with 10 milliseconds of single-channel audio\n\tstruct PcmMonoChunk\n\t{\n\t\tstd::array<float, FFT_STEP> mono;\n\t};\n\t// PCM buffer with 10 milliseconds of interleaved stereo\n\tstruct PcmStereoChunk\n\t{\n\t\tstd::array<float, FFT_STEP * 2> stereo;\n\t};\n\n\t__interface iSampleHandler;\n\n\tconstexpr HRESULT E_EOF = HRESULT_FROM_WIN32( ERROR_HANDLE_EOF );\n\n\t// Utility class which reads chunks of FFT_STEP FP32 PCM samples from the MF source reader\n\t// The class always delivers mono chunks, and can optionally deliver stereo in a separate buffer.\n\tclass PcmReader\n\t{\n\t\t// A small intermediate buffer with PCM data for complete media foundation samples\n\t\tAudioBuffer pcm;\n\t\t// Index of the first unconsumed sample in the pcm buffer\n\t\tsize_t bufferReadOffset = 0;\n\t\t// Utility object to abstract away mono versus stereo shenanigans\n\t\tconst iSampleHandler* sampleHandler;\n\t\t// The underlying MF source reader which delivers audio data\n\t\tCComPtr<IMFSourceReader> reader;\n\t\t// True after we consumed all available media samples from the reader\n\t\tbool m_readerEndOfFile = false;\n\t\t// True if this object delivers stereo samples\n\t\tbool m_stereoOutput = false;\n\t\t// The count of chunks we expect to get from the reader\n\t\tsize_t m_length = 0;\n\t\t// Read next sample from the reader, store in the PCM buffer in this class\n\t\tHRESULT readNextSample();\n\n\tpublic:\n\n\t\tPcmReader( const iAudioReader* reader );\n\n\t\t// Count of chunks in the MEL spectrogram.\n\t\t// The PCM audio is generally slightly longer than that, due to the incomplete last chunk.\n\t\tsize_t getLength() const noexcept\n\t\t{\n\t\t\treturn m_length;\n\t\t}\n\n\t\t// True when the stereo flag passed to constructor, and the audio stream actually has 2 or more audio channels\n\t\tbool outputsStereo() const { return m_stereoOutput; }\n\n\t\t// Load another 10ms chunk from the stream\n\t\t// For the last chunk in the stream, the output buffers are padded with zeros\n\t\tHRESULT readChunk( PcmMonoChunk& mono, PcmStereoChunk* stereo );\n\t};\n}"
  },
  {
    "path": "Whisper/MF/loadAudioFile.cpp",
    "content": "﻿#include \"stdafx.h\"\n#include \"../ComLightLib/comLightServer.h\"\n#include \"loadAudioFile.h\"\n#include \"mfUtils.h\"\n#include \"AudioBuffer.h\"\n#include <mfidl.h>\n#include <mfreadwrite.h>\n#include <mfapi.h>\n#pragma comment(lib, \"Mfreadwrite.lib\")\n#pragma comment(lib, \"mfuuid.lib\")\n\nnamespace Whisper\n{\n\tclass MediaFileBuffer : public ComLight::ObjectRoot<iAudioBuffer>\n\t{\n\t\tAudioBuffer pcm;\n\t\tuint32_t channels = 0;\n\n\t\tuint32_t COMLIGHTCALL countSamples() const noexcept override final\n\t\t{\n\t\t\treturn (uint32_t)( pcm.mono.size() );\n\t\t}\n\t\tconst float* COMLIGHTCALL getPcmMono() const noexcept override final\n\t\t{\n\t\t\tif( !pcm.mono.empty() )\n\t\t\t\treturn pcm.mono.data();\n\t\t\treturn nullptr;\n\t\t}\n\t\tconst float* COMLIGHTCALL getPcmStereo() const noexcept override final\n\t\t{\n\t\t\tif( !pcm.stereo.empty() )\n\t\t\t\treturn pcm.stereo.data();\n\t\t\treturn nullptr;\n\t\t}\n\t\tHRESULT COMLIGHTCALL getTime( int64_t& rdi ) const noexcept override final\n\t\t{\n\t\t\trdi = 0;\n\t\t\treturn S_OK;\n\t\t}\n\tpublic:\n\t\tHRESULT load( LPCTSTR path, bool stereo );\n\t};\n\n\tHRESULT MediaFileBuffer::load( LPCTSTR path, bool stereo )\n\t{\n\t\tCComPtr<IMFSourceReader> reader;\n\t\tHRESULT hr = MFCreateSourceReaderFromURL( path, nullptr, &reader );\n\t\tif( FAILED( hr ) )\n\t\t{\n\t\t\tlogError16( L\"Unable to decode audio file \\\"%s\\\", MFCreateSourceReaderFromURL failed\", path );\n\t\t\treturn hr;\n\t\t}\n\n\t\tCHECK( reader->SetStreamSelection( MF_SOURCE_READER_ALL_STREAMS, FALSE ) );\n\t\tCHECK( reader->SetStreamSelection( MF_SOURCE_READER_FIRST_AUDIO_STREAM, TRUE ) );\n\n\t\tCComPtr<IMFMediaType> mtNative;\n\t\tCHECK( reader->GetNativeMediaType( MF_SOURCE_READER_FIRST_AUDIO_STREAM, MF_SOURCE_READER_CURRENT_TYPE_INDEX, &mtNative ) );\n\t\tUINT32 numChannels;\n\t\tCHECK( mtNative->GetUINT32( MF_MT_AUDIO_NUM_CHANNELS, &numChannels ) );\n\t\tconst bool sourceMono = numChannels == 1;\n\t\tconst AudioBuffer::pfnAppendSamples pfn = AudioBuffer::appendSamplesFunc( sourceMono, stereo );\n\t\tchannels = ( stereo && !sourceMono ) ? 2 : 1;\n\n\t\tCComPtr<IMFMediaType> mt;\n\t\tCHECK( createMediaType( !sourceMono, &mt ) );\n\n\t\tCHECK( reader->SetCurrentMediaType( MF_SOURCE_READER_FIRST_AUDIO_STREAM, nullptr, mt ) );\n\n\t\twhile( true )\n\t\t{\n\t\t\tDWORD dwFlags = 0;\n\t\t\tCComPtr<IMFSample> sample;\n\n\t\t\t// Read the next sample.\n\t\t\thr = reader->ReadSample( (DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM, 0, nullptr, &dwFlags, nullptr, &sample );\n\t\t\tif( FAILED( hr ) )\n\t\t\t{\n\t\t\t\tlogErrorHr( hr, u8\"IMFSourceReader.ReadSample\" );\n\t\t\t\treturn hr;\n\t\t\t}\n\n\t\t\tif( dwFlags & MF_SOURCE_READERF_CURRENTMEDIATYPECHANGED )\n\t\t\t{\n\t\t\t\tlogError( u8\"Media type changes ain’t supported by the library.\" );\n\t\t\t\treturn E_UNEXPECTED;\n\t\t\t}\n\n\t\t\tif( dwFlags & MF_SOURCE_READERF_ENDOFSTREAM )\n\t\t\t\tbreak;\n\n\t\t\tif( !sample )\n\t\t\t{\n\t\t\t\t// printf( \"No sample\\n\" );\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Get a pointer to the audio data in the sample.\n\t\t\tCComPtr<IMFMediaBuffer> buffer;\n\t\t\thr = sample->ConvertToContiguousBuffer( &buffer );\n\t\t\tif( FAILED( hr ) )\n\t\t\t\treturn hr;\n\n\t\t\tconst float* pAudioData = nullptr;\n\t\t\tDWORD cbBuffer;\n\t\t\thr = buffer->Lock( (BYTE**)&pAudioData, nullptr, &cbBuffer );\n\t\t\tif( FAILED( hr ) )\n\t\t\t\treturn hr;\n\n\t\t\ttry\n\t\t\t{\n\t\t\t\tconst size_t countFloats = cbBuffer / sizeof( float );\n\t\t\t\t( pcm.*pfn )( pAudioData, countFloats );\n\t\t\t}\n\t\t\tcatch( const std::bad_alloc& )\n\t\t\t{\n\t\t\t\treturn E_OUTOFMEMORY;\n\t\t\t}\n\n\t\t\t// Unlock the buffer\n\t\t\thr = buffer->Unlock();\n\t\t\tif( FAILED( hr ) )\n\t\t\t\treturn hr;\n\t\t}\n\n\t\tconst size_t len = pcm.mono.size();\n\t\tif( len == 0 )\n\t\t{\n\t\t\tlogError16( L\"The audio file \\\"%s\\\" has no samples\", path );\n\t\t\treturn E_INVALIDARG;\n\t\t}\n\t\tif( len < SAMPLE_RATE / 2 )\n\t\t\tlogError16( L\"The file \\\"%s\\\" only has %zu samples, less than 0.5 seconds of audio\", path, len );\n\t\telse\n\t\t\tlogDebug16( L\"Loaded audio file from \\\"%s\\\": %zu samples, %g seconds\", path, len, (int)len * ( 1.0 / SAMPLE_RATE ) );\n\t\treturn S_OK;\n\n\t}\n}\n\nHRESULT COMLIGHTCALL Whisper::loadAudioFile( LPCTSTR path, bool stereo, iAudioBuffer** pp )\n{\n\tif( nullptr == path || nullptr == pp )\n\t\treturn E_POINTER;\n\n\tComLight::CComPtr<ComLight::Object<MediaFileBuffer>> obj;\n\tCHECK( ComLight::Object<MediaFileBuffer>::create( obj ) );\n\tCHECK( obj->load( path, stereo ) );\n\tobj.detach( pp );\n\treturn S_OK;\n}"
  },
  {
    "path": "Whisper/MF/loadAudioFile.h",
    "content": "#pragma once\n#include \"../API/iMediaFoundation.cl.h\"\n\nnamespace Whisper\n{\n\tHRESULT COMLIGHTCALL loadAudioFile( LPCTSTR path, bool stereo, iAudioBuffer** pp );\n}"
  },
  {
    "path": "Whisper/MF/mfStartup.cpp",
    "content": "#include \"stdafx.h\"\n#include \"mfStartup.h\"\n#include <atlbase.h>\n#include <mfapi.h>\n#pragma comment(lib, \"Mfplat.lib\")\n\nnamespace\n{\n\tstruct sCoInitStatus\n\t{\n\t\t// Possible state:\n\t\t// -1 is the initial state, coInitialize never called\n\t\t// S_OK - CoInitializeEx succeeded, in this state the counter tracks the count of coInitialize() for the current thread\n\t\t// S_FALSE - CoInitializeEx failed with RPC_E_CHANGED_MODE, or did nothing because already initialized for the current thread\n\t\t// Error status - CoInitializeEx failed for some other reason\n\t\tHRESULT code = -1;\n\t\tuint32_t counter = 0;\n\t};\n\tthread_local sCoInitStatus coInitStatus;\n\n\tstatic HRESULT coInitialize()\n\t{\n\t\tsCoInitStatus& cis = coInitStatus;\n\t\tHRESULT hr = cis.code;\n\t\tif( SUCCEEDED( hr ) )\n\t\t{\n\t\t\tif( S_OK == hr )\n\t\t\t\tcis.counter++;\n\t\t\treturn S_FALSE;\n\t\t}\n\n\t\tif( hr == HRESULT( -1 ) )\n\t\t{\n\t\t\thr = CoInitializeEx( nullptr, COINIT_MULTITHREADED );\n\t\t\tif( S_OK == hr )\n\t\t\t{\n\t\t\t\tcis.counter = 1;\n\t\t\t\treturn cis.code = S_OK;\n\t\t\t}\n\t\t\tif( S_FALSE == hr || RPC_E_CHANGED_MODE == hr )\n\t\t\t{\n\t\t\t\treturn cis.code = S_FALSE;\n\t\t\t}\n\t\t\tcis.code = hr;\n\t\t\treturn hr;\n\t\t}\n\t\t\n\t\treturn hr;\n\t}\n\n\tstatic void coUninitialize()\n\t{\n\t\tsCoInitStatus& cis = coInitStatus;\n\t\tif( cis.code == S_OK )\n\t\t{\n\t\t\tassert( cis.counter > 0 );\n\t\t\tcis.counter--;\n\t\t\tif( 0 == cis.counter )\n\t\t\t\tCoUninitialize();\n\t\t}\n\t}\n\n\tstatic CComAutoCriticalSection s_lock;\n#define LOCK() CComCritSecLock<CComAutoCriticalSection> lock{ s_lock }\n\tstatic uint32_t mfStartupCounter = 0;\n\n\tconstexpr uint8_t FlagCOM = 1;\n\tconstexpr uint8_t FlagMF = 0x10;\n}\n\nusing namespace Whisper;\n\nMfStartupRaii::~MfStartupRaii()\n{\n\tif( 0 != ( successFlags & FlagMF ) )\n\t{\n\t\tLOCK();\n\t\tassert( mfStartupCounter > 0 );\n\t\tmfStartupCounter--;\n\t\tif( mfStartupCounter > 0 )\n\t\t\treturn;\n\t\tMFShutdown();\n\t\tsuccessFlags &= ~FlagMF;\n\t}\n\t\n\tif( 0 != ( successFlags & FlagCOM ) )\n\t{\n\t\tcoUninitialize();\n\t\tsuccessFlags &= ~FlagCOM;\n\t}\n}\n\nHRESULT MfStartupRaii::startup()\n{\n\tif( 0 != ( successFlags & FlagMF ) )\n\t\treturn HRESULT_FROM_WIN32( ERROR_ALREADY_INITIALIZED );\n\n\tHRESULT hr = coInitialize();\n\tCHECK( hr );\n\tif( hr == S_OK )\n\t\tsuccessFlags |= FlagCOM;\n\n\tLOCK();\n\n\tif( 0 == mfStartupCounter )\n\t{\n\t\tHRESULT hr = MFStartup( MF_VERSION, MFSTARTUP_LITE );\n\t\tif( SUCCEEDED( hr ) )\n\t\t{\n\t\t\tmfStartupCounter = 1;\n\t\t\tsuccessFlags |= FlagMF;\n\t\t\treturn S_OK;\n\t\t}\n\n\t\tif( 0 != ( successFlags & FlagCOM ) )\n\t\t{\n\t\t\tcoUninitialize();\n\t\t\tsuccessFlags &= ~FlagCOM;\n\t\t}\n\t\treturn hr;\n\t}\n\telse\n\t{\n\t\tmfStartupCounter++;\n\t\tsuccessFlags |= FlagMF;\n\t\treturn S_FALSE;\n\t}\n}"
  },
  {
    "path": "Whisper/MF/mfStartup.h",
    "content": "#pragma once\n\nnamespace Whisper\n{\n\tclass MfStartupRaii\n\t{\n\t\tuint8_t successFlags = 0;\n\tpublic:\n\t\tMfStartupRaii() = default;\n\t\t~MfStartupRaii();\n\t\tMfStartupRaii( const MfStartupRaii& ) = delete;\n\n\t\tHRESULT startup();\n\t};\n}"
  },
  {
    "path": "Whisper/MF/mfUtils.cpp",
    "content": "#include \"stdafx.h\"\n#include \"mfUtils.h\"\n#include <mfapi.h>\n\nHRESULT Whisper::createMediaType( bool stereo, IMFMediaType** pp )\n{\n\tif( nullptr == pp )\n\t\treturn E_POINTER;\n\n\tCComPtr<IMFMediaType> mt;\n\tCHECK( MFCreateMediaType( &mt ) );\n\tCHECK( mt->SetGUID( MF_MT_MAJOR_TYPE, MFMediaType_Audio ) );\n\tCHECK( mt->SetGUID( MF_MT_SUBTYPE, MFAudioFormat_Float ) );\n\tCHECK( mt->SetUINT32( MF_MT_AUDIO_SAMPLES_PER_SECOND, SAMPLE_RATE ) );\n\n\tconst uint32_t channels = stereo ? 2 : 1;\n\tCHECK( mt->SetUINT32( MF_MT_AUDIO_NUM_CHANNELS, channels ) );\n\tCHECK( mt->SetUINT32( MF_MT_AUDIO_BLOCK_ALIGNMENT, channels * 4 ) );\n\tCHECK( mt->SetUINT32( MF_MT_AUDIO_AVG_BYTES_PER_SECOND, channels * 4 * SAMPLE_RATE ) );\n\tCHECK( mt->SetUINT32( MF_MT_AUDIO_BITS_PER_SAMPLE, 32 ) );\n\tCHECK( mt->SetUINT32( MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE ) );\n\n\t*pp = mt.Detach();\n\n\treturn S_OK;\n}\n\nHRESULT Whisper::getStreamDuration( IMFSourceReader* reader, int64_t& duration )\n{\n\tPROPVARIANT var;\n\tPropVariantInit( &var );\n\tCHECK( reader->GetPresentationAttribute( MF_SOURCE_READER_MEDIASOURCE, MF_PD_DURATION, &var ) );\n\n\tif( var.vt == VT_UI8 )\n\t{\n\t\t// The documentation says the type of that attribute is UINT64\n\t\t// https://learn.microsoft.com/en-us/windows/win32/medfound/mf-pd-duration-attribute\n\t\tduration = var.uhVal.QuadPart;\n\t\treturn S_OK;\n\t}\n\tlogError( u8\"Unexpected type of MF_PD_DURATION attribute\" );\n\treturn E_INVALIDARG;\n}\n\nHRESULT Whisper::validateCurrentMediaType( IMFSourceReader* reader, uint32_t expectedChannels )\n{\n\tCComPtr<IMFMediaType> mt;\n\tCHECK( reader->GetCurrentMediaType( MF_SOURCE_READER_FIRST_AUDIO_STREAM, &mt ) );\n\n\tGUID guid;\n\tCHECK( mt->GetGUID( MF_MT_MAJOR_TYPE, &guid ) );\n\tif( guid != MFMediaType_Audio )\n\t\treturn E_FAIL;\n\n\tCHECK( mt->GetGUID( MF_MT_SUBTYPE, &guid ) );\n\tif( guid != MFAudioFormat_Float )\n\t\treturn E_FAIL;\n\n\tUINT32 u32;\n\tCHECK( mt->GetUINT32( MF_MT_AUDIO_SAMPLES_PER_SECOND, &u32 ) );\n\tif( u32 != SAMPLE_RATE )\n\t\treturn E_FAIL;\n\n\tCHECK( mt->GetUINT32( MF_MT_AUDIO_NUM_CHANNELS, &u32 ) );\n\tif( u32 != expectedChannels )\n\t\treturn E_FAIL;\n\n\treturn S_OK;\n}"
  },
  {
    "path": "Whisper/MF/mfUtils.h",
    "content": "#pragma once\n#include <stdint.h>\n#include <mfidl.h>\n#include <mfobjects.h>\n#include <mfreadwrite.h>\n#include \"../Whisper/audioConstants.h\"\n\nnamespace Whisper\n{\n\tHRESULT createMediaType( bool stereo, IMFMediaType** pp );\n\n\tHRESULT getStreamDuration( IMFSourceReader* reader, int64_t& duration );\n\n\tHRESULT validateCurrentMediaType( IMFSourceReader* reader, uint32_t expectedChannels );\n\n\tstruct iAudioReader;\n\tvoid setPreciseSamplesCount( const iAudioReader* ar, int64_t count );\n}"
  },
  {
    "path": "Whisper/ML/ConstantBuffer.cpp",
    "content": "#include \"stdafx.h\"\n#include \"ConstantBuffer.h\"\n#include \"../D3D/MappedResource.h\"\nusing namespace DirectCompute;\n\nHRESULT ConstantBuffer::create()\n{\n\tif( nullptr == buffer )\n\t{\n\t\tCD3D11_BUFFER_DESC desc{ 16 * 3 * 2, D3D11_BIND_CONSTANT_BUFFER, D3D11_USAGE_DYNAMIC, D3D11_CPU_ACCESS_WRITE };\n\t\treturn device()->CreateBuffer( &desc, nullptr, &buffer );\n\t}\n\treturn HRESULT_FROM_WIN32( ERROR_ALREADY_INITIALIZED );\n}\n\nnamespace\n{\n\t__forceinline void copy32( __m128i* rdi, const TensorShape& ts )\n\t{\n\t\t_mm_storeu_si128( rdi, ts.sizeVec() );\n\t\t_mm_storeu_si128( rdi + 1, ts.stridesVec() );\n\t}\n}\n\nHRESULT ConstantBuffer::update( const TensorShape& t0 )\n{\n\tMappedResource mapped;\n\tCHECK( mapped.map( buffer, false ) );\n\n\t__m128i* const rdi = ( __m128i* )mapped.data();\n\tcopy32( rdi, t0 );\n\treturn S_OK;\n}\n\nHRESULT ConstantBuffer::update( const TensorShape& t0, const TensorShape& t1 )\n{\n\tMappedResource mapped;\n\tCHECK( mapped.map( buffer, false ) );\n\n\t__m128i* const rdi = ( __m128i* )mapped.data();\n\tcopy32( rdi, t0 );\n\tcopy32( rdi + 2, t1 );\n\treturn S_OK;\n}\n\nHRESULT ConstantBuffer::update( const TensorShape& t0, const TensorShape& t1, const TensorShape& t2 )\n{\n\tMappedResource mapped;\n\tCHECK( mapped.map( buffer, false ) );\n\n\t__m128i* const rdi = ( __m128i* )mapped.data();\n\tcopy32( rdi, t0 );\n\tcopy32( rdi + 2, t1 );\n\tcopy32( rdi + 4, t2 );\n\treturn S_OK;\n}\n\nvoid ConstantBuffer::bind() const\n{\n\tID3D11Buffer* p = buffer;\n\tassert( nullptr != p );\n\tcontext()->CSSetConstantBuffers( 0, 1, &p );\n}"
  },
  {
    "path": "Whisper/ML/ConstantBuffer.h",
    "content": "#pragma once\n#include \"../D3D/device.h\"\n#include \"TensorShape.h\"\n\nnamespace DirectCompute\n{\n\t// 96 bytes dynamic constant buffers, with dimensions and VRAM layout of 2-3 tensors\n\tclass ConstantBuffer\n\t{\n\t\tCComPtr<ID3D11Buffer> buffer;\n\n\tpublic:\n\t\tHRESULT create();\n\t\tHRESULT update( const TensorShape& t0 );\n\t\tHRESULT update( const TensorShape& t0, const TensorShape& t1 );\n\t\tHRESULT update( const TensorShape& t0, const TensorShape& t1, const TensorShape& t2 );\n\n\t\tvoid bind() const;\n\n\t\t__m128i getMemoryUse() const\n\t\t{\n\t\t\treturn bufferMemoryUsage( buffer );\n\t\t}\n\t};\n}"
  },
  {
    "path": "Whisper/ML/Context.ops.cpp",
    "content": "#include \"stdafx.h\"\n#include \"MlContext.h\"\n#include \"testUtils.h\"\nusing namespace DirectCompute;\n\nTensor MlContext::createTensor( eDataType type, const std::array<uint32_t, 4>& ne )\n{\n\tTensor res;\n\tcheck( res.create( type, ne ) );\n\treturn res;\n}\n\nTensor MlContext::createTensor( eDataType type, std::initializer_list<uint32_t> ne )\n{\n\tsize_t nDims = ne.size();\n\tif( 0 == nDims || nDims > 4 )\n\t\tthrow E_INVALIDARG;\n\tstd::array<uint32_t, 4> arr;\n\tfor( size_t i = 0; i < nDims; i++ )\n\t\tarr[ i ] = ne.begin()[ i ];\n\tfor( size_t i = nDims; i < 4; i++ )\n\t\tarr[ i ] = 1;\n\treturn createTensor( type, arr );\n}\n\nTensor MlContext::conv_1d_1s( const Tensor& a, const Tensor& b )\n{\n\tassert( b.isMatrix() );\n\tassert( a.ne[ 1 ] == b.ne[ 1 ] );\n\tassert( a.ne[ 3 ] == 1 );\n\n\tTensor res = createTensor( eDataType::FP32, { b.ne[ 0 ], a.ne[ 2 ] } );\n\n\tconvolution( a, b, res );\n\treturn res;\n}\n\nTensor MlContext::conv_1d_2s( const Tensor& a, const Tensor& b )\n{\n\tassert( b.isMatrix() );\n\tassert( a.ne[ 1 ] == b.ne[ 1 ] );\n\tassert( a.ne[ 3 ] == 1 );\n\n\tTensor res = createTensor( eDataType::FP32, { b.ne[ 0 ] / 2, a.ne[ 2 ] } );\n#if 0\n\tstatic PrintUniqueTensorSizes printSize( \"conv_1d_2s\" );\n\tprintSize.print( a, b );\n#endif\n\tconvolution2( a, b, res );\n\treturn res;\n}\n\nnamespace\n{\n\tinline bool canRepeat( const TensorShape& t0, const TensorShape& t1 )\n\t{\n\t\treturn ( t1.ne[ 0 ] % t0.ne[ 0 ] == 0 ) &&\n\t\t\t( t1.ne[ 1 ] % t0.ne[ 1 ] == 0 ) &&\n\t\t\t( t1.ne[ 2 ] % t0.ne[ 2 ] == 0 ) &&\n\t\t\t( t1.ne[ 3 ] % t0.ne[ 3 ] == 0 );\n\t}\n}\n\nTensor MlContext::cwiseBinary( const Tensor& a, const Tensor& b, eComputeShader cs )\n{\n\tassert( isSameShape( a, b ) );\n\tTensor res = createTensor( a.getType(), a.ne );\n\tcwiseBinary( a, b, res, cs );\n\treturn res;\n}\n\nTensor __declspec( noinline ) MlContext::view2d( const Tensor& a, uint32_t ne0, uint32_t ne1, uint32_t nb1, uint32_t offset )\n{\n\tif( 0 != offset )\n\t\tthrow E_NOTIMPL;\n\n\tTensor res = a;\n\tres.ne = { ne0, ne1, 1, 1 };\n\n\tres.nb[ 1 ] = nb1;\n\tres.nb[ 2 ] = res.nb[ 3 ] = nb1 * ne1;\n\treturn res;\n}\n\nTensor MlContext::transpose( const Tensor& a )\n{\n\tTensor result;\n\n\t// A magic number for _mm_shuffle_epi32 SSE2 instruction to swap two lower int32 lanes in a vector\n\tconstexpr int swapXy = _MM_SHUFFLE( 3, 2, 0, 1 );\n\n\t__m128i v = a.sizeVec();\n\tv = _mm_shuffle_epi32( v, swapXy );\n\tstore( result.ne, v );\n\n\tv = a.stridesVec();\n\tv = _mm_shuffle_epi32( v, swapXy );\n\tstore( result.nb, v );\n\n\tresult.setGpuViews( a, a );\n\treturn result;\n}\n\nTensor MlContext::norm( const Tensor& a )\n{\n\tTensor res = createTensor( a.getType(), a.ne );\n\tnorm( a, res );\n\treturn res;\n}\n\nTensor MlContext::mulMat( const Tensor& a, const Tensor& b )\n{\n\tif( !canMulMat( a, b ) )\n\t\tthrow E_INVALIDARG;\n\tTensor res = createTensor( eDataType::FP32, { a.ne[ 1 ], b.ne[ 1 ], a.ne[ 2 ], b.ne[ 3 ] } );\n\tif constexpr( enableInexactOptimizations )\n\t\tmulMatTiled( a, b, res );\n\telse\n\t\tmulMat( a, b, res );\n#if 0\n\tTensor testTiled;\n\tcheck( testTiled.create( eDataType::FP32, res.ne ) );\n\tmulMatTiled( a, b, testTiled );\n\n\tstd::vector<float> current, tiled;\n\tres.download( current );\n\ttestTiled.download( tiled );\n\tsTensorDiff diff = computeDiff( current.data(), tiled.data(), current.size() );\n\tdiff.print( \"mulMatTiled\" );\n#endif\n\treturn res;\n}\n\nTensor MlContext::mulMatEx( const Tensor& a, const Tensor& b, const char* tagName )\n{\n\tif( !canMulMat( a, b ) )\n\t\tthrow E_INVALIDARG;\n\tif( 0 != a.nb[ 0 ] )\n\t\tthrow E_INVALIDARG;\t// The first argument is expected to be pre-transposed\n\t\n\tconst uint16_t tag = profiler.setNextTag( tagName );\n\n\tif( b.ne[ 1 ] != 1 )\n\t{\n\t\tif( b.nb[ 0 ] != 0 )\n\t\t{\n\t\t\tTensor rhs = reshapePanels( b );\n\t\t\tprofiler.setNextTag( tag );\n\t\t\treturn mulMatTiledEx( a, rhs );\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// Second argument already reshaped into these panels\n\t\t\treturn mulMatTiledEx( a, b );\n\t\t}\n\t}\n\telse\n\t{\n\t\tif( 0 != b.nb[ 0 ] )\n\t\t\treturn mulMatByRowTiledEx( a, b );\n\n\t\t// That shader requires classic VRAM layout of the second argument, gonna fail with pre-transposed one\n\t\tthrow E_INVALIDARG;\n\t}\n}\n\nTensor MlContext::permute( const Tensor& a, uint8_t axis0, uint8_t axis1, uint8_t axis2, uint8_t axis3 )\n{\n\tassert( axis0 < 4 );\n\tassert( axis1 < 4 );\n\tassert( axis2 < 4 );\n\tassert( axis3 < 4 );\n\n\tassert( axis0 != axis1 );\n\tassert( axis0 != axis2 );\n\tassert( axis0 != axis3 );\n\tassert( axis1 != axis2 );\n\tassert( axis1 != axis3 );\n\tassert( axis2 != axis3 );\n\n\tTensor res = a;\n\tres.ne[ axis0 ] = a.ne[ 0 ];\n\tres.ne[ axis1 ] = a.ne[ 1 ];\n\tres.ne[ axis2 ] = a.ne[ 2 ];\n\tres.ne[ axis3 ] = a.ne[ 3 ];\n\n\tres.nb[ axis0 ] = a.nb[ 0 ];\n\tres.nb[ axis1 ] = a.nb[ 1 ];\n\tres.nb[ axis2 ] = a.nb[ 2 ];\n\tres.nb[ axis3 ] = a.nb[ 3 ];\n\treturn res;\n}\n\nTensor MlContext::flashAttention( const Tensor& q, const Tensor& k, const Tensor& v, bool masked )\n{\n\tif( !canMulMat( k, q ) )\n\t\tthrow E_INVALIDARG;\n\n\tif constexpr( enableInexactOptimizations )\n\t{\n\t\tif( !masked )\n\t\t{\n\t\t\tprofiler.setNextTag( \"flashAttn.1\" );\n\t\t\tTensor tmp = mulMat( k, q );\n\n\t\t\tprofiler.setNextTag( \"flashAttention\" );\n\t\t\tconst float tempScale = (float)( 1.0 / sqrt( (double)(int)q.ne[ 0 ] ) );\n\t\t\tsoftMax( tmp, tempScale );\n\n\t\t\tprofiler.setNextTag( \"flashAttn.2\" );\n\t\t\treturn mulMat( v, tmp );\n\t\t}\n\t}\n\n\tTensor res = createTensor( eDataType::FP32, q.ne );\n\tflashAttention( q, k, v, res, masked );\n\n#if 0\n\tTensor tmpMat = mulMat( k, q );\n\tfloat scale = (float)( 1.0 / sqrt( (double)(int)q.ne[ 0 ] ) );\n\tsoftMax( tmpMat, scale );\n\tTensor testRes = mulMat( v, tmpMat );\n\tcomputeDiff( res, testRes ).print( \"flashAttention mulmat\" );\n#endif\n\n\treturn res;\n}\n\nTensor MlContext::copy( const Tensor& a, eDataType type, std::initializer_list<uint32_t> size )\n{\n\tconst size_t dims = size.size();\n\tif( 0 == dims || dims > 4 )\n\t\tthrow E_BOUNDS;\n\n\tsize_t nRequested = 1;\n\tfor( size_t i = 0; i < dims; i++ )\n\t{\n\t\tuint32_t n = size.begin()[ i ];\n\t\tnRequested *= n;\n\t}\n\tif( nRequested != a.countElements() )\n\t\tthrow E_INVALIDARG;\n\n\tconst eDataType st = a.getType();\n\tTensor res;\n\tif( a.isContinuous() && st == type )\n\t{\n\t\t// Same type, and it's dense - no need to call any compute shaders, equal to reshape\n\t\tres = a;\n\t\tfor( size_t i = 0; i < dims; i++ )\n\t\t\tres.ne[ i ] = size.begin()[ i ];;\n\t\tfor( size_t i = dims; i < 4; i++ )\n\t\t\tres.ne[ i ] = 1;\n\t\tres.setDenseStrides();\n\t}\n\telse\n\t{\n\t\t// Either converting non-continuous to continuous, or converting types\n\t\tres = createTensor( type, size );\n\t\tcopyImpl( a, res, st == eDataType::FP32 && type == eDataType::FP16 );\n\t}\n\treturn res;\n}\n\nvoid MlContext::copyInPlace( Tensor& dest, const Tensor& a, eDataType type, std::initializer_list<uint32_t> size )\n{\n\tassert( type == dest.getType() );\n\n\tconst size_t dims = size.size();\n\tif( 0 == dims || dims > 4 )\n\t\tthrow E_BOUNDS;\n\n\tsize_t nRequested = 1;\n\tfor( size_t i = 0; i < dims; i++ )\n\t{\n\t\tuint32_t n = size.begin()[ i ];\n\t\tnRequested *= n;\n\t}\n\tif( nRequested != a.countElements() || nRequested != dest.countElements() )\n\t\tthrow E_INVALIDARG;\n\n\t// Reshape the destination\n\tfor( size_t i = 0; i < dims; i++ )\n\t\tdest.ne[ i ] = size.begin()[ i ];\n\tfor( size_t i = dims; i < 4; i++ )\n\t\tdest.ne[ i ] = 1;\n\tdest.setDenseStrides();\n\n\t// Call the shader\n\tconst eDataType st = a.getType();\n\tcopyImpl( a, dest, st == eDataType::FP32 && type == eDataType::FP16 );\n}"
  },
  {
    "path": "Whisper/ML/DbgNanTest.cpp",
    "content": "#include \"stdafx.h\"\n#include \"DbgNanTest.h\"\n#include \"../D3D/MappedResource.h\"\nusing namespace DirectCompute;\n\nHRESULT DbgNanTest::create()\n{\n\tID3D11Device* const dev = DirectCompute::device();\n\n\tCD3D11_BUFFER_DESC desc{ 4, D3D11_BIND_UNORDERED_ACCESS };\n\tCHECK( dev->CreateBuffer( &desc, nullptr, &bufferDefault ) );\n\n\tdesc.Usage = D3D11_USAGE_STAGING;\n\tdesc.BindFlags = 0;\n\tdesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;\n\tCHECK( dev->CreateBuffer( &desc, nullptr, &bufferStaging ) );\n\n\tCD3D11_UNORDERED_ACCESS_VIEW_DESC viewDesc{ D3D11_UAV_DIMENSION_BUFFER, DXGI_FORMAT_R32_UINT, 0, 1 };\n\tCHECK( dev->CreateUnorderedAccessView( bufferDefault, &viewDesc, &uav ) );\n\n\treturn S_OK;\n}\n\nvoid DbgNanTest::destroy()\n{\n\tuav = nullptr;\n\tbufferStaging = nullptr;\n\tbufferDefault = nullptr;\n}\n\nbool DbgNanTest::test() const\n{\n\tcontext()->CopyResource( bufferStaging, bufferDefault );\n\tMappedResource mapped;\n\tcheck( mapped.map( bufferStaging, true ) );\n\tconst BOOL val = *(const BOOL*)mapped.data();\n\treturn val != 0;\n}"
  },
  {
    "path": "Whisper/ML/DbgNanTest.h",
    "content": "#pragma once\n\nnamespace DirectCompute\n{\n\tclass DbgNanTest\n\t{\n\t\tCComPtr<ID3D11Buffer> bufferDefault, bufferStaging;\n\t\tCComPtr<ID3D11UnorderedAccessView> uav;\n\tpublic:\n\t\tHRESULT create();\n\t\tvoid destroy();\n\t\toperator ID3D11UnorderedAccessView* ( ) const\n\t\t{\n\t\t\treturn uav;\n\t\t}\n\t\tbool test() const;\n\t};\n\n#if DBG_TEST_NAN\n\tconst DbgNanTest& getNanTestBuffers();\n#endif\n}"
  },
  {
    "path": "Whisper/ML/Device.cpp",
    "content": "#include \"stdafx.h\"\n#include \"Device.h\"\n#include \"../D3D/createDevice.h\"\n#include \"../D3D/shaders.h\"\n#include \"../D3D/device.h\"\n#include \"mlUtils.h\"\n#include \"../D3D/MappedResource.h\"\n\nusing namespace DirectCompute;\n\nHRESULT Device::create( uint32_t flags, const std::wstring& adapter )\n{\n\tCHECK( validateFlags( flags ) );\n\tCHECK( createDevice( adapter, &device, &context ) );\n\tCHECK( queryDeviceInfo( gpuInfo, device, flags ) );\n\n\tCHECK( createComputeShaders( shaders ) );\n\n\tCHECK( lookupTables.create() );\n\t{\n\t\tCD3D11_BUFFER_DESC desc{ 16, D3D11_BIND_CONSTANT_BUFFER, D3D11_USAGE_DYNAMIC, D3D11_CPU_ACCESS_WRITE };\n\t\tCHECK( device->CreateBuffer( &desc, nullptr, &smallCb ) );\n\t}\n\n#if DBG_TEST_NAN\n\tCHECK( nanTestBuffers.create() );\n#endif\n\n\treturn S_OK;\n}\n\nHRESULT Device::createClone( const Device& source )\n{\n\tCHECK( cloneDevice( source.device, &device, &context ) );\n\tgpuInfo = source.gpuInfo;\n\n\tCHECK( createComputeShaders( shaders ) );\n\tCHECK( lookupTables.createClone( source.lookupTables ) );\n\n\t{\n\t\tCD3D11_BUFFER_DESC desc{ 16, D3D11_BIND_CONSTANT_BUFFER, D3D11_USAGE_DYNAMIC, D3D11_CPU_ACCESS_WRITE };\n\t\tCHECK( device->CreateBuffer( &desc, nullptr, &smallCb ) );\n\t}\n\n#if DBG_TEST_NAN\n\tCHECK( nanTestBuffers.create() );\n#endif\n\n\treturn S_OK;\n}\n\nvoid Device::destroy()\n{\n#if DBG_TEST_NAN\n\tnanTestBuffers.destroy();\n#endif\n\tsmallCb = nullptr;\n\tshaders.clear();\n\tcontext = nullptr;\n\tdevice = nullptr;\n}\n\n__m128i __declspec( noinline ) DirectCompute::bufferMemoryUsage( ID3D11Buffer* buffer )\n{\n\tif( nullptr != buffer )\n\t{\n\t\tD3D11_BUFFER_DESC desc;\n\t\tbuffer->GetDesc( &desc );\n\n\t\tif( desc.Usage != D3D11_USAGE_STAGING )\n\t\t\treturn setHigh_size( desc.ByteWidth );\n\t\telse\n\t\t\treturn setLow_size( desc.ByteWidth );\n\t}\n\treturn _mm_setzero_si128();\n}\n\n__m128i __declspec( noinline ) DirectCompute::resourceMemoryUsage( ID3D11ShaderResourceView* srv )\n{\n\tif( nullptr != srv )\n\t{\n\t\tCComPtr<ID3D11Resource> res;\n\t\tsrv->GetResource( &res );\n\t\tCComPtr<ID3D11Buffer> buff;\n\t\tif( SUCCEEDED( res.QueryInterface( &buff ) ) )\n\t\t\treturn bufferMemoryUsage( buff );\n\t\tassert( false );\t// We don't use textures in this project\n\t}\n\treturn _mm_setzero_si128();\n}\n\nstatic thread_local const Device* ts_device = nullptr;\n\nID3D11Device* DirectCompute::device()\n{\n\tconst Device* dev = ts_device;\n\tif( nullptr != dev )\n\t\treturn dev->device;\n\tthrow OLE_E_BLANK;\n}\n\nID3D11DeviceContext* DirectCompute::context()\n{\n\tconst Device* dev = ts_device;\n\tif( nullptr != dev )\n\t\treturn dev->context;\n\tthrow OLE_E_BLANK;\n}\n\nconst sGpuInfo& DirectCompute::gpuInfo()\n{\n\tconst Device* dev = ts_device;\n\tif( nullptr != dev )\n\t\treturn dev->gpuInfo;\n\tthrow OLE_E_BLANK;\n}\n\nconst LookupTables& DirectCompute::lookupTables()\n{\n\tconst Device* dev = ts_device;\n\tif( nullptr != dev )\n\t\treturn dev->lookupTables;\n\tthrow OLE_E_BLANK;\n}\n\nvoid DirectCompute::bindShader( eComputeShader shader )\n{\n\tconst Device* dev = ts_device;\n\tif( nullptr != dev )\n\t{\n\t\tID3D11ComputeShader* cs = dev->shaders[ (uint16_t)shader ];\n\t\tdev->context->CSSetShader( cs, nullptr, 0 );\n\t\treturn;\n\t}\n\tthrow OLE_E_BLANK;\n}\n\nID3D11Buffer* __vectorcall DirectCompute::updateSmallCb( __m128i cbData )\n{\n\tconst Device* dev = ts_device;\n\tif( nullptr != dev && nullptr != dev->smallCb )\n\t{\n\t\tID3D11Buffer* cb = dev->smallCb;\n\t\tMappedResource mapped;\n\t\tcheck( mapped.map( cb, false ) );\n\t\tstore16( mapped.data(), cbData );\n\t\treturn cb;\n\t}\n\n\tthrow OLE_E_BLANK;\n}\n\n#if DBG_TEST_NAN\nconst DbgNanTest& DirectCompute::getNanTestBuffers()\n{\n\tconst Device* dev = ts_device;\n\tif( nullptr != dev )\n\t\treturn dev->nanTestBuffers;\n\tthrow OLE_E_BLANK;\n}\n#endif\n\nDevice::ThreadSetupRaii::ThreadSetupRaii( const Device* dev )\n{\n\tassert( ts_device == nullptr );\n\tts_device = dev;\n\tsetup = true;\n}\n\nDevice::ThreadSetupRaii::~ThreadSetupRaii()\n{\n\tif( setup )\n\t{\n\t\tassert( ts_device != nullptr );\n\t\tts_device = nullptr;\n\t}\n}"
  },
  {
    "path": "Whisper/ML/Device.h",
    "content": "#pragma once\n#include <string>\n#include \"../D3D/sGpuInfo.h\"\n#include \"LookupTables.h\"\n#include \"DbgNanTest.h\"\n\nnamespace DirectCompute\n{\n\tstruct Device\n\t{\n\t\tCComPtr<ID3D11Device> device;\n\t\tCComPtr<ID3D11DeviceContext> context;\n\n\t\tstd::vector<CComPtr<ID3D11ComputeShader>> shaders;\n\t\tCComPtr<ID3D11Buffer> smallCb;\n\t\tsGpuInfo gpuInfo;\n\t\tLookupTables lookupTables;\n#if DBG_TEST_NAN\n\t\tDbgNanTest nanTestBuffers;\n#endif\n\n\t\tHRESULT create( uint32_t flags, const std::wstring& adapter );\n\t\tHRESULT createClone( const Device& source );\n\t\tvoid destroy();\n\n\t\tclass ThreadSetupRaii\n\t\t{\n\t\t\tbool setup;\n\t\tpublic:\n\t\t\tThreadSetupRaii( const Device* dev );\n\t\t\t~ThreadSetupRaii();\n\t\t\tThreadSetupRaii( ThreadSetupRaii&& that ) noexcept\n\t\t\t{\n\t\t\t\tsetup = that.setup;\n\t\t\t\tthat.setup = false;\n\t\t\t}\n\t\t\tThreadSetupRaii( const ThreadSetupRaii& ) = delete;\n\t\t\tvoid operator=( const ThreadSetupRaii& ) = delete;\n\t\t};\n\n\t\tThreadSetupRaii setForCurrentThread() const\n\t\t{\n\t\t\treturn ThreadSetupRaii{ this };\n\t\t}\n\t};\n}"
  },
  {
    "path": "Whisper/ML/LookupTables.cpp",
    "content": "#include \"stdafx.h\"\n#include \"LookupTables.h\"\n#include \"LookupTablesData.h\"\n#include <memory>\n#include \"mlUtils.h\"\nusing namespace DirectCompute;\n\nnamespace\n{\n\tHRESULT uploadLookupTable( const std::array<uint16_t, 0x10000>& rsi, CComPtr<ID3D11ShaderResourceView>& rdi )\n\t{\n\t\trdi = nullptr;\n\t\tCComPtr<ID3D11Buffer> buffer;\n\n\t\tCD3D11_BUFFER_DESC desc{ 0x10000 * 2, D3D11_BIND_SHADER_RESOURCE, D3D11_USAGE_IMMUTABLE };\n\t\tif( gpuInfo().cloneableModel() )\n\t\t{\n\t\t\tdesc.Usage = D3D11_USAGE_DEFAULT;\n\t\t\tdesc.MiscFlags |= D3D11_RESOURCE_MISC_SHARED;\n\t\t}\n\t\tD3D11_SUBRESOURCE_DATA srd{ rsi.data(), 0, 0 };\n\t\tCHECK( device()->CreateBuffer( &desc, &srd, &buffer ) );\n\n\t\tCD3D11_SHADER_RESOURCE_VIEW_DESC viewDesc{ D3D11_SRV_DIMENSION_BUFFER, DXGI_FORMAT_R16_UINT, 0, 0x10000 };\n\t\tCHECK( device()->CreateShaderResourceView( buffer, &viewDesc, &rdi ) );\n\n\t\treturn S_OK;\n\t}\n}\n\nHRESULT LookupTables::create()\n{\n\tstd::unique_ptr<LookupTablesData> data;\n\ttry\n\t{\n\t\tdata = std::make_unique<LookupTablesData>();\n\t}\n\tcatch( const std::bad_alloc& )\n\t{\n\t\treturn E_OUTOFMEMORY;\n\t}\n\n\tCHECK( uploadLookupTable( data->gelu, m_gelu ) );\n\tCHECK( uploadLookupTable( data->exponent, m_exponent ) );\n\n\treturn S_OK;\n}\n\nHRESULT LookupTables::createClone( const LookupTables& source )\n{\n\tCHECK( cloneResourceView( source.m_gelu, &m_gelu ) );\n\tCHECK( cloneResourceView( source.m_exponent, &m_exponent ) );\n\treturn S_OK;\n}\n\nvoid LookupTables::clear()\n{\n\tm_gelu = nullptr;\n\tm_exponent = nullptr;\n}\n\n__m128i LookupTables::getMemoryUsage() const\n{\n\t__m128i v = resourceMemoryUsage( m_gelu );\n\tv = _mm_add_epi64( v, resourceMemoryUsage( m_exponent ) );\n\treturn v;\n}"
  },
  {
    "path": "Whisper/ML/LookupTables.h",
    "content": "#pragma once\n#include \"../D3D/device.h\"\n\nnamespace DirectCompute\n{\n\tclass LookupTables\n\t{\n\t\tCComPtr<ID3D11ShaderResourceView> m_gelu, m_exponent;\n\n\tpublic:\n\n\t\tHRESULT create();\n\t\tHRESULT createClone( const LookupTables& source );\n\t\tvoid clear();\n\t\tID3D11ShaderResourceView* gelu() const { return m_gelu; }\n\t\tID3D11ShaderResourceView* exponent() const { return m_exponent; }\n\n\t\t__m128i getMemoryUsage() const;\n\t};\n\n\tconst LookupTables& lookupTables();\n}"
  },
  {
    "path": "Whisper/ML/LookupTablesData.cpp",
    "content": "#include \"stdafx.h\"\n#include \"LookupTablesData.h\"\n#include <immintrin.h>\n#include <atlfile.h>\n#include <Utils/LZ4/lz4.h>\nusing namespace DirectCompute;\n\nnamespace\n{\n\tinline float fp32( uint16_t f16 )\n\t{\n\t\t__m128i i = _mm_cvtsi32_si128( f16 );\n\t\t__m128 f = _mm_cvtph_ps( i );\n\t\treturn _mm_cvtss_f32( f );\n\t}\n\n\tinline uint16_t fp16( float fp32 )\n\t{\n\t\t__m128 f = _mm_set_ss( fp32 );\n\t\t__m128i i = _mm_cvtps_ph( f, 0 );\n\t\tuint32_t res = (uint32_t)_mm_cvtsi128_si32( i );\n\t\treturn (uint16_t)res;\n\t}\n\n\tconstexpr double GELU_COEF_A = 0.044715;\n\tconstexpr double SQRT_2_OVER_PI = 0.79788456080286535587989211986876;\n\n\tinline float computeGelu( float x )\n\t{\n\t\treturn (float)( 0.5 * x * ( 1.0 + tanh( SQRT_2_OVER_PI * x * ( 1.0 + GELU_COEF_A * x * x ) ) ) );\n\t}\n}\n\n#ifndef __AVX__\nnamespace\n{\n\t// Compressed data of these two lookup tables\n#include \"LookupTablesData.inl\"\n}\n#endif\n\nLookupTablesData::LookupTablesData()\n{\n#ifdef __AVX__\n\t// When compiling for AVX, we assume the CPU also has F16C\n\t// https://en.wikipedia.org/wiki/F16C\n\tfor( int i = 0; i < 0x10000; i++ )\n\t{\n\t\tconst float f = fp32( i );\n\t\tgelu[ i ] = fp16( computeGelu( f ) );\n\t\texponent[ i ] = fp16( (float)exp( f ) );\n\t}\n#else\n\t// When compiling without AVX, use the data compiled into the DLL\n\tconstexpr int cbThis = (int)( sizeof( *this ) );\n\tconst int lz4Status = LZ4_decompress_safe( (const char*)s_tableData.data(), (char*)this, (int)s_tableData.size(), cbThis );\n\tif( lz4Status != cbThis )\n\t{\n\t\tlogError( u8\"LZ4_decompress_safe failed with status %i\", lz4Status );\n\t\tthrow PLA_E_CABAPI_FAILURE;\n\t}\n#endif\n\n#if false\n\t// Temporary code to save the content of these lookup tables\n\tCAtlFile tempFile;\n\ttempFile.Create( LR\"(C:\\Temp\\2remove\\Whisper\\tables.bin)\", GENERIC_WRITE, 0, CREATE_ALWAYS );\n\ttempFile.Write( this, (DWORD)sizeof( *this ) );\n#endif\n}"
  },
  {
    "path": "Whisper/ML/LookupTablesData.h",
    "content": "#pragma once\n#include <stdint.h>\n#include <array>\n\nnamespace DirectCompute\n{\n\tstruct LookupTablesData\n\t{\n\t\tstd::array<uint16_t, 0x10000> gelu;\n\t\tstd::array<uint16_t, 0x10000> exponent;\n\n\t\tLookupTablesData();\n\t};\n}"
  },
  {
    "path": "Whisper/ML/LookupTablesData.inl",
    "content": "// This source file is generated by a tool\nstatic const std::array<uint8_t, 126808> s_tableData = {\n\t0xF0, 0xFF, 0xFF, 0xFF, 0xF1, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x02, 0x00, 0x03, 0x00, 0x03, 0x00, 0x04, 0x00, 0x04, 0x00, 0x05, 0x00, 0x05, 0x00, 0x06, 0x00, 0x06, 0x00, 0x07,\n\t0x00, 0x07, 0x00, 0x08, 0x00, 0x08, 0x00, 0x09, 0x00, 0x09, 0x00, 0x0A, 0x00, 0x0A, 0x00, 0x0B, 0x00, 0x0B, 0x00, 0x0C, 0x00, 0x0C, 0x00, 0x0D, 0x00, 0x0D, 0x00, 0x0E, 0x00, 0x0E, 0x00, 0x0F,\n\t0x00, 0x0F, 0x00, 0x10, 0x00, 0x10, 0x00, 0x11, 0x00, 0x11, 0x00, 0x12, 0x00, 0x12, 0x00, 0x13, 0x00, 0x13, 0x00, 0x14, 0x00, 0x14, 0x00, 0x15, 0x00, 0x15, 0x00, 0x16, 0x00, 0x16, 0x00, 0x17,\n\t0x00, 0x17, 0x00, 0x18, 0x00, 0x18, 0x00, 0x19, 0x00, 0x19, 0x00, 0x1A, 0x00, 0x1A, 0x00, 0x1B, 0x00, 0x1B, 0x00, 0x1C, 0x00, 0x1C, 0x00, 0x1D, 0x00, 0x1D, 0x00, 0x1E, 0x00, 0x1E, 0x00, 0x1F,\n\t0x00, 0x1F, 0x00, 0x20, 0x00, 0x20, 0x00, 0x21, 0x00, 0x21, 0x00, 0x22, 0x00, 0x22, 0x00, 0x23, 0x00, 0x23, 0x00, 0x24, 0x00, 0x24, 0x00, 0x25, 0x00, 0x25, 0x00, 0x26, 0x00, 0x26, 0x00, 0x27,\n\t0x00, 0x27, 0x00, 0x28, 0x00, 0x28, 0x00, 0x29, 0x00, 0x29, 0x00, 0x2A, 0x00, 0x2A, 0x00, 0x2B, 0x00, 0x2B, 0x00, 0x2C, 0x00, 0x2C, 0x00, 0x2D, 0x00, 0x2D, 0x00, 0x2E, 0x00, 0x2E, 0x00, 0x2F,\n\t0x00, 0x2F, 0x00, 0x30, 0x00, 0x30, 0x00, 0x31, 0x00, 0x31, 0x00, 0x32, 0x00, 0x32, 0x00, 0x33, 0x00, 0x33, 0x00, 0x34, 0x00, 0x34, 0x00, 0x35, 0x00, 0x35, 0x00, 0x36, 0x00, 0x36, 0x00, 0x37,\n\t0x00, 0x37, 0x00, 0x38, 0x00, 0x38, 0x00, 0x39, 0x00, 0x39, 0x00, 0x3A, 0x00, 0x3A, 0x00, 0x3B, 0x00, 0x3B, 0x00, 0x3C, 0x00, 0x3C, 0x00, 0x3D, 0x00, 0x3D, 0x00, 0x3E, 0x00, 0x3E, 0x00, 0x3F,\n\t0x00, 0x3F, 0x00, 0x40, 0x00, 0x40, 0x00, 0x41, 0x00, 0x41, 0x00, 0x42, 0x00, 0x42, 0x00, 0x43, 0x00, 0x43, 0x00, 0x44, 0x00, 0x44, 0x00, 0x45, 0x00, 0x45, 0x00, 0x46, 0x00, 0x46, 0x00, 0x47,\n\t0x00, 0x47, 0x00, 0x48, 0x00, 0x48, 0x00, 0x49, 0x00, 0x49, 0x00, 0x4A, 0x00, 0x4A, 0x00, 0x4B, 0x00, 0x4B, 0x00, 0x4C, 0x00, 0x4C, 0x00, 0x4D, 0x00, 0x4D, 0x00, 0x4E, 0x00, 0x4E, 0x00, 0x4F,\n\t0x00, 0x4F, 0x00, 0x50, 0x00, 0x50, 0x00, 0x51, 0x00, 0x51, 0x00, 0x52, 0x00, 0x52, 0x00, 0x53, 0x00, 0x53, 0x00, 0x54, 0x00, 0x54, 0x00, 0x55, 0x00, 0x55, 0x00, 0x56, 0x00, 0x56, 0x00, 0x57,\n\t0x00, 0x57, 0x00, 0x58, 0x00, 0x58, 0x00, 0x59, 0x00, 0x59, 0x00, 0x5A, 0x00, 0x5A, 0x00, 0x5B, 0x00, 0x5B, 0x00, 0x5C, 0x00, 0x5C, 0x00, 0x5D, 0x00, 0x5D, 0x00, 0x5E, 0x00, 0x5E, 0x00, 0x5F,\n\t0x00, 0x5F, 0x00, 0x60, 0x00, 0x60, 0x00, 0x61, 0x00, 0x61, 0x00, 0x62, 0x00, 0x62, 0x00, 0x63, 0x00, 0x63, 0x00, 0x64, 0x00, 0x64, 0x00, 0x65, 0x00, 0x65, 0x00, 0x66, 0x00, 0x66, 0x00, 0x67,\n\t0x00, 0x67, 0x00, 0x68, 0x00, 0x68, 0x00, 0x69, 0x00, 0x69, 0x00, 0x6A, 0x00, 0x6A, 0x00, 0x6B, 0x00, 0x6B, 0x00, 0x6C, 0x00, 0x6C, 0x00, 0x6D, 0x00, 0x6D, 0x00, 0x6E, 0x00, 0x6E, 0x00, 0x6F,\n\t0x00, 0x6F, 0x00, 0x70, 0x00, 0x70, 0x00, 0x71, 0x00, 0x71, 0x00, 0x72, 0x00, 0x72, 0x00, 0x73, 0x00, 0x73, 0x00, 0x74, 0x00, 0x74, 0x00, 0x75, 0x00, 0x75, 0x00, 0x76, 0x00, 0x76, 0x00, 0x77,\n\t0x00, 0x77, 0x00, 0x78, 0x00, 0x78, 0x00, 0x79, 0x00, 0x79, 0x00, 0x7A, 0x00, 0x7A, 0x00, 0x7B, 0x00, 0x7B, 0x00, 0x7C, 0x00, 0x7C, 0x00, 0x7D, 0x00, 0x7D, 0x00, 0x7E, 0x00, 0x7E, 0x00, 0x7F,\n\t0x00, 0x7F, 0x00, 0x80, 0x00, 0x80, 0x00, 0x81, 0x00, 0x81, 0x00, 0x82, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, 0x84, 0x00, 0x84, 0x00, 0x85, 0x00, 0x85, 0x00, 0x86, 0x00, 0x86, 0x00, 0x87,\n\t0x00, 0x87, 0x00, 0x88, 0x00, 0x88, 0x00, 0x89, 0x00, 0x89, 0x00, 0x8A, 0x00, 0x8A, 0x00, 0x8B, 0x00, 0x8B, 0x00, 0x8C, 0x00, 0x8C, 0x00, 0x8D, 0x00, 0x8D, 0x00, 0x8E, 0x00, 0x8E, 0x00, 0x8F,\n\t0x00, 0x8F, 0x00, 0x90, 0x00, 0x90, 0x00, 0x91, 0x00, 0x91, 0x00, 0x92, 0x00, 0x92, 0x00, 0x93, 0x00, 0x93, 0x00, 0x94, 0x00, 0x94, 0x00, 0x95, 0x00, 0x95, 0x00, 0x96, 0x00, 0x96, 0x00, 0x97,\n\t0x00, 0x97, 0x00, 0x98, 0x00, 0x98, 0x00, 0x99, 0x00, 0x99, 0x00, 0x9A, 0x00, 0x9A, 0x00, 0x9B, 0x00, 0x9B, 0x00, 0x9C, 0x00, 0x9C, 0x00, 0x9D, 0x00, 0x9D, 0x00, 0x9E, 0x00, 0x9E, 0x00, 0x9F,\n\t0x00, 0x9F, 0x00, 0xA0, 0x00, 0xA0, 0x00, 0xA1, 0x00, 0xA1, 0x00, 0xA2, 0x00, 0xA2, 0x00, 0xA3, 0x00, 0xA3, 0x00, 0xA4, 0x00, 0xA4, 0x00, 0xA5, 0x00, 0xA5, 0x00, 0xA6, 0x00, 0xA6, 0x00, 0xA7,\n\t0x00, 0xA7, 0x00, 0xA8, 0x00, 0xA8, 0x00, 0xA9, 0x00, 0xA9, 0x00, 0xAA, 0x00, 0xAA, 0x00, 0xAB, 0x00, 0xAB, 0x00, 0xAC, 0x00, 0xAC, 0x00, 0xAD, 0x00, 0xAD, 0x00, 0xAE, 0x00, 0xAE, 0x00, 0xAF,\n\t0x00, 0xAF, 0x00, 0xB0, 0x00, 0xB0, 0x00, 0xB1, 0x00, 0xB1, 0x00, 0xB2, 0x00, 0xB2, 0x00, 0xB3, 0x00, 0xB3, 0x00, 0xB4, 0x00, 0xB4, 0x00, 0xB5, 0x00, 0xB5, 0x00, 0xB6, 0x00, 0xB6, 0x00, 0xB7,\n\t0x00, 0xB7, 0x00, 0xB8, 0x00, 0xB8, 0x00, 0xB9, 0x00, 0xB9, 0x00, 0xBA, 0x00, 0xBA, 0x00, 0xBB, 0x00, 0xBB, 0x00, 0xBC, 0x00, 0xBC, 0x00, 0xBD, 0x00, 0xBD, 0x00, 0xBE, 0x00, 0xBE, 0x00, 0xBF,\n\t0x00, 0xBF, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC1, 0x00, 0xC1, 0x00, 0xC2, 0x00, 0xC2, 0x00, 0xC3, 0x00, 0xC3, 0x00, 0xC4, 0x00, 0xC4, 0x00, 0xC5, 0x00, 0xC5, 0x00, 0xC6, 0x00, 0xC6, 0x00, 0xC7,\n\t0x00, 0xC7, 0x00, 0xC8, 0x00, 0xC8, 0x00, 0xC9, 0x00, 0xC9, 0x00, 0xCA, 0x00, 0xCA, 0x00, 0xCB, 0x00, 0xCB, 0x00, 0xCC, 0x00, 0xCC, 0x00, 0xCD, 0x00, 0xCD, 0x00, 0xCE, 0x00, 0xCE, 0x00, 0xCF,\n\t0x00, 0xCF, 0x00, 0xD0, 0x00, 0xD0, 0x00, 0xD1, 0x00, 0xD1, 0x00, 0xD2, 0x00, 0xD2, 0x00, 0xD3, 0x00, 0xD3, 0x00, 0xD4, 0x00, 0xD4, 0x00, 0xD5, 0x00, 0xD5, 0x00, 0xD6, 0x00, 0xD6, 0x00, 0xD7,\n\t0x00, 0xD7, 0x00, 0xD8, 0x00, 0xD8, 0x00, 0xD9, 0x00, 0xD9, 0x00, 0xDA, 0x00, 0xDA, 0x00, 0xDB, 0x00, 0xDB, 0x00, 0xDC, 0x00, 0xDC, 0x00, 0xDD, 0x00, 0xDD, 0x00, 0xDE, 0x00, 0xDE, 0x00, 0xDF,\n\t0x00, 0xDF, 0x00, 0xE0, 0x00, 0xE0, 0x00, 0xE1, 0x00, 0xE1, 0x00, 0xE2, 0x00, 0xE2, 0x00, 0xE3, 0x00, 0xE3, 0x00, 0xE4, 0x00, 0xE4, 0x00, 0xE5, 0x00, 0xE5, 0x00, 0xE6, 0x00, 0xE6, 0x00, 0xE7,\n\t0x00, 0xE7, 0x00, 0xE8, 0x00, 0xE8, 0x00, 0xE9, 0x00, 0xE9, 0x00, 0xEA, 0x00, 0xEA, 0x00, 0xEB, 0x00, 0xEB, 0x00, 0xEC, 0x00, 0xEC, 0x00, 0xED, 0x00, 0xED, 0x00, 0xEE, 0x00, 0xEE, 0x00, 0xEF,\n\t0x00, 0xEF, 0x00, 0xF0, 0x00, 0xF0, 0x00, 0xF1, 0x00, 0xF1, 0x00, 0xF2, 0x00, 0xF2, 0x00, 0xF3, 0x00, 0xF3, 0x00, 0xF4, 0x00, 0xF4, 0x00, 0xF5, 0x00, 0xF5, 0x00, 0xF6, 0x00, 0xF6, 0x00, 0xF7,\n\t0x00, 0xF7, 0x00, 0xF8, 0x00, 0xF8, 0x00, 0xF9, 0x00, 0xF9, 0x00, 0xFA, 0x00, 0xFA, 0x00, 0xFB, 0x00, 0xFB, 0x00, 0xFC, 0x00, 0xFC, 0x00, 0xFD, 0x00, 0xFD, 0x00, 0xFE, 0x00, 0xFE, 0x00, 0xFF,\n\t0x00, 0xFF, 0xFB, 0x03, 0x10, 0x01, 0x01, 0x00, 0xF1, 0xFF, 0xFF, 0xFF, 0xEB, 0x02, 0x01, 0x02, 0x01, 0x03, 0x01, 0x03, 0x01, 0x04, 0x01, 0x04, 0x01, 0x05, 0x01, 0x05, 0x01, 0x06, 0x01, 0x06,\n\t0x01, 0x07, 0x01, 0x07, 0x01, 0x08, 0x01, 0x08, 0x01, 0x09, 0x01, 0x09, 0x01, 0x0A, 0x01, 0x0A, 0x01, 0x0B, 0x01, 0x0B, 0x01, 0x0C, 0x01, 0x0C, 0x01, 0x0D, 0x01, 0x0D, 0x01, 0x0E, 0x01, 0x0E,\n\t0x01, 0x0F, 0x01, 0x0F, 0x01, 0x10, 0x01, 0x10, 0x01, 0x11, 0x01, 0x11, 0x01, 0x12, 0x01, 0x12, 0x01, 0x13, 0x01, 0x13, 0x01, 0x14, 0x01, 0x14, 0x01, 0x15, 0x01, 0x15, 0x01, 0x16, 0x01, 0x16,\n\t0x01, 0x17, 0x01, 0x17, 0x01, 0x18, 0x01, 0x18, 0x01, 0x19, 0x01, 0x19, 0x01, 0x1A, 0x01, 0x1A, 0x01, 0x1B, 0x01, 0x1B, 0x01, 0x1C, 0x01, 0x1C, 0x01, 0x1D, 0x01, 0x1D, 0x01, 0x1E, 0x01, 0x1E,\n\t0x01, 0x1F, 0x01, 0x1F, 0x01, 0x20, 0x01, 0x20, 0x01, 0x21, 0x01, 0x21, 0x01, 0x22, 0x01, 0x22, 0x01, 0x23, 0x01, 0x23, 0x01, 0x24, 0x01, 0x24, 0x01, 0x25, 0x01, 0x25, 0x01, 0x26, 0x01, 0x26,\n\t0x01, 0x27, 0x01, 0x27, 0x01, 0x28, 0x01, 0x28, 0x01, 0x29, 0x01, 0x29, 0x01, 0x2A, 0x01, 0x2A, 0x01, 0x2B, 0x01, 0x2B, 0x01, 0x2C, 0x01, 0x2C, 0x01, 0x2D, 0x01, 0x2D, 0x01, 0x2E, 0x01, 0x2E,\n\t0x01, 0x2F, 0x01, 0x2F, 0x01, 0x30, 0x01, 0x30, 0x01, 0x31, 0x01, 0x31, 0x01, 0x32, 0x01, 0x32, 0x01, 0x33, 0x01, 0x33, 0x01, 0x34, 0x01, 0x34, 0x01, 0x35, 0x01, 0x35, 0x01, 0x36, 0x01, 0x36,\n\t0x01, 0x37, 0x01, 0x37, 0x01, 0x38, 0x01, 0x38, 0x01, 0x39, 0x01, 0x39, 0x01, 0x3A, 0x01, 0x3A, 0x01, 0x3B, 0x01, 0x3B, 0x01, 0x3C, 0x01, 0x3C, 0x01, 0x3D, 0x01, 0x3D, 0x01, 0x3E, 0x01, 0x3E,\n\t0x01, 0x3F, 0x01, 0x3F, 0x01, 0x40, 0x01, 0x40, 0x01, 0x41, 0x01, 0x41, 0x01, 0x42, 0x01, 0x42, 0x01, 0x43, 0x01, 0x43, 0x01, 0x44, 0x01, 0x44, 0x01, 0x45, 0x01, 0x45, 0x01, 0x46, 0x01, 0x46,\n\t0x01, 0x47, 0x01, 0x47, 0x01, 0x48, 0x01, 0x48, 0x01, 0x49, 0x01, 0x49, 0x01, 0x4A, 0x01, 0x4A, 0x01, 0x4B, 0x01, 0x4B, 0x01, 0x4C, 0x01, 0x4C, 0x01, 0x4D, 0x01, 0x4D, 0x01, 0x4E, 0x01, 0x4E,\n\t0x01, 0x4F, 0x01, 0x4F, 0x01, 0x50, 0x01, 0x50, 0x01, 0x51, 0x01, 0x51, 0x01, 0x52, 0x01, 0x52, 0x01, 0x53, 0x01, 0x53, 0x01, 0x54, 0x01, 0x54, 0x01, 0x55, 0x01, 0x55, 0x01, 0x56, 0x01, 0x56,\n\t0x01, 0x57, 0x01, 0x57, 0x01, 0x58, 0x01, 0x58, 0x01, 0x59, 0x01, 0x59, 0x01, 0x5A, 0x01, 0x5A, 0x01, 0x5B, 0x01, 0x5B, 0x01, 0x5C, 0x01, 0x5C, 0x01, 0x5D, 0x01, 0x5D, 0x01, 0x5E, 0x01, 0x5E,\n\t0x01, 0x5F, 0x01, 0x5F, 0x01, 0x60, 0x01, 0x60, 0x01, 0x61, 0x01, 0x61, 0x01, 0x62, 0x01, 0x62, 0x01, 0x63, 0x01, 0x63, 0x01, 0x64, 0x01, 0x64, 0x01, 0x65, 0x01, 0x65, 0x01, 0x66, 0x01, 0x66,\n\t0x01, 0x67, 0x01, 0x67, 0x01, 0x68, 0x01, 0x68, 0x01, 0x69, 0x01, 0x69, 0x01, 0x6A, 0x01, 0x6A, 0x01, 0x6B, 0x01, 0x6B, 0x01, 0x6C, 0x01, 0x6C, 0x01, 0x6D, 0x01, 0x6D, 0x01, 0x6E, 0x01, 0x6E,\n\t0x01, 0x6F, 0x01, 0x6F, 0x01, 0x70, 0x01, 0x70, 0x01, 0x71, 0x01, 0x71, 0x01, 0x72, 0x01, 0x72, 0x01, 0x73, 0x01, 0x73, 0x01, 0x74, 0x01, 0x74, 0x01, 0x75, 0x01, 0x75, 0x01, 0x76, 0x01, 0x76,\n\t0x01, 0x77, 0x01, 0x77, 0x01, 0x78, 0x01, 0x78, 0x01, 0x79, 0x01, 0x79, 0x01, 0x7A, 0x01, 0x7A, 0x01, 0x7B, 0x01, 0x7B, 0x01, 0x7C, 0x01, 0x7C, 0x01, 0x7D, 0x01, 0x7D, 0x01, 0x7E, 0x01, 0x7E,\n\t0x01, 0x7F, 0x01, 0x7F, 0x01, 0x80, 0x01, 0x80, 0x01, 0x81, 0x01, 0x81, 0x01, 0x82, 0x01, 0x82, 0x01, 0x83, 0x01, 0x83, 0x01, 0x84, 0x01, 0x84, 0x01, 0x85, 0x01, 0x85, 0x01, 0x86, 0x01, 0x86,\n\t0x01, 0x87, 0x01, 0x87, 0x01, 0x88, 0x01, 0x88, 0x01, 0x89, 0x01, 0x89, 0x01, 0x8A, 0x01, 0x8A, 0x01, 0x8B, 0x01, 0x8B, 0x01, 0x8C, 0x01, 0x8C, 0x01, 0x8D, 0x01, 0x8D, 0x01, 0x8E, 0x01, 0x8E,\n\t0x01, 0x8F, 0x01, 0x8F, 0x01, 0x90, 0x01, 0x90, 0x01, 0x91, 0x01, 0x91, 0x01, 0x92, 0x01, 0x92, 0x01, 0x93, 0x01, 0x93, 0x01, 0x94, 0x01, 0x94, 0x01, 0x95, 0x01, 0x95, 0x01, 0x96, 0x01, 0x96,\n\t0x01, 0x97, 0x01, 0x97, 0x01, 0x98, 0x01, 0x98, 0x01, 0x99, 0x01, 0x99, 0x01, 0x9A, 0x01, 0x9A, 0x01, 0x9B, 0x01, 0x9B, 0x01, 0x9C, 0x01, 0x9C, 0x01, 0x9D, 0x01, 0x9D, 0x01, 0x9E, 0x01, 0x9E,\n\t0x01, 0x9F, 0x01, 0x9F, 0x01, 0xA0, 0x01, 0xA0, 0x01, 0xA1, 0x01, 0xA1, 0x01, 0xA2, 0x01, 0xA2, 0x01, 0xA3, 0x01, 0xA3, 0x01, 0xA4, 0x01, 0xA4, 0x01, 0xA5, 0x01, 0xA5, 0x01, 0xA6, 0x01, 0xA6,\n\t0x01, 0xA7, 0x01, 0xA7, 0x01, 0xA8, 0x01, 0xA8, 0x01, 0xA9, 0x01, 0xA9, 0x01, 0xAA, 0x01, 0xAA, 0x01, 0xAB, 0x01, 0xAB, 0x01, 0xAC, 0x01, 0xAC, 0x01, 0xAD, 0x01, 0xAD, 0x01, 0xAE, 0x01, 0xAE,\n\t0x01, 0xAF, 0x01, 0xAF, 0x01, 0xB0, 0x01, 0xB0, 0x01, 0xB1, 0x01, 0xB1, 0x01, 0xB2, 0x01, 0xB2, 0x01, 0xB3, 0x01, 0xB3, 0x01, 0xB4, 0x01, 0xB4, 0x01, 0xB5, 0x01, 0xB5, 0x01, 0xB6, 0x01, 0xB6,\n\t0x01, 0xB7, 0x01, 0xB7, 0x01, 0xB8, 0x01, 0xB8, 0x01, 0xB9, 0x01, 0xB9, 0x01, 0xBA, 0x01, 0xBA, 0x01, 0xBB, 0x01, 0xBB, 0x01, 0xBC, 0x01, 0xBC, 0x01, 0xBD, 0x01, 0xBD, 0x01, 0xBE, 0x01, 0xBE,\n\t0x01, 0xBF, 0x01, 0xBF, 0x01, 0xC0, 0x01, 0xC0, 0x01, 0xC1, 0x01, 0xC1, 0x01, 0xC2, 0x01, 0xC2, 0x01, 0xC3, 0x01, 0xC3, 0x01, 0xC4, 0x01, 0xC4, 0x01, 0xC5, 0x01, 0xC5, 0x01, 0xC6, 0x01, 0xC6,\n\t0x01, 0xC7, 0x01, 0xC7, 0x01, 0xC8, 0x01, 0xC8, 0x01, 0xC9, 0x01, 0xC9, 0x01, 0xCA, 0x01, 0xCA, 0x01, 0xCB, 0x01, 0xCB, 0x01, 0xCC, 0x01, 0xCC, 0x01, 0xCD, 0x01, 0xCD, 0x01, 0xCE, 0x01, 0xCE,\n\t0x01, 0xCF, 0x01, 0xCF, 0x01, 0xD0, 0x01, 0xD0, 0x01, 0xD1, 0x01, 0xD1, 0x01, 0xD2, 0x01, 0xD2, 0x01, 0xD3, 0x01, 0xD3, 0x01, 0xD4, 0x01, 0xD4, 0x01, 0xD5, 0x01, 0xD5, 0x01, 0xD6, 0x01, 0xD6,\n\t0x01, 0xD7, 0x01, 0xD7, 0x01, 0xD8, 0x01, 0xD8, 0x01, 0xD9, 0x01, 0xD9, 0x01, 0xDA, 0x01, 0xDA, 0x01, 0xDB, 0x01, 0xDB, 0x01, 0xDC, 0x01, 0xDC, 0x01, 0xDD, 0x01, 0xDD, 0x01, 0xDE, 0x01, 0xDE,\n\t0x01, 0xDF, 0x01, 0xDF, 0x01, 0xE0, 0x01, 0xE0, 0x01, 0xE1, 0x01, 0xE1, 0x01, 0xE2, 0x01, 0xE2, 0x01, 0xE3, 0x01, 0xE3, 0x01, 0xE4, 0x01, 0xE4, 0x01, 0xE5, 0x01, 0xE5, 0x01, 0xE6, 0x01, 0xE6,\n\t0x01, 0xE7, 0x01, 0xE7, 0x01, 0xE8, 0x01, 0xE8, 0x01, 0xE9, 0x01, 0xE9, 0x01, 0xEA, 0x01, 0xEA, 0x01, 0xEB, 0x01, 0xEB, 0x01, 0xEC, 0x01, 0xEC, 0x01, 0xED, 0x01, 0xED, 0x01, 0xEE, 0x01, 0xEE,\n\t0x01, 0xEF, 0x01, 0xEF, 0x01, 0xF0, 0x01, 0xF0, 0x01, 0xF1, 0x01, 0xF1, 0x01, 0xF2, 0x01, 0xF2, 0x01, 0xF3, 0x01, 0xF3, 0x01, 0xF4, 0x01, 0xF4, 0x01, 0xF5, 0x01, 0xF5, 0x01, 0xF6, 0x01, 0xF6,\n\t0x01, 0xF7, 0x01, 0xF7, 0x01, 0xF8, 0x01, 0xF8, 0x01, 0xF9, 0x01, 0xF9, 0x01, 0xFA, 0x01, 0xFA, 0x01, 0xFB, 0x01, 0xFB, 0x01, 0xFC, 0x01, 0xFC, 0x01, 0xFD, 0x01, 0xFD, 0x01, 0xFE, 0x01, 0xFE,\n\t0x01, 0xFF, 0x01, 0xFF, 0xF9, 0x07, 0x00, 0xFD, 0x03, 0x00, 0x01, 0x00, 0xF1, 0xFF, 0xFF, 0xFF, 0xE7, 0x03, 0x02, 0x03, 0x02, 0x04, 0x02, 0x04, 0x02, 0x05, 0x02, 0x05, 0x02, 0x06, 0x02, 0x06,\n\t0x02, 0x07, 0x02, 0x07, 0x02, 0x08, 0x02, 0x08, 0x02, 0x09, 0x02, 0x09, 0x02, 0x0A, 0x02, 0x0A, 0x02, 0x0B, 0x02, 0x0B, 0x02, 0x0C, 0x02, 0x0C, 0x02, 0x0D, 0x02, 0x0D, 0x02, 0x0E, 0x02, 0x0E,\n\t0x02, 0x0F, 0x02, 0x0F, 0x02, 0x10, 0x02, 0x10, 0x02, 0x11, 0x02, 0x11, 0x02, 0x12, 0x02, 0x12, 0x02, 0x13, 0x02, 0x13, 0x02, 0x14, 0x02, 0x14, 0x02, 0x15, 0x02, 0x15, 0x02, 0x16, 0x02, 0x16,\n\t0x02, 0x17, 0x02, 0x17, 0x02, 0x18, 0x02, 0x18, 0x02, 0x19, 0x02, 0x19, 0x02, 0x1A, 0x02, 0x1A, 0x02, 0x1B, 0x02, 0x1B, 0x02, 0x1C, 0x02, 0x1C, 0x02, 0x1D, 0x02, 0x1D, 0x02, 0x1E, 0x02, 0x1E,\n\t0x02, 0x1F, 0x02, 0x1F, 0x02, 0x20, 0x02, 0x20, 0x02, 0x21, 0x02, 0x21, 0x02, 0x22, 0x02, 0x22, 0x02, 0x23, 0x02, 0x23, 0x02, 0x24, 0x02, 0x24, 0x02, 0x25, 0x02, 0x25, 0x02, 0x26, 0x02, 0x26,\n\t0x02, 0x27, 0x02, 0x27, 0x02, 0x28, 0x02, 0x28, 0x02, 0x29, 0x02, 0x29, 0x02, 0x2A, 0x02, 0x2A, 0x02, 0x2B, 0x02, 0x2B, 0x02, 0x2C, 0x02, 0x2C, 0x02, 0x2D, 0x02, 0x2D, 0x02, 0x2E, 0x02, 0x2E,\n\t0x02, 0x2F, 0x02, 0x2F, 0x02, 0x30, 0x02, 0x30, 0x02, 0x31, 0x02, 0x31, 0x02, 0x32, 0x02, 0x32, 0x02, 0x33, 0x02, 0x33, 0x02, 0x34, 0x02, 0x34, 0x02, 0x35, 0x02, 0x35, 0x02, 0x36, 0x02, 0x36,\n\t0x02, 0x37, 0x02, 0x37, 0x02, 0x38, 0x02, 0x38, 0x02, 0x39, 0x02, 0x39, 0x02, 0x3A, 0x02, 0x3A, 0x02, 0x3B, 0x02, 0x3B, 0x02, 0x3C, 0x02, 0x3C, 0x02, 0x3D, 0x02, 0x3D, 0x02, 0x3E, 0x02, 0x3E,\n\t0x02, 0x3F, 0x02, 0x3F, 0x02, 0x40, 0x02, 0x40, 0x02, 0x41, 0x02, 0x41, 0x02, 0x42, 0x02, 0x42, 0x02, 0x43, 0x02, 0x43, 0x02, 0x44, 0x02, 0x44, 0x02, 0x45, 0x02, 0x45, 0x02, 0x46, 0x02, 0x46,\n\t0x02, 0x47, 0x02, 0x47, 0x02, 0x48, 0x02, 0x48, 0x02, 0x49, 0x02, 0x49, 0x02, 0x4A, 0x02, 0x4A, 0x02, 0x4B, 0x02, 0x4B, 0x02, 0x4C, 0x02, 0x4C, 0x02, 0x4D, 0x02, 0x4D, 0x02, 0x4E, 0x02, 0x4E,\n\t0x02, 0x4F, 0x02, 0x4F, 0x02, 0x50, 0x02, 0x50, 0x02, 0x51, 0x02, 0x51, 0x02, 0x52, 0x02, 0x52, 0x02, 0x53, 0x02, 0x53, 0x02, 0x54, 0x02, 0x54, 0x02, 0x55, 0x02, 0x55, 0x02, 0x56, 0x02, 0x56,\n\t0x02, 0x57, 0x02, 0x57, 0x02, 0x58, 0x02, 0x58, 0x02, 0x59, 0x02, 0x59, 0x02, 0x5A, 0x02, 0x5A, 0x02, 0x5B, 0x02, 0x5B, 0x02, 0x5C, 0x02, 0x5C, 0x02, 0x5D, 0x02, 0x5D, 0x02, 0x5E, 0x02, 0x5E,\n\t0x02, 0x5F, 0x02, 0x5F, 0x02, 0x60, 0x02, 0x60, 0x02, 0x61, 0x02, 0x61, 0x02, 0x62, 0x02, 0x62, 0x02, 0x63, 0x02, 0x63, 0x02, 0x64, 0x02, 0x64, 0x02, 0x65, 0x02, 0x65, 0x02, 0x66, 0x02, 0x66,\n\t0x02, 0x67, 0x02, 0x67, 0x02, 0x68, 0x02, 0x68, 0x02, 0x69, 0x02, 0x69, 0x02, 0x6A, 0x02, 0x6A, 0x02, 0x6B, 0x02, 0x6B, 0x02, 0x6C, 0x02, 0x6C, 0x02, 0x6D, 0x02, 0x6D, 0x02, 0x6E, 0x02, 0x6E,\n\t0x02, 0x6F, 0x02, 0x6F, 0x02, 0x70, 0x02, 0x70, 0x02, 0x71, 0x02, 0x71, 0x02, 0x72, 0x02, 0x72, 0x02, 0x73, 0x02, 0x73, 0x02, 0x74, 0x02, 0x74, 0x02, 0x75, 0x02, 0x75, 0x02, 0x76, 0x02, 0x76,\n\t0x02, 0x77, 0x02, 0x77, 0x02, 0x78, 0x02, 0x78, 0x02, 0x79, 0x02, 0x79, 0x02, 0x7A, 0x02, 0x7A, 0x02, 0x7B, 0x02, 0x7B, 0x02, 0x7C, 0x02, 0x7C, 0x02, 0x7D, 0x02, 0x7D, 0x02, 0x7E, 0x02, 0x7E,\n\t0x02, 0x7F, 0x02, 0x7F, 0x02, 0x80, 0x02, 0x80, 0x02, 0x81, 0x02, 0x81, 0x02, 0x82, 0x02, 0x82, 0x02, 0x83, 0x02, 0x83, 0x02, 0x84, 0x02, 0x84, 0x02, 0x85, 0x02, 0x85, 0x02, 0x86, 0x02, 0x86,\n\t0x02, 0x87, 0x02, 0x87, 0x02, 0x88, 0x02, 0x88, 0x02, 0x89, 0x02, 0x89, 0x02, 0x8A, 0x02, 0x8A, 0x02, 0x8B, 0x02, 0x8B, 0x02, 0x8C, 0x02, 0x8C, 0x02, 0x8D, 0x02, 0x8D, 0x02, 0x8E, 0x02, 0x8E,\n\t0x02, 0x8F, 0x02, 0x8F, 0x02, 0x90, 0x02, 0x90, 0x02, 0x91, 0x02, 0x91, 0x02, 0x92, 0x02, 0x92, 0x02, 0x93, 0x02, 0x93, 0x02, 0x94, 0x02, 0x94, 0x02, 0x95, 0x02, 0x95, 0x02, 0x96, 0x02, 0x96,\n\t0x02, 0x97, 0x02, 0x97, 0x02, 0x98, 0x02, 0x98, 0x02, 0x99, 0x02, 0x99, 0x02, 0x9A, 0x02, 0x9A, 0x02, 0x9B, 0x02, 0x9B, 0x02, 0x9C, 0x02, 0x9C, 0x02, 0x9D, 0x02, 0x9D, 0x02, 0x9E, 0x02, 0x9E,\n\t0x02, 0x9F, 0x02, 0x9F, 0x02, 0xA0, 0x02, 0xA0, 0x02, 0xA1, 0x02, 0xA1, 0x02, 0xA2, 0x02, 0xA2, 0x02, 0xA3, 0x02, 0xA3, 0x02, 0xA4, 0x02, 0xA4, 0x02, 0xA5, 0x02, 0xA5, 0x02, 0xA6, 0x02, 0xA6,\n\t0x02, 0xA7, 0x02, 0xA7, 0x02, 0xA8, 0x02, 0xA8, 0x02, 0xA9, 0x02, 0xA9, 0x02, 0xAA, 0x02, 0xAA, 0x02, 0xAB, 0x02, 0xAB, 0x02, 0xAC, 0x02, 0xAC, 0x02, 0xAD, 0x02, 0xAD, 0x02, 0xAE, 0x02, 0xAE,\n\t0x02, 0xAF, 0x02, 0xAF, 0x02, 0xB0, 0x02, 0xB0, 0x02, 0xB1, 0x02, 0xB1, 0x02, 0xB2, 0x02, 0xB2, 0x02, 0xB3, 0x02, 0xB3, 0x02, 0xB4, 0x02, 0xB4, 0x02, 0xB5, 0x02, 0xB5, 0x02, 0xB6, 0x02, 0xB6,\n\t0x02, 0xB7, 0x02, 0xB7, 0x02, 0xB8, 0x02, 0xB8, 0x02, 0xB9, 0x02, 0xB9, 0x02, 0xBA, 0x02, 0xBA, 0x02, 0xBB, 0x02, 0xBB, 0x02, 0xBC, 0x02, 0xBC, 0x02, 0xBD, 0x02, 0xBD, 0x02, 0xBE, 0x02, 0xBE,\n\t0x02, 0xBF, 0x02, 0xBF, 0x02, 0xC0, 0x02, 0xC0, 0x02, 0xC1, 0x02, 0xC1, 0x02, 0xC2, 0x02, 0xC2, 0x02, 0xC3, 0x02, 0xC3, 0x02, 0xC4, 0x02, 0xC4, 0x02, 0xC5, 0x02, 0xC5, 0x02, 0xC6, 0x02, 0xC6,\n\t0x02, 0xC7, 0x02, 0xC7, 0x02, 0xC8, 0x02, 0xC8, 0x02, 0xC9, 0x02, 0xC9, 0x02, 0xCA, 0x02, 0xCA, 0x02, 0xCB, 0x02, 0xCB, 0x02, 0xCC, 0x02, 0xCC, 0x02, 0xCD, 0x02, 0xCD, 0x02, 0xCE, 0x02, 0xCE,\n\t0x02, 0xCF, 0x02, 0xCF, 0x02, 0xD0, 0x02, 0xD0, 0x02, 0xD1, 0x02, 0xD1, 0x02, 0xD2, 0x02, 0xD2, 0x02, 0xD3, 0x02, 0xD3, 0x02, 0xD4, 0x02, 0xD4, 0x02, 0xD5, 0x02, 0xD5, 0x02, 0xD6, 0x02, 0xD6,\n\t0x02, 0xD7, 0x02, 0xD7, 0x02, 0xD8, 0x02, 0xD8, 0x02, 0xD9, 0x02, 0xD9, 0x02, 0xDA, 0x02, 0xDA, 0x02, 0xDB, 0x02, 0xDB, 0x02, 0xDC, 0x02, 0xDC, 0x02, 0xDD, 0x02, 0xDD, 0x02, 0xDE, 0x02, 0xDE,\n\t0x02, 0xDF, 0x02, 0xDF, 0x02, 0xE0, 0x02, 0xE0, 0x02, 0xE1, 0x02, 0xE1, 0x02, 0xE2, 0x02, 0xE2, 0x02, 0xE3, 0x02, 0xE3, 0x02, 0xE4, 0x02, 0xE4, 0x02, 0xE5, 0x02, 0xE5, 0x02, 0xE6, 0x02, 0xE6,\n\t0x02, 0xE7, 0x02, 0xE7, 0x02, 0xE8, 0x02, 0xE8, 0x02, 0xE9, 0x02, 0xE9, 0x02, 0xEA, 0x02, 0xEA, 0x02, 0xEB, 0x02, 0xEB, 0x02, 0xEC, 0x02, 0xEC, 0x02, 0xED, 0x02, 0xED, 0x02, 0xEE, 0x02, 0xEE,\n\t0x02, 0xEF, 0x02, 0xEF, 0x02, 0xF0, 0x02, 0xF0, 0x02, 0xF1, 0x02, 0xF1, 0x02, 0xF2, 0x02, 0xF2, 0x02, 0xF3, 0x02, 0xF3, 0x02, 0xF4, 0x02, 0xF4, 0x02, 0xF5, 0x02, 0xF5, 0x02, 0xF6, 0x02, 0xF6,\n\t0x02, 0xF7, 0x02, 0xF7, 0x02, 0xF8, 0x02, 0xF8, 0x02, 0xF9, 0x02, 0xF9, 0x02, 0xFA, 0x02, 0xFA, 0x02, 0xFB, 0x02, 0xFB, 0x02, 0xFC, 0x02, 0xFC, 0x02, 0xFD, 0x02, 0xFD, 0x02, 0xFE, 0x02, 0xFE,\n\t0x02, 0xFF, 0x02, 0xFF, 0xF5, 0x0B, 0x00, 0xF9, 0x07, 0x00, 0xFD, 0x03, 0x00, 0x01, 0x00, 0xF1, 0xFF, 0xFF, 0xFF, 0xE3, 0x04, 0x03, 0x04, 0x03, 0x05, 0x03, 0x05, 0x03, 0x06, 0x03, 0x06, 0x03,\n\t0x07, 0x03, 0x07, 0x03, 0x08, 0x03, 0x08, 0x03, 0x09, 0x03, 0x09, 0x03, 0x0A, 0x03, 0x0A, 0x03, 0x0B, 0x03, 0x0B, 0x03, 0x0C, 0x03, 0x0C, 0x03, 0x0D, 0x03, 0x0D, 0x03, 0x0E, 0x03, 0x0E, 0x03,\n\t0x0F, 0x03, 0x0F, 0x03, 0x10, 0x03, 0x10, 0x03, 0x11, 0x03, 0x11, 0x03, 0x12, 0x03, 0x12, 0x03, 0x13, 0x03, 0x13, 0x03, 0x14, 0x03, 0x14, 0x03, 0x15, 0x03, 0x15, 0x03, 0x16, 0x03, 0x16, 0x03,\n\t0x17, 0x03, 0x17, 0x03, 0x18, 0x03, 0x18, 0x03, 0x19, 0x03, 0x19, 0x03, 0x1A, 0x03, 0x1A, 0x03, 0x1B, 0x03, 0x1B, 0x03, 0x1C, 0x03, 0x1C, 0x03, 0x1D, 0x03, 0x1D, 0x03, 0x1E, 0x03, 0x1E, 0x03,\n\t0x1F, 0x03, 0x1F, 0x03, 0x20, 0x03, 0x20, 0x03, 0x21, 0x03, 0x21, 0x03, 0x22, 0x03, 0x22, 0x03, 0x23, 0x03, 0x23, 0x03, 0x24, 0x03, 0x24, 0x03, 0x25, 0x03, 0x25, 0x03, 0x26, 0x03, 0x26, 0x03,\n\t0x27, 0x03, 0x27, 0x03, 0x28, 0x03, 0x28, 0x03, 0x29, 0x03, 0x29, 0x03, 0x2A, 0x03, 0x2A, 0x03, 0x2B, 0x03, 0x2B, 0x03, 0x2C, 0x03, 0x2C, 0x03, 0x2D, 0x03, 0x2D, 0x03, 0x2E, 0x03, 0x2E, 0x03,\n\t0x2F, 0x03, 0x2F, 0x03, 0x30, 0x03, 0x30, 0x03, 0x31, 0x03, 0x31, 0x03, 0x32, 0x03, 0x32, 0x03, 0x33, 0x03, 0x33, 0x03, 0x34, 0x03, 0x34, 0x03, 0x35, 0x03, 0x35, 0x03, 0x36, 0x03, 0x36, 0x03,\n\t0x37, 0x03, 0x37, 0x03, 0x38, 0x03, 0x38, 0x03, 0x39, 0x03, 0x39, 0x03, 0x3A, 0x03, 0x3A, 0x03, 0x3B, 0x03, 0x3B, 0x03, 0x3C, 0x03, 0x3C, 0x03, 0x3D, 0x03, 0x3D, 0x03, 0x3E, 0x03, 0x3E, 0x03,\n\t0x3F, 0x03, 0x3F, 0x03, 0x40, 0x03, 0x40, 0x03, 0x41, 0x03, 0x41, 0x03, 0x42, 0x03, 0x42, 0x03, 0x43, 0x03, 0x43, 0x03, 0x44, 0x03, 0x44, 0x03, 0x45, 0x03, 0x45, 0x03, 0x46, 0x03, 0x46, 0x03,\n\t0x47, 0x03, 0x47, 0x03, 0x48, 0x03, 0x48, 0x03, 0x49, 0x03, 0x49, 0x03, 0x4A, 0x03, 0x4A, 0x03, 0x4B, 0x03, 0x4B, 0x03, 0x4C, 0x03, 0x4C, 0x03, 0x4D, 0x03, 0x4D, 0x03, 0x4E, 0x03, 0x4E, 0x03,\n\t0x4F, 0x03, 0x4F, 0x03, 0x50, 0x03, 0x50, 0x03, 0x51, 0x03, 0x51, 0x03, 0x52, 0x03, 0x52, 0x03, 0x53, 0x03, 0x53, 0x03, 0x54, 0x03, 0x54, 0x03, 0x55, 0x03, 0x55, 0x03, 0x56, 0x03, 0x56, 0x03,\n\t0x57, 0x03, 0x57, 0x03, 0x58, 0x03, 0x58, 0x03, 0x59, 0x03, 0x59, 0x03, 0x5A, 0x03, 0x5A, 0x03, 0x5B, 0x03, 0x5B, 0x03, 0x5C, 0x03, 0x5C, 0x03, 0x5D, 0x03, 0x5D, 0x03, 0x5E, 0x03, 0x5E, 0x03,\n\t0x5F, 0x03, 0x5F, 0x03, 0x60, 0x03, 0x60, 0x03, 0x61, 0x03, 0x61, 0x03, 0x62, 0x03, 0x62, 0x03, 0x63, 0x03, 0x63, 0x03, 0x64, 0x03, 0x64, 0x03, 0x65, 0x03, 0x65, 0x03, 0x66, 0x03, 0x66, 0x03,\n\t0x67, 0x03, 0x67, 0x03, 0x68, 0x03, 0x68, 0x03, 0x69, 0x03, 0x69, 0x03, 0x6A, 0x03, 0x6A, 0x03, 0x6B, 0x03, 0x6B, 0x03, 0x6C, 0x03, 0x6C, 0x03, 0x6D, 0x03, 0x6D, 0x03, 0x6E, 0x03, 0x6E, 0x03,\n\t0x6F, 0x03, 0x6F, 0x03, 0x70, 0x03, 0x70, 0x03, 0x71, 0x03, 0x71, 0x03, 0x72, 0x03, 0x72, 0x03, 0x73, 0x03, 0x73, 0x03, 0x74, 0x03, 0x74, 0x03, 0x75, 0x03, 0x75, 0x03, 0x76, 0x03, 0x76, 0x03,\n\t0x77, 0x03, 0x77, 0x03, 0x78, 0x03, 0x78, 0x03, 0x79, 0x03, 0x79, 0x03, 0x7A, 0x03, 0x7A, 0x03, 0x7B, 0x03, 0x7B, 0x03, 0x7C, 0x03, 0x7C, 0x03, 0x7D, 0x03, 0x7D, 0x03, 0x7E, 0x03, 0x7E, 0x03,\n\t0x7F, 0x03, 0x7F, 0x03, 0x80, 0x03, 0x80, 0x03, 0x81, 0x03, 0x81, 0x03, 0x82, 0x03, 0x82, 0x03, 0x83, 0x03, 0x83, 0x03, 0x84, 0x03, 0x84, 0x03, 0x85, 0x03, 0x85, 0x03, 0x86, 0x03, 0x86, 0x03,\n\t0x87, 0x03, 0x87, 0x03, 0x88, 0x03, 0x88, 0x03, 0x89, 0x03, 0x89, 0x03, 0x8A, 0x03, 0x8A, 0x03, 0x8B, 0x03, 0x8B, 0x03, 0x8C, 0x03, 0x8C, 0x03, 0x8D, 0x03, 0x8D, 0x03, 0x8E, 0x03, 0x8E, 0x03,\n\t0x8F, 0x03, 0x8F, 0x03, 0x90, 0x03, 0x90, 0x03, 0x91, 0x03, 0x91, 0x03, 0x92, 0x03, 0x92, 0x03, 0x93, 0x03, 0x93, 0x03, 0x94, 0x03, 0x94, 0x03, 0x95, 0x03, 0x95, 0x03, 0x96, 0x03, 0x96, 0x03,\n\t0x97, 0x03, 0x97, 0x03, 0x98, 0x03, 0x98, 0x03, 0x99, 0x03, 0x99, 0x03, 0x9A, 0x03, 0x9A, 0x03, 0x9B, 0x03, 0x9B, 0x03, 0x9C, 0x03, 0x9C, 0x03, 0x9D, 0x03, 0x9D, 0x03, 0x9E, 0x03, 0x9E, 0x03,\n\t0x9F, 0x03, 0x9F, 0x03, 0xA0, 0x03, 0xA0, 0x03, 0xA1, 0x03, 0xA1, 0x03, 0xA2, 0x03, 0xA2, 0x03, 0xA3, 0x03, 0xA3, 0x03, 0xA4, 0x03, 0xA4, 0x03, 0xA5, 0x03, 0xA5, 0x03, 0xA6, 0x03, 0xA6, 0x03,\n\t0xA7, 0x03, 0xA7, 0x03, 0xA8, 0x03, 0xA8, 0x03, 0xA9, 0x03, 0xA9, 0x03, 0xAA, 0x03, 0xAA, 0x03, 0xAB, 0x03, 0xAB, 0x03, 0xAC, 0x03, 0xAC, 0x03, 0xAD, 0x03, 0xAD, 0x03, 0xAE, 0x03, 0xAE, 0x03,\n\t0xAF, 0x03, 0xAF, 0x03, 0xB0, 0x03, 0xB0, 0x03, 0xB1, 0x03, 0xB1, 0x03, 0xB2, 0x03, 0xB2, 0x03, 0xB3, 0x03, 0xB3, 0x03, 0xB4, 0x03, 0xB4, 0x03, 0xB5, 0x03, 0xB5, 0x03, 0xB6, 0x03, 0xB6, 0x03,\n\t0xB7, 0x03, 0xB7, 0x03, 0xB8, 0x03, 0xB8, 0x03, 0xB9, 0x03, 0xB9, 0x03, 0xBA, 0x03, 0xBA, 0x03, 0xBB, 0x03, 0xBB, 0x03, 0xBC, 0x03, 0xBC, 0x03, 0xBD, 0x03, 0xBD, 0x03, 0xBE, 0x03, 0xBE, 0x03,\n\t0xBF, 0x03, 0xBF, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC1, 0x03, 0xC1, 0x03, 0xC2, 0x03, 0xC2, 0x03, 0xC3, 0x03, 0xC3, 0x03, 0xC4, 0x03, 0xC4, 0x03, 0xC5, 0x03, 0xC5, 0x03, 0xC6, 0x03, 0xC6, 0x03,\n\t0xC7, 0x03, 0xC7, 0x03, 0xC8, 0x03, 0xC8, 0x03, 0xC9, 0x03, 0xC9, 0x03, 0xCA, 0x03, 0xCA, 0x03, 0xCB, 0x03, 0xCB, 0x03, 0xCC, 0x03, 0xCC, 0x03, 0xCD, 0x03, 0xCD, 0x03, 0xCE, 0x03, 0xCE, 0x03,\n\t0xCF, 0x03, 0xCF, 0x03, 0xD0, 0x03, 0xD0, 0x03, 0xD1, 0x03, 0xD1, 0x03, 0xD2, 0x03, 0xD2, 0x03, 0xD3, 0x03, 0xD3, 0x03, 0xD4, 0x03, 0xD4, 0x03, 0xD5, 0x03, 0xD5, 0x03, 0xD6, 0x03, 0xD6, 0x03,\n\t0xD7, 0x03, 0xD7, 0x03, 0xD8, 0x03, 0xD8, 0x03, 0xD9, 0x03, 0xD9, 0x03, 0xDA, 0x03, 0xDA, 0x03, 0xDB, 0x03, 0xDB, 0x03, 0xDC, 0x03, 0xDC, 0x03, 0xDD, 0x03, 0xDD, 0x03, 0xDE, 0x03, 0xDE, 0x03,\n\t0xDF, 0x03, 0xDF, 0x03, 0xE0, 0x03, 0xE0, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE2, 0x03, 0xE2, 0x03, 0xE3, 0x03, 0xE3, 0x03, 0xE4, 0x03, 0xE4, 0x03, 0xE5, 0x03, 0xE5, 0x03, 0xE6, 0x03, 0xE6, 0x03,\n\t0xE7, 0x03, 0xE7, 0x03, 0xE8, 0x03, 0xE8, 0x03, 0xE9, 0x03, 0xE9, 0x03, 0xEA, 0x03, 0xEA, 0x03, 0xEB, 0x03, 0xEB, 0x03, 0xEC, 0x03, 0xEC, 0x03, 0xED, 0x03, 0xED, 0x03, 0xEE, 0x03, 0xEE, 0x03,\n\t0xEF, 0x03, 0xEF, 0x03, 0xF0, 0x03, 0xF0, 0x03, 0xF1, 0x03, 0xF1, 0x03, 0xF2, 0x03, 0xF2, 0x03, 0xF3, 0x03, 0xF3, 0x03, 0xF4, 0x03, 0xF4, 0x03, 0xF5, 0x03, 0xF5, 0x03, 0xF6, 0x03, 0xF6, 0x03,\n\t0xF7, 0x03, 0xF7, 0x03, 0xF8, 0x03, 0xF8, 0x03, 0xF9, 0x03, 0xF9, 0x03, 0xFA, 0x03, 0xFA, 0x03, 0xFB, 0x03, 0xFB, 0x03, 0xFC, 0x03, 0xFC, 0x03, 0xFD, 0x03, 0xFD, 0x03, 0xFE, 0x03, 0xFE, 0x03,\n\t0xFF, 0x03, 0xFF, 0xF1, 0x0F, 0xF1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFB, 0x01, 0x04, 0x02, 0x04, 0x03, 0x04, 0x04, 0x04, 0x05, 0x04,\n\t0x06, 0x04, 0x07, 0x04, 0x08, 0x04, 0x09, 0x04, 0x0A, 0x04, 0x0B, 0x04, 0x0C, 0x04, 0x0D, 0x04, 0x0E, 0x04, 0x0F, 0x04, 0x10, 0x04, 0x11, 0x04, 0x12, 0x04, 0x13, 0x04, 0x14, 0x04, 0x15, 0x04,\n\t0x16, 0x04, 0x17, 0x04, 0x18, 0x04, 0x19, 0x04, 0x1A, 0x04, 0x1B, 0x04, 0x1C, 0x04, 0x1D, 0x04, 0x1E, 0x04, 0x1F, 0x04, 0x20, 0x04, 0x21, 0x04, 0x22, 0x04, 0x23, 0x04, 0x24, 0x04, 0x25, 0x04,\n\t0x26, 0x04, 0x27, 0x04, 0x28, 0x04, 0x29, 0x04, 0x2A, 0x04, 0x2B, 0x04, 0x2C, 0x04, 0x2D, 0x04, 0x2E, 0x04, 0x2F, 0x04, 0x30, 0x04, 0x31, 0x04, 0x32, 0x04, 0x33, 0x04, 0x34, 0x04, 0x35, 0x04,\n\t0x36, 0x04, 0x37, 0x04, 0x38, 0x04, 0x39, 0x04, 0x3A, 0x04, 0x3B, 0x04, 0x3C, 0x04, 0x3D, 0x04, 0x3E, 0x04, 0x3F, 0x04, 0x40, 0x04, 0x41, 0x04, 0x42, 0x04, 0x43, 0x04, 0x44, 0x04, 0x45, 0x04,\n\t0x46, 0x04, 0x47, 0x04, 0x48, 0x04, 0x49, 0x04, 0x4A, 0x04, 0x4B, 0x04, 0x4C, 0x04, 0x4D, 0x04, 0x4E, 0x04, 0x4F, 0x04, 0x50, 0x04, 0x51, 0x04, 0x52, 0x04, 0x53, 0x04, 0x54, 0x04, 0x55, 0x04,\n\t0x56, 0x04, 0x57, 0x04, 0x58, 0x04, 0x59, 0x04, 0x5A, 0x04, 0x5B, 0x04, 0x5C, 0x04, 0x5D, 0x04, 0x5E, 0x04, 0x5F, 0x04, 0x60, 0x04, 0x61, 0x04, 0x62, 0x04, 0x63, 0x04, 0x64, 0x04, 0x65, 0x04,\n\t0x66, 0x04, 0x67, 0x04, 0x68, 0x04, 0x69, 0x04, 0x6A, 0x04, 0x6B, 0x04, 0x6C, 0x04, 0x6D, 0x04, 0x6E, 0x04, 0x6F, 0x04, 0x70, 0x04, 0x71, 0x04, 0x72, 0x04, 0x73, 0x04, 0x74, 0x04, 0x75, 0x04,\n\t0x76, 0x04, 0x77, 0x04, 0x78, 0x04, 0x79, 0x04, 0x7A, 0x04, 0x7B, 0x04, 0x7C, 0x04, 0x7D, 0x04, 0x7E, 0x04, 0x7F, 0x04, 0x80, 0x04, 0x81, 0x04, 0x82, 0x04, 0x83, 0x04, 0x84, 0x04, 0x85, 0x04,\n\t0x86, 0x04, 0x87, 0x04, 0x88, 0x04, 0x89, 0x04, 0x8A, 0x04, 0x8B, 0x04, 0x8C, 0x04, 0x8D, 0x04, 0x8E, 0x04, 0x8F, 0x04, 0x90, 0x04, 0x91, 0x04, 0x92, 0x04, 0x93, 0x04, 0x94, 0x04, 0x95, 0x04,\n\t0x96, 0x04, 0x97, 0x04, 0x98, 0x04, 0x99, 0x04, 0x9A, 0x04, 0x9B, 0x04, 0x9C, 0x04, 0x9D, 0x04, 0x9E, 0x04, 0x9F, 0x04, 0xA0, 0x04, 0xA1, 0x04, 0xA2, 0x04, 0xA3, 0x04, 0xA4, 0x04, 0xA5, 0x04,\n\t0xA6, 0x04, 0xA7, 0x04, 0xA8, 0x04, 0xA9, 0x04, 0xAA, 0x04, 0xAB, 0x04, 0xAC, 0x04, 0xAD, 0x04, 0xAE, 0x04, 0xAF, 0x04, 0xB0, 0x04, 0xB1, 0x04, 0xB2, 0x04, 0xB3, 0x04, 0xB4, 0x04, 0xB5, 0x04,\n\t0xB6, 0x04, 0xB7, 0x04, 0xB8, 0x04, 0xB9, 0x04, 0xBA, 0x04, 0xBB, 0x04, 0xBC, 0x04, 0xBD, 0x04, 0xBE, 0x04, 0xBF, 0x04, 0xC0, 0x04, 0xC1, 0x04, 0xC2, 0x04, 0xC3, 0x04, 0xC4, 0x04, 0xC5, 0x04,\n\t0xC6, 0x04, 0xC7, 0x04, 0xC8, 0x04, 0xC9, 0x04, 0xCA, 0x04, 0xCB, 0x04, 0xCC, 0x04, 0xCD, 0x04, 0xCE, 0x04, 0xCF, 0x04, 0xD0, 0x04, 0xD1, 0x04, 0xD2, 0x04, 0xD3, 0x04, 0xD4, 0x04, 0xD5, 0x04,\n\t0xD6, 0x04, 0xD7, 0x04, 0xD8, 0x04, 0xD9, 0x04, 0xDA, 0x04, 0xDB, 0x04, 0xDC, 0x04, 0xDD, 0x04, 0xDE, 0x04, 0xDF, 0x04, 0xE0, 0x04, 0xE1, 0x04, 0xE2, 0x04, 0xE3, 0x04, 0xE4, 0x04, 0xE5, 0x04,\n\t0xE6, 0x04, 0xE7, 0x04, 0xE8, 0x04, 0xE9, 0x04, 0xEA, 0x04, 0xEB, 0x04, 0xEC, 0x04, 0xED, 0x04, 0xEE, 0x04, 0xEF, 0x04, 0xF0, 0x04, 0xF1, 0x04, 0xF2, 0x04, 0xF3, 0x04, 0xF4, 0x04, 0xF5, 0x04,\n\t0xF6, 0x04, 0xF7, 0x04, 0xF8, 0x04, 0xF9, 0x04, 0xFA, 0x04, 0xFB, 0x04, 0xFC, 0x04, 0xFD, 0x04, 0xFE, 0x04, 0xFF, 0x04, 0x00, 0x05, 0x01, 0x05, 0x02, 0x05, 0x03, 0x05, 0x04, 0x05, 0x05, 0x05,\n\t0x06, 0x05, 0x07, 0x05, 0x08, 0x05, 0x09, 0x05, 0x0A, 0x05, 0x0B, 0x05, 0x0C, 0x05, 0x0D, 0x05, 0x0E, 0x05, 0x0F, 0x05, 0x10, 0x05, 0x11, 0x05, 0x12, 0x05, 0x13, 0x05, 0x14, 0x05, 0x15, 0x05,\n\t0x16, 0x05, 0x17, 0x05, 0x18, 0x05, 0x19, 0x05, 0x1A, 0x05, 0x1B, 0x05, 0x1C, 0x05, 0x1D, 0x05, 0x1E, 0x05, 0x1F, 0x05, 0x20, 0x05, 0x21, 0x05, 0x22, 0x05, 0x23, 0x05, 0x24, 0x05, 0x25, 0x05,\n\t0x26, 0x05, 0x27, 0x05, 0x28, 0x05, 0x29, 0x05, 0x2A, 0x05, 0x2B, 0x05, 0x2C, 0x05, 0x2D, 0x05, 0x2E, 0x05, 0x2F, 0x05, 0x30, 0x05, 0x31, 0x05, 0x32, 0x05, 0x33, 0x05, 0x34, 0x05, 0x35, 0x05,\n\t0x36, 0x05, 0x37, 0x05, 0x38, 0x05, 0x39, 0x05, 0x3A, 0x05, 0x3B, 0x05, 0x3C, 0x05, 0x3D, 0x05, 0x3E, 0x05, 0x3F, 0x05, 0x40, 0x05, 0x41, 0x05, 0x42, 0x05, 0x43, 0x05, 0x44, 0x05, 0x45, 0x05,\n\t0x46, 0x05, 0x47, 0x05, 0x48, 0x05, 0x49, 0x05, 0x4A, 0x05, 0x4B, 0x05, 0x4C, 0x05, 0x4D, 0x05, 0x4E, 0x05, 0x4F, 0x05, 0x50, 0x05, 0x51, 0x05, 0x52, 0x05, 0x53, 0x05, 0x54, 0x05, 0x55, 0x05,\n\t0x56, 0x05, 0x57, 0x05, 0x58, 0x05, 0x59, 0x05, 0x5A, 0x05, 0x5B, 0x05, 0x5C, 0x05, 0x5D, 0x05, 0x5E, 0x05, 0x5F, 0x05, 0x60, 0x05, 0x61, 0x05, 0x62, 0x05, 0x63, 0x05, 0x64, 0x05, 0x65, 0x05,\n\t0x66, 0x05, 0x67, 0x05, 0x68, 0x05, 0x69, 0x05, 0x6A, 0x05, 0x6B, 0x05, 0x6C, 0x05, 0x6D, 0x05, 0x6E, 0x05, 0x6F, 0x05, 0x70, 0x05, 0x71, 0x05, 0x72, 0x05, 0x73, 0x05, 0x74, 0x05, 0x75, 0x05,\n\t0x76, 0x05, 0x77, 0x05, 0x78, 0x05, 0x79, 0x05, 0x7A, 0x05, 0x7B, 0x05, 0x7C, 0x05, 0x7D, 0x05, 0x7E, 0x05, 0x7F, 0x05, 0x80, 0x05, 0x81, 0x05, 0x82, 0x05, 0x83, 0x05, 0x84, 0x05, 0x85, 0x05,\n\t0x86, 0x05, 0x87, 0x05, 0x88, 0x05, 0x89, 0x05, 0x8A, 0x05, 0x8B, 0x05, 0x8C, 0x05, 0x8D, 0x05, 0x8E, 0x05, 0x8F, 0x05, 0x90, 0x05, 0x91, 0x05, 0x92, 0x05, 0x93, 0x05, 0x94, 0x05, 0x95, 0x05,\n\t0x96, 0x05, 0x97, 0x05, 0x98, 0x05, 0x99, 0x05, 0x9A, 0x05, 0x9B, 0x05, 0x9C, 0x05, 0x9D, 0x05, 0x9E, 0x05, 0x9F, 0x05, 0xA0, 0x05, 0xA1, 0x05, 0xA2, 0x05, 0xA3, 0x05, 0xA4, 0x05, 0xA5, 0x05,\n\t0xA6, 0x05, 0xA7, 0x05, 0xA8, 0x05, 0xA9, 0x05, 0xAA, 0x05, 0xAB, 0x05, 0xAC, 0x05, 0xAD, 0x05, 0xAE, 0x05, 0xAF, 0x05, 0xB0, 0x05, 0xB1, 0x05, 0xB2, 0x05, 0xB3, 0x05, 0xB4, 0x05, 0xB5, 0x05,\n\t0xB6, 0x05, 0xB7, 0x05, 0xB8, 0x05, 0xB9, 0x05, 0xBA, 0x05, 0xBB, 0x05, 0xBC, 0x05, 0xBD, 0x05, 0xBE, 0x05, 0xBF, 0x05, 0xC0, 0x05, 0xC1, 0x05, 0xC2, 0x05, 0xC3, 0x05, 0xC4, 0x05, 0xC5, 0x05,\n\t0xC6, 0x05, 0xC7, 0x05, 0xC8, 0x05, 0xC9, 0x05, 0xCA, 0x05, 0xCB, 0x05, 0xCC, 0x05, 0xCD, 0x05, 0xCE, 0x05, 0xCF, 0x05, 0xD0, 0x05, 0xD1, 0x05, 0xD2, 0x05, 0xD3, 0x05, 0xD4, 0x05, 0xD5, 0x05,\n\t0xD6, 0x05, 0xD7, 0x05, 0xD8, 0x05, 0xD9, 0x05, 0xDA, 0x05, 0xDB, 0x05, 0xDC, 0x05, 0xDD, 0x05, 0xDE, 0x05, 0xDF, 0x05, 0xE0, 0x05, 0xE1, 0x05, 0xE2, 0x05, 0xE3, 0x05, 0xE4, 0x05, 0xE5, 0x05,\n\t0xE6, 0x05, 0xE7, 0x05, 0xE8, 0x05, 0xE9, 0x05, 0xEA, 0x05, 0xEB, 0x05, 0xEC, 0x05, 0xED, 0x05, 0xEE, 0x05, 0xEF, 0x05, 0xF0, 0x05, 0xF1, 0x05, 0xF2, 0x05, 0xF3, 0x05, 0xF4, 0x05, 0xF5, 0x05,\n\t0xF6, 0x05, 0xF7, 0x05, 0xF8, 0x05, 0xF9, 0x05, 0xFA, 0x05, 0xFB, 0x05, 0xFC, 0x05, 0xFD, 0x05, 0xFE, 0x05, 0xFF, 0x05, 0x00, 0x06, 0x01, 0x06, 0x02, 0x06, 0x03, 0x06, 0x04, 0x06, 0x05, 0x06,\n\t0x06, 0x06, 0x07, 0x06, 0x08, 0x06, 0x09, 0x06, 0x0A, 0x06, 0x0B, 0x06, 0x0C, 0x06, 0x0D, 0x06, 0x0E, 0x06, 0x0F, 0x06, 0x10, 0x06, 0x11, 0x06, 0x12, 0x06, 0x13, 0x06, 0x14, 0x06, 0x15, 0x06,\n\t0x16, 0x06, 0x17, 0x06, 0x18, 0x06, 0x19, 0x06, 0x1A, 0x06, 0x1B, 0x06, 0x1C, 0x06, 0x1D, 0x06, 0x1E, 0x06, 0x1F, 0x06, 0x20, 0x06, 0x21, 0x06, 0x22, 0x06, 0x23, 0x06, 0x24, 0x06, 0x25, 0x06,\n\t0x26, 0x06, 0x27, 0x06, 0x28, 0x06, 0x29, 0x06, 0x2A, 0x06, 0x2B, 0x06, 0x2C, 0x06, 0x2D, 0x06, 0x2E, 0x06, 0x2F, 0x06, 0x30, 0x06, 0x31, 0x06, 0x32, 0x06, 0x33, 0x06, 0x34, 0x06, 0x35, 0x06,\n\t0x36, 0x06, 0x37, 0x06, 0x38, 0x06, 0x39, 0x06, 0x3A, 0x06, 0x3B, 0x06, 0x3C, 0x06, 0x3D, 0x06, 0x3E, 0x06, 0x3F, 0x06, 0x40, 0x06, 0x41, 0x06, 0x42, 0x06, 0x43, 0x06, 0x44, 0x06, 0x45, 0x06,\n\t0x46, 0x06, 0x47, 0x06, 0x48, 0x06, 0x49, 0x06, 0x4A, 0x06, 0x4B, 0x06, 0x4C, 0x06, 0x4D, 0x06, 0x4E, 0x06, 0x4F, 0x06, 0x50, 0x06, 0x51, 0x06, 0x52, 0x06, 0x53, 0x06, 0x54, 0x06, 0x55, 0x06,\n\t0x56, 0x06, 0x57, 0x06, 0x58, 0x06, 0x59, 0x06, 0x5A, 0x06, 0x5B, 0x06, 0x5C, 0x06, 0x5D, 0x06, 0x5E, 0x06, 0x5F, 0x06, 0x60, 0x06, 0x61, 0x06, 0x62, 0x06, 0x63, 0x06, 0x64, 0x06, 0x65, 0x06,\n\t0x66, 0x06, 0x67, 0x06, 0x68, 0x06, 0x69, 0x06, 0x6A, 0x06, 0x6B, 0x06, 0x6C, 0x06, 0x6D, 0x06, 0x6E, 0x06, 0x6F, 0x06, 0x70, 0x06, 0x71, 0x06, 0x72, 0x06, 0x73, 0x06, 0x74, 0x06, 0x75, 0x06,\n\t0x76, 0x06, 0x77, 0x06, 0x78, 0x06, 0x79, 0x06, 0x7A, 0x06, 0x7B, 0x06, 0x7C, 0x06, 0x7D, 0x06, 0x7E, 0x06, 0x7F, 0x06, 0x80, 0x06, 0x81, 0x06, 0x82, 0x06, 0x83, 0x06, 0x84, 0x06, 0x85, 0x06,\n\t0x86, 0x06, 0x87, 0x06, 0x88, 0x06, 0x89, 0x06, 0x8A, 0x06, 0x8B, 0x06, 0x8C, 0x06, 0x8D, 0x06, 0x8E, 0x06, 0x8F, 0x06, 0x90, 0x06, 0x91, 0x06, 0x92, 0x06, 0x93, 0x06, 0x94, 0x06, 0x95, 0x06,\n\t0x96, 0x06, 0x97, 0x06, 0x98, 0x06, 0x99, 0x06, 0x9A, 0x06, 0x9B, 0x06, 0x9C, 0x06, 0x9D, 0x06, 0x9E, 0x06, 0x9F, 0x06, 0xA0, 0x06, 0xA1, 0x06, 0xA2, 0x06, 0xA3, 0x06, 0xA4, 0x06, 0xA5, 0x06,\n\t0xA6, 0x06, 0xA7, 0x06, 0xA8, 0x06, 0xA9, 0x06, 0xAA, 0x06, 0xAB, 0x06, 0xAC, 0x06, 0xAD, 0x06, 0xAE, 0x06, 0xAF, 0x06, 0xB0, 0x06, 0xB1, 0x06, 0xB2, 0x06, 0xB3, 0x06, 0xB4, 0x06, 0xB5, 0x06,\n\t0xB6, 0x06, 0xB7, 0x06, 0xB8, 0x06, 0xB9, 0x06, 0xBA, 0x06, 0xBB, 0x06, 0xBC, 0x06, 0xBD, 0x06, 0xBE, 0x06, 0xBF, 0x06, 0xC0, 0x06, 0xC1, 0x06, 0xC2, 0x06, 0xC3, 0x06, 0xC4, 0x06, 0xC5, 0x06,\n\t0xC6, 0x06, 0xC7, 0x06, 0xC8, 0x06, 0xC9, 0x06, 0xCA, 0x06, 0xCB, 0x06, 0xCC, 0x06, 0xCD, 0x06, 0xCE, 0x06, 0xCF, 0x06, 0xD0, 0x06, 0xD1, 0x06, 0xD2, 0x06, 0xD3, 0x06, 0xD4, 0x06, 0xD5, 0x06,\n\t0xD6, 0x06, 0xD7, 0x06, 0xD8, 0x06, 0xD9, 0x06, 0xDA, 0x06, 0xDB, 0x06, 0xDC, 0x06, 0xDD, 0x06, 0xDE, 0x06, 0xDF, 0x06, 0xE0, 0x06, 0xE1, 0x06, 0xE2, 0x06, 0xE3, 0x06, 0xE4, 0x06, 0xE5, 0x06,\n\t0xE6, 0x06, 0xE7, 0x06, 0xE8, 0x06, 0xE9, 0x06, 0xEA, 0x06, 0xEB, 0x06, 0xEC, 0x06, 0xED, 0x06, 0xEE, 0x06, 0xEF, 0x06, 0xF0, 0x06, 0xF1, 0x06, 0xF2, 0x06, 0xF3, 0x06, 0xF4, 0x06, 0xF5, 0x06,\n\t0xF6, 0x06, 0xF7, 0x06, 0xF8, 0x06, 0xF9, 0x06, 0xFA, 0x06, 0xFB, 0x06, 0xFC, 0x06, 0xFD, 0x06, 0xFE, 0x06, 0xFF, 0x06, 0x00, 0x07, 0x01, 0x07, 0x02, 0x07, 0x03, 0x07, 0x04, 0x07, 0x05, 0x07,\n\t0x06, 0x07, 0x07, 0x07, 0x08, 0x07, 0x09, 0x07, 0x0A, 0x07, 0x0B, 0x07, 0x0C, 0x07, 0x0D, 0x07, 0x0E, 0x07, 0x0F, 0x07, 0x10, 0x07, 0x11, 0x07, 0x12, 0x07, 0x13, 0x07, 0x14, 0x07, 0x15, 0x07,\n\t0x16, 0x07, 0x17, 0x07, 0x18, 0x07, 0x19, 0x07, 0x1A, 0x07, 0x1B, 0x07, 0x1C, 0x07, 0x1D, 0x07, 0x1E, 0x07, 0x1F, 0x07, 0x20, 0x07, 0x21, 0x07, 0x22, 0x07, 0x23, 0x07, 0x24, 0x07, 0x25, 0x07,\n\t0x26, 0x07, 0x27, 0x07, 0x28, 0x07, 0x29, 0x07, 0x2A, 0x07, 0x2B, 0x07, 0x2C, 0x07, 0x2D, 0x07, 0x2E, 0x07, 0x2F, 0x07, 0x30, 0x07, 0x31, 0x07, 0x32, 0x07, 0x33, 0x07, 0x34, 0x07, 0x35, 0x07,\n\t0x36, 0x07, 0x37, 0x07, 0x38, 0x07, 0x39, 0x07, 0x3A, 0x07, 0x3B, 0x07, 0x3C, 0x07, 0x3D, 0x07, 0x3E, 0x07, 0x3F, 0x07, 0x40, 0x07, 0x41, 0x07, 0x42, 0x07, 0x43, 0x07, 0x44, 0x07, 0x45, 0x07,\n\t0x46, 0x07, 0x47, 0x07, 0x48, 0x07, 0x49, 0x07, 0x4A, 0x07, 0x4B, 0x07, 0x4C, 0x07, 0x4D, 0x07, 0x4E, 0x07, 0x4F, 0x07, 0x50, 0x07, 0x51, 0x07, 0x52, 0x07, 0x53, 0x07, 0x54, 0x07, 0x55, 0x07,\n\t0x56, 0x07, 0x57, 0x07, 0x58, 0x07, 0x59, 0x07, 0x5A, 0x07, 0x5B, 0x07, 0x5C, 0x07, 0x5D, 0x07, 0x5E, 0x07, 0x5F, 0x07, 0x60, 0x07, 0x61, 0x07, 0x62, 0x07, 0x63, 0x07, 0x64, 0x07, 0x65, 0x07,\n\t0x66, 0x07, 0x67, 0x07, 0x68, 0x07, 0x69, 0x07, 0x6A, 0x07, 0x6B, 0x07, 0x6C, 0x07, 0x6D, 0x07, 0x6E, 0x07, 0x6F, 0x07, 0x70, 0x07, 0x71, 0x07, 0x72, 0x07, 0x73, 0x07, 0x74, 0x07, 0x75, 0x07,\n\t0x76, 0x07, 0x77, 0x07, 0x78, 0x07, 0x79, 0x07, 0x7A, 0x07, 0x7B, 0x07, 0x7C, 0x07, 0x7D, 0x07, 0x7E, 0x07, 0x7F, 0x07, 0x80, 0x07, 0x81, 0x07, 0x82, 0x07, 0x83, 0x07, 0x84, 0x07, 0x85, 0x07,\n\t0x86, 0x07, 0x87, 0x07, 0x88, 0x07, 0x89, 0x07, 0x8A, 0x07, 0x8B, 0x07, 0x8C, 0x07, 0x8D, 0x07, 0x8E, 0x07, 0x8F, 0x07, 0x90, 0x07, 0x91, 0x07, 0x92, 0x07, 0x93, 0x07, 0x94, 0x07, 0x95, 0x07,\n\t0x96, 0x07, 0x97, 0x07, 0x98, 0x07, 0x99, 0x07, 0x9A, 0x07, 0x9B, 0x07, 0x9C, 0x07, 0x9D, 0x07, 0x9E, 0x07, 0x9F, 0x07, 0xA0, 0x07, 0xA1, 0x07, 0xA2, 0x07, 0xA3, 0x07, 0xA4, 0x07, 0xA5, 0x07,\n\t0xA6, 0x07, 0xA7, 0x07, 0xA8, 0x07, 0xA9, 0x07, 0xAA, 0x07, 0xAB, 0x07, 0xAC, 0x07, 0xAD, 0x07, 0xAE, 0x07, 0xAF, 0x07, 0xB0, 0x07, 0xB1, 0x07, 0xB2, 0x07, 0xB3, 0x07, 0xB4, 0x07, 0xB5, 0x07,\n\t0xB6, 0x07, 0xB7, 0x07, 0xB8, 0x07, 0xB9, 0x07, 0xBA, 0x07, 0xBB, 0x07, 0xBC, 0x07, 0xBD, 0x07, 0xBE, 0x07, 0xBF, 0x07, 0xC0, 0x07, 0xC1, 0x07, 0xC2, 0x07, 0xC3, 0x07, 0xC4, 0x07, 0xC5, 0x07,\n\t0xC6, 0x07, 0xC7, 0x07, 0xC8, 0x07, 0xC9, 0x07, 0xCA, 0x07, 0xCB, 0x07, 0xCC, 0x07, 0xCD, 0x07, 0xCE, 0x07, 0xCF, 0x07, 0xD0, 0x07, 0xD1, 0x07, 0xD2, 0x07, 0xD3, 0x07, 0xD4, 0x07, 0xD5, 0x07,\n\t0xD6, 0x07, 0xD7, 0x07, 0xD8, 0x07, 0xD9, 0x07, 0xDA, 0x07, 0xDB, 0x07, 0xDC, 0x07, 0xDD, 0x07, 0xDE, 0x07, 0xDF, 0x07, 0xE0, 0x07, 0xE1, 0x07, 0xE2, 0x07, 0xE3, 0x07, 0xE4, 0x07, 0xE5, 0x07,\n\t0xE6, 0x07, 0xE7, 0x07, 0xE8, 0x07, 0xE9, 0x07, 0xEA, 0x07, 0xEB, 0x07, 0xEC, 0x07, 0xED, 0x07, 0xEE, 0x07, 0xEF, 0x07, 0xF0, 0x07, 0xF1, 0x07, 0xF2, 0x07, 0xF3, 0x07, 0xF4, 0x07, 0xF5, 0x07,\n\t0xF6, 0x07, 0xF7, 0x07, 0xF8, 0x07, 0xF9, 0x07, 0xFA, 0x07, 0xFB, 0x07, 0xFC, 0x07, 0xFD, 0x07, 0xFE, 0x07, 0xFF, 0x07, 0x00, 0x08, 0x01, 0x08, 0x02, 0x08, 0x03, 0x08, 0x04, 0x08, 0x05, 0x08,\n\t0x06, 0x08, 0x07, 0x08, 0x08, 0x08, 0x09, 0x08, 0x0A, 0x08, 0x0B, 0x08, 0x0C, 0x08, 0x0D, 0x08, 0x0E, 0x08, 0x0F, 0x08, 0x10, 0x08, 0x11, 0x08, 0x12, 0x08, 0x13, 0x08, 0x14, 0x08, 0x15, 0x08,\n\t0x16, 0x08, 0x17, 0x08, 0x18, 0x08, 0x19, 0x08, 0x1A, 0x08, 0x1B, 0x08, 0x1C, 0x08, 0x1D, 0x08, 0x1E, 0x08, 0x1F, 0x08, 0x20, 0x08, 0x21, 0x08, 0x22, 0x08, 0x23, 0x08, 0x24, 0x08, 0x25, 0x08,\n\t0x26, 0x08, 0x27, 0x08, 0x28, 0x08, 0x29, 0x08, 0x2A, 0x08, 0x2B, 0x08, 0x2C, 0x08, 0x2D, 0x08, 0x2E, 0x08, 0x2F, 0x08, 0x30, 0x08, 0x31, 0x08, 0x32, 0x08, 0x33, 0x08, 0x34, 0x08, 0x35, 0x08,\n\t0x36, 0x08, 0x37, 0x08, 0x38, 0x08, 0x39, 0x08, 0x3A, 0x08, 0x3B, 0x08, 0x3C, 0x08, 0x3D, 0x08, 0x3E, 0x08, 0x3F, 0x08, 0x40, 0x08, 0x41, 0x08, 0x42, 0x08, 0x43, 0x08, 0x44, 0x08, 0x45, 0x08,\n\t0x46, 0x08, 0x47, 0x08, 0x48, 0x08, 0x49, 0x08, 0x4A, 0x08, 0x4B, 0x08, 0x4C, 0x08, 0x4D, 0x08, 0x4E, 0x08, 0x4F, 0x08, 0x50, 0x08, 0x51, 0x08, 0x52, 0x08, 0x53, 0x08, 0x54, 0x08, 0x55, 0x08,\n\t0x56, 0x08, 0x57, 0x08, 0x58, 0x08, 0x59, 0x08, 0x5A, 0x08, 0x5B, 0x08, 0x5C, 0x08, 0x5D, 0x08, 0x5E, 0x08, 0x5F, 0x08, 0x60, 0x08, 0x61, 0x08, 0x62, 0x08, 0x63, 0x08, 0x64, 0x08, 0x65, 0x08,\n\t0x66, 0x08, 0x67, 0x08, 0x68, 0x08, 0x69, 0x08, 0x6A, 0x08, 0x6B, 0x08, 0x6C, 0x08, 0x6D, 0x08, 0x6E, 0x08, 0x6F, 0x08, 0x70, 0x08, 0x71, 0x08, 0x72, 0x08, 0x73, 0x08, 0x74, 0x08, 0x75, 0x08,\n\t0x76, 0x08, 0x77, 0x08, 0x78, 0x08, 0x79, 0x08, 0x7A, 0x08, 0x7B, 0x08, 0x7C, 0x08, 0x7D, 0x08, 0x7E, 0x08, 0x7F, 0x08, 0x80, 0x08, 0x81, 0x08, 0x82, 0x08, 0x83, 0x08, 0x84, 0x08, 0x85, 0x08,\n\t0x86, 0x08, 0x87, 0x08, 0x88, 0x08, 0x89, 0x08, 0x8A, 0x08, 0x8B, 0x08, 0x8C, 0x08, 0x8D, 0x08, 0x8E, 0x08, 0x8F, 0x08, 0x90, 0x08, 0x91, 0x08, 0x92, 0x08, 0x93, 0x08, 0x94, 0x08, 0x95, 0x08,\n\t0x96, 0x08, 0x97, 0x08, 0x98, 0x08, 0x99, 0x08, 0x9A, 0x08, 0x9B, 0x08, 0x9C, 0x08, 0x9D, 0x08, 0x9E, 0x08, 0x9F, 0x08, 0xA0, 0x08, 0xA1, 0x08, 0xA2, 0x08, 0xA3, 0x08, 0xA4, 0x08, 0xA5, 0x08,\n\t0xA6, 0x08, 0xA7, 0x08, 0xA8, 0x08, 0xA9, 0x08, 0xAA, 0x08, 0xAB, 0x08, 0xAC, 0x08, 0xAD, 0x08, 0xAE, 0x08, 0xAF, 0x08, 0xB0, 0x08, 0xB1, 0x08, 0xB2, 0x08, 0xB3, 0x08, 0xB4, 0x08, 0xB5, 0x08,\n\t0xB6, 0x08, 0xB7, 0x08, 0xB8, 0x08, 0xB9, 0x08, 0xBA, 0x08, 0xBB, 0x08, 0xBC, 0x08, 0xBD, 0x08, 0xBE, 0x08, 0xBF, 0x08, 0xC0, 0x08, 0xC1, 0x08, 0xC2, 0x08, 0xC3, 0x08, 0xC4, 0x08, 0xC5, 0x08,\n\t0xC6, 0x08, 0xC7, 0x08, 0xC8, 0x08, 0xC9, 0x08, 0xCA, 0x08, 0xCB, 0x08, 0xCC, 0x08, 0xCD, 0x08, 0xCE, 0x08, 0xCF, 0x08, 0xD0, 0x08, 0xD1, 0x08, 0xD2, 0x08, 0xD3, 0x08, 0xD4, 0x08, 0xD5, 0x08,\n\t0xD6, 0x08, 0xD7, 0x08, 0xD8, 0x08, 0xD9, 0x08, 0xDA, 0x08, 0xDB, 0x08, 0xDC, 0x08, 0xDD, 0x08, 0xDE, 0x08, 0xDF, 0x08, 0xE0, 0x08, 0xE1, 0x08, 0xE2, 0x08, 0xE3, 0x08, 0xE4, 0x08, 0xE5, 0x08,\n\t0xE6, 0x08, 0xE7, 0x08, 0xE8, 0x08, 0xE9, 0x08, 0xEA, 0x08, 0xEB, 0x08, 0xEC, 0x08, 0xED, 0x08, 0xEE, 0x08, 0xEF, 0x08, 0xF0, 0x08, 0xF1, 0x08, 0xF2, 0x08, 0xF3, 0x08, 0xF4, 0x08, 0xF5, 0x08,\n\t0xF6, 0x08, 0xF7, 0x08, 0xF8, 0x08, 0xF9, 0x08, 0xFA, 0x08, 0xFB, 0x08, 0xFC, 0x08, 0xFD, 0x08, 0xFE, 0x08, 0xFF, 0x08, 0x00, 0x09, 0x01, 0x09, 0x02, 0x09, 0x03, 0x09, 0x04, 0x09, 0x05, 0x09,\n\t0x06, 0x09, 0x07, 0x09, 0x08, 0x09, 0x09, 0x09, 0x0A, 0x09, 0x0B, 0x09, 0x0C, 0x09, 0x0D, 0x09, 0x0E, 0x09, 0x0F, 0x09, 0x10, 0x09, 0x11, 0x09, 0x12, 0x09, 0x13, 0x09, 0x14, 0x09, 0x15, 0x09,\n\t0x16, 0x09, 0x17, 0x09, 0x18, 0x09, 0x19, 0x09, 0x1A, 0x09, 0x1B, 0x09, 0x1C, 0x09, 0x1D, 0x09, 0x1E, 0x09, 0x1F, 0x09, 0x20, 0x09, 0x21, 0x09, 0x22, 0x09, 0x23, 0x09, 0x24, 0x09, 0x25, 0x09,\n\t0x26, 0x09, 0x27, 0x09, 0x28, 0x09, 0x29, 0x09, 0x2A, 0x09, 0x2B, 0x09, 0x2C, 0x09, 0x2D, 0x09, 0x2E, 0x09, 0x2F, 0x09, 0x30, 0x09, 0x31, 0x09, 0x32, 0x09, 0x33, 0x09, 0x34, 0x09, 0x35, 0x09,\n\t0x36, 0x09, 0x37, 0x09, 0x38, 0x09, 0x39, 0x09, 0x3A, 0x09, 0x3B, 0x09, 0x3C, 0x09, 0x3D, 0x09, 0x3E, 0x09, 0x3F, 0x09, 0x40, 0x09, 0x41, 0x09, 0x42, 0x09, 0x43, 0x09, 0x44, 0x09, 0x45, 0x09,\n\t0x46, 0x09, 0x47, 0x09, 0x48, 0x09, 0x49, 0x09, 0x4A, 0x09, 0x4B, 0x09, 0x4C, 0x09, 0x4D, 0x09, 0x4E, 0x09, 0x4F, 0x09, 0x50, 0x09, 0x51, 0x09, 0x52, 0x09, 0x53, 0x09, 0x54, 0x09, 0x55, 0x09,\n\t0x56, 0x09, 0x57, 0x09, 0x58, 0x09, 0x59, 0x09, 0x5A, 0x09, 0x5B, 0x09, 0x5C, 0x09, 0x5D, 0x09, 0x5E, 0x09, 0x5F, 0x09, 0x60, 0x09, 0x61, 0x09, 0x62, 0x09, 0x63, 0x09, 0x64, 0x09, 0x65, 0x09,\n\t0x66, 0x09, 0x67, 0x09, 0x68, 0x09, 0x69, 0x09, 0x6A, 0x09, 0x6B, 0x09, 0x6C, 0x09, 0x6D, 0x09, 0x6E, 0x09, 0x6F, 0x09, 0x70, 0x09, 0x71, 0x09, 0x72, 0x09, 0x73, 0x09, 0x74, 0x09, 0x75, 0x09,\n\t0x76, 0x09, 0x77, 0x09, 0x78, 0x09, 0x79, 0x09, 0x7A, 0x09, 0x7B, 0x09, 0x7C, 0x09, 0x7D, 0x09, 0x7E, 0x09, 0x7F, 0x09, 0x80, 0x09, 0x81, 0x09, 0x82, 0x09, 0x83, 0x09, 0x84, 0x09, 0x85, 0x09,\n\t0x86, 0x09, 0x87, 0x09, 0x88, 0x09, 0x89, 0x09, 0x8A, 0x09, 0x8B, 0x09, 0x8C, 0x09, 0x8D, 0x09, 0x8E, 0x09, 0x8F, 0x09, 0x90, 0x09, 0x91, 0x09, 0x92, 0x09, 0x93, 0x09, 0x94, 0x09, 0x95, 0x09,\n\t0x96, 0x09, 0x97, 0x09, 0x98, 0x09, 0x99, 0x09, 0x9A, 0x09, 0x9B, 0x09, 0x9C, 0x09, 0x9D, 0x09, 0x9E, 0x09, 0x9F, 0x09, 0xA0, 0x09, 0xA1, 0x09, 0xA2, 0x09, 0xA3, 0x09, 0xA4, 0x09, 0xA5, 0x09,\n\t0xA6, 0x09, 0xA7, 0x09, 0xA8, 0x09, 0xA9, 0x09, 0xAA, 0x09, 0xAB, 0x09, 0xAC, 0x09, 0xAD, 0x09, 0xAE, 0x09, 0xAF, 0x09, 0xB0, 0x09, 0xB1, 0x09, 0xB2, 0x09, 0xB3, 0x09, 0xB4, 0x09, 0xB5, 0x09,\n\t0xB6, 0x09, 0xB7, 0x09, 0xB8, 0x09, 0xB9, 0x09, 0xBA, 0x09, 0xBB, 0x09, 0xBC, 0x09, 0xBD, 0x09, 0xBE, 0x09, 0xBF, 0x09, 0xC0, 0x09, 0xC1, 0x09, 0xC2, 0x09, 0xC3, 0x09, 0xC4, 0x09, 0xC5, 0x09,\n\t0xC6, 0x09, 0xC7, 0x09, 0xC8, 0x09, 0xC9, 0x09, 0xCA, 0x09, 0xCB, 0x09, 0xCC, 0x09, 0xCD, 0x09, 0xCE, 0x09, 0xCF, 0x09, 0xD0, 0x09, 0xD1, 0x09, 0xD2, 0x09, 0xD3, 0x09, 0xD4, 0x09, 0xD5, 0x09,\n\t0xD6, 0x09, 0xD7, 0x09, 0xD8, 0x09, 0xD9, 0x09, 0xDA, 0x09, 0xDB, 0x09, 0xDC, 0x09, 0xDD, 0x09, 0xDE, 0x09, 0xDF, 0x09, 0xE0, 0x09, 0xE1, 0x09, 0xE2, 0x09, 0xE3, 0x09, 0xE4, 0x09, 0xE5, 0x09,\n\t0xE6, 0x09, 0xE7, 0x09, 0xE8, 0x09, 0xE9, 0x09, 0xEA, 0x09, 0xEB, 0x09, 0xEC, 0x09, 0xED, 0x09, 0xEE, 0x09, 0xEF, 0x09, 0xF0, 0x09, 0xF1, 0x09, 0xF2, 0x09, 0xF3, 0x09, 0xF4, 0x09, 0xF5, 0x09,\n\t0xF6, 0x09, 0xF7, 0x09, 0xF8, 0x09, 0xF9, 0x09, 0xFA, 0x09, 0xFB, 0x09, 0xFC, 0x09, 0xFD, 0x09, 0xFE, 0x09, 0xFF, 0x09, 0x00, 0x0A, 0x01, 0x0A, 0x02, 0x0A, 0x03, 0x0A, 0x04, 0x0A, 0x05, 0x0A,\n\t0x06, 0x0A, 0x07, 0x0A, 0x08, 0x0A, 0x09, 0x0A, 0x0A, 0x0A, 0x0B, 0x0A, 0x0C, 0x0A, 0x0D, 0x0A, 0x0E, 0x0A, 0x0F, 0x0A, 0x10, 0x0A, 0x11, 0x0A, 0x12, 0x0A, 0x13, 0x0A, 0x14, 0x0A, 0x15, 0x0A,\n\t0x16, 0x0A, 0x17, 0x0A, 0x18, 0x0A, 0x19, 0x0A, 0x1A, 0x0A, 0x1B, 0x0A, 0x1C, 0x0A, 0x1D, 0x0A, 0x1E, 0x0A, 0x1F, 0x0A, 0x20, 0x0A, 0x21, 0x0A, 0x22, 0x0A, 0x23, 0x0A, 0x24, 0x0A, 0x25, 0x0A,\n\t0x26, 0x0A, 0x27, 0x0A, 0x28, 0x0A, 0x29, 0x0A, 0x2A, 0x0A, 0x2B, 0x0A, 0x2C, 0x0A, 0x2D, 0x0A, 0x2E, 0x0A, 0x2F, 0x0A, 0x30, 0x0A, 0x31, 0x0A, 0x32, 0x0A, 0x33, 0x0A, 0x34, 0x0A, 0x35, 0x0A,\n\t0x36, 0x0A, 0x37, 0x0A, 0x38, 0x0A, 0x39, 0x0A, 0x3A, 0x0A, 0x3B, 0x0A, 0x3C, 0x0A, 0x3D, 0x0A, 0x3E, 0x0A, 0x3F, 0x0A, 0x40, 0x0A, 0x41, 0x0A, 0x42, 0x0A, 0x43, 0x0A, 0x44, 0x0A, 0x45, 0x0A,\n\t0x46, 0x0A, 0x47, 0x0A, 0x48, 0x0A, 0x49, 0x0A, 0x4A, 0x0A, 0x4B, 0x0A, 0x4C, 0x0A, 0x4D, 0x0A, 0x4E, 0x0A, 0x4F, 0x0A, 0x50, 0x0A, 0x51, 0x0A, 0x52, 0x0A, 0x53, 0x0A, 0x54, 0x0A, 0x55, 0x0A,\n\t0x57, 0x0A, 0x58, 0x0A, 0x59, 0x0A, 0x5A, 0x0A, 0x5B, 0x0A, 0x5C, 0x0A, 0x5D, 0x0A, 0x5E, 0x0A, 0x5F, 0x0A, 0x60, 0x0A, 0x61, 0x0A, 0x62, 0x0A, 0x63, 0x0A, 0x64, 0x0A, 0x65, 0x0A, 0x66, 0x0A,\n\t0x67, 0x0A, 0x68, 0x0A, 0x69, 0x0A, 0x6A, 0x0A, 0x6B, 0x0A, 0x6C, 0x0A, 0x6D, 0x0A, 0x6E, 0x0A, 0x6F, 0x0A, 0x70, 0x0A, 0x71, 0x0A, 0x72, 0x0A, 0x73, 0x0A, 0x74, 0x0A, 0x75, 0x0A, 0x76, 0x0A,\n\t0x77, 0x0A, 0x78, 0x0A, 0x79, 0x0A, 0x7A, 0x0A, 0x7B, 0x0A, 0x7C, 0x0A, 0x7D, 0x0A, 0x7E, 0x0A, 0x7F, 0x0A, 0x80, 0x0A, 0x81, 0x0A, 0x82, 0x0A, 0x83, 0x0A, 0x84, 0x0A, 0x85, 0x0A, 0x86, 0x0A,\n\t0x87, 0x0A, 0x88, 0x0A, 0x89, 0x0A, 0x8A, 0x0A, 0x8B, 0x0A, 0x8C, 0x0A, 0x8D, 0x0A, 0x8E, 0x0A, 0x8F, 0x0A, 0x90, 0x0A, 0x91, 0x0A, 0x92, 0x0A, 0x93, 0x0A, 0x94, 0x0A, 0x95, 0x0A, 0x96, 0x0A,\n\t0x97, 0x0A, 0x98, 0x0A, 0x99, 0x0A, 0x9A, 0x0A, 0x9B, 0x0A, 0x9C, 0x0A, 0x9D, 0x0A, 0x9E, 0x0A, 0x9F, 0x0A, 0xA0, 0x0A, 0xA1, 0x0A, 0xA2, 0x0A, 0xA3, 0x0A, 0xA4, 0x0A, 0xA5, 0x0A, 0xA6, 0x0A,\n\t0xA7, 0x0A, 0xA8, 0x0A, 0xA9, 0x0A, 0xAA, 0x0A, 0xAB, 0x0A, 0xAC, 0x0A, 0xAD, 0x0A, 0xAE, 0x0A, 0xAF, 0x0A, 0xB0, 0x0A, 0xB1, 0x0A, 0xB2, 0x0A, 0xB3, 0x0A, 0xB4, 0x0A, 0xB5, 0x0A, 0xB6, 0x0A,\n\t0xB7, 0x0A, 0xB8, 0x0A, 0xB9, 0x0A, 0xBA, 0x0A, 0xBB, 0x0A, 0xBC, 0x0A, 0xBD, 0x0A, 0xBE, 0x0A, 0xBF, 0x0A, 0xC0, 0x0A, 0xC1, 0x0A, 0xC2, 0x0A, 0xC3, 0x0A, 0xC4, 0x0A, 0xC5, 0x0A, 0xC6, 0x0A,\n\t0xC7, 0x0A, 0xC8, 0x0A, 0xC9, 0x0A, 0xCA, 0x0A, 0xCB, 0x0A, 0xCC, 0x0A, 0xCD, 0x0A, 0xCE, 0x0A, 0xCF, 0x0A, 0xD0, 0x0A, 0xD1, 0x0A, 0xD2, 0x0A, 0xD3, 0x0A, 0xD4, 0x0A, 0xD5, 0x0A, 0xD6, 0x0A,\n\t0xD7, 0x0A, 0xD8, 0x0A, 0xD9, 0x0A, 0xDA, 0x0A, 0xDB, 0x0A, 0xDC, 0x0A, 0xDD, 0x0A, 0xDE, 0x0A, 0xDF, 0x0A, 0xE0, 0x0A, 0xE1, 0x0A, 0xE2, 0x0A, 0xE3, 0x0A, 0xE4, 0x0A, 0xE5, 0x0A, 0xE6, 0x0A,\n\t0xE7, 0x0A, 0xE8, 0x0A, 0xE9, 0x0A, 0xEA, 0x0A, 0xEB, 0x0A, 0xEC, 0x0A, 0xED, 0x0A, 0xEE, 0x0A, 0xEF, 0x0A, 0xF0, 0x0A, 0xF1, 0x0A, 0xF2, 0x0A, 0xF3, 0x0A, 0xF4, 0x0A, 0xF5, 0x0A, 0xF6, 0x0A,\n\t0xF7, 0x0A, 0xF8, 0x0A, 0xF9, 0x0A, 0xFA, 0x0A, 0xFB, 0x0A, 0xFC, 0x0A, 0xFD, 0x0A, 0xFE, 0x0A, 0xFF, 0x0A, 0x00, 0x0B, 0x01, 0x0B, 0x02, 0x0B, 0x03, 0x0B, 0x04, 0x0B, 0x05, 0x0B, 0x06, 0x0B,\n\t0x07, 0x0B, 0x08, 0x0B, 0x09, 0x0B, 0x0A, 0x0B, 0x0B, 0x0B, 0x0C, 0x0B, 0x0D, 0x0B, 0x0E, 0x0B, 0x0F, 0x0B, 0x10, 0x0B, 0x11, 0x0B, 0x12, 0x0B, 0x13, 0x0B, 0x14, 0x0B, 0x15, 0x0B, 0x16, 0x0B,\n\t0x17, 0x0B, 0x18, 0x0B, 0x19, 0x0B, 0x1A, 0x0B, 0x1B, 0x0B, 0x1C, 0x0B, 0x1D, 0x0B, 0x1E, 0x0B, 0x1F, 0x0B, 0x20, 0x0B, 0x21, 0x0B, 0x22, 0x0B, 0x23, 0x0B, 0x24, 0x0B, 0x25, 0x0B, 0x26, 0x0B,\n\t0x27, 0x0B, 0x28, 0x0B, 0x29, 0x0B, 0x2A, 0x0B, 0x2B, 0x0B, 0x2C, 0x0B, 0x2D, 0x0B, 0x2E, 0x0B, 0x2F, 0x0B, 0x30, 0x0B, 0x31, 0x0B, 0x32, 0x0B, 0x33, 0x0B, 0x34, 0x0B, 0x35, 0x0B, 0x36, 0x0B,\n\t0x37, 0x0B, 0x38, 0x0B, 0x39, 0x0B, 0x3A, 0x0B, 0x3B, 0x0B, 0x3C, 0x0B, 0x3D, 0x0B, 0x3E, 0x0B, 0x3F, 0x0B, 0x40, 0x0B, 0x41, 0x0B, 0x42, 0x0B, 0x43, 0x0B, 0x44, 0x0B, 0x45, 0x0B, 0x46, 0x0B,\n\t0x47, 0x0B, 0x48, 0x0B, 0x49, 0x0B, 0x4A, 0x0B, 0x4B, 0x0B, 0x4C, 0x0B, 0x4D, 0x0B, 0x4E, 0x0B, 0x4F, 0x0B, 0x50, 0x0B, 0x51, 0x0B, 0x52, 0x0B, 0x53, 0x0B, 0x54, 0x0B, 0x55, 0x0B, 0x56, 0x0B,\n\t0x57, 0x0B, 0x58, 0x0B, 0x59, 0x0B, 0x5A, 0x0B, 0x5B, 0x0B, 0x5C, 0x0B, 0x5D, 0x0B, 0x5E, 0x0B, 0x5F, 0x0B, 0x60, 0x0B, 0x61, 0x0B, 0x62, 0x0B, 0x63, 0x0B, 0x64, 0x0B, 0x65, 0x0B, 0x66, 0x0B,\n\t0x67, 0x0B, 0x68, 0x0B, 0x69, 0x0B, 0x6A, 0x0B, 0x6B, 0x0B, 0x6C, 0x0B, 0x6D, 0x0B, 0x6E, 0x0B, 0x6F, 0x0B, 0x70, 0x0B, 0x71, 0x0B, 0x72, 0x0B, 0x73, 0x0B, 0x74, 0x0B, 0x75, 0x0B, 0x76, 0x0B,\n\t0x77, 0x0B, 0x78, 0x0B, 0x79, 0x0B, 0x7A, 0x0B, 0x7B, 0x0B, 0x7C, 0x0B, 0x7D, 0x0B, 0x7E, 0x0B, 0x7F, 0x0B, 0x80, 0x0B, 0x81, 0x0B, 0x82, 0x0B, 0x83, 0x0B, 0x84, 0x0B, 0x85, 0x0B, 0x86, 0x0B,\n\t0x87, 0x0B, 0x88, 0x0B, 0x89, 0x0B, 0x8A, 0x0B, 0x8B, 0x0B, 0x8C, 0x0B, 0x8D, 0x0B, 0x8E, 0x0B, 0x8F, 0x0B, 0x90, 0x0B, 0x91, 0x0B, 0x92, 0x0B, 0x93, 0x0B, 0x94, 0x0B, 0x95, 0x0B, 0x96, 0x0B,\n\t0x97, 0x0B, 0x98, 0x0B, 0x99, 0x0B, 0x9A, 0x0B, 0x9B, 0x0B, 0x9C, 0x0B, 0x9D, 0x0B, 0x9E, 0x0B, 0x9F, 0x0B, 0xA0, 0x0B, 0xA1, 0x0B, 0xA2, 0x0B, 0xA3, 0x0B, 0xA4, 0x0B, 0xA5, 0x0B, 0xA6, 0x0B,\n\t0xA7, 0x0B, 0xA8, 0x0B, 0xA9, 0x0B, 0xAA, 0x0B, 0xAB, 0x0B, 0xAC, 0x0B, 0xAD, 0x0B, 0xAE, 0x0B, 0xAF, 0x0B, 0xB0, 0x0B, 0xB1, 0x0B, 0xB2, 0x0B, 0xB3, 0x0B, 0xB4, 0x0B, 0xB5, 0x0B, 0xB6, 0x0B,\n\t0xB7, 0x0B, 0xB8, 0x0B, 0xB9, 0x0B, 0xBA, 0x0B, 0xBB, 0x0B, 0xBC, 0x0B, 0xBD, 0x0B, 0xBE, 0x0B, 0xBF, 0x0B, 0xC0, 0x0B, 0xC1, 0x0B, 0xC2, 0x0B, 0xC3, 0x0B, 0xC4, 0x0B, 0xC5, 0x0B, 0xC6, 0x0B,\n\t0xC7, 0x0B, 0xC8, 0x0B, 0xC9, 0x0B, 0xCA, 0x0B, 0xCB, 0x0B, 0xCC, 0x0B, 0xCD, 0x0B, 0xCE, 0x0B, 0xCF, 0x0B, 0xD0, 0x0B, 0xD1, 0x0B, 0xD2, 0x0B, 0xD3, 0x0B, 0xD4, 0x0B, 0xD5, 0x0B, 0xD6, 0x0B,\n\t0xD7, 0x0B, 0xD8, 0x0B, 0xD9, 0x0B, 0xDA, 0x0B, 0xDB, 0x0B, 0xDC, 0x0B, 0xDD, 0x0B, 0xDE, 0x0B, 0xDF, 0x0B, 0xE0, 0x0B, 0xE1, 0x0B, 0xE2, 0x0B, 0xE3, 0x0B, 0xE4, 0x0B, 0xE5, 0x0B, 0xE6, 0x0B,\n\t0xE7, 0x0B, 0xE8, 0x0B, 0xE9, 0x0B, 0xEA, 0x0B, 0xEB, 0x0B, 0xEC, 0x0B, 0xED, 0x0B, 0xEE, 0x0B, 0xEF, 0x0B, 0xF0, 0x0B, 0xF1, 0x0B, 0xF2, 0x0B, 0xF3, 0x0B, 0xF4, 0x0B, 0xF5, 0x0B, 0xF6, 0x0B,\n\t0xF7, 0x0B, 0xF8, 0x0B, 0xF9, 0x0B, 0xFA, 0x0B, 0xFB, 0x0B, 0xFC, 0x0B, 0xFD, 0x0B, 0xFE, 0x0B, 0xFF, 0xD1, 0x1F, 0xF1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF1, 0x01, 0x0C, 0x02, 0x0C,\n\t0x03, 0x0C, 0x04, 0x0C, 0x05, 0x0C, 0x06, 0x0C, 0x07, 0x0C, 0x08, 0x0C, 0x09, 0x0C, 0x0A, 0x0C, 0x0B, 0x0C, 0x0C, 0x0C, 0x0D, 0x0C, 0x0E, 0x0C, 0x0F, 0x0C, 0x10, 0x0C, 0x11, 0x0C, 0x12, 0x0C,\n\t0x13, 0x0C, 0x14, 0x0C, 0x15, 0x0C, 0x16, 0x0C, 0x17, 0x0C, 0x18, 0x0C, 0x19, 0x0C, 0x1A, 0x0C, 0x1B, 0x0C, 0x1C, 0x0C, 0x1D, 0x0C, 0x1E, 0x0C, 0x1F, 0x0C, 0x20, 0x0C, 0x21, 0x0C, 0x22, 0x0C,\n\t0x23, 0x0C, 0x24, 0x0C, 0x25, 0x0C, 0x26, 0x0C, 0x27, 0x0C, 0x28, 0x0C, 0x29, 0x0C, 0x2A, 0x0C, 0x2B, 0x0C, 0x2C, 0x0C, 0x2D, 0x0C, 0x2E, 0x0C, 0x2F, 0x0C, 0x30, 0x0C, 0x31, 0x0C, 0x32, 0x0C,\n\t0x33, 0x0C, 0x34, 0x0C, 0x35, 0x0C, 0x36, 0x0C, 0x37, 0x0C, 0x38, 0x0C, 0x39, 0x0C, 0x3A, 0x0C, 0x3B, 0x0C, 0x3C, 0x0C, 0x3D, 0x0C, 0x3E, 0x0C, 0x3F, 0x0C, 0x40, 0x0C, 0x41, 0x0C, 0x42, 0x0C,\n\t0x43, 0x0C, 0x44, 0x0C, 0x45, 0x0C, 0x46, 0x0C, 0x47, 0x0C, 0x48, 0x0C, 0x49, 0x0C, 0x4A, 0x0C, 0x4B, 0x0C, 0x4C, 0x0C, 0x4D, 0x0C, 0x4E, 0x0C, 0x4F, 0x0C, 0x50, 0x0C, 0x51, 0x0C, 0x52, 0x0C,\n\t0x53, 0x0C, 0x54, 0x0C, 0x55, 0x0C, 0x56, 0x0C, 0x57, 0x0C, 0x58, 0x0C, 0x59, 0x0C, 0x5A, 0x0C, 0x5B, 0x0C, 0x5C, 0x0C, 0x5D, 0x0C, 0x5E, 0x0C, 0x5F, 0x0C, 0x60, 0x0C, 0x61, 0x0C, 0x62, 0x0C,\n\t0x63, 0x0C, 0x64, 0x0C, 0x65, 0x0C, 0x66, 0x0C, 0x67, 0x0C, 0x68, 0x0C, 0x69, 0x0C, 0x6A, 0x0C, 0x6B, 0x0C, 0x6C, 0x0C, 0x6D, 0x0C, 0x6E, 0x0C, 0x6F, 0x0C, 0x70, 0x0C, 0x71, 0x0C, 0x72, 0x0C,\n\t0x73, 0x0C, 0x74, 0x0C, 0x75, 0x0C, 0x76, 0x0C, 0x77, 0x0C, 0x78, 0x0C, 0x79, 0x0C, 0x7A, 0x0C, 0x7C, 0x0C, 0x7D, 0x0C, 0x7E, 0x0C, 0x7F, 0x0C, 0x80, 0x0C, 0x81, 0x0C, 0x82, 0x0C, 0x83, 0x0C,\n\t0x84, 0x0C, 0x85, 0x0C, 0x86, 0x0C, 0x87, 0x0C, 0x88, 0x0C, 0x89, 0x0C, 0x8A, 0x0C, 0x8B, 0x0C, 0x8C, 0x0C, 0x8D, 0x0C, 0x8E, 0x0C, 0x8F, 0x0C, 0x90, 0x0C, 0x91, 0x0C, 0x92, 0x0C, 0x93, 0x0C,\n\t0x94, 0x0C, 0x95, 0x0C, 0x96, 0x0C, 0x97, 0x0C, 0x98, 0x0C, 0x99, 0x0C, 0x9A, 0x0C, 0x9B, 0x0C, 0x9C, 0x0C, 0x9D, 0x0C, 0x9E, 0x0C, 0x9F, 0x0C, 0xA0, 0x0C, 0xA1, 0x0C, 0xA2, 0x0C, 0xA3, 0x0C,\n\t0xA4, 0x0C, 0xA5, 0x0C, 0xA6, 0x0C, 0xA7, 0x0C, 0xA8, 0x0C, 0xA9, 0x0C, 0xAA, 0x0C, 0xAB, 0x0C, 0xAC, 0x0C, 0xAD, 0x0C, 0xAE, 0x0C, 0xAF, 0x0C, 0xB0, 0x0C, 0xB1, 0x0C, 0xB2, 0x0C, 0xB3, 0x0C,\n\t0xB4, 0x0C, 0xB5, 0x0C, 0xB6, 0x0C, 0xB7, 0x0C, 0xB8, 0x0C, 0xB9, 0x0C, 0xBA, 0x0C, 0xBB, 0x0C, 0xBC, 0x0C, 0xBD, 0x0C, 0xBE, 0x0C, 0xBF, 0x0C, 0xC0, 0x0C, 0xC1, 0x0C, 0xC2, 0x0C, 0xC3, 0x0C,\n\t0xC4, 0x0C, 0xC5, 0x0C, 0xC6, 0x0C, 0xC7, 0x0C, 0xC8, 0x0C, 0xC9, 0x0C, 0xCA, 0x0C, 0xCB, 0x0C, 0xCC, 0x0C, 0xCD, 0x0C, 0xCE, 0x0C, 0xCF, 0x0C, 0xD0, 0x0C, 0xD1, 0x0C, 0xD2, 0x0C, 0xD3, 0x0C,\n\t0xD4, 0x0C, 0xD5, 0x0C, 0xD6, 0x0C, 0xD7, 0x0C, 0xD8, 0x0C, 0xD9, 0x0C, 0xDA, 0x0C, 0xDB, 0x0C, 0xDC, 0x0C, 0xDD, 0x0C, 0xDE, 0x0C, 0xDF, 0x0C, 0xE0, 0x0C, 0xE1, 0x0C, 0xE2, 0x0C, 0xE3, 0x0C,\n\t0xE4, 0x0C, 0xE5, 0x0C, 0xE6, 0x0C, 0xE7, 0x0C, 0xE8, 0x0C, 0xE9, 0x0C, 0xEA, 0x0C, 0xEB, 0x0C, 0xEC, 0x0C, 0xED, 0x0C, 0xEE, 0x0C, 0xEF, 0x0C, 0xF0, 0x0C, 0xF1, 0x0C, 0xF2, 0x0C, 0xF3, 0x0C,\n\t0xF4, 0x0C, 0xF5, 0x0C, 0xF6, 0x0C, 0xF7, 0x0C, 0xF8, 0x0C, 0xF9, 0x0C, 0xFA, 0x0C, 0xFB, 0x0C, 0xFC, 0x0C, 0xFD, 0x0C, 0xFE, 0x0C, 0xFF, 0x0C, 0x00, 0x0D, 0x01, 0x0D, 0x02, 0x0D, 0x03, 0x0D,\n\t0x04, 0x0D, 0x05, 0x0D, 0x06, 0x0D, 0x07, 0x0D, 0x08, 0x0D, 0x09, 0x0D, 0x0A, 0x0D, 0x0B, 0x0D, 0x0C, 0x0D, 0x0D, 0x0D, 0x0E, 0x0D, 0x0F, 0x0D, 0x10, 0x0D, 0x11, 0x0D, 0x12, 0x0D, 0x13, 0x0D,\n\t0x14, 0x0D, 0x15, 0x0D, 0x16, 0x0D, 0x17, 0x0D, 0x18, 0x0D, 0x19, 0x0D, 0x1A, 0x0D, 0x1B, 0x0D, 0x1C, 0x0D, 0x1D, 0x0D, 0x1E, 0x0D, 0x1F, 0x0D, 0x20, 0x0D, 0x21, 0x0D, 0x22, 0x0D, 0x23, 0x0D,\n\t0x24, 0x0D, 0x25, 0x0D, 0x26, 0x0D, 0x27, 0x0D, 0x28, 0x0D, 0x29, 0x0D, 0x2A, 0x0D, 0x2B, 0x0D, 0x2C, 0x0D, 0x2D, 0x0D, 0x2E, 0x0D, 0x2F, 0x0D, 0x30, 0x0D, 0x31, 0x0D, 0x32, 0x0D, 0x33, 0x0D,\n\t0x34, 0x0D, 0x35, 0x0D, 0x36, 0x0D, 0x37, 0x0D, 0x38, 0x0D, 0x39, 0x0D, 0x3A, 0x0D, 0x3B, 0x0D, 0x3C, 0x0D, 0x3D, 0x0D, 0x3E, 0x0D, 0x3F, 0x0D, 0x40, 0x0D, 0x41, 0x0D, 0x42, 0x0D, 0x43, 0x0D,\n\t0x44, 0x0D, 0x45, 0x0D, 0x46, 0x0D, 0x47, 0x0D, 0x48, 0x0D, 0x49, 0x0D, 0x4A, 0x0D, 0x4B, 0x0D, 0x4C, 0x0D, 0x4D, 0x0D, 0x4E, 0x0D, 0x4F, 0x0D, 0x50, 0x0D, 0x51, 0x0D, 0x52, 0x0D, 0x53, 0x0D,\n\t0x54, 0x0D, 0x55, 0x0D, 0x56, 0x0D, 0x57, 0x0D, 0x58, 0x0D, 0x59, 0x0D, 0x5A, 0x0D, 0x5B, 0x0D, 0x5C, 0x0D, 0x5D, 0x0D, 0x5E, 0x0D, 0x5F, 0x0D, 0x60, 0x0D, 0x61, 0x0D, 0x62, 0x0D, 0x63, 0x0D,\n\t0x64, 0x0D, 0x65, 0x0D, 0x66, 0x0D, 0x67, 0x0D, 0x68, 0x0D, 0x69, 0x0D, 0x6A, 0x0D, 0x6B, 0x0D, 0x6C, 0x0D, 0x6D, 0x0D, 0x6E, 0x0D, 0x6F, 0x0D, 0x70, 0x0D, 0x71, 0x0D, 0x72, 0x0D, 0x73, 0x0D,\n\t0x74, 0x0D, 0x75, 0x0D, 0x76, 0x0D, 0x77, 0x0D, 0x78, 0x0D, 0x79, 0x0D, 0x7A, 0x0D, 0x7B, 0x0D, 0x7C, 0x0D, 0x7D, 0x0D, 0x7E, 0x0D, 0x7F, 0x0D, 0x80, 0x0D, 0x81, 0x0D, 0x82, 0x0D, 0x83, 0x0D,\n\t0x84, 0x0D, 0x85, 0x0D, 0x86, 0x0D, 0x87, 0x0D, 0x88, 0x0D, 0x89, 0x0D, 0x8A, 0x0D, 0x8B, 0x0D, 0x8C, 0x0D, 0x8D, 0x0D, 0x8E, 0x0D, 0x8F, 0x0D, 0x90, 0x0D, 0x91, 0x0D, 0x92, 0x0D, 0x93, 0x0D,\n\t0x94, 0x0D, 0x95, 0x0D, 0x96, 0x0D, 0x97, 0x0D, 0x98, 0x0D, 0x99, 0x0D, 0x9A, 0x0D, 0x9B, 0x0D, 0x9C, 0x0D, 0x9D, 0x0D, 0x9E, 0x0D, 0x9F, 0x0D, 0xA0, 0x0D, 0xA1, 0x0D, 0xA2, 0x0D, 0xA3, 0x0D,\n\t0xA4, 0x0D, 0xA5, 0x0D, 0xA6, 0x0D, 0xA7, 0x0D, 0xA8, 0x0D, 0xA9, 0x0D, 0xAA, 0x0D, 0xAB, 0x0D, 0xAC, 0x0D, 0xAD, 0x0D, 0xAE, 0x0D, 0xAF, 0x0D, 0xB0, 0x0D, 0xB1, 0x0D, 0xB2, 0x0D, 0xB3, 0x0D,\n\t0xB4, 0x0D, 0xB5, 0x0D, 0xB6, 0x0D, 0xB7, 0x0D, 0xB8, 0x0D, 0xB9, 0x0D, 0xBA, 0x0D, 0xBB, 0x0D, 0xBC, 0x0D, 0xBD, 0x0D, 0xBE, 0x0D, 0xBF, 0x0D, 0xC0, 0x0D, 0xC1, 0x0D, 0xC2, 0x0D, 0xC3, 0x0D,\n\t0xC4, 0x0D, 0xC5, 0x0D, 0xC6, 0x0D, 0xC7, 0x0D, 0xC8, 0x0D, 0xC9, 0x0D, 0xCA, 0x0D, 0xCB, 0x0D, 0xCC, 0x0D, 0xCD, 0x0D, 0xCE, 0x0D, 0xCF, 0x0D, 0xD0, 0x0D, 0xD1, 0x0D, 0xD2, 0x0D, 0xD3, 0x0D,\n\t0xD4, 0x0D, 0xD5, 0x0D, 0xD6, 0x0D, 0xD7, 0x0D, 0xD8, 0x0D, 0xD9, 0x0D, 0xDA, 0x0D, 0xDB, 0x0D, 0xDC, 0x0D, 0xDD, 0x0D, 0xDE, 0x0D, 0xDF, 0x0D, 0xE0, 0x0D, 0xE1, 0x0D, 0xE2, 0x0D, 0xE3, 0x0D,\n\t0xE4, 0x0D, 0xE5, 0x0D, 0xE6, 0x0D, 0xE7, 0x0D, 0xE8, 0x0D, 0xE9, 0x0D, 0xEA, 0x0D, 0xEB, 0x0D, 0xEC, 0x0D, 0xED, 0x0D, 0xEE, 0x0D, 0xEF, 0x0D, 0xF0, 0x0D, 0xF1, 0x0D, 0xF2, 0x0D, 0xF3, 0x0D,\n\t0xF4, 0x0D, 0xF5, 0x0D, 0xF6, 0x0D, 0xF7, 0x0D, 0xF8, 0x0D, 0xF9, 0x0D, 0xFA, 0x0D, 0xFB, 0x0D, 0xFC, 0x0D, 0xFD, 0x0D, 0xFE, 0x0D, 0xFF, 0x0D, 0x00, 0x0E, 0x01, 0x0E, 0x02, 0x0E, 0x03, 0x0E,\n\t0x04, 0x0E, 0x05, 0x0E, 0x06, 0x0E, 0x07, 0x0E, 0x08, 0x0E, 0x09, 0x0E, 0x0A, 0x0E, 0x0B, 0x0E, 0x0C, 0x0E, 0x0D, 0x0E, 0x0E, 0x0E, 0x0F, 0x0E, 0x10, 0x0E, 0x11, 0x0E, 0x12, 0x0E, 0x13, 0x0E,\n\t0x14, 0x0E, 0x15, 0x0E, 0x16, 0x0E, 0x17, 0x0E, 0x18, 0x0E, 0x19, 0x0E, 0x1A, 0x0E, 0x1B, 0x0E, 0x1C, 0x0E, 0x1D, 0x0E, 0x1E, 0x0E, 0x1F, 0x0E, 0x20, 0x0E, 0x21, 0x0E, 0x22, 0x0E, 0x23, 0x0E,\n\t0x24, 0x0E, 0x25, 0x0E, 0x26, 0x0E, 0x27, 0x0E, 0x28, 0x0E, 0x29, 0x0E, 0x2A, 0x0E, 0x2B, 0x0E, 0x2C, 0x0E, 0x2D, 0x0E, 0x2E, 0x0E, 0x2F, 0x0E, 0x30, 0x0E, 0x31, 0x0E, 0x32, 0x0E, 0x33, 0x0E,\n\t0x34, 0x0E, 0x35, 0x0E, 0x36, 0x0E, 0x37, 0x0E, 0x38, 0x0E, 0x39, 0x0E, 0x3A, 0x0E, 0x3B, 0x0E, 0x3C, 0x0E, 0x3D, 0x0E, 0x3E, 0x0E, 0x3F, 0x0E, 0x40, 0x0E, 0x41, 0x0E, 0x42, 0x0E, 0x43, 0x0E,\n\t0x44, 0x0E, 0x45, 0x0E, 0x46, 0x0E, 0x47, 0x0E, 0x48, 0x0E, 0x49, 0x0E, 0x4A, 0x0E, 0x4B, 0x0E, 0x4C, 0x0E, 0x4D, 0x0E, 0x4E, 0x0E, 0x4F, 0x0E, 0x50, 0x0E, 0x51, 0x0E, 0x52, 0x0E, 0x53, 0x0E,\n\t0x54, 0x0E, 0x55, 0x0E, 0x56, 0x0E, 0x57, 0x0E, 0x58, 0x0E, 0x59, 0x0E, 0x5A, 0x0E, 0x5B, 0x0E, 0x5C, 0x0E, 0x5D, 0x0E, 0x5E, 0x0E, 0x5F, 0x0E, 0x60, 0x0E, 0x61, 0x0E, 0x62, 0x0E, 0x63, 0x0E,\n\t0x64, 0x0E, 0x65, 0x0E, 0x66, 0x0E, 0x67, 0x0E, 0x68, 0x0E, 0x69, 0x0E, 0x6A, 0x0E, 0x6B, 0x0E, 0x6C, 0x0E, 0x6D, 0x0E, 0x6E, 0x0E, 0x6F, 0x0E, 0x70, 0x0E, 0x71, 0x0E, 0x72, 0x0E, 0x73, 0x0E,\n\t0x74, 0x0E, 0x75, 0x0E, 0x76, 0x0E, 0x77, 0x0E, 0x78, 0x0E, 0x79, 0x0E, 0x7A, 0x0E, 0x7B, 0x0E, 0x7C, 0x0E, 0x7D, 0x0E, 0x7E, 0x0E, 0x7F, 0x0E, 0x80, 0x0E, 0x81, 0x0E, 0x82, 0x0E, 0x83, 0x0E,\n\t0x84, 0x0E, 0x85, 0x0E, 0x86, 0x0E, 0x87, 0x0E, 0x88, 0x0E, 0x89, 0x0E, 0x8A, 0x0E, 0x8B, 0x0E, 0x8C, 0x0E, 0x8D, 0x0E, 0x8E, 0x0E, 0x8F, 0x0E, 0x90, 0x0E, 0x91, 0x0E, 0x92, 0x0E, 0x93, 0x0E,\n\t0x94, 0x0E, 0x95, 0x0E, 0x96, 0x0E, 0x97, 0x0E, 0x98, 0x0E, 0x99, 0x0E, 0x9A, 0x0E, 0x9B, 0x0E, 0x9C, 0x0E, 0x9D, 0x0E, 0x9E, 0x0E, 0x9F, 0x0E, 0xA0, 0x0E, 0xA1, 0x0E, 0xA2, 0x0E, 0xA3, 0x0E,\n\t0xA4, 0x0E, 0xA5, 0x0E, 0xA6, 0x0E, 0xA7, 0x0E, 0xA8, 0x0E, 0xA9, 0x0E, 0xAA, 0x0E, 0xAB, 0x0E, 0xAC, 0x0E, 0xAD, 0x0E, 0xAE, 0x0E, 0xAF, 0x0E, 0xB0, 0x0E, 0xB1, 0x0E, 0xB2, 0x0E, 0xB3, 0x0E,\n\t0xB4, 0x0E, 0xB5, 0x0E, 0xB6, 0x0E, 0xB7, 0x0E, 0xB8, 0x0E, 0xB9, 0x0E, 0xBA, 0x0E, 0xBB, 0x0E, 0xBC, 0x0E, 0xBD, 0x0E, 0xBE, 0x0E, 0xBF, 0x0E, 0xC0, 0x0E, 0xC1, 0x0E, 0xC2, 0x0E, 0xC3, 0x0E,\n\t0xC4, 0x0E, 0xC5, 0x0E, 0xC6, 0x0E, 0xC7, 0x0E, 0xC8, 0x0E, 0xC9, 0x0E, 0xCA, 0x0E, 0xCB, 0x0E, 0xCC, 0x0E, 0xCD, 0x0E, 0xCE, 0x0E, 0xCF, 0x0E, 0xD0, 0x0E, 0xD1, 0x0E, 0xD2, 0x0E, 0xD3, 0x0E,\n\t0xD4, 0x0E, 0xD5, 0x0E, 0xD6, 0x0E, 0xD7, 0x0E, 0xD8, 0x0E, 0xD9, 0x0E, 0xDA, 0x0E, 0xDB, 0x0E, 0xDC, 0x0E, 0xDD, 0x0E, 0xDE, 0x0E, 0xDF, 0x0E, 0xE0, 0x0E, 0xE1, 0x0E, 0xE2, 0x0E, 0xE3, 0x0E,\n\t0xE4, 0x0E, 0xE5, 0x0E, 0xE6, 0x0E, 0xE7, 0x0E, 0xE8, 0x0E, 0xE9, 0x0E, 0xEA, 0x0E, 0xEB, 0x0E, 0xEC, 0x0E, 0xED, 0x0E, 0xEE, 0x0E, 0xEF, 0x0E, 0xF0, 0x0E, 0xF1, 0x0E, 0xF2, 0x0E, 0xF3, 0x0E,\n\t0xF4, 0x0E, 0xF5, 0x0E, 0xF6, 0x0E, 0xF7, 0x0E, 0xF8, 0x0E, 0xF9, 0x0E, 0xFA, 0x0E, 0xFB, 0x0E, 0xFC, 0x0E, 0xFD, 0x0E, 0xFE, 0x0E, 0xFF, 0x0E, 0x00, 0x0F, 0x01, 0x0F, 0x02, 0x0F, 0x03, 0x0F,\n\t0x04, 0x0F, 0x05, 0x0F, 0x06, 0x0F, 0x07, 0x0F, 0x08, 0x0F, 0x09, 0x0F, 0x0A, 0x0F, 0x0B, 0x0F, 0x0C, 0x0F, 0x0D, 0x0F, 0x0E, 0x0F, 0x0F, 0x0F, 0x10, 0x0F, 0x11, 0x0F, 0x12, 0x0F, 0x13, 0x0F,\n\t0x14, 0x0F, 0x15, 0x0F, 0x16, 0x0F, 0x17, 0x0F, 0x18, 0x0F, 0x19, 0x0F, 0x1A, 0x0F, 0x1B, 0x0F, 0x1C, 0x0F, 0x1D, 0x0F, 0x1E, 0x0F, 0x1F, 0x0F, 0x20, 0x0F, 0x21, 0x0F, 0x22, 0x0F, 0x23, 0x0F,\n\t0x24, 0x0F, 0x25, 0x0F, 0x26, 0x0F, 0x27, 0x0F, 0x28, 0x0F, 0x29, 0x0F, 0x2A, 0x0F, 0x2B, 0x0F, 0x2C, 0x0F, 0x2D, 0x0F, 0x2E, 0x0F, 0x2F, 0x0F, 0x30, 0x0F, 0x31, 0x0F, 0x32, 0x0F, 0x33, 0x0F,\n\t0x34, 0x0F, 0x35, 0x0F, 0x36, 0x0F, 0x37, 0x0F, 0x38, 0x0F, 0x39, 0x0F, 0x3A, 0x0F, 0x3B, 0x0F, 0x3C, 0x0F, 0x3D, 0x0F, 0x3E, 0x0F, 0x3F, 0x0F, 0x40, 0x0F, 0x41, 0x0F, 0x42, 0x0F, 0x43, 0x0F,\n\t0x44, 0x0F, 0x45, 0x0F, 0x46, 0x0F, 0x47, 0x0F, 0x48, 0x0F, 0x49, 0x0F, 0x4A, 0x0F, 0x4B, 0x0F, 0x4C, 0x0F, 0x4D, 0x0F, 0x4E, 0x0F, 0x4F, 0x0F, 0x50, 0x0F, 0x51, 0x0F, 0x52, 0x0F, 0x53, 0x0F,\n\t0x54, 0x0F, 0x55, 0x0F, 0x56, 0x0F, 0x57, 0x0F, 0x58, 0x0F, 0x59, 0x0F, 0x5A, 0x0F, 0x5B, 0x0F, 0x5C, 0x0F, 0x5D, 0x0F, 0x5E, 0x0F, 0x5F, 0x0F, 0x60, 0x0F, 0x61, 0x0F, 0x62, 0x0F, 0x63, 0x0F,\n\t0x64, 0x0F, 0x65, 0x0F, 0x66, 0x0F, 0x67, 0x0F, 0x68, 0x0F, 0x69, 0x0F, 0x6A, 0x0F, 0x6B, 0x0F, 0x6C, 0x0F, 0x6D, 0x0F, 0x6E, 0x0F, 0x6F, 0x0F, 0x70, 0x0F, 0x71, 0x0F, 0x72, 0x0F, 0x73, 0x0F,\n\t0x74, 0x0F, 0x75, 0x0F, 0x76, 0x0F, 0x77, 0x0F, 0x78, 0x0F, 0x79, 0x0F, 0x7A, 0x0F, 0x7B, 0x0F, 0x7C, 0x0F, 0x7D, 0x0F, 0x7E, 0x0F, 0x7F, 0x0F, 0x80, 0x0F, 0x81, 0x0F, 0x82, 0x0F, 0x83, 0x0F,\n\t0x84, 0x0F, 0x85, 0x0F, 0x86, 0x0F, 0x87, 0x0F, 0x88, 0x0F, 0x89, 0x0F, 0x8A, 0x0F, 0x8B, 0x0F, 0x8C, 0x0F, 0x8D, 0x0F, 0x8E, 0x0F, 0x8F, 0x0F, 0x90, 0x0F, 0x91, 0x0F, 0x92, 0x0F, 0x93, 0x0F,\n\t0x94, 0x0F, 0x95, 0x0F, 0x96, 0x0F, 0x97, 0x0F, 0x98, 0x0F, 0x99, 0x0F, 0x9A, 0x0F, 0x9B, 0x0F, 0x9C, 0x0F, 0x9D, 0x0F, 0x9E, 0x0F, 0x9F, 0x0F, 0xA0, 0x0F, 0xA1, 0x0F, 0xA2, 0x0F, 0xA3, 0x0F,\n\t0xA4, 0x0F, 0xA5, 0x0F, 0xA6, 0x0F, 0xA7, 0x0F, 0xA8, 0x0F, 0xA9, 0x0F, 0xAA, 0x0F, 0xAB, 0x0F, 0xAC, 0x0F, 0xAD, 0x0F, 0xAE, 0x0F, 0xAF, 0x0F, 0xB0, 0x0F, 0xB1, 0x0F, 0xB2, 0x0F, 0xB3, 0x0F,\n\t0xB4, 0x0F, 0xB5, 0x0F, 0xB6, 0x0F, 0xB7, 0x0F, 0xB8, 0x0F, 0xB9, 0x0F, 0xBA, 0x0F, 0xBB, 0x0F, 0xBC, 0x0F, 0xBD, 0x0F, 0xBE, 0x0F, 0xBF, 0x0F, 0xC0, 0x0F, 0xC1, 0x0F, 0xC2, 0x0F, 0xC4, 0x0F,\n\t0xC5, 0x0F, 0xC6, 0x0F, 0xC7, 0x0F, 0xC8, 0x0F, 0xC9, 0x0F, 0xCA, 0x0F, 0xCB, 0x0F, 0xCC, 0x0F, 0xCD, 0x0F, 0xCE, 0x0F, 0xCF, 0x0F, 0xD0, 0x0F, 0xD1, 0x0F, 0xD2, 0x0F, 0xD3, 0x0F, 0xD4, 0x0F,\n\t0xD5, 0x0F, 0xD6, 0x0F, 0xD7, 0x0F, 0xD8, 0x0F, 0xD9, 0x0F, 0xDA, 0x0F, 0xDB, 0x0F, 0xDC, 0x0F, 0xDD, 0x0F, 0xDE, 0x0F, 0xDF, 0x0F, 0xE0, 0x0F, 0xE1, 0x0F, 0xE2, 0x0F, 0xE3, 0x0F, 0xE4, 0x0F,\n\t0xE5, 0x0F, 0xE6, 0x0F, 0xE7, 0x0F, 0xE8, 0x0F, 0xE9, 0x0F, 0xEA, 0x0F, 0xEB, 0x0F, 0xEC, 0x0F, 0xED, 0x0F, 0xEE, 0x0F, 0xEF, 0x0F, 0xF0, 0x0F, 0xF1, 0x0F, 0xF2, 0x0F, 0xF3, 0x0F, 0xF4, 0x0F,\n\t0xF5, 0x0F, 0xF6, 0x0F, 0xF7, 0x0F, 0xF8, 0x0F, 0xF9, 0x0F, 0xFA, 0x0F, 0xFB, 0x0F, 0xFC, 0x0F, 0xFD, 0x0F, 0xFE, 0x0F, 0xFF, 0xBF, 0x27, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF4,\n\t0x01, 0x10, 0x02, 0x10, 0x03, 0x10, 0x04, 0x10, 0x05, 0x10, 0x06, 0x10, 0x07, 0x10, 0x08, 0x10, 0x09, 0x10, 0x0A, 0x10, 0x0B, 0x10, 0x0C, 0x10, 0x0D, 0x10, 0x0E, 0x10, 0x0F, 0x10, 0x10, 0x10,\n\t0x11, 0x10, 0x12, 0x10, 0x13, 0x10, 0x14, 0x10, 0x15, 0x10, 0x16, 0x10, 0x17, 0x10, 0x18, 0x10, 0x19, 0x10, 0x1A, 0x10, 0x1B, 0x10, 0x1C, 0x10, 0x1D, 0x10, 0x1E, 0x10, 0x1F, 0x10, 0x20, 0x10,\n\t0x21, 0x10, 0x22, 0x10, 0x23, 0x10, 0x24, 0x10, 0x25, 0x10, 0x26, 0x10, 0x27, 0x10, 0x28, 0x10, 0x29, 0x10, 0x2A, 0x10, 0x2B, 0x10, 0x2C, 0x10, 0x2D, 0x10, 0x2E, 0x10, 0x2F, 0x10, 0x30, 0x10,\n\t0x31, 0x10, 0x32, 0x10, 0x33, 0x10, 0x34, 0x10, 0x35, 0x10, 0x36, 0x10, 0x37, 0x10, 0x38, 0x10, 0x39, 0x10, 0x3A, 0x10, 0x3B, 0x10, 0x3C, 0x10, 0x3D, 0x10, 0x3E, 0x10, 0x3F, 0x10, 0x40, 0x10,\n\t0x41, 0x10, 0x42, 0x10, 0x43, 0x10, 0x44, 0x10, 0x45, 0x10, 0x46, 0x10, 0x47, 0x10, 0x48, 0x10, 0x49, 0x10, 0x4A, 0x10, 0x4B, 0x10, 0x4C, 0x10, 0x4D, 0x10, 0x4E, 0x10, 0x4F, 0x10, 0x50, 0x10,\n\t0x51, 0x10, 0x52, 0x10, 0x53, 0x10, 0x54, 0x10, 0x55, 0x10, 0x56, 0x10, 0x57, 0x10, 0x58, 0x10, 0x59, 0x10, 0x5A, 0x10, 0x5B, 0x10, 0x5C, 0x10, 0x5D, 0x10, 0x5E, 0x10, 0x5F, 0x10, 0x60, 0x10,\n\t0x61, 0x10, 0x62, 0x10, 0x63, 0x10, 0x64, 0x10, 0x65, 0x10, 0x66, 0x10, 0x67, 0x10, 0x68, 0x10, 0x69, 0x10, 0x6A, 0x10, 0x6B, 0x10, 0x6C, 0x10, 0x6D, 0x10, 0x6E, 0x10, 0x6F, 0x10, 0x70, 0x10,\n\t0x71, 0x10, 0x72, 0x10, 0x73, 0x10, 0x74, 0x10, 0x75, 0x10, 0x76, 0x10, 0x77, 0x10, 0x78, 0x10, 0x79, 0x10, 0x7A, 0x10, 0x7B, 0x10, 0x7C, 0x10, 0x7D, 0x10, 0x7E, 0x10, 0x7F, 0x10, 0x80, 0x10,\n\t0x81, 0x10, 0x82, 0x10, 0x83, 0x10, 0x84, 0x10, 0x85, 0x10, 0x86, 0x10, 0x87, 0x10, 0x88, 0x10, 0x89, 0x10, 0x8A, 0x10, 0x8B, 0x10, 0x8C, 0x10, 0x8D, 0x10, 0x8E, 0x10, 0x8F, 0x10, 0x90, 0x10,\n\t0x91, 0x10, 0x92, 0x10, 0x93, 0x10, 0x94, 0x10, 0x95, 0x10, 0x96, 0x10, 0x97, 0x10, 0x98, 0x10, 0x99, 0x10, 0x9A, 0x10, 0x9B, 0x10, 0x9C, 0x10, 0x9D, 0x10, 0x9E, 0x10, 0x9F, 0x10, 0xA0, 0x10,\n\t0xA1, 0x10, 0xA2, 0x10, 0xA3, 0x10, 0xA4, 0x10, 0xA5, 0x10, 0xA6, 0x10, 0xA7, 0x10, 0xA8, 0x10, 0xA9, 0x10, 0xAA, 0x10, 0xAB, 0x10, 0xAC, 0x10, 0xAD, 0x10, 0xAE, 0x10, 0xAF, 0x10, 0xB0, 0x10,\n\t0xB1, 0x10, 0xB2, 0x10, 0xB3, 0x10, 0xB4, 0x10, 0xB5, 0x10, 0xB6, 0x10, 0xB7, 0x10, 0xB8, 0x10, 0xB9, 0x10, 0xBA, 0x10, 0xBB, 0x10, 0xBC, 0x10, 0xBD, 0x10, 0xBE, 0x10, 0xBF, 0x10, 0xC0, 0x10,\n\t0xC1, 0x10, 0xC2, 0x10, 0xC3, 0x10, 0xC4, 0x10, 0xC5, 0x10, 0xC6, 0x10, 0xC7, 0x10, 0xC8, 0x10, 0xC9, 0x10, 0xCA, 0x10, 0xCB, 0x10, 0xCC, 0x10, 0xCD, 0x10, 0xCE, 0x10, 0xCF, 0x10, 0xD0, 0x10,\n\t0xD1, 0x10, 0xD2, 0x10, 0xD3, 0x10, 0xD4, 0x10, 0xD5, 0x10, 0xD6, 0x10, 0xD7, 0x10, 0xD8, 0x10, 0xD9, 0x10, 0xDA, 0x10, 0xDB, 0x10, 0xDC, 0x10, 0xDD, 0x10, 0xDE, 0x10, 0xDF, 0x10, 0xE0, 0x10,\n\t0xE1, 0x10, 0xE2, 0x10, 0xE3, 0x10, 0xE4, 0x10, 0xE5, 0x10, 0xE6, 0x10, 0xE7, 0x10, 0xE8, 0x10, 0xE9, 0x10, 0xEA, 0x10, 0xEB, 0x10, 0xEC, 0x10, 0xED, 0x10, 0xEE, 0x10, 0xEF, 0x10, 0xF0, 0x10,\n\t0xF1, 0x10, 0xF2, 0x10, 0xF3, 0x10, 0xF4, 0x10, 0xF5, 0x10, 0xF6, 0x10, 0xF7, 0x10, 0xF8, 0x10, 0xF9, 0x10, 0xFA, 0x10, 0xFB, 0x10, 0xFC, 0x10, 0xFD, 0x10, 0xFE, 0x10, 0xFF, 0x10, 0x00, 0x11,\n\t0x01, 0x11, 0x02, 0x11, 0x03, 0x11, 0x04, 0x11, 0x05, 0x11, 0x06, 0x11, 0x07, 0x11, 0x08, 0x11, 0x09, 0x11, 0x0A, 0x11, 0x0B, 0x11, 0x0C, 0x11, 0x0D, 0x11, 0x0E, 0x11, 0x0F, 0x11, 0x10, 0x11,\n\t0x11, 0x11, 0x12, 0x11, 0x13, 0x11, 0x14, 0x11, 0x15, 0x11, 0x16, 0x11, 0x17, 0x11, 0x18, 0x11, 0x19, 0x11, 0x1A, 0x11, 0x1B, 0x11, 0x1C, 0x11, 0x1D, 0x11, 0x1E, 0x11, 0x1F, 0x11, 0x20, 0x11,\n\t0x21, 0x11, 0x22, 0x11, 0x23, 0x11, 0x24, 0x11, 0x25, 0x11, 0x26, 0x11, 0x27, 0x11, 0x28, 0x11, 0x29, 0x11, 0x2A, 0x11, 0x2B, 0x11, 0x2C, 0x11, 0x2D, 0x11, 0x2E, 0x11, 0x2F, 0x11, 0x30, 0x11,\n\t0x31, 0x11, 0x32, 0x11, 0x33, 0x11, 0x34, 0x11, 0x35, 0x11, 0x36, 0x11, 0x37, 0x11, 0x38, 0x11, 0x39, 0x11, 0x3A, 0x11, 0x3B, 0x11, 0x3C, 0x11, 0x3D, 0x11, 0x3E, 0x11, 0x3F, 0x11, 0x40, 0x11,\n\t0x41, 0x11, 0x42, 0x11, 0x43, 0x11, 0x44, 0x11, 0x45, 0x11, 0x46, 0x11, 0x47, 0x11, 0x48, 0x11, 0x49, 0x11, 0x4A, 0x11, 0x4B, 0x11, 0x4C, 0x11, 0x4D, 0x11, 0x4E, 0x11, 0x4F, 0x11, 0x50, 0x11,\n\t0x51, 0x11, 0x52, 0x11, 0x53, 0x11, 0x54, 0x11, 0x55, 0x11, 0x56, 0x11, 0x57, 0x11, 0x58, 0x11, 0x59, 0x11, 0x5A, 0x11, 0x5B, 0x11, 0x5C, 0x11, 0x5D, 0x11, 0x5E, 0x11, 0x5F, 0x11, 0x60, 0x11,\n\t0x61, 0x11, 0x62, 0x11, 0x63, 0x11, 0x64, 0x11, 0x65, 0x11, 0x66, 0x11, 0x67, 0x11, 0x68, 0x11, 0x69, 0x11, 0x6A, 0x11, 0x6B, 0x11, 0x6C, 0x11, 0x6D, 0x11, 0x6E, 0x11, 0x6F, 0x11, 0x70, 0x11,\n\t0x71, 0x11, 0x72, 0x11, 0x73, 0x11, 0x74, 0x11, 0x75, 0x11, 0x76, 0x11, 0x77, 0x11, 0x78, 0x11, 0x79, 0x11, 0x7A, 0x11, 0x7B, 0x11, 0x7C, 0x11, 0x7E, 0x11, 0x7F, 0x11, 0x80, 0x11, 0x81, 0x11,\n\t0x82, 0x11, 0x83, 0x11, 0x84, 0x11, 0x85, 0x11, 0x86, 0x11, 0x87, 0x11, 0x88, 0x11, 0x89, 0x11, 0x8A, 0x11, 0x8B, 0x11, 0x8C, 0x11, 0x8D, 0x11, 0x8E, 0x11, 0x8F, 0x11, 0x90, 0x11, 0x91, 0x11,\n\t0x92, 0x11, 0x93, 0x11, 0x94, 0x11, 0x95, 0x11, 0x96, 0x11, 0x97, 0x11, 0x98, 0x11, 0x99, 0x11, 0x9A, 0x11, 0x9B, 0x11, 0x9C, 0x11, 0x9D, 0x11, 0x9E, 0x11, 0x9F, 0x11, 0xA0, 0x11, 0xA1, 0x11,\n\t0xA2, 0x11, 0xA3, 0x11, 0xA4, 0x11, 0xA5, 0x11, 0xA6, 0x11, 0xA7, 0x11, 0xA8, 0x11, 0xA9, 0x11, 0xAA, 0x11, 0xAB, 0x11, 0xAC, 0x11, 0xAD, 0x11, 0xAE, 0x11, 0xAF, 0x11, 0xB0, 0x11, 0xB1, 0x11,\n\t0xB2, 0x11, 0xB3, 0x11, 0xB4, 0x11, 0xB5, 0x11, 0xB6, 0x11, 0xB7, 0x11, 0xB8, 0x11, 0xB9, 0x11, 0xBA, 0x11, 0xBB, 0x11, 0xBC, 0x11, 0xBD, 0x11, 0xBE, 0x11, 0xBF, 0x11, 0xC0, 0x11, 0xC1, 0x11,\n\t0xC2, 0x11, 0xC3, 0x11, 0xC4, 0x11, 0xC5, 0x11, 0xC6, 0x11, 0xC7, 0x11, 0xC8, 0x11, 0xC9, 0x11, 0xCA, 0x11, 0xCB, 0x11, 0xCC, 0x11, 0xCD, 0x11, 0xCE, 0x11, 0xCF, 0x11, 0xD0, 0x11, 0xD1, 0x11,\n\t0xD2, 0x11, 0xD3, 0x11, 0xD4, 0x11, 0xD5, 0x11, 0xD6, 0x11, 0xD7, 0x11, 0xD8, 0x11, 0xD9, 0x11, 0xDA, 0x11, 0xDB, 0x11, 0xDC, 0x11, 0xDD, 0x11, 0xDE, 0x11, 0xDF, 0x11, 0xE0, 0x11, 0xE1, 0x11,\n\t0xE2, 0x11, 0xE3, 0x11, 0xE4, 0x11, 0xE5, 0x11, 0xE6, 0x11, 0xE7, 0x11, 0xE8, 0x11, 0xE9, 0x11, 0xEA, 0x11, 0xEB, 0x11, 0xEC, 0x11, 0xED, 0x11, 0xEE, 0x11, 0xEF, 0x11, 0xF0, 0x11, 0xF1, 0x11,\n\t0xF2, 0x11, 0xF3, 0x11, 0xF4, 0x11, 0xF5, 0x11, 0xF6, 0x11, 0xF7, 0x11, 0xF8, 0x11, 0xF9, 0x11, 0xFA, 0x11, 0xFB, 0x11, 0xFC, 0x11, 0xFD, 0x11, 0xFE, 0x11, 0xFF, 0x11, 0x00, 0x12, 0x01, 0x12,\n\t0x02, 0x12, 0x03, 0x12, 0x04, 0x12, 0x05, 0x12, 0x06, 0x12, 0x07, 0x12, 0x08, 0x12, 0x09, 0x12, 0x0A, 0x12, 0x0B, 0x12, 0x0C, 0x12, 0x0D, 0x12, 0x0E, 0x12, 0x0F, 0x12, 0x10, 0x12, 0x11, 0x12,\n\t0x12, 0x12, 0x13, 0x12, 0x14, 0x12, 0x15, 0x12, 0x16, 0x12, 0x17, 0x12, 0x18, 0x12, 0x19, 0x12, 0x1A, 0x12, 0x1B, 0x12, 0x1C, 0x12, 0x1D, 0x12, 0x1E, 0x12, 0x1F, 0x12, 0x20, 0x12, 0x21, 0x12,\n\t0x22, 0x12, 0x23, 0x12, 0x24, 0x12, 0x25, 0x12, 0x26, 0x12, 0x27, 0x12, 0x28, 0x12, 0x29, 0x12, 0x2A, 0x12, 0x2B, 0x12, 0x2C, 0x12, 0x2D, 0x12, 0x2E, 0x12, 0x2F, 0x12, 0x30, 0x12, 0x31, 0x12,\n\t0x32, 0x12, 0x33, 0x12, 0x34, 0x12, 0x35, 0x12, 0x36, 0x12, 0x37, 0x12, 0x38, 0x12, 0x39, 0x12, 0x3A, 0x12, 0x3B, 0x12, 0x3C, 0x12, 0x3D, 0x12, 0x3E, 0x12, 0x3F, 0x12, 0x40, 0x12, 0x41, 0x12,\n\t0x42, 0x12, 0x43, 0x12, 0x44, 0x12, 0x45, 0x12, 0x46, 0x12, 0x47, 0x12, 0x48, 0x12, 0x49, 0x12, 0x4A, 0x12, 0x4B, 0x12, 0x4C, 0x12, 0x4D, 0x12, 0x4E, 0x12, 0x4F, 0x12, 0x50, 0x12, 0x51, 0x12,\n\t0x52, 0x12, 0x53, 0x12, 0x54, 0x12, 0x55, 0x12, 0x56, 0x12, 0x57, 0x12, 0x58, 0x12, 0x59, 0x12, 0x5A, 0x12, 0x5B, 0x12, 0x5C, 0x12, 0x5D, 0x12, 0x5E, 0x12, 0x5F, 0x12, 0x60, 0x12, 0x61, 0x12,\n\t0x62, 0x12, 0x63, 0x12, 0x64, 0x12, 0x65, 0x12, 0x66, 0x12, 0x67, 0x12, 0x68, 0x12, 0x69, 0x12, 0x6A, 0x12, 0x6B, 0x12, 0x6C, 0x12, 0x6D, 0x12, 0x6E, 0x12, 0x6F, 0x12, 0x70, 0x12, 0x71, 0x12,\n\t0x72, 0x12, 0x73, 0x12, 0x74, 0x12, 0x75, 0x12, 0x76, 0x12, 0x77, 0x12, 0x78, 0x12, 0x79, 0x12, 0x7A, 0x12, 0x7B, 0x12, 0x7C, 0x12, 0x7D, 0x12, 0x7E, 0x12, 0x7F, 0x12, 0x80, 0x12, 0x81, 0x12,\n\t0x82, 0x12, 0x83, 0x12, 0x84, 0x12, 0x85, 0x12, 0x86, 0x12, 0x87, 0x12, 0x88, 0x12, 0x89, 0x12, 0x8A, 0x12, 0x8B, 0x12, 0x8C, 0x12, 0x8D, 0x12, 0x8E, 0x12, 0x8F, 0x12, 0x90, 0x12, 0x91, 0x12,\n\t0x92, 0x12, 0x93, 0x12, 0x94, 0x12, 0x95, 0x12, 0x96, 0x12, 0x97, 0x12, 0x98, 0x12, 0x99, 0x12, 0x9A, 0x12, 0x9B, 0x12, 0x9C, 0x12, 0x9D, 0x12, 0x9E, 0x12, 0x9F, 0x12, 0xA0, 0x12, 0xA1, 0x12,\n\t0xA2, 0x12, 0xA3, 0x12, 0xA4, 0x12, 0xA5, 0x12, 0xA6, 0x12, 0xA7, 0x12, 0xA8, 0x12, 0xA9, 0x12, 0xAA, 0x12, 0xAB, 0x12, 0xAC, 0x12, 0xAD, 0x12, 0xAE, 0x12, 0xAF, 0x12, 0xB0, 0x12, 0xB1, 0x12,\n\t0xB2, 0x12, 0xB3, 0x12, 0xB4, 0x12, 0xB5, 0x12, 0xB6, 0x12, 0xB7, 0x12, 0xB8, 0x12, 0xB9, 0x12, 0xBA, 0x12, 0xBB, 0x12, 0xBC, 0x12, 0xBD, 0x12, 0xBE, 0x12, 0xBF, 0x12, 0xC0, 0x12, 0xC1, 0x12,\n\t0xC2, 0x12, 0xC3, 0x12, 0xC4, 0x12, 0xC5, 0x12, 0xC6, 0x12, 0xC7, 0x12, 0xC8, 0x12, 0xC9, 0x12, 0xCA, 0x12, 0xCB, 0x12, 0xCC, 0x12, 0xCD, 0x12, 0xCE, 0x12, 0xCF, 0x12, 0xD0, 0x12, 0xD1, 0x12,\n\t0xD2, 0x12, 0xD3, 0x12, 0xD4, 0x12, 0xD5, 0x12, 0xD6, 0x12, 0xD7, 0x12, 0xD8, 0x12, 0xD9, 0x12, 0xDA, 0x12, 0xDB, 0x12, 0xDC, 0x12, 0xDD, 0x12, 0xDE, 0x12, 0xDF, 0x12, 0xE0, 0x12, 0xE1, 0x12,\n\t0xE2, 0x12, 0xE3, 0x12, 0xE4, 0x12, 0xE5, 0x12, 0xE6, 0x12, 0xE7, 0x12, 0xE8, 0x12, 0xE9, 0x12, 0xEA, 0x12, 0xEB, 0x12, 0xEC, 0x12, 0xED, 0x12, 0xEE, 0x12, 0xEF, 0x12, 0xF0, 0x12, 0xF1, 0x12,\n\t0xF2, 0x12, 0xF3, 0x12, 0xF4, 0x12, 0xF5, 0x12, 0xF6, 0x12, 0xF7, 0x12, 0xF8, 0x12, 0xF9, 0x12, 0xFA, 0x12, 0xFB, 0x12, 0xFC, 0x12, 0xFD, 0x12, 0xFE, 0x12, 0xFF, 0x12, 0x00, 0x13, 0x01, 0x13,\n\t0x02, 0x13, 0x03, 0x13, 0x04, 0x13, 0x05, 0x13, 0x06, 0x13, 0x07, 0x13, 0x08, 0x13, 0x09, 0x13, 0x0A, 0x13, 0x0B, 0x13, 0x0C, 0x13, 0x0D, 0x13, 0x0E, 0x13, 0x0F, 0x13, 0x10, 0x13, 0x11, 0x13,\n\t0x12, 0x13, 0x13, 0x13, 0x14, 0x13, 0x15, 0x13, 0x16, 0x13, 0x18, 0x13, 0x19, 0x13, 0x1A, 0x13, 0x1B, 0x13, 0x1C, 0x13, 0x1D, 0x13, 0x1E, 0x13, 0x1F, 0x13, 0x20, 0x13, 0x21, 0x13, 0x22, 0x13,\n\t0x23, 0x13, 0x24, 0x13, 0x25, 0x13, 0x26, 0x13, 0x27, 0x13, 0x28, 0x13, 0x29, 0x13, 0x2A, 0x13, 0x2B, 0x13, 0x2C, 0x13, 0x2D, 0x13, 0x2E, 0x13, 0x2F, 0x13, 0x30, 0x13, 0x31, 0x13, 0x32, 0x13,\n\t0x33, 0x13, 0x34, 0x13, 0x35, 0x13, 0x36, 0x13, 0x37, 0x13, 0x38, 0x13, 0x39, 0x13, 0x3A, 0x13, 0x3B, 0x13, 0x3C, 0x13, 0x3D, 0x13, 0x3E, 0x13, 0x3F, 0x13, 0x40, 0x13, 0x41, 0x13, 0x42, 0x13,\n\t0x43, 0x13, 0x44, 0x13, 0x45, 0x13, 0x46, 0x13, 0x47, 0x13, 0x48, 0x13, 0x49, 0x13, 0x4A, 0x13, 0x4B, 0x13, 0x4C, 0x13, 0x4D, 0x13, 0x4E, 0x13, 0x4F, 0x13, 0x50, 0x13, 0x51, 0x13, 0x52, 0x13,\n\t0x53, 0x13, 0x54, 0x13, 0x55, 0x13, 0x56, 0x13, 0x57, 0x13, 0x58, 0x13, 0x59, 0x13, 0x5A, 0x13, 0x5B, 0x13, 0x5C, 0x13, 0x5D, 0x13, 0x5E, 0x13, 0x5F, 0x13, 0x60, 0x13, 0x61, 0x13, 0x62, 0x13,\n\t0x63, 0x13, 0x64, 0x13, 0x65, 0x13, 0x66, 0x13, 0x67, 0x13, 0x68, 0x13, 0x69, 0x13, 0x6A, 0x13, 0x6B, 0x13, 0x6C, 0x13, 0x6D, 0x13, 0x6E, 0x13, 0x6F, 0x13, 0x70, 0x13, 0x71, 0x13, 0x72, 0x13,\n\t0x73, 0x13, 0x74, 0x13, 0x75, 0x13, 0x76, 0x13, 0x77, 0x13, 0x78, 0x13, 0x79, 0x13, 0x7A, 0x13, 0x7B, 0x13, 0x7C, 0x13, 0x7D, 0x13, 0x7E, 0x13, 0x7F, 0x13, 0x80, 0x13, 0x81, 0x13, 0x82, 0x13,\n\t0x83, 0x13, 0x84, 0x13, 0x85, 0x13, 0x86, 0x13, 0x87, 0x13, 0x88, 0x13, 0x89, 0x13, 0x8A, 0x13, 0x8B, 0x13, 0x8C, 0x13, 0x8D, 0x13, 0x8E, 0x13, 0x8F, 0x13, 0x90, 0x13, 0x91, 0x13, 0x92, 0x13,\n\t0x93, 0x13, 0x94, 0x13, 0x95, 0x13, 0x96, 0x13, 0x97, 0x13, 0x98, 0x13, 0x99, 0x13, 0x9A, 0x13, 0x9B, 0x13, 0x9C, 0x13, 0x9D, 0x13, 0x9E, 0x13, 0x9F, 0x13, 0xA0, 0x13, 0xA1, 0x13, 0xA2, 0x13,\n\t0xA3, 0x13, 0xA4, 0x13, 0xA5, 0x13, 0xA6, 0x13, 0xA7, 0x13, 0xA8, 0x13, 0xA9, 0x13, 0xAA, 0x13, 0xAB, 0x13, 0xAC, 0x13, 0xAD, 0x13, 0xAE, 0x13, 0xAF, 0x13, 0xB0, 0x13, 0xB1, 0x13, 0xB2, 0x13,\n\t0xB3, 0x13, 0xB4, 0x13, 0xB5, 0x13, 0xB6, 0x13, 0xB7, 0x13, 0xB8, 0x13, 0xB9, 0x13, 0xBA, 0x13, 0xBB, 0x13, 0xBC, 0x13, 0xBD, 0x13, 0xBE, 0x13, 0xBF, 0x13, 0xC0, 0x13, 0xC1, 0x13, 0xC2, 0x13,\n\t0xC3, 0x13, 0xC4, 0x13, 0xC5, 0x13, 0xC6, 0x13, 0xC7, 0x13, 0xC8, 0x13, 0xC9, 0x13, 0xCA, 0x13, 0xCB, 0x13, 0xCC, 0x13, 0xCD, 0x13, 0xCE, 0x13, 0xCF, 0x13, 0xD0, 0x13, 0xD1, 0x13, 0xD2, 0x13,\n\t0xD3, 0x13, 0xD4, 0x13, 0xD5, 0x13, 0xD6, 0x13, 0xD7, 0x13, 0xD8, 0x13, 0xD9, 0x13, 0xDA, 0x13, 0xDB, 0x13, 0xDC, 0x13, 0xDD, 0x13, 0xDE, 0x13, 0xDF, 0x13, 0xE0, 0x13, 0xE1, 0x13, 0xE2, 0x13,\n\t0xE3, 0x13, 0xE4, 0x13, 0xE5, 0x13, 0xE6, 0x13, 0xE7, 0x13, 0xE8, 0x13, 0xE9, 0x13, 0xEA, 0x13, 0xEB, 0x13, 0xEC, 0x13, 0xED, 0x13, 0xEE, 0x13, 0xEF, 0x13, 0xF0, 0x13, 0xF1, 0x13, 0xF2, 0x13,\n\t0xF3, 0x13, 0xF4, 0x13, 0xF5, 0x13, 0xF6, 0x13, 0xF7, 0x13, 0xF8, 0x13, 0xF9, 0x13, 0xFA, 0x13, 0xFB, 0x13, 0xFC, 0x13, 0xFD, 0x13, 0xFE, 0x13, 0xFF, 0x13, 0x00, 0x14, 0xAF, 0x2B, 0xF0, 0xFF,\n\t0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEE, 0x02, 0x14, 0x03, 0x14, 0x04, 0x14, 0x05, 0x14, 0x06, 0x14, 0x07, 0x14, 0x08, 0x14, 0x09, 0x14, 0x0A, 0x14, 0x0B, 0x14, 0x0C, 0x14, 0x0D, 0x14, 0x0E,\n\t0x14, 0x0F, 0x14, 0x10, 0x14, 0x11, 0x14, 0x12, 0x14, 0x13, 0x14, 0x14, 0x14, 0x15, 0x14, 0x16, 0x14, 0x17, 0x14, 0x18, 0x14, 0x19, 0x14, 0x1A, 0x14, 0x1B, 0x14, 0x1C, 0x14, 0x1D, 0x14, 0x1E,\n\t0x14, 0x1F, 0x14, 0x20, 0x14, 0x21, 0x14, 0x22, 0x14, 0x23, 0x14, 0x24, 0x14, 0x25, 0x14, 0x26, 0x14, 0x27, 0x14, 0x28, 0x14, 0x29, 0x14, 0x2A, 0x14, 0x2B, 0x14, 0x2C, 0x14, 0x2D, 0x14, 0x2E,\n\t0x14, 0x2F, 0x14, 0x30, 0x14, 0x31, 0x14, 0x32, 0x14, 0x33, 0x14, 0x34, 0x14, 0x35, 0x14, 0x36, 0x14, 0x37, 0x14, 0x38, 0x14, 0x39, 0x14, 0x3A, 0x14, 0x3B, 0x14, 0x3C, 0x14, 0x3D, 0x14, 0x3E,\n\t0x14, 0x3F, 0x14, 0x40, 0x14, 0x41, 0x14, 0x42, 0x14, 0x43, 0x14, 0x44, 0x14, 0x45, 0x14, 0x46, 0x14, 0x47, 0x14, 0x48, 0x14, 0x49, 0x14, 0x4A, 0x14, 0x4B, 0x14, 0x4C, 0x14, 0x4D, 0x14, 0x4E,\n\t0x14, 0x4F, 0x14, 0x50, 0x14, 0x51, 0x14, 0x52, 0x14, 0x53, 0x14, 0x54, 0x14, 0x55, 0x14, 0x56, 0x14, 0x57, 0x14, 0x58, 0x14, 0x59, 0x14, 0x5A, 0x14, 0x5B, 0x14, 0x5C, 0x14, 0x5D, 0x14, 0x5E,\n\t0x14, 0x5F, 0x14, 0x60, 0x14, 0x61, 0x14, 0x62, 0x14, 0x63, 0x14, 0x64, 0x14, 0x65, 0x14, 0x66, 0x14, 0x67, 0x14, 0x68, 0x14, 0x69, 0x14, 0x6A, 0x14, 0x6B, 0x14, 0x6C, 0x14, 0x6D, 0x14, 0x6E,\n\t0x14, 0x6F, 0x14, 0x70, 0x14, 0x71, 0x14, 0x72, 0x14, 0x73, 0x14, 0x74, 0x14, 0x75, 0x14, 0x76, 0x14, 0x77, 0x14, 0x78, 0x14, 0x79, 0x14, 0x7A, 0x14, 0x7B, 0x14, 0x7C, 0x14, 0x7D, 0x14, 0x7E,\n\t0x14, 0x7F, 0x14, 0x80, 0x14, 0x81, 0x14, 0x82, 0x14, 0x83, 0x14, 0x84, 0x14, 0x85, 0x14, 0x86, 0x14, 0x87, 0x14, 0x88, 0x14, 0x89, 0x14, 0x8A, 0x14, 0x8B, 0x14, 0x8C, 0x14, 0x8D, 0x14, 0x8E,\n\t0x14, 0x8F, 0x14, 0x90, 0x14, 0x91, 0x14, 0x92, 0x14, 0x93, 0x14, 0x94, 0x14, 0x95, 0x14, 0x96, 0x14, 0x97, 0x14, 0x98, 0x14, 0x99, 0x14, 0x9A, 0x14, 0x9B, 0x14, 0x9C, 0x14, 0x9D, 0x14, 0x9E,\n\t0x14, 0x9F, 0x14, 0xA0, 0x14, 0xA1, 0x14, 0xA2, 0x14, 0xA3, 0x14, 0xA4, 0x14, 0xA5, 0x14, 0xA6, 0x14, 0xA7, 0x14, 0xA8, 0x14, 0xA9, 0x14, 0xAA, 0x14, 0xAB, 0x14, 0xAC, 0x14, 0xAD, 0x14, 0xAE,\n\t0x14, 0xAF, 0x14, 0xB0, 0x14, 0xB1, 0x14, 0xB2, 0x14, 0xB3, 0x14, 0xB4, 0x14, 0xB5, 0x14, 0xB6, 0x14, 0xB7, 0x14, 0xB8, 0x14, 0xB9, 0x14, 0xBA, 0x14, 0xBB, 0x14, 0xBC, 0x14, 0xBD, 0x14, 0xBE,\n\t0x14, 0xBF, 0x14, 0xC0, 0x14, 0xC1, 0x14, 0xC2, 0x14, 0xC3, 0x14, 0xC4, 0x14, 0xC5, 0x14, 0xC6, 0x14, 0xC7, 0x14, 0xC8, 0x14, 0xC9, 0x14, 0xCA, 0x14, 0xCB, 0x14, 0xCC, 0x14, 0xCD, 0x14, 0xCE,\n\t0x14, 0xCF, 0x14, 0xD0, 0x14, 0xD1, 0x14, 0xD2, 0x14, 0xD3, 0x14, 0xD4, 0x14, 0xD5, 0x14, 0xD6, 0x14, 0xD7, 0x14, 0xD8, 0x14, 0xD9, 0x14, 0xDA, 0x14, 0xDB, 0x14, 0xDC, 0x14, 0xDD, 0x14, 0xDE,\n\t0x14, 0xDF, 0x14, 0xE0, 0x14, 0xE1, 0x14, 0xE2, 0x14, 0xE3, 0x14, 0xE4, 0x14, 0xE5, 0x14, 0xE6, 0x14, 0xE7, 0x14, 0xE8, 0x14, 0xE9, 0x14, 0xEA, 0x14, 0xEB, 0x14, 0xEC, 0x14, 0xED, 0x14, 0xEE,\n\t0x14, 0xEF, 0x14, 0xF0, 0x14, 0xF1, 0x14, 0xF2, 0x14, 0xF3, 0x14, 0xF4, 0x14, 0xF5, 0x14, 0xF6, 0x14, 0xF7, 0x14, 0xF8, 0x14, 0xF9, 0x14, 0xFA, 0x14, 0xFB, 0x14, 0xFC, 0x14, 0xFD, 0x14, 0xFE,\n\t0x14, 0xFF, 0x14, 0x00, 0x15, 0x01, 0x15, 0x02, 0x15, 0x03, 0x15, 0x05, 0x15, 0x06, 0x15, 0x07, 0x15, 0x08, 0x15, 0x09, 0x15, 0x0A, 0x15, 0x0B, 0x15, 0x0C, 0x15, 0x0D, 0x15, 0x0E, 0x15, 0x0F,\n\t0x15, 0x10, 0x15, 0x11, 0x15, 0x12, 0x15, 0x13, 0x15, 0x14, 0x15, 0x15, 0x15, 0x16, 0x15, 0x17, 0x15, 0x18, 0x15, 0x19, 0x15, 0x1A, 0x15, 0x1B, 0x15, 0x1C, 0x15, 0x1D, 0x15, 0x1E, 0x15, 0x1F,\n\t0x15, 0x20, 0x15, 0x21, 0x15, 0x22, 0x15, 0x23, 0x15, 0x24, 0x15, 0x25, 0x15, 0x26, 0x15, 0x27, 0x15, 0x28, 0x15, 0x29, 0x15, 0x2A, 0x15, 0x2B, 0x15, 0x2C, 0x15, 0x2D, 0x15, 0x2E, 0x15, 0x2F,\n\t0x15, 0x30, 0x15, 0x31, 0x15, 0x32, 0x15, 0x33, 0x15, 0x34, 0x15, 0x35, 0x15, 0x36, 0x15, 0x37, 0x15, 0x38, 0x15, 0x39, 0x15, 0x3A, 0x15, 0x3B, 0x15, 0x3C, 0x15, 0x3D, 0x15, 0x3E, 0x15, 0x3F,\n\t0x15, 0x40, 0x15, 0x41, 0x15, 0x42, 0x15, 0x43, 0x15, 0x44, 0x15, 0x45, 0x15, 0x46, 0x15, 0x47, 0x15, 0x48, 0x15, 0x49, 0x15, 0x4A, 0x15, 0x4B, 0x15, 0x4C, 0x15, 0x4D, 0x15, 0x4E, 0x15, 0x4F,\n\t0x15, 0x50, 0x15, 0x51, 0x15, 0x52, 0x15, 0x53, 0x15, 0x54, 0x15, 0x55, 0x15, 0x56, 0x15, 0x57, 0x15, 0x58, 0x15, 0x59, 0x15, 0x5A, 0x15, 0x5B, 0x15, 0x5C, 0x15, 0x5D, 0x15, 0x5E, 0x15, 0x5F,\n\t0x15, 0x60, 0x15, 0x61, 0x15, 0x62, 0x15, 0x63, 0x15, 0x64, 0x15, 0x65, 0x15, 0x66, 0x15, 0x67, 0x15, 0x68, 0x15, 0x69, 0x15, 0x6A, 0x15, 0x6B, 0x15, 0x6C, 0x15, 0x6D, 0x15, 0x6E, 0x15, 0x6F,\n\t0x15, 0x70, 0x15, 0x71, 0x15, 0x72, 0x15, 0x73, 0x15, 0x74, 0x15, 0x75, 0x15, 0x76, 0x15, 0x77, 0x15, 0x78, 0x15, 0x79, 0x15, 0x7A, 0x15, 0x7B, 0x15, 0x7C, 0x15, 0x7D, 0x15, 0x7E, 0x15, 0x7F,\n\t0x15, 0x80, 0x15, 0x81, 0x15, 0x82, 0x15, 0x83, 0x15, 0x84, 0x15, 0x85, 0x15, 0x86, 0x15, 0x87, 0x15, 0x88, 0x15, 0x89, 0x15, 0x8A, 0x15, 0x8B, 0x15, 0x8C, 0x15, 0x8D, 0x15, 0x8E, 0x15, 0x8F,\n\t0x15, 0x90, 0x15, 0x91, 0x15, 0x92, 0x15, 0x93, 0x15, 0x94, 0x15, 0x95, 0x15, 0x96, 0x15, 0x97, 0x15, 0x98, 0x15, 0x99, 0x15, 0x9A, 0x15, 0x9B, 0x15, 0x9C, 0x15, 0x9D, 0x15, 0x9E, 0x15, 0x9F,\n\t0x15, 0xA0, 0x15, 0xA1, 0x15, 0xA2, 0x15, 0xA3, 0x15, 0xA4, 0x15, 0xA5, 0x15, 0xA6, 0x15, 0xA7, 0x15, 0xA8, 0x15, 0xA9, 0x15, 0xAA, 0x15, 0xAB, 0x15, 0xAC, 0x15, 0xAD, 0x15, 0xAE, 0x15, 0xAF,\n\t0x15, 0xB0, 0x15, 0xB1, 0x15, 0xB2, 0x15, 0xB3, 0x15, 0xB4, 0x15, 0xB5, 0x15, 0xB6, 0x15, 0xB7, 0x15, 0xB8, 0x15, 0xB9, 0x15, 0xBA, 0x15, 0xBB, 0x15, 0xBC, 0x15, 0xBD, 0x15, 0xBE, 0x15, 0xBF,\n\t0x15, 0xC0, 0x15, 0xC1, 0x15, 0xC2, 0x15, 0xC3, 0x15, 0xC4, 0x15, 0xC5, 0x15, 0xC6, 0x15, 0xC7, 0x15, 0xC8, 0x15, 0xC9, 0x15, 0xCA, 0x15, 0xCB, 0x15, 0xCC, 0x15, 0xCD, 0x15, 0xCE, 0x15, 0xCF,\n\t0x15, 0xD0, 0x15, 0xD1, 0x15, 0xD2, 0x15, 0xD3, 0x15, 0xD4, 0x15, 0xD5, 0x15, 0xD6, 0x15, 0xD7, 0x15, 0xD8, 0x15, 0xD9, 0x15, 0xDA, 0x15, 0xDB, 0x15, 0xDC, 0x15, 0xDD, 0x15, 0xDE, 0x15, 0xDF,\n\t0x15, 0xE0, 0x15, 0xE1, 0x15, 0xE2, 0x15, 0xE3, 0x15, 0xE4, 0x15, 0xE5, 0x15, 0xE6, 0x15, 0xE7, 0x15, 0xE8, 0x15, 0xE9, 0x15, 0xEA, 0x15, 0xEB, 0x15, 0xEC, 0x15, 0xED, 0x15, 0xEE, 0x15, 0xEF,\n\t0x15, 0xF1, 0x15, 0xF2, 0x15, 0xF3, 0x15, 0xF4, 0x15, 0xF5, 0x15, 0xF6, 0x15, 0xF7, 0x15, 0xF8, 0x15, 0xF9, 0x15, 0xFA, 0x15, 0xFB, 0x15, 0xFC, 0x15, 0xFD, 0x15, 0xFE, 0x15, 0xFF, 0x15, 0x00,\n\t0x16, 0x01, 0x16, 0x02, 0x16, 0x03, 0x16, 0x04, 0x16, 0x05, 0x16, 0x06, 0x16, 0x07, 0x16, 0x08, 0x16, 0x09, 0x16, 0x0A, 0x16, 0x0B, 0x16, 0x0C, 0x16, 0x0D, 0x16, 0x0E, 0x16, 0x0F, 0x16, 0x10,\n\t0x16, 0x11, 0x16, 0x12, 0x16, 0x13, 0x16, 0x14, 0x16, 0x15, 0x16, 0x16, 0x16, 0x17, 0x16, 0x18, 0x16, 0x19, 0x16, 0x1A, 0x16, 0x1B, 0x16, 0x1C, 0x16, 0x1D, 0x16, 0x1E, 0x16, 0x1F, 0x16, 0x20,\n\t0x16, 0x21, 0x16, 0x22, 0x16, 0x23, 0x16, 0x24, 0x16, 0x25, 0x16, 0x26, 0x16, 0x27, 0x16, 0x28, 0x16, 0x29, 0x16, 0x2A, 0x16, 0x2B, 0x16, 0x2C, 0x16, 0x2D, 0x16, 0x2E, 0x16, 0x2F, 0x16, 0x30,\n\t0x16, 0x31, 0x16, 0x32, 0x16, 0x33, 0x16, 0x34, 0x16, 0x35, 0x16, 0x36, 0x16, 0x37, 0x16, 0x38, 0x16, 0x39, 0x16, 0x3A, 0x16, 0x3B, 0x16, 0x3C, 0x16, 0x3D, 0x16, 0x3E, 0x16, 0x3F, 0x16, 0x40,\n\t0x16, 0x41, 0x16, 0x42, 0x16, 0x43, 0x16, 0x44, 0x16, 0x45, 0x16, 0x46, 0x16, 0x47, 0x16, 0x48, 0x16, 0x49, 0x16, 0x4A, 0x16, 0x4B, 0x16, 0x4C, 0x16, 0x4D, 0x16, 0x4E, 0x16, 0x4F, 0x16, 0x50,\n\t0x16, 0x51, 0x16, 0x52, 0x16, 0x53, 0x16, 0x54, 0x16, 0x55, 0x16, 0x56, 0x16, 0x57, 0x16, 0x58, 0x16, 0x59, 0x16, 0x5A, 0x16, 0x5B, 0x16, 0x5C, 0x16, 0x5D, 0x16, 0x5E, 0x16, 0x5F, 0x16, 0x60,\n\t0x16, 0x61, 0x16, 0x62, 0x16, 0x63, 0x16, 0x64, 0x16, 0x65, 0x16, 0x66, 0x16, 0x67, 0x16, 0x68, 0x16, 0x69, 0x16, 0x6A, 0x16, 0x6B, 0x16, 0x6C, 0x16, 0x6D, 0x16, 0x6E, 0x16, 0x6F, 0x16, 0x70,\n\t0x16, 0x71, 0x16, 0x72, 0x16, 0x73, 0x16, 0x74, 0x16, 0x75, 0x16, 0x76, 0x16, 0x77, 0x16, 0x78, 0x16, 0x79, 0x16, 0x7A, 0x16, 0x7B, 0x16, 0x7C, 0x16, 0x7D, 0x16, 0x7E, 0x16, 0x7F, 0x16, 0x80,\n\t0x16, 0x81, 0x16, 0x82, 0x16, 0x83, 0x16, 0x84, 0x16, 0x85, 0x16, 0x86, 0x16, 0x87, 0x16, 0x88, 0x16, 0x89, 0x16, 0x8A, 0x16, 0x8B, 0x16, 0x8C, 0x16, 0x8D, 0x16, 0x8E, 0x16, 0x8F, 0x16, 0x90,\n\t0x16, 0x91, 0x16, 0x92, 0x16, 0x93, 0x16, 0x94, 0x16, 0x95, 0x16, 0x96, 0x16, 0x97, 0x16, 0x98, 0x16, 0x99, 0x16, 0x9A, 0x16, 0x9B, 0x16, 0x9C, 0x16, 0x9D, 0x16, 0x9E, 0x16, 0x9F, 0x16, 0xA0,\n\t0x16, 0xA1, 0x16, 0xA2, 0x16, 0xA3, 0x16, 0xA4, 0x16, 0xA5, 0x16, 0xA6, 0x16, 0xA7, 0x16, 0xA8, 0x16, 0xA9, 0x16, 0xAA, 0x16, 0xAB, 0x16, 0xAC, 0x16, 0xAD, 0x16, 0xAE, 0x16, 0xAF, 0x16, 0xB0,\n\t0x16, 0xB1, 0x16, 0xB2, 0x16, 0xB3, 0x16, 0xB4, 0x16, 0xB5, 0x16, 0xB6, 0x16, 0xB7, 0x16, 0xB8, 0x16, 0xB9, 0x16, 0xBA, 0x16, 0xBB, 0x16, 0xBD, 0x16, 0xBE, 0x16, 0xBF, 0x16, 0xC0, 0x16, 0xC1,\n\t0x16, 0xC2, 0x16, 0xC3, 0x16, 0xC4, 0x16, 0xC5, 0x16, 0xC6, 0x16, 0xC7, 0x16, 0xC8, 0x16, 0xC9, 0x16, 0xCA, 0x16, 0xCB, 0x16, 0xCC, 0x16, 0xCD, 0x16, 0xCE, 0x16, 0xCF, 0x16, 0xD0, 0x16, 0xD1,\n\t0x16, 0xD2, 0x16, 0xD3, 0x16, 0xD4, 0x16, 0xD5, 0x16, 0xD6, 0x16, 0xD7, 0x16, 0xD8, 0x16, 0xD9, 0x16, 0xDA, 0x16, 0xDB, 0x16, 0xDC, 0x16, 0xDD, 0x16, 0xDE, 0x16, 0xDF, 0x16, 0xE0, 0x16, 0xE1,\n\t0x16, 0xE2, 0x16, 0xE3, 0x16, 0xE4, 0x16, 0xE5, 0x16, 0xE6, 0x16, 0xE7, 0x16, 0xE8, 0x16, 0xE9, 0x16, 0xEA, 0x16, 0xEB, 0x16, 0xEC, 0x16, 0xED, 0x16, 0xEE, 0x16, 0xEF, 0x16, 0xF0, 0x16, 0xF1,\n\t0x16, 0xF2, 0x16, 0xF3, 0x16, 0xF4, 0x16, 0xF5, 0x16, 0xF6, 0x16, 0xF7, 0x16, 0xF8, 0x16, 0xF9, 0x16, 0xFA, 0x16, 0xFB, 0x16, 0xFC, 0x16, 0xFD, 0x16, 0xFE, 0x16, 0xFF, 0x16, 0x00, 0x17, 0x01,\n\t0x17, 0x02, 0x17, 0x03, 0x17, 0x04, 0x17, 0x05, 0x17, 0x06, 0x17, 0x07, 0x17, 0x08, 0x17, 0x09, 0x17, 0x0A, 0x17, 0x0B, 0x17, 0x0C, 0x17, 0x0D, 0x17, 0x0E, 0x17, 0x0F, 0x17, 0x10, 0x17, 0x11,\n\t0x17, 0x12, 0x17, 0x13, 0x17, 0x14, 0x17, 0x15, 0x17, 0x16, 0x17, 0x17, 0x17, 0x18, 0x17, 0x19, 0x17, 0x1A, 0x17, 0x1B, 0x17, 0x1C, 0x17, 0x1D, 0x17, 0x1E, 0x17, 0x1F, 0x17, 0x20, 0x17, 0x21,\n\t0x17, 0x22, 0x17, 0x23, 0x17, 0x24, 0x17, 0x25, 0x17, 0x26, 0x17, 0x27, 0x17, 0x28, 0x17, 0x29, 0x17, 0x2A, 0x17, 0x2B, 0x17, 0x2C, 0x17, 0x2D, 0x17, 0x2E, 0x17, 0x2F, 0x17, 0x30, 0x17, 0x31,\n\t0x17, 0x32, 0x17, 0x33, 0x17, 0x34, 0x17, 0x35, 0x17, 0x36, 0x17, 0x37, 0x17, 0x38, 0x17, 0x39, 0x17, 0x3A, 0x17, 0x3B, 0x17, 0x3C, 0x17, 0x3D, 0x17, 0x3E, 0x17, 0x3F, 0x17, 0x40, 0x17, 0x41,\n\t0x17, 0x42, 0x17, 0x43, 0x17, 0x44, 0x17, 0x45, 0x17, 0x46, 0x17, 0x47, 0x17, 0x48, 0x17, 0x49, 0x17, 0x4A, 0x17, 0x4B, 0x17, 0x4C, 0x17, 0x4D, 0x17, 0x4E, 0x17, 0x4F, 0x17, 0x50, 0x17, 0x51,\n\t0x17, 0x52, 0x17, 0x53, 0x17, 0x54, 0x17, 0x55, 0x17, 0x56, 0x17, 0x57, 0x17, 0x58, 0x17, 0x59, 0x17, 0x5A, 0x17, 0x5B, 0x17, 0x5C, 0x17, 0x5D, 0x17, 0x5E, 0x17, 0x5F, 0x17, 0x60, 0x17, 0x61,\n\t0x17, 0x62, 0x17, 0x63, 0x17, 0x64, 0x17, 0x65, 0x17, 0x66, 0x17, 0x67, 0x17, 0x68, 0x17, 0x69, 0x17, 0x6A, 0x17, 0x6B, 0x17, 0x6C, 0x17, 0x6D, 0x17, 0x6E, 0x17, 0x6F, 0x17, 0x70, 0x17, 0x71,\n\t0x17, 0x72, 0x17, 0x74, 0x17, 0x75, 0x17, 0x76, 0x17, 0x77, 0x17, 0x78, 0x17, 0x79, 0x17, 0x7A, 0x17, 0x7B, 0x17, 0x7C, 0x17, 0x7D, 0x17, 0x7E, 0x17, 0x7F, 0x17, 0x80, 0x17, 0x81, 0x17, 0x82,\n\t0x17, 0x83, 0x17, 0x84, 0x17, 0x85, 0x17, 0x86, 0x17, 0x87, 0x17, 0x88, 0x17, 0x89, 0x17, 0x8A, 0x17, 0x8B, 0x17, 0x8C, 0x17, 0x8D, 0x17, 0x8E, 0x17, 0x8F, 0x17, 0x90, 0x17, 0x91, 0x17, 0x92,\n\t0x17, 0x93, 0x17, 0x94, 0x17, 0x95, 0x17, 0x96, 0x17, 0x97, 0x17, 0x98, 0x17, 0x99, 0x17, 0x9A, 0x17, 0x9B, 0x17, 0x9C, 0x17, 0x9D, 0x17, 0x9E, 0x17, 0x9F, 0x17, 0xA0, 0x17, 0xA1, 0x17, 0xA2,\n\t0x17, 0xA3, 0x17, 0xA4, 0x17, 0xA5, 0x17, 0xA6, 0x17, 0xA7, 0x17, 0xA8, 0x17, 0xA9, 0x17, 0xAA, 0x17, 0xAB, 0x17, 0xAC, 0x17, 0xAD, 0x17, 0xAE, 0x17, 0xAF, 0x17, 0xB0, 0x17, 0xB1, 0x17, 0xB2,\n\t0x17, 0xB3, 0x17, 0xB4, 0x17, 0xB5, 0x17, 0xB6, 0x17, 0xB7, 0x17, 0xB8, 0x17, 0xB9, 0x17, 0xBA, 0x17, 0xBB, 0x17, 0xBC, 0x17, 0xBD, 0x17, 0xBE, 0x17, 0xBF, 0x17, 0xC0, 0x17, 0xC1, 0x17, 0xC2,\n\t0x17, 0xC3, 0x17, 0xC4, 0x17, 0xC5, 0x17, 0xC6, 0x17, 0xC7, 0x17, 0xC8, 0x17, 0xC9, 0x17, 0xCA, 0x17, 0xCB, 0x17, 0xCC, 0x17, 0xCD, 0x17, 0xCE, 0x17, 0xCF, 0x17, 0xD0, 0x17, 0xD1, 0x17, 0xD2,\n\t0x17, 0xD3, 0x17, 0xD4, 0x17, 0xD5, 0x17, 0xD6, 0x17, 0xD7, 0x17, 0xD8, 0x17, 0xD9, 0x17, 0xDA, 0x17, 0xDB, 0x17, 0xDC, 0x17, 0xDD, 0x17, 0xDE, 0x17, 0xDF, 0x17, 0xE0, 0x17, 0xE1, 0x17, 0xE2,\n\t0x17, 0xE3, 0x17, 0xE4, 0x17, 0xE5, 0x17, 0xE6, 0x17, 0xE7, 0x17, 0xE8, 0x17, 0xE9, 0x17, 0xEA, 0x17, 0xEB, 0x17, 0xEC, 0x17, 0xED, 0x17, 0xEE, 0x17, 0xEF, 0x17, 0xF0, 0x17, 0xF1, 0x17, 0xF2,\n\t0x17, 0xF3, 0x17, 0xF4, 0x17, 0xF5, 0x17, 0xF6, 0x17, 0xF7, 0x17, 0xF8, 0x17, 0xF9, 0x17, 0xFA, 0x17, 0xFB, 0x17, 0xFC, 0x17, 0xFD, 0x17, 0xFE, 0x17, 0xFF, 0x17, 0x00, 0x18, 0x99, 0x33, 0x00,\n\t0x9D, 0x2F, 0x00, 0xA1, 0x2B, 0xF1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xDB, 0x04, 0x18, 0x05, 0x18, 0x06, 0x18, 0x07, 0x18, 0x08, 0x18, 0x09, 0x18, 0x0A, 0x18, 0x0B, 0x18, 0x0C, 0x18,\n\t0x0D, 0x18, 0x0E, 0x18, 0x0F, 0x18, 0x10, 0x18, 0x11, 0x18, 0x12, 0x18, 0x13, 0x18, 0x14, 0x18, 0x15, 0x18, 0x16, 0x18, 0x17, 0x18, 0x18, 0x18, 0x19, 0x18, 0x1A, 0x18, 0x1B, 0x18, 0x1C, 0x18,\n\t0x1D, 0x18, 0x1E, 0x18, 0x1F, 0x18, 0x20, 0x18, 0x21, 0x18, 0x22, 0x18, 0x23, 0x18, 0x24, 0x18, 0x25, 0x18, 0x26, 0x18, 0x27, 0x18, 0x28, 0x18, 0x29, 0x18, 0x2A, 0x18, 0x2B, 0x18, 0x2C, 0x18,\n\t0x2D, 0x18, 0x2E, 0x18, 0x2F, 0x18, 0x30, 0x18, 0x31, 0x18, 0x32, 0x18, 0x33, 0x18, 0x35, 0x18, 0x36, 0x18, 0x37, 0x18, 0x38, 0x18, 0x39, 0x18, 0x3A, 0x18, 0x3B, 0x18, 0x3C, 0x18, 0x3D, 0x18,\n\t0x3E, 0x18, 0x3F, 0x18, 0x40, 0x18, 0x41, 0x18, 0x42, 0x18, 0x43, 0x18, 0x44, 0x18, 0x45, 0x18, 0x46, 0x18, 0x47, 0x18, 0x48, 0x18, 0x49, 0x18, 0x4A, 0x18, 0x4B, 0x18, 0x4C, 0x18, 0x4D, 0x18,\n\t0x4E, 0x18, 0x4F, 0x18, 0x50, 0x18, 0x51, 0x18, 0x52, 0x18, 0x53, 0x18, 0x54, 0x18, 0x55, 0x18, 0x56, 0x18, 0x57, 0x18, 0x58, 0x18, 0x59, 0x18, 0x5A, 0x18, 0x5B, 0x18, 0x5C, 0x18, 0x5D, 0x18,\n\t0x5E, 0x18, 0x5F, 0x18, 0x60, 0x18, 0x61, 0x18, 0x62, 0x18, 0x63, 0x18, 0x64, 0x18, 0x65, 0x18, 0x66, 0x18, 0x67, 0x18, 0x68, 0x18, 0x69, 0x18, 0x6A, 0x18, 0x6B, 0x18, 0x6C, 0x18, 0x6D, 0x18,\n\t0x6E, 0x18, 0x6F, 0x18, 0x70, 0x18, 0x71, 0x18, 0x72, 0x18, 0x73, 0x18, 0x74, 0x18, 0x75, 0x18, 0x76, 0x18, 0x77, 0x18, 0x78, 0x18, 0x79, 0x18, 0x7A, 0x18, 0x7B, 0x18, 0x7C, 0x18, 0x7D, 0x18,\n\t0x7E, 0x18, 0x7F, 0x18, 0x80, 0x18, 0x81, 0x18, 0x82, 0x18, 0x83, 0x18, 0x84, 0x18, 0x85, 0x18, 0x86, 0x18, 0x87, 0x18, 0x88, 0x18, 0x89, 0x18, 0x8A, 0x18, 0x8B, 0x18, 0x8C, 0x18, 0x8D, 0x18,\n\t0x8E, 0x18, 0x8F, 0x18, 0x90, 0x18, 0x91, 0x18, 0x92, 0x18, 0x93, 0x18, 0x94, 0x18, 0x95, 0x18, 0x96, 0x18, 0x97, 0x18, 0x98, 0x18, 0x99, 0x18, 0x9A, 0x18, 0x9B, 0x18, 0x9C, 0x18, 0x9D, 0x18,\n\t0x9E, 0x18, 0x9F, 0x18, 0xA0, 0x18, 0xA1, 0x18, 0xA2, 0x18, 0xA3, 0x18, 0xA4, 0x18, 0xA5, 0x18, 0xA6, 0x18, 0xA7, 0x18, 0xA8, 0x18, 0xA9, 0x18, 0xAA, 0x18, 0xAB, 0x18, 0xAC, 0x18, 0xAD, 0x18,\n\t0xAE, 0x18, 0xAF, 0x18, 0xB0, 0x18, 0xB1, 0x18, 0xB2, 0x18, 0xB3, 0x18, 0xB4, 0x18, 0xB5, 0x18, 0xB6, 0x18, 0xB7, 0x18, 0xB8, 0x18, 0xB9, 0x18, 0xBA, 0x18, 0xBB, 0x18, 0xBC, 0x18, 0xBD, 0x18,\n\t0xBE, 0x18, 0xBF, 0x18, 0xC0, 0x18, 0xC1, 0x18, 0xC2, 0x18, 0xC3, 0x18, 0xC5, 0x18, 0xC6, 0x18, 0xC7, 0x18, 0xC8, 0x18, 0xC9, 0x18, 0xCA, 0x18, 0xCB, 0x18, 0xCC, 0x18, 0xCD, 0x18, 0xCE, 0x18,\n\t0xCF, 0x18, 0xD0, 0x18, 0xD1, 0x18, 0xD2, 0x18, 0xD3, 0x18, 0xD4, 0x18, 0xD5, 0x18, 0xD6, 0x18, 0xD7, 0x18, 0xD8, 0x18, 0xD9, 0x18, 0xDA, 0x18, 0xDB, 0x18, 0xDC, 0x18, 0xDD, 0x18, 0xDE, 0x18,\n\t0xDF, 0x18, 0xE0, 0x18, 0xE1, 0x18, 0xE2, 0x18, 0xE3, 0x18, 0xE4, 0x18, 0xE5, 0x18, 0xE6, 0x18, 0xE7, 0x18, 0xE8, 0x18, 0xE9, 0x18, 0xEA, 0x18, 0xEB, 0x18, 0xEC, 0x18, 0xED, 0x18, 0xEE, 0x18,\n\t0xEF, 0x18, 0xF0, 0x18, 0xF1, 0x18, 0xF2, 0x18, 0xF3, 0x18, 0xF4, 0x18, 0xF5, 0x18, 0xF6, 0x18, 0xF7, 0x18, 0xF8, 0x18, 0xF9, 0x18, 0xFA, 0x18, 0xFB, 0x18, 0xFC, 0x18, 0xFD, 0x18, 0xFE, 0x18,\n\t0xFF, 0x18, 0x00, 0x19, 0x01, 0x19, 0x02, 0x19, 0x03, 0x19, 0x04, 0x19, 0x05, 0x19, 0x06, 0x19, 0x07, 0x19, 0x08, 0x19, 0x09, 0x19, 0x0A, 0x19, 0x0B, 0x19, 0x0C, 0x19, 0x0D, 0x19, 0x0E, 0x19,\n\t0x0F, 0x19, 0x10, 0x19, 0x11, 0x19, 0x12, 0x19, 0x13, 0x19, 0x14, 0x19, 0x15, 0x19, 0x16, 0x19, 0x17, 0x19, 0x18, 0x19, 0x19, 0x19, 0x1A, 0x19, 0x1B, 0x19, 0x1C, 0x19, 0x1D, 0x19, 0x1E, 0x19,\n\t0x1F, 0x19, 0x20, 0x19, 0x21, 0x19, 0x22, 0x19, 0x23, 0x19, 0x24, 0x19, 0x25, 0x19, 0x26, 0x19, 0x27, 0x19, 0x28, 0x19, 0x29, 0x19, 0x2A, 0x19, 0x2B, 0x19, 0x2C, 0x19, 0x2D, 0x19, 0x2E, 0x19,\n\t0x2F, 0x19, 0x30, 0x19, 0x31, 0x19, 0x32, 0x19, 0x33, 0x19, 0x34, 0x19, 0x35, 0x19, 0x36, 0x19, 0x37, 0x19, 0x38, 0x19, 0x39, 0x19, 0x3A, 0x19, 0x3B, 0x19, 0x3C, 0x19, 0x3D, 0x19, 0x3E, 0x19,\n\t0x3F, 0x19, 0x40, 0x19, 0x41, 0x19, 0x42, 0x19, 0x43, 0x19, 0x44, 0x19, 0x45, 0x19, 0x47, 0x19, 0x48, 0x19, 0x49, 0x19, 0x4A, 0x19, 0x4B, 0x19, 0x4C, 0x19, 0x4D, 0x19, 0x4E, 0x19, 0x4F, 0x19,\n\t0x50, 0x19, 0x51, 0x19, 0x52, 0x19, 0x53, 0x19, 0x54, 0x19, 0x55, 0x19, 0x56, 0x19, 0x57, 0x19, 0x58, 0x19, 0x59, 0x19, 0x5A, 0x19, 0x5B, 0x19, 0x5C, 0x19, 0x5D, 0x19, 0x5E, 0x19, 0x5F, 0x19,\n\t0x60, 0x19, 0x61, 0x19, 0x62, 0x19, 0x63, 0x19, 0x64, 0x19, 0x65, 0x19, 0x66, 0x19, 0x67, 0x19, 0x68, 0x19, 0x69, 0x19, 0x6A, 0x19, 0x6B, 0x19, 0x6C, 0x19, 0x6D, 0x19, 0x6E, 0x19, 0x6F, 0x19,\n\t0x70, 0x19, 0x71, 0x19, 0x72, 0x19, 0x73, 0x19, 0x74, 0x19, 0x75, 0x19, 0x76, 0x19, 0x77, 0x19, 0x78, 0x19, 0x79, 0x19, 0x7A, 0x19, 0x7B, 0x19, 0x7C, 0x19, 0x7D, 0x19, 0x7E, 0x19, 0x7F, 0x19,\n\t0x80, 0x19, 0x81, 0x19, 0x82, 0x19, 0x83, 0x19, 0x84, 0x19, 0x85, 0x19, 0x86, 0x19, 0x87, 0x19, 0x88, 0x19, 0x89, 0x19, 0x8A, 0x19, 0x8B, 0x19, 0x8C, 0x19, 0x8D, 0x19, 0x8E, 0x19, 0x8F, 0x19,\n\t0x90, 0x19, 0x91, 0x19, 0x92, 0x19, 0x93, 0x19, 0x94, 0x19, 0x95, 0x19, 0x96, 0x19, 0x97, 0x19, 0x98, 0x19, 0x99, 0x19, 0x9A, 0x19, 0x9B, 0x19, 0x9C, 0x19, 0x9D, 0x19, 0x9E, 0x19, 0x9F, 0x19,\n\t0xA0, 0x19, 0xA1, 0x19, 0xA2, 0x19, 0xA3, 0x19, 0xA4, 0x19, 0xA5, 0x19, 0xA6, 0x19, 0xA7, 0x19, 0xA8, 0x19, 0xA9, 0x19, 0xAA, 0x19, 0xAB, 0x19, 0xAC, 0x19, 0xAD, 0x19, 0xAE, 0x19, 0xAF, 0x19,\n\t0xB0, 0x19, 0xB1, 0x19, 0xB2, 0x19, 0xB3, 0x19, 0xB4, 0x19, 0xB5, 0x19, 0xB6, 0x19, 0xB7, 0x19, 0xB8, 0x19, 0xB9, 0x19, 0xBA, 0x19, 0xBB, 0x19, 0xBD, 0x19, 0xBE, 0x19, 0xBF, 0x19, 0xC0, 0x19,\n\t0xC1, 0x19, 0xC2, 0x19, 0xC3, 0x19, 0xC4, 0x19, 0xC5, 0x19, 0xC6, 0x19, 0xC7, 0x19, 0xC8, 0x19, 0xC9, 0x19, 0xCA, 0x19, 0xCB, 0x19, 0xCC, 0x19, 0xCD, 0x19, 0xCE, 0x19, 0xCF, 0x19, 0xD0, 0x19,\n\t0xD1, 0x19, 0xD2, 0x19, 0xD3, 0x19, 0xD4, 0x19, 0xD5, 0x19, 0xD6, 0x19, 0xD7, 0x19, 0xD8, 0x19, 0xD9, 0x19, 0xDA, 0x19, 0xDB, 0x19, 0xDC, 0x19, 0xDD, 0x19, 0xDE, 0x19, 0xDF, 0x19, 0xE0, 0x19,\n\t0xE1, 0x19, 0xE2, 0x19, 0xE3, 0x19, 0xE4, 0x19, 0xE5, 0x19, 0xE6, 0x19, 0xE7, 0x19, 0xE8, 0x19, 0xE9, 0x19, 0xEA, 0x19, 0xEB, 0x19, 0xEC, 0x19, 0xED, 0x19, 0xEE, 0x19, 0xEF, 0x19, 0xF0, 0x19,\n\t0xF1, 0x19, 0xF2, 0x19, 0xF3, 0x19, 0xF4, 0x19, 0xF5, 0x19, 0xF6, 0x19, 0xF7, 0x19, 0xF8, 0x19, 0xF9, 0x19, 0xFA, 0x19, 0xFB, 0x19, 0xFC, 0x19, 0xFD, 0x19, 0xFE, 0x19, 0xFF, 0x19, 0x00, 0x1A,\n\t0x01, 0x1A, 0x02, 0x1A, 0x03, 0x1A, 0x04, 0x1A, 0x05, 0x1A, 0x06, 0x1A, 0x07, 0x1A, 0x08, 0x1A, 0x09, 0x1A, 0x0A, 0x1A, 0x0B, 0x1A, 0x0C, 0x1A, 0x0D, 0x1A, 0x0E, 0x1A, 0x0F, 0x1A, 0x10, 0x1A,\n\t0x11, 0x1A, 0x12, 0x1A, 0x13, 0x1A, 0x14, 0x1A, 0x15, 0x1A, 0x16, 0x1A, 0x17, 0x1A, 0x18, 0x1A, 0x19, 0x1A, 0x1A, 0x1A, 0x1B, 0x1A, 0x1C, 0x1A, 0x1D, 0x1A, 0x1E, 0x1A, 0x1F, 0x1A, 0x20, 0x1A,\n\t0x21, 0x1A, 0x22, 0x1A, 0x23, 0x1A, 0x24, 0x1A, 0x25, 0x1A, 0x26, 0x1A, 0x27, 0x1A, 0x28, 0x1A, 0x2A, 0x1A, 0x2B, 0x1A, 0x2C, 0x1A, 0x2D, 0x1A, 0x2E, 0x1A, 0x2F, 0x1A, 0x30, 0x1A, 0x31, 0x1A,\n\t0x32, 0x1A, 0x33, 0x1A, 0x34, 0x1A, 0x35, 0x1A, 0x36, 0x1A, 0x37, 0x1A, 0x38, 0x1A, 0x39, 0x1A, 0x3A, 0x1A, 0x3B, 0x1A, 0x3C, 0x1A, 0x3D, 0x1A, 0x3E, 0x1A, 0x3F, 0x1A, 0x40, 0x1A, 0x41, 0x1A,\n\t0x42, 0x1A, 0x43, 0x1A, 0x44, 0x1A, 0x45, 0x1A, 0x46, 0x1A, 0x47, 0x1A, 0x48, 0x1A, 0x49, 0x1A, 0x4A, 0x1A, 0x4B, 0x1A, 0x4C, 0x1A, 0x4D, 0x1A, 0x4E, 0x1A, 0x4F, 0x1A, 0x50, 0x1A, 0x51, 0x1A,\n\t0x52, 0x1A, 0x53, 0x1A, 0x54, 0x1A, 0x55, 0x1A, 0x56, 0x1A, 0x57, 0x1A, 0x58, 0x1A, 0x59, 0x1A, 0x5A, 0x1A, 0x5B, 0x1A, 0x5C, 0x1A, 0x5D, 0x1A, 0x5E, 0x1A, 0x5F, 0x1A, 0x60, 0x1A, 0x61, 0x1A,\n\t0x62, 0x1A, 0x63, 0x1A, 0x64, 0x1A, 0x65, 0x1A, 0x66, 0x1A, 0x67, 0x1A, 0x68, 0x1A, 0x69, 0x1A, 0x6A, 0x1A, 0x6B, 0x1A, 0x6C, 0x1A, 0x6D, 0x1A, 0x6E, 0x1A, 0x6F, 0x1A, 0x70, 0x1A, 0x71, 0x1A,\n\t0x72, 0x1A, 0x73, 0x1A, 0x74, 0x1A, 0x75, 0x1A, 0x76, 0x1A, 0x77, 0x1A, 0x78, 0x1A, 0x79, 0x1A, 0x7A, 0x1A, 0x7B, 0x1A, 0x7C, 0x1A, 0x7D, 0x1A, 0x7E, 0x1A, 0x7F, 0x1A, 0x80, 0x1A, 0x81, 0x1A,\n\t0x82, 0x1A, 0x83, 0x1A, 0x84, 0x1A, 0x85, 0x1A, 0x86, 0x1A, 0x87, 0x1A, 0x88, 0x1A, 0x89, 0x1A, 0x8A, 0x1A, 0x8B, 0x1A, 0x8C, 0x1A, 0x8D, 0x1A, 0x8E, 0x1A, 0x8F, 0x1A, 0x91, 0x1A, 0x92, 0x1A,\n\t0x93, 0x1A, 0x94, 0x1A, 0x95, 0x1A, 0x96, 0x1A, 0x97, 0x1A, 0x98, 0x1A, 0x99, 0x1A, 0x9A, 0x1A, 0x9B, 0x1A, 0x9C, 0x1A, 0x9D, 0x1A, 0x9E, 0x1A, 0x9F, 0x1A, 0xA0, 0x1A, 0xA1, 0x1A, 0xA2, 0x1A,\n\t0xA3, 0x1A, 0xA4, 0x1A, 0xA5, 0x1A, 0xA6, 0x1A, 0xA7, 0x1A, 0xA8, 0x1A, 0xA9, 0x1A, 0xAA, 0x1A, 0xAB, 0x1A, 0xAC, 0x1A, 0xAD, 0x1A, 0xAE, 0x1A, 0xAF, 0x1A, 0xB0, 0x1A, 0xB1, 0x1A, 0xB2, 0x1A,\n\t0xB3, 0x1A, 0xB4, 0x1A, 0xB5, 0x1A, 0xB6, 0x1A, 0xB7, 0x1A, 0xB8, 0x1A, 0xB9, 0x1A, 0xBA, 0x1A, 0xBB, 0x1A, 0xBC, 0x1A, 0xBD, 0x1A, 0xBE, 0x1A, 0xBF, 0x1A, 0xC0, 0x1A, 0xC1, 0x1A, 0xC2, 0x1A,\n\t0xC3, 0x1A, 0xC4, 0x1A, 0xC5, 0x1A, 0xC6, 0x1A, 0xC7, 0x1A, 0xC8, 0x1A, 0xC9, 0x1A, 0xCA, 0x1A, 0xCB, 0x1A, 0xCC, 0x1A, 0xCD, 0x1A, 0xCE, 0x1A, 0xCF, 0x1A, 0xD0, 0x1A, 0xD1, 0x1A, 0xD2, 0x1A,\n\t0xD3, 0x1A, 0xD4, 0x1A, 0xD5, 0x1A, 0xD6, 0x1A, 0xD7, 0x1A, 0xD8, 0x1A, 0xD9, 0x1A, 0xDA, 0x1A, 0xDB, 0x1A, 0xDC, 0x1A, 0xDD, 0x1A, 0xDE, 0x1A, 0xDF, 0x1A, 0xE0, 0x1A, 0xE1, 0x1A, 0xE2, 0x1A,\n\t0xE3, 0x1A, 0xE4, 0x1A, 0xE5, 0x1A, 0xE6, 0x1A, 0xE7, 0x1A, 0xE8, 0x1A, 0xE9, 0x1A, 0xEA, 0x1A, 0xEB, 0x1A, 0xEC, 0x1A, 0xED, 0x1A, 0xEE, 0x1A, 0xEF, 0x1A, 0xF1, 0x1A, 0xF2, 0x1A, 0xF3, 0x1A,\n\t0xF4, 0x1A, 0xF5, 0x1A, 0xF6, 0x1A, 0xF7, 0x1A, 0xF8, 0x1A, 0xF9, 0x1A, 0xFA, 0x1A, 0xFB, 0x1A, 0xFC, 0x1A, 0xFD, 0x1A, 0xFE, 0x1A, 0xFF, 0x1A, 0x00, 0x1B, 0x01, 0x1B, 0x02, 0x1B, 0x03, 0x1B,\n\t0x04, 0x1B, 0x05, 0x1B, 0x06, 0x1B, 0x07, 0x1B, 0x08, 0x1B, 0x09, 0x1B, 0x0A, 0x1B, 0x0B, 0x1B, 0x0C, 0x1B, 0x0D, 0x1B, 0x0E, 0x1B, 0x0F, 0x1B, 0x10, 0x1B, 0x11, 0x1B, 0x12, 0x1B, 0x13, 0x1B,\n\t0x14, 0x1B, 0x15, 0x1B, 0x16, 0x1B, 0x17, 0x1B, 0x18, 0x1B, 0x19, 0x1B, 0x1A, 0x1B, 0x1B, 0x1B, 0x1C, 0x1B, 0x1D, 0x1B, 0x1E, 0x1B, 0x1F, 0x1B, 0x20, 0x1B, 0x21, 0x1B, 0x22, 0x1B, 0x23, 0x1B,\n\t0x24, 0x1B, 0x25, 0x1B, 0x26, 0x1B, 0x27, 0x1B, 0x28, 0x1B, 0x29, 0x1B, 0x2A, 0x1B, 0x2B, 0x1B, 0x2C, 0x1B, 0x2D, 0x1B, 0x2E, 0x1B, 0x2F, 0x1B, 0x30, 0x1B, 0x31, 0x1B, 0x32, 0x1B, 0x33, 0x1B,\n\t0x34, 0x1B, 0x35, 0x1B, 0x36, 0x1B, 0x37, 0x1B, 0x38, 0x1B, 0x39, 0x1B, 0x3A, 0x1B, 0x3B, 0x1B, 0x3C, 0x1B, 0x3D, 0x1B, 0x3E, 0x1B, 0x3F, 0x1B, 0x40, 0x1B, 0x41, 0x1B, 0x42, 0x1B, 0x43, 0x1B,\n\t0x44, 0x1B, 0x45, 0x1B, 0x46, 0x1B, 0x47, 0x1B, 0x48, 0x1B, 0x49, 0x1B, 0x4A, 0x1B, 0x4B, 0x1B, 0x4D, 0x1B, 0x4E, 0x1B, 0x4F, 0x1B, 0x50, 0x1B, 0x51, 0x1B, 0x52, 0x1B, 0x53, 0x1B, 0x54, 0x1B,\n\t0x55, 0x1B, 0x56, 0x1B, 0x57, 0x1B, 0x58, 0x1B, 0x59, 0x1B, 0x5A, 0x1B, 0x5B, 0x1B, 0x5C, 0x1B, 0x5D, 0x1B, 0x5E, 0x1B, 0x5F, 0x1B, 0x60, 0x1B, 0x61, 0x1B, 0x62, 0x1B, 0x63, 0x1B, 0x64, 0x1B,\n\t0x65, 0x1B, 0x66, 0x1B, 0x67, 0x1B, 0x68, 0x1B, 0x69, 0x1B, 0x6A, 0x1B, 0x6B, 0x1B, 0x6C, 0x1B, 0x6D, 0x1B, 0x6E, 0x1B, 0x6F, 0x1B, 0x70, 0x1B, 0x71, 0x1B, 0x72, 0x1B, 0x73, 0x1B, 0x74, 0x1B,\n\t0x75, 0x1B, 0x76, 0x1B, 0x77, 0x1B, 0x78, 0x1B, 0x79, 0x1B, 0x7A, 0x1B, 0x7B, 0x1B, 0x7C, 0x1B, 0x7D, 0x1B, 0x7E, 0x1B, 0x7F, 0x1B, 0x80, 0x1B, 0x81, 0x1B, 0x82, 0x1B, 0x83, 0x1B, 0x84, 0x1B,\n\t0x85, 0x1B, 0x86, 0x1B, 0x87, 0x1B, 0x88, 0x1B, 0x89, 0x1B, 0x8A, 0x1B, 0x8B, 0x1B, 0x8C, 0x1B, 0x8D, 0x1B, 0x8E, 0x1B, 0x8F, 0x1B, 0x90, 0x1B, 0x91, 0x1B, 0x92, 0x1B, 0x93, 0x1B, 0x94, 0x1B,\n\t0x95, 0x1B, 0x96, 0x1B, 0x97, 0x1B, 0x98, 0x1B, 0x99, 0x1B, 0x9A, 0x1B, 0x9B, 0x1B, 0x9C, 0x1B, 0x9D, 0x1B, 0x9E, 0x1B, 0x9F, 0x1B, 0xA0, 0x1B, 0xA1, 0x1B, 0xA2, 0x1B, 0xA4, 0x1B, 0xA5, 0x1B,\n\t0xA6, 0x1B, 0xA7, 0x1B, 0xA8, 0x1B, 0xA9, 0x1B, 0xAA, 0x1B, 0xAB, 0x1B, 0xAC, 0x1B, 0xAD, 0x1B, 0xAE, 0x1B, 0xAF, 0x1B, 0xB0, 0x1B, 0xB1, 0x1B, 0xB2, 0x1B, 0xB3, 0x1B, 0xB4, 0x1B, 0xB5, 0x1B,\n\t0xB6, 0x1B, 0xB7, 0x1B, 0xB8, 0x1B, 0xB9, 0x1B, 0xBA, 0x1B, 0xBB, 0x1B, 0xBC, 0x1B, 0xBD, 0x1B, 0xBE, 0x1B, 0xBF, 0x1B, 0xC0, 0x1B, 0xC1, 0x1B, 0xC2, 0x1B, 0xC3, 0x1B, 0xC4, 0x1B, 0xC5, 0x1B,\n\t0xC6, 0x1B, 0xC7, 0x1B, 0xC8, 0x1B, 0xC9, 0x1B, 0xCA, 0x1B, 0xCB, 0x1B, 0xCC, 0x1B, 0xCD, 0x1B, 0xCE, 0x1B, 0xCF, 0x1B, 0xD0, 0x1B, 0xD1, 0x1B, 0xD2, 0x1B, 0xD3, 0x1B, 0xD4, 0x1B, 0xD5, 0x1B,\n\t0xD6, 0x1B, 0xD7, 0x1B, 0xD8, 0x1B, 0xD9, 0x1B, 0xDA, 0x1B, 0xDB, 0x1B, 0xDC, 0x1B, 0xDD, 0x1B, 0xDE, 0x1B, 0xDF, 0x1B, 0xE0, 0x1B, 0xE1, 0x1B, 0xE2, 0x1B, 0xE3, 0x1B, 0xE4, 0x1B, 0xE5, 0x1B,\n\t0xE6, 0x1B, 0xE7, 0x1B, 0xE8, 0x1B, 0xE9, 0x1B, 0xEA, 0x1B, 0xEB, 0x1B, 0xEC, 0x1B, 0xED, 0x1B, 0xEE, 0x1B, 0xEF, 0x1B, 0xF0, 0x1B, 0xF1, 0x1B, 0xF2, 0x1B, 0xF3, 0x1B, 0xF4, 0x1B, 0xF5, 0x1B,\n\t0xF6, 0x1B, 0xF8, 0x1B, 0xF9, 0x1B, 0xFA, 0x1B, 0xFB, 0x1B, 0xFC, 0x1B, 0xFD, 0x1B, 0xFE, 0x1B, 0xFF, 0x79, 0x3F, 0x00, 0x7D, 0x3B, 0x00, 0x81, 0x37, 0x00, 0x85, 0x33, 0xF1, 0xFF, 0xFF, 0xFF,\n\t0xFF, 0xFF, 0xFF, 0xFF, 0xCF, 0x04, 0x1C, 0x04, 0x1C, 0x05, 0x1C, 0x05, 0x1C, 0x06, 0x1C, 0x06, 0x1C, 0x07, 0x1C, 0x08, 0x1C, 0x09, 0x1C, 0x0A, 0x1C, 0x0B, 0x1C, 0x0C, 0x1C, 0x0D, 0x1C, 0x0E,\n\t0x1C, 0x0F, 0x1C, 0x11, 0x1C, 0x12, 0x1C, 0x13, 0x1C, 0x14, 0x1C, 0x15, 0x1C, 0x16, 0x1C, 0x17, 0x1C, 0x18, 0x1C, 0x19, 0x1C, 0x1A, 0x1C, 0x1B, 0x1C, 0x1C, 0x1C, 0x1D, 0x1C, 0x1E, 0x1C, 0x1F,\n\t0x1C, 0x20, 0x1C, 0x21, 0x1C, 0x22, 0x1C, 0x23, 0x1C, 0x24, 0x1C, 0x25, 0x1C, 0x26, 0x1C, 0x27, 0x1C, 0x28, 0x1C, 0x29, 0x1C, 0x2A, 0x1C, 0x2B, 0x1C, 0x2C, 0x1C, 0x2D, 0x1C, 0x2E, 0x1C, 0x2F,\n\t0x1C, 0x30, 0x1C, 0x31, 0x1C, 0x32, 0x1C, 0x33, 0x1C, 0x34, 0x1C, 0x35, 0x1C, 0x36, 0x1C, 0x37, 0x1C, 0x38, 0x1C, 0x39, 0x1C, 0x3A, 0x1C, 0x3B, 0x1C, 0x3C, 0x1C, 0x3D, 0x1C, 0x3E, 0x1C, 0x3F,\n\t0x1C, 0x40, 0x1C, 0x41, 0x1C, 0x42, 0x1C, 0x43, 0x1C, 0x44, 0x1C, 0x45, 0x1C, 0x46, 0x1C, 0x47, 0x1C, 0x48, 0x1C, 0x49, 0x1C, 0x4A, 0x1C, 0x4B, 0x1C, 0x4C, 0x1C, 0x4D, 0x1C, 0x4E, 0x1C, 0x4F,\n\t0x1C, 0x50, 0x1C, 0x51, 0x1C, 0x52, 0x1C, 0x53, 0x1C, 0x54, 0x1C, 0x55, 0x1C, 0x56, 0x1C, 0x57, 0x1C, 0x58, 0x1C, 0x59, 0x1C, 0x5A, 0x1C, 0x5B, 0x1C, 0x5C, 0x1C, 0x5E, 0x1C, 0x5F, 0x1C, 0x60,\n\t0x1C, 0x61, 0x1C, 0x62, 0x1C, 0x63, 0x1C, 0x64, 0x1C, 0x65, 0x1C, 0x66, 0x1C, 0x67, 0x1C, 0x68, 0x1C, 0x69, 0x1C, 0x6A, 0x1C, 0x6B, 0x1C, 0x6C, 0x1C, 0x6D, 0x1C, 0x6E, 0x1C, 0x6F, 0x1C, 0x70,\n\t0x1C, 0x71, 0x1C, 0x72, 0x1C, 0x73, 0x1C, 0x74, 0x1C, 0x75, 0x1C, 0x76, 0x1C, 0x77, 0x1C, 0x78, 0x1C, 0x79, 0x1C, 0x7A, 0x1C, 0x7B, 0x1C, 0x7C, 0x1C, 0x7D, 0x1C, 0x7E, 0x1C, 0x7F, 0x1C, 0x80,\n\t0x1C, 0x81, 0x1C, 0x82, 0x1C, 0x83, 0x1C, 0x84, 0x1C, 0x85, 0x1C, 0x86, 0x1C, 0x87, 0x1C, 0x88, 0x1C, 0x89, 0x1C, 0x8A, 0x1C, 0x8B, 0x1C, 0x8C, 0x1C, 0x8D, 0x1C, 0x8E, 0x1C, 0x8F, 0x1C, 0x90,\n\t0x1C, 0x91, 0x1C, 0x92, 0x1C, 0x93, 0x1C, 0x94, 0x1C, 0x95, 0x1C, 0x96, 0x1C, 0x97, 0x1C, 0x98, 0x1C, 0x99, 0x1C, 0x9A, 0x1C, 0x9B, 0x1C, 0x9C, 0x1C, 0x9D, 0x1C, 0x9E, 0x1C, 0x9F, 0x1C, 0xA0,\n\t0x1C, 0xA1, 0x1C, 0xA2, 0x1C, 0xA3, 0x1C, 0xA4, 0x1C, 0xA5, 0x1C, 0xA7, 0x1C, 0xA8, 0x1C, 0xA9, 0x1C, 0xAA, 0x1C, 0xAB, 0x1C, 0xAC, 0x1C, 0xAD, 0x1C, 0xAE, 0x1C, 0xAF, 0x1C, 0xB0, 0x1C, 0xB1,\n\t0x1C, 0xB2, 0x1C, 0xB3, 0x1C, 0xB4, 0x1C, 0xB5, 0x1C, 0xB6, 0x1C, 0xB7, 0x1C, 0xB8, 0x1C, 0xB9, 0x1C, 0xBA, 0x1C, 0xBB, 0x1C, 0xBC, 0x1C, 0xBD, 0x1C, 0xBE, 0x1C, 0xBF, 0x1C, 0xC0, 0x1C, 0xC1,\n\t0x1C, 0xC2, 0x1C, 0xC3, 0x1C, 0xC4, 0x1C, 0xC5, 0x1C, 0xC6, 0x1C, 0xC7, 0x1C, 0xC8, 0x1C, 0xC9, 0x1C, 0xCA, 0x1C, 0xCB, 0x1C, 0xCC, 0x1C, 0xCD, 0x1C, 0xCE, 0x1C, 0xCF, 0x1C, 0xD0, 0x1C, 0xD1,\n\t0x1C, 0xD2, 0x1C, 0xD3, 0x1C, 0xD4, 0x1C, 0xD5, 0x1C, 0xD6, 0x1C, 0xD7, 0x1C, 0xD8, 0x1C, 0xD9, 0x1C, 0xDA, 0x1C, 0xDB, 0x1C, 0xDC, 0x1C, 0xDD, 0x1C, 0xDE, 0x1C, 0xDF, 0x1C, 0xE0, 0x1C, 0xE1,\n\t0x1C, 0xE2, 0x1C, 0xE3, 0x1C, 0xE4, 0x1C, 0xE5, 0x1C, 0xE6, 0x1C, 0xE7, 0x1C, 0xE8, 0x1C, 0xE9, 0x1C, 0xEA, 0x1C, 0xEC, 0x1C, 0xED, 0x1C, 0xEE, 0x1C, 0xEF, 0x1C, 0xF0, 0x1C, 0xF1, 0x1C, 0xF2,\n\t0x1C, 0xF3, 0x1C, 0xF4, 0x1C, 0xF5, 0x1C, 0xF6, 0x1C, 0xF7, 0x1C, 0xF8, 0x1C, 0xF9, 0x1C, 0xFA, 0x1C, 0xFB, 0x1C, 0xFC, 0x1C, 0xFD, 0x1C, 0xFE, 0x1C, 0xFF, 0x1C, 0x00, 0x1D, 0x01, 0x1D, 0x02,\n\t0x1D, 0x03, 0x1D, 0x04, 0x1D, 0x05, 0x1D, 0x06, 0x1D, 0x07, 0x1D, 0x08, 0x1D, 0x09, 0x1D, 0x0A, 0x1D, 0x0B, 0x1D, 0x0C, 0x1D, 0x0D, 0x1D, 0x0E, 0x1D, 0x0F, 0x1D, 0x10, 0x1D, 0x11, 0x1D, 0x12,\n\t0x1D, 0x13, 0x1D, 0x14, 0x1D, 0x15, 0x1D, 0x16, 0x1D, 0x17, 0x1D, 0x18, 0x1D, 0x19, 0x1D, 0x1A, 0x1D, 0x1B, 0x1D, 0x1C, 0x1D, 0x1D, 0x1D, 0x1E, 0x1D, 0x1F, 0x1D, 0x20, 0x1D, 0x21, 0x1D, 0x22,\n\t0x1D, 0x23, 0x1D, 0x24, 0x1D, 0x25, 0x1D, 0x26, 0x1D, 0x27, 0x1D, 0x28, 0x1D, 0x29, 0x1D, 0x2A, 0x1D, 0x2B, 0x1D, 0x2D, 0x1D, 0x2E, 0x1D, 0x2F, 0x1D, 0x30, 0x1D, 0x31, 0x1D, 0x32, 0x1D, 0x33,\n\t0x1D, 0x34, 0x1D, 0x35, 0x1D, 0x36, 0x1D, 0x37, 0x1D, 0x38, 0x1D, 0x39, 0x1D, 0x3A, 0x1D, 0x3B, 0x1D, 0x3C, 0x1D, 0x3D, 0x1D, 0x3E, 0x1D, 0x3F, 0x1D, 0x40, 0x1D, 0x41, 0x1D, 0x42, 0x1D, 0x43,\n\t0x1D, 0x44, 0x1D, 0x45, 0x1D, 0x46, 0x1D, 0x47, 0x1D, 0x48, 0x1D, 0x49, 0x1D, 0x4A, 0x1D, 0x4B, 0x1D, 0x4C, 0x1D, 0x4D, 0x1D, 0x4E, 0x1D, 0x4F, 0x1D, 0x50, 0x1D, 0x51, 0x1D, 0x52, 0x1D, 0x53,\n\t0x1D, 0x54, 0x1D, 0x55, 0x1D, 0x56, 0x1D, 0x57, 0x1D, 0x58, 0x1D, 0x59, 0x1D, 0x5A, 0x1D, 0x5B, 0x1D, 0x5C, 0x1D, 0x5D, 0x1D, 0x5E, 0x1D, 0x5F, 0x1D, 0x60, 0x1D, 0x61, 0x1D, 0x62, 0x1D, 0x63,\n\t0x1D, 0x64, 0x1D, 0x65, 0x1D, 0x66, 0x1D, 0x67, 0x1D, 0x68, 0x1D, 0x69, 0x1D, 0x6B, 0x1D, 0x6C, 0x1D, 0x6D, 0x1D, 0x6E, 0x1D, 0x6F, 0x1D, 0x70, 0x1D, 0x71, 0x1D, 0x72, 0x1D, 0x73, 0x1D, 0x74,\n\t0x1D, 0x75, 0x1D, 0x76, 0x1D, 0x77, 0x1D, 0x78, 0x1D, 0x79, 0x1D, 0x7A, 0x1D, 0x7B, 0x1D, 0x7C, 0x1D, 0x7D, 0x1D, 0x7E, 0x1D, 0x7F, 0x1D, 0x80, 0x1D, 0x81, 0x1D, 0x82, 0x1D, 0x83, 0x1D, 0x84,\n\t0x1D, 0x85, 0x1D, 0x86, 0x1D, 0x87, 0x1D, 0x88, 0x1D, 0x89, 0x1D, 0x8A, 0x1D, 0x8B, 0x1D, 0x8C, 0x1D, 0x8D, 0x1D, 0x8E, 0x1D, 0x8F, 0x1D, 0x90, 0x1D, 0x91, 0x1D, 0x92, 0x1D, 0x93, 0x1D, 0x94,\n\t0x1D, 0x95, 0x1D, 0x96, 0x1D, 0x97, 0x1D, 0x98, 0x1D, 0x99, 0x1D, 0x9A, 0x1D, 0x9B, 0x1D, 0x9C, 0x1D, 0x9D, 0x1D, 0x9E, 0x1D, 0x9F, 0x1D, 0xA0, 0x1D, 0xA1, 0x1D, 0xA2, 0x1D, 0xA3, 0x1D, 0xA4,\n\t0x1D, 0xA6, 0x1D, 0xA7, 0x1D, 0xA8, 0x1D, 0xA9, 0x1D, 0xAA, 0x1D, 0xAB, 0x1D, 0xAC, 0x1D, 0xAD, 0x1D, 0xAE, 0x1D, 0xAF, 0x1D, 0xB0, 0x1D, 0xB1, 0x1D, 0xB2, 0x1D, 0xB3, 0x1D, 0xB4, 0x1D, 0xB5,\n\t0x1D, 0xB6, 0x1D, 0xB7, 0x1D, 0xB8, 0x1D, 0xB9, 0x1D, 0xBA, 0x1D, 0xBB, 0x1D, 0xBC, 0x1D, 0xBD, 0x1D, 0xBE, 0x1D, 0xBF, 0x1D, 0xC0, 0x1D, 0xC1, 0x1D, 0xC2, 0x1D, 0xC3, 0x1D, 0xC4, 0x1D, 0xC5,\n\t0x1D, 0xC6, 0x1D, 0xC7, 0x1D, 0xC8, 0x1D, 0xC9, 0x1D, 0xCA, 0x1D, 0xCB, 0x1D, 0xCC, 0x1D, 0xCD, 0x1D, 0xCE, 0x1D, 0xCF, 0x1D, 0xD0, 0x1D, 0xD1, 0x1D, 0xD2, 0x1D, 0xD3, 0x1D, 0xD4, 0x1D, 0xD5,\n\t0x1D, 0xD6, 0x1D, 0xD7, 0x1D, 0xD8, 0x1D, 0xD9, 0x1D, 0xDA, 0x1D, 0xDB, 0x1D, 0xDC, 0x1D, 0xDD, 0x1D, 0xDE, 0x1D, 0xE0, 0x1D, 0xE1, 0x1D, 0xE2, 0x1D, 0xE3, 0x1D, 0xE4, 0x1D, 0xE5, 0x1D, 0xE6,\n\t0x1D, 0xE7, 0x1D, 0xE8, 0x1D, 0xE9, 0x1D, 0xEA, 0x1D, 0xEB, 0x1D, 0xEC, 0x1D, 0xED, 0x1D, 0xEE, 0x1D, 0xEF, 0x1D, 0xF0, 0x1D, 0xF1, 0x1D, 0xF2, 0x1D, 0xF3, 0x1D, 0xF4, 0x1D, 0xF5, 0x1D, 0xF6,\n\t0x1D, 0xF7, 0x1D, 0xF8, 0x1D, 0xF9, 0x1D, 0xFA, 0x1D, 0xFB, 0x1D, 0xFC, 0x1D, 0xFD, 0x1D, 0xFE, 0x1D, 0xFF, 0x1D, 0x00, 0x1E, 0x01, 0x1E, 0x02, 0x1E, 0x03, 0x1E, 0x04, 0x1E, 0x05, 0x1E, 0x06,\n\t0x1E, 0x07, 0x1E, 0x08, 0x1E, 0x09, 0x1E, 0x0A, 0x1E, 0x0B, 0x1E, 0x0C, 0x1E, 0x0D, 0x1E, 0x0E, 0x1E, 0x0F, 0x1E, 0x10, 0x1E, 0x11, 0x1E, 0x12, 0x1E, 0x13, 0x1E, 0x14, 0x1E, 0x15, 0x1E, 0x17,\n\t0x1E, 0x18, 0x1E, 0x19, 0x1E, 0x1A, 0x1E, 0x1B, 0x1E, 0x1C, 0x1E, 0x1D, 0x1E, 0x1E, 0x1E, 0x1F, 0x1E, 0x20, 0x1E, 0x21, 0x1E, 0x22, 0x1E, 0x23, 0x1E, 0x24, 0x1E, 0x25, 0x1E, 0x26, 0x1E, 0x27,\n\t0x1E, 0x28, 0x1E, 0x29, 0x1E, 0x2A, 0x1E, 0x2B, 0x1E, 0x2C, 0x1E, 0x2D, 0x1E, 0x2E, 0x1E, 0x2F, 0x1E, 0x30, 0x1E, 0x31, 0x1E, 0x32, 0x1E, 0x33, 0x1E, 0x34, 0x1E, 0x35, 0x1E, 0x36, 0x1E, 0x37,\n\t0x1E, 0x38, 0x1E, 0x39, 0x1E, 0x3A, 0x1E, 0x3B, 0x1E, 0x3C, 0x1E, 0x3D, 0x1E, 0x3E, 0x1E, 0x3F, 0x1E, 0x40, 0x1E, 0x41, 0x1E, 0x42, 0x1E, 0x43, 0x1E, 0x44, 0x1E, 0x45, 0x1E, 0x46, 0x1E, 0x47,\n\t0x1E, 0x48, 0x1E, 0x49, 0x1E, 0x4A, 0x1E, 0x4C, 0x1E, 0x4D, 0x1E, 0x4E, 0x1E, 0x4F, 0x1E, 0x50, 0x1E, 0x51, 0x1E, 0x52, 0x1E, 0x53, 0x1E, 0x54, 0x1E, 0x55, 0x1E, 0x56, 0x1E, 0x57, 0x1E, 0x58,\n\t0x1E, 0x59, 0x1E, 0x5A, 0x1E, 0x5B, 0x1E, 0x5C, 0x1E, 0x5D, 0x1E, 0x5E, 0x1E, 0x5F, 0x1E, 0x60, 0x1E, 0x61, 0x1E, 0x62, 0x1E, 0x63, 0x1E, 0x64, 0x1E, 0x65, 0x1E, 0x66, 0x1E, 0x67, 0x1E, 0x68,\n\t0x1E, 0x69, 0x1E, 0x6A, 0x1E, 0x6B, 0x1E, 0x6C, 0x1E, 0x6D, 0x1E, 0x6E, 0x1E, 0x6F, 0x1E, 0x70, 0x1E, 0x71, 0x1E, 0x72, 0x1E, 0x73, 0x1E, 0x74, 0x1E, 0x75, 0x1E, 0x76, 0x1E, 0x77, 0x1E, 0x78,\n\t0x1E, 0x79, 0x1E, 0x7A, 0x1E, 0x7B, 0x1E, 0x7C, 0x1E, 0x7D, 0x1E, 0x7E, 0x1E, 0x80, 0x1E, 0x81, 0x1E, 0x82, 0x1E, 0x83, 0x1E, 0x84, 0x1E, 0x85, 0x1E, 0x86, 0x1E, 0x87, 0x1E, 0x88, 0x1E, 0x89,\n\t0x1E, 0x8A, 0x1E, 0x8B, 0x1E, 0x8C, 0x1E, 0x8D, 0x1E, 0x8E, 0x1E, 0x8F, 0x1E, 0x90, 0x1E, 0x91, 0x1E, 0x92, 0x1E, 0x93, 0x1E, 0x94, 0x1E, 0x95, 0x1E, 0x96, 0x1E, 0x97, 0x1E, 0x98, 0x1E, 0x99,\n\t0x1E, 0x9A, 0x1E, 0x9B, 0x1E, 0x9C, 0x1E, 0x9D, 0x1E, 0x9E, 0x1E, 0x9F, 0x1E, 0xA0, 0x1E, 0xA1, 0x1E, 0xA2, 0x1E, 0xA3, 0x1E, 0xA4, 0x1E, 0xA5, 0x1E, 0xA6, 0x1E, 0xA7, 0x1E, 0xA8, 0x1E, 0xA9,\n\t0x1E, 0xAA, 0x1E, 0xAB, 0x1E, 0xAC, 0x1E, 0xAD, 0x1E, 0xAE, 0x1E, 0xAF, 0x1E, 0xB0, 0x1E, 0xB2, 0x1E, 0xB3, 0x1E, 0xB4, 0x1E, 0xB5, 0x1E, 0xB6, 0x1E, 0xB7, 0x1E, 0xB8, 0x1E, 0xB9, 0x1E, 0xBA,\n\t0x1E, 0xBB, 0x1E, 0xBC, 0x1E, 0xBD, 0x1E, 0xBE, 0x1E, 0xBF, 0x1E, 0xC0, 0x1E, 0xC1, 0x1E, 0xC2, 0x1E, 0xC3, 0x1E, 0xC4, 0x1E, 0xC5, 0x1E, 0xC6, 0x1E, 0xC7, 0x1E, 0xC8, 0x1E, 0xC9, 0x1E, 0xCA,\n\t0x1E, 0xCB, 0x1E, 0xCC, 0x1E, 0xCD, 0x1E, 0xCE, 0x1E, 0xCF, 0x1E, 0xD0, 0x1E, 0xD1, 0x1E, 0xD2, 0x1E, 0xD3, 0x1E, 0xD4, 0x1E, 0xD5, 0x1E, 0xD6, 0x1E, 0xD7, 0x1E, 0xD8, 0x1E, 0xD9, 0x1E, 0xDA,\n\t0x1E, 0xDB, 0x1E, 0xDC, 0x1E, 0xDD, 0x1E, 0xDE, 0x1E, 0xDF, 0x1E, 0xE0, 0x1E, 0xE1, 0x1E, 0xE3, 0x1E, 0xE4, 0x1E, 0xE5, 0x1E, 0xE6, 0x1E, 0xE7, 0x1E, 0xE8, 0x1E, 0xE9, 0x1E, 0xEA, 0x1E, 0xEB,\n\t0x1E, 0xEC, 0x1E, 0xED, 0x1E, 0xEE, 0x1E, 0xEF, 0x1E, 0xF0, 0x1E, 0xF1, 0x1E, 0xF2, 0x1E, 0xF3, 0x1E, 0xF4, 0x1E, 0xF5, 0x1E, 0xF6, 0x1E, 0xF7, 0x1E, 0xF8, 0x1E, 0xF9, 0x1E, 0xFA, 0x1E, 0xFB,\n\t0x1E, 0xFC, 0x1E, 0xFD, 0x1E, 0xFE, 0x1E, 0xFF, 0x1E, 0x00, 0x1F, 0x01, 0x1F, 0x02, 0x1F, 0x03, 0x1F, 0x04, 0x1F, 0x05, 0x1F, 0x06, 0x1F, 0x07, 0x1F, 0x08, 0x1F, 0x09, 0x1F, 0x0A, 0x1F, 0x0B,\n\t0x1F, 0x0C, 0x1F, 0x0D, 0x1F, 0x0E, 0x1F, 0x0F, 0x1F, 0x10, 0x1F, 0x12, 0x1F, 0x13, 0x1F, 0x14, 0x1F, 0x15, 0x1F, 0x16, 0x1F, 0x17, 0x1F, 0x18, 0x1F, 0x19, 0x1F, 0x1A, 0x1F, 0x1B, 0x1F, 0x1C,\n\t0x1F, 0x1D, 0x1F, 0x1E, 0x1F, 0x1F, 0x1F, 0x20, 0x1F, 0x21, 0x1F, 0x22, 0x1F, 0x23, 0x1F, 0x24, 0x1F, 0x25, 0x1F, 0x26, 0x1F, 0x27, 0x1F, 0x28, 0x1F, 0x29, 0x1F, 0x2A, 0x1F, 0x2B, 0x1F, 0x2C,\n\t0x1F, 0x2D, 0x1F, 0x2E, 0x1F, 0x2F, 0x1F, 0x30, 0x1F, 0x31, 0x1F, 0x32, 0x1F, 0x33, 0x1F, 0x34, 0x1F, 0x35, 0x1F, 0x36, 0x1F, 0x37, 0x1F, 0x38, 0x1F, 0x39, 0x1F, 0x3A, 0x1F, 0x3B, 0x1F, 0x3C,\n\t0x1F, 0x3D, 0x1F, 0x3E, 0x1F, 0x3F, 0x1F, 0x41, 0x1F, 0x42, 0x1F, 0x43, 0x1F, 0x44, 0x1F, 0x45, 0x1F, 0x46, 0x1F, 0x47, 0x1F, 0x48, 0x1F, 0x49, 0x1F, 0x4A, 0x1F, 0x4B, 0x1F, 0x4C, 0x1F, 0x4D,\n\t0x1F, 0x4E, 0x1F, 0x4F, 0x1F, 0x50, 0x1F, 0x51, 0x1F, 0x52, 0x1F, 0x53, 0x1F, 0x54, 0x1F, 0x55, 0x1F, 0x56, 0x1F, 0x57, 0x1F, 0x58, 0x1F, 0x59, 0x1F, 0x5A, 0x1F, 0x5B, 0x1F, 0x5C, 0x1F, 0x5D,\n\t0x1F, 0x5E, 0x1F, 0x5F, 0x1F, 0x60, 0x1F, 0x61, 0x1F, 0x62, 0x1F, 0x63, 0x1F, 0x64, 0x1F, 0x65, 0x1F, 0x66, 0x1F, 0x67, 0x1F, 0x68, 0x1F, 0x69, 0x1F, 0x6A, 0x1F, 0x6B, 0x1F, 0x6C, 0x1F, 0x6E,\n\t0x1F, 0x6F, 0x1F, 0x70, 0x1F, 0x71, 0x1F, 0x72, 0x1F, 0x73, 0x1F, 0x74, 0x1F, 0x75, 0x1F, 0x76, 0x1F, 0x77, 0x1F, 0x78, 0x1F, 0x79, 0x1F, 0x7A, 0x1F, 0x7B, 0x1F, 0x7C, 0x1F, 0x7D, 0x1F, 0x7E,\n\t0x1F, 0x7F, 0x1F, 0x80, 0x1F, 0x81, 0x1F, 0x82, 0x1F, 0x83, 0x1F, 0x84, 0x1F, 0x85, 0x1F, 0x86, 0x1F, 0x87, 0x1F, 0x88, 0x1F, 0x89, 0x1F, 0x8A, 0x1F, 0x8B, 0x1F, 0x8C, 0x1F, 0x8D, 0x1F, 0x8E,\n\t0x1F, 0x8F, 0x1F, 0x90, 0x1F, 0x91, 0x1F, 0x92, 0x1F, 0x93, 0x1F, 0x94, 0x1F, 0x95, 0x1F, 0x96, 0x1F, 0x97, 0x1F, 0x98, 0x1F, 0x9A, 0x1F, 0x9B, 0x1F, 0x9C, 0x1F, 0x9D, 0x1F, 0x9E, 0x1F, 0x9F,\n\t0x1F, 0xA0, 0x1F, 0xA1, 0x1F, 0xA2, 0x1F, 0xA3, 0x1F, 0xA4, 0x1F, 0xA5, 0x1F, 0xA6, 0x1F, 0xA7, 0x1F, 0xA8, 0x1F, 0xA9, 0x1F, 0xAA, 0x1F, 0xAB, 0x1F, 0xAC, 0x1F, 0xAD, 0x1F, 0xAE, 0x1F, 0xAF,\n\t0x1F, 0xB0, 0x1F, 0xB1, 0x1F, 0xB2, 0x1F, 0xB3, 0x1F, 0xB4, 0x1F, 0xB5, 0x1F, 0xB6, 0x1F, 0xB7, 0x1F, 0xB8, 0x1F, 0xB9, 0x1F, 0xBA, 0x1F, 0xBB, 0x1F, 0xBC, 0x1F, 0xBD, 0x1F, 0xBE, 0x1F, 0xBF,\n\t0x1F, 0xC0, 0x1F, 0xC1, 0x1F, 0xC2, 0x1F, 0xC3, 0x1F, 0xC5, 0x1F, 0xC6, 0x1F, 0xC7, 0x1F, 0xC8, 0x1F, 0xC9, 0x1F, 0xCA, 0x1F, 0xCB, 0x1F, 0xCC, 0x1F, 0xCD, 0x1F, 0xCE, 0x1F, 0xCF, 0x1F, 0xD0,\n\t0x1F, 0xD1, 0x1F, 0xD2, 0x1F, 0xD3, 0x1F, 0xD4, 0x1F, 0xD5, 0x1F, 0xD6, 0x1F, 0xD7, 0x1F, 0xD8, 0x1F, 0xD9, 0x1F, 0xDA, 0x1F, 0xDB, 0x1F, 0xDC, 0x1F, 0xDD, 0x1F, 0xDE, 0x1F, 0xDF, 0x1F, 0xE0,\n\t0x1F, 0xE1, 0x1F, 0xE2, 0x1F, 0xE3, 0x1F, 0xE4, 0x1F, 0xE5, 0x1F, 0xE6, 0x1F, 0xE7, 0x1F, 0xE8, 0x1F, 0xE9, 0x1F, 0xEA, 0x1F, 0xEB, 0x1F, 0xEC, 0x1F, 0xED, 0x1F, 0xEE, 0x1F, 0xF0, 0x1F, 0xF1,\n\t0x1F, 0xF2, 0x1F, 0xF3, 0x1F, 0xF4, 0x1F, 0xF5, 0x1F, 0xF6, 0x1F, 0xF7, 0x1F, 0xF8, 0x1F, 0xF9, 0x1F, 0xFA, 0x1F, 0xFB, 0x1F, 0xFC, 0x1F, 0xFD, 0x1F, 0xFE, 0x1F, 0xFF, 0x51, 0x47, 0x00, 0x55,\n\t0x43, 0x20, 0x02, 0x20, 0x5B, 0x3B, 0xF1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xB9, 0x04, 0x20, 0x04, 0x20, 0x05, 0x20, 0x05, 0x20, 0x06, 0x20, 0x06, 0x20, 0x07, 0x20, 0x07, 0x20, 0x08,\n\t0x20, 0x08, 0x20, 0x09, 0x20, 0x09, 0x20, 0x0A, 0x20, 0x0A, 0x20, 0x0B, 0x20, 0x0B, 0x20, 0x0C, 0x20, 0x0C, 0x20, 0x0D, 0x20, 0x0E, 0x20, 0x0F, 0x20, 0x10, 0x20, 0x11, 0x20, 0x12, 0x20, 0x13,\n\t0x20, 0x14, 0x20, 0x15, 0x20, 0x16, 0x20, 0x17, 0x20, 0x18, 0x20, 0x19, 0x20, 0x1A, 0x20, 0x1B, 0x20, 0x1C, 0x20, 0x1D, 0x20, 0x1E, 0x20, 0x1F, 0x20, 0x20, 0x20, 0x21, 0x20, 0x22, 0x20, 0x23,\n\t0x20, 0x24, 0x20, 0x25, 0x20, 0x26, 0x20, 0x27, 0x20, 0x28, 0x20, 0x29, 0x20, 0x2A, 0x20, 0x2C, 0x20, 0x2D, 0x20, 0x2E, 0x20, 0x2F, 0x20, 0x30, 0x20, 0x31, 0x20, 0x32, 0x20, 0x33, 0x20, 0x34,\n\t0x20, 0x35, 0x20, 0x36, 0x20, 0x37, 0x20, 0x38, 0x20, 0x39, 0x20, 0x3A, 0x20, 0x3B, 0x20, 0x3C, 0x20, 0x3D, 0x20, 0x3E, 0x20, 0x3F, 0x20, 0x40, 0x20, 0x41, 0x20, 0x42, 0x20, 0x43, 0x20, 0x44,\n\t0x20, 0x45, 0x20, 0x46, 0x20, 0x47, 0x20, 0x48, 0x20, 0x49, 0x20, 0x4A, 0x20, 0x4B, 0x20, 0x4C, 0x20, 0x4D, 0x20, 0x4E, 0x20, 0x4F, 0x20, 0x50, 0x20, 0x51, 0x20, 0x53, 0x20, 0x54, 0x20, 0x55,\n\t0x20, 0x56, 0x20, 0x57, 0x20, 0x58, 0x20, 0x59, 0x20, 0x5A, 0x20, 0x5B, 0x20, 0x5C, 0x20, 0x5D, 0x20, 0x5E, 0x20, 0x5F, 0x20, 0x60, 0x20, 0x61, 0x20, 0x62, 0x20, 0x63, 0x20, 0x64, 0x20, 0x65,\n\t0x20, 0x66, 0x20, 0x67, 0x20, 0x68, 0x20, 0x69, 0x20, 0x6A, 0x20, 0x6B, 0x20, 0x6C, 0x20, 0x6D, 0x20, 0x6E, 0x20, 0x6F, 0x20, 0x70, 0x20, 0x71, 0x20, 0x72, 0x20, 0x73, 0x20, 0x74, 0x20, 0x75,\n\t0x20, 0x76, 0x20, 0x77, 0x20, 0x79, 0x20, 0x7A, 0x20, 0x7B, 0x20, 0x7C, 0x20, 0x7D, 0x20, 0x7E, 0x20, 0x7F, 0x20, 0x80, 0x20, 0x81, 0x20, 0x82, 0x20, 0x83, 0x20, 0x84, 0x20, 0x85, 0x20, 0x86,\n\t0x20, 0x87, 0x20, 0x88, 0x20, 0x89, 0x20, 0x8A, 0x20, 0x8B, 0x20, 0x8C, 0x20, 0x8D, 0x20, 0x8E, 0x20, 0x8F, 0x20, 0x90, 0x20, 0x91, 0x20, 0x92, 0x20, 0x93, 0x20, 0x94, 0x20, 0x95, 0x20, 0x96,\n\t0x20, 0x97, 0x20, 0x98, 0x20, 0x99, 0x20, 0x9A, 0x20, 0x9B, 0x20, 0x9C, 0x20, 0x9E, 0x20, 0x9F, 0x20, 0xA0, 0x20, 0xA1, 0x20, 0xA2, 0x20, 0xA3, 0x20, 0xA4, 0x20, 0xA5, 0x20, 0xA6, 0x20, 0xA7,\n\t0x20, 0xA8, 0x20, 0xA9, 0x20, 0xAA, 0x20, 0xAB, 0x20, 0xAC, 0x20, 0xAD, 0x20, 0xAE, 0x20, 0xAF, 0x20, 0xB0, 0x20, 0xB1, 0x20, 0xB2, 0x20, 0xB3, 0x20, 0xB4, 0x20, 0xB5, 0x20, 0xB6, 0x20, 0xB7,\n\t0x20, 0xB8, 0x20, 0xB9, 0x20, 0xBA, 0x20, 0xBB, 0x20, 0xBC, 0x20, 0xBD, 0x20, 0xBE, 0x20, 0xBF, 0x20, 0xC1, 0x20, 0xC2, 0x20, 0xC3, 0x20, 0xC4, 0x20, 0xC5, 0x20, 0xC6, 0x20, 0xC7, 0x20, 0xC8,\n\t0x20, 0xC9, 0x20, 0xCA, 0x20, 0xCB, 0x20, 0xCC, 0x20, 0xCD, 0x20, 0xCE, 0x20, 0xCF, 0x20, 0xD0, 0x20, 0xD1, 0x20, 0xD2, 0x20, 0xD3, 0x20, 0xD4, 0x20, 0xD5, 0x20, 0xD6, 0x20, 0xD7, 0x20, 0xD8,\n\t0x20, 0xD9, 0x20, 0xDA, 0x20, 0xDB, 0x20, 0xDC, 0x20, 0xDD, 0x20, 0xDE, 0x20, 0xDF, 0x20, 0xE0, 0x20, 0xE1, 0x20, 0xE2, 0x20, 0xE4, 0x20, 0xE5, 0x20, 0xE6, 0x20, 0xE7, 0x20, 0xE8, 0x20, 0xE9,\n\t0x20, 0xEA, 0x20, 0xEB, 0x20, 0xEC, 0x20, 0xED, 0x20, 0xEE, 0x20, 0xEF, 0x20, 0xF0, 0x20, 0xF1, 0x20, 0xF2, 0x20, 0xF3, 0x20, 0xF4, 0x20, 0xF5, 0x20, 0xF6, 0x20, 0xF7, 0x20, 0xF8, 0x20, 0xF9,\n\t0x20, 0xFA, 0x20, 0xFB, 0x20, 0xFC, 0x20, 0xFD, 0x20, 0xFE, 0x20, 0xFF, 0x20, 0x00, 0x21, 0x01, 0x21, 0x02, 0x21, 0x03, 0x21, 0x04, 0x21, 0x06, 0x21, 0x07, 0x21, 0x08, 0x21, 0x09, 0x21, 0x0A,\n\t0x21, 0x0B, 0x21, 0x0C, 0x21, 0x0D, 0x21, 0x0E, 0x21, 0x0F, 0x21, 0x10, 0x21, 0x11, 0x21, 0x12, 0x21, 0x13, 0x21, 0x14, 0x21, 0x15, 0x21, 0x16, 0x21, 0x17, 0x21, 0x18, 0x21, 0x19, 0x21, 0x1A,\n\t0x21, 0x1B, 0x21, 0x1C, 0x21, 0x1D, 0x21, 0x1E, 0x21, 0x1F, 0x21, 0x20, 0x21, 0x21, 0x21, 0x22, 0x21, 0x23, 0x21, 0x24, 0x21, 0x25, 0x21, 0x27, 0x21, 0x28, 0x21, 0x29, 0x21, 0x2A, 0x21, 0x2B,\n\t0x21, 0x2C, 0x21, 0x2D, 0x21, 0x2E, 0x21, 0x2F, 0x21, 0x30, 0x21, 0x31, 0x21, 0x32, 0x21, 0x33, 0x21, 0x34, 0x21, 0x35, 0x21, 0x36, 0x21, 0x37, 0x21, 0x38, 0x21, 0x39, 0x21, 0x3A, 0x21, 0x3B,\n\t0x21, 0x3C, 0x21, 0x3D, 0x21, 0x3E, 0x21, 0x3F, 0x21, 0x40, 0x21, 0x41, 0x21, 0x42, 0x21, 0x43, 0x21, 0x44, 0x21, 0x45, 0x21, 0x47, 0x21, 0x48, 0x21, 0x49, 0x21, 0x4A, 0x21, 0x4B, 0x21, 0x4C,\n\t0x21, 0x4D, 0x21, 0x4E, 0x21, 0x4F, 0x21, 0x50, 0x21, 0x51, 0x21, 0x52, 0x21, 0x53, 0x21, 0x54, 0x21, 0x55, 0x21, 0x56, 0x21, 0x57, 0x21, 0x58, 0x21, 0x59, 0x21, 0x5A, 0x21, 0x5B, 0x21, 0x5C,\n\t0x21, 0x5D, 0x21, 0x5E, 0x21, 0x5F, 0x21, 0x60, 0x21, 0x61, 0x21, 0x62, 0x21, 0x63, 0x21, 0x64, 0x21, 0x65, 0x21, 0x67, 0x21, 0x68, 0x21, 0x69, 0x21, 0x6A, 0x21, 0x6B, 0x21, 0x6C, 0x21, 0x6D,\n\t0x21, 0x6E, 0x21, 0x6F, 0x21, 0x70, 0x21, 0x71, 0x21, 0x72, 0x21, 0x73, 0x21, 0x74, 0x21, 0x75, 0x21, 0x76, 0x21, 0x77, 0x21, 0x78, 0x21, 0x79, 0x21, 0x7A, 0x21, 0x7B, 0x21, 0x7C, 0x21, 0x7D,\n\t0x21, 0x7E, 0x21, 0x7F, 0x21, 0x80, 0x21, 0x81, 0x21, 0x82, 0x21, 0x83, 0x21, 0x84, 0x21, 0x86, 0x21, 0x87, 0x21, 0x88, 0x21, 0x89, 0x21, 0x8A, 0x21, 0x8B, 0x21, 0x8C, 0x21, 0x8D, 0x21, 0x8E,\n\t0x21, 0x8F, 0x21, 0x90, 0x21, 0x91, 0x21, 0x92, 0x21, 0x93, 0x21, 0x94, 0x21, 0x95, 0x21, 0x96, 0x21, 0x97, 0x21, 0x98, 0x21, 0x99, 0x21, 0x9A, 0x21, 0x9B, 0x21, 0x9C, 0x21, 0x9D, 0x21, 0x9E,\n\t0x21, 0x9F, 0x21, 0xA0, 0x21, 0xA1, 0x21, 0xA2, 0x21, 0xA4, 0x21, 0xA5, 0x21, 0xA6, 0x21, 0xA7, 0x21, 0xA8, 0x21, 0xA9, 0x21, 0xAA, 0x21, 0xAB, 0x21, 0xAC, 0x21, 0xAD, 0x21, 0xAE, 0x21, 0xAF,\n\t0x21, 0xB0, 0x21, 0xB1, 0x21, 0xB2, 0x21, 0xB3, 0x21, 0xB4, 0x21, 0xB5, 0x21, 0xB6, 0x21, 0xB7, 0x21, 0xB8, 0x21, 0xB9, 0x21, 0xBA, 0x21, 0xBB, 0x21, 0xBC, 0x21, 0xBD, 0x21, 0xBE, 0x21, 0xBF,\n\t0x21, 0xC0, 0x21, 0xC2, 0x21, 0xC3, 0x21, 0xC4, 0x21, 0xC5, 0x21, 0xC6, 0x21, 0xC7, 0x21, 0xC8, 0x21, 0xC9, 0x21, 0xCA, 0x21, 0xCB, 0x21, 0xCC, 0x21, 0xCD, 0x21, 0xCE, 0x21, 0xCF, 0x21, 0xD0,\n\t0x21, 0xD1, 0x21, 0xD2, 0x21, 0xD3, 0x21, 0xD4, 0x21, 0xD5, 0x21, 0xD6, 0x21, 0xD7, 0x21, 0xD8, 0x21, 0xD9, 0x21, 0xDA, 0x21, 0xDB, 0x21, 0xDC, 0x21, 0xDD, 0x21, 0xDF, 0x21, 0xE0, 0x21, 0xE1,\n\t0x21, 0xE2, 0x21, 0xE3, 0x21, 0xE4, 0x21, 0xE5, 0x21, 0xE6, 0x21, 0xE7, 0x21, 0xE8, 0x21, 0xE9, 0x21, 0xEA, 0x21, 0xEB, 0x21, 0xEC, 0x21, 0xED, 0x21, 0xEE, 0x21, 0xEF, 0x21, 0xF0, 0x21, 0xF1,\n\t0x21, 0xF2, 0x21, 0xF3, 0x21, 0xF4, 0x21, 0xF5, 0x21, 0xF6, 0x21, 0xF7, 0x21, 0xF8, 0x21, 0xF9, 0x21, 0xFB, 0x21, 0xFC, 0x21, 0xFD, 0x21, 0xFE, 0x21, 0xFF, 0x21, 0x00, 0x22, 0x01, 0x22, 0x02,\n\t0x22, 0x03, 0x22, 0x04, 0x22, 0x05, 0x22, 0x06, 0x22, 0x07, 0x22, 0x08, 0x22, 0x09, 0x22, 0x0A, 0x22, 0x0B, 0x22, 0x0C, 0x22, 0x0D, 0x22, 0x0E, 0x22, 0x0F, 0x22, 0x10, 0x22, 0x11, 0x22, 0x12,\n\t0x22, 0x13, 0x22, 0x14, 0x22, 0x15, 0x22, 0x16, 0x22, 0x18, 0x22, 0x19, 0x22, 0x1A, 0x22, 0x1B, 0x22, 0x1C, 0x22, 0x1D, 0x22, 0x1E, 0x22, 0x1F, 0x22, 0x20, 0x22, 0x21, 0x22, 0x22, 0x22, 0x23,\n\t0x22, 0x24, 0x22, 0x25, 0x22, 0x26, 0x22, 0x27, 0x22, 0x28, 0x22, 0x29, 0x22, 0x2A, 0x22, 0x2B, 0x22, 0x2C, 0x22, 0x2D, 0x22, 0x2E, 0x22, 0x2F, 0x22, 0x30, 0x22, 0x31, 0x22, 0x33, 0x22, 0x34,\n\t0x22, 0x35, 0x22, 0x36, 0x22, 0x37, 0x22, 0x38, 0x22, 0x39, 0x22, 0x3A, 0x22, 0x3B, 0x22, 0x3C, 0x22, 0x3D, 0x22, 0x3E, 0x22, 0x3F, 0x22, 0x40, 0x22, 0x41, 0x22, 0x42, 0x22, 0x43, 0x22, 0x44,\n\t0x22, 0x45, 0x22, 0x46, 0x22, 0x47, 0x22, 0x48, 0x22, 0x49, 0x22, 0x4A, 0x22, 0x4B, 0x22, 0x4C, 0x22, 0x4E, 0x22, 0x4F, 0x22, 0x50, 0x22, 0x51, 0x22, 0x52, 0x22, 0x53, 0x22, 0x54, 0x22, 0x55,\n\t0x22, 0x56, 0x22, 0x57, 0x22, 0x58, 0x22, 0x59, 0x22, 0x5A, 0x22, 0x5B, 0x22, 0x5C, 0x22, 0x5D, 0x22, 0x5E, 0x22, 0x5F, 0x22, 0x60, 0x22, 0x61, 0x22, 0x62, 0x22, 0x63, 0x22, 0x64, 0x22, 0x65,\n\t0x22, 0x66, 0x22, 0x67, 0x22, 0x69, 0x22, 0x6A, 0x22, 0x6B, 0x22, 0x6C, 0x22, 0x6D, 0x22, 0x6E, 0x22, 0x6F, 0x22, 0x70, 0x22, 0x71, 0x22, 0x72, 0x22, 0x73, 0x22, 0x74, 0x22, 0x75, 0x22, 0x76,\n\t0x22, 0x77, 0x22, 0x78, 0x22, 0x79, 0x22, 0x7A, 0x22, 0x7B, 0x22, 0x7C, 0x22, 0x7D, 0x22, 0x7E, 0x22, 0x7F, 0x22, 0x80, 0x22, 0x81, 0x22, 0x83, 0x22, 0x84, 0x22, 0x85, 0x22, 0x86, 0x22, 0x87,\n\t0x22, 0x88, 0x22, 0x89, 0x22, 0x8A, 0x22, 0x8B, 0x22, 0x8C, 0x22, 0x8D, 0x22, 0x8E, 0x22, 0x8F, 0x22, 0x90, 0x22, 0x91, 0x22, 0x92, 0x22, 0x93, 0x22, 0x94, 0x22, 0x95, 0x22, 0x96, 0x22, 0x97,\n\t0x22, 0x98, 0x22, 0x99, 0x22, 0x9A, 0x22, 0x9B, 0x22, 0x9D, 0x22, 0x9E, 0x22, 0x9F, 0x22, 0xA0, 0x22, 0xA1, 0x22, 0xA2, 0x22, 0xA3, 0x22, 0xA4, 0x22, 0xA5, 0x22, 0xA6, 0x22, 0xA7, 0x22, 0xA8,\n\t0x22, 0xA9, 0x22, 0xAA, 0x22, 0xAB, 0x22, 0xAC, 0x22, 0xAD, 0x22, 0xAE, 0x22, 0xAF, 0x22, 0xB0, 0x22, 0xB1, 0x22, 0xB2, 0x22, 0xB3, 0x22, 0xB4, 0x22, 0xB5, 0x22, 0xB7, 0x22, 0xB8, 0x22, 0xB9,\n\t0x22, 0xBA, 0x22, 0xBB, 0x22, 0xBC, 0x22, 0xBD, 0x22, 0xBE, 0x22, 0xBF, 0x22, 0xC0, 0x22, 0xC1, 0x22, 0xC2, 0x22, 0xC3, 0x22, 0xC4, 0x22, 0xC5, 0x22, 0xC6, 0x22, 0xC7, 0x22, 0xC8, 0x22, 0xC9,\n\t0x22, 0xCA, 0x22, 0xCB, 0x22, 0xCC, 0x22, 0xCD, 0x22, 0xCE, 0x22, 0xD0, 0x22, 0xD1, 0x22, 0xD2, 0x22, 0xD3, 0x22, 0xD4, 0x22, 0xD5, 0x22, 0xD6, 0x22, 0xD7, 0x22, 0xD8, 0x22, 0xD9, 0x22, 0xDA,\n\t0x22, 0xDB, 0x22, 0xDC, 0x22, 0xDD, 0x22, 0xDE, 0x22, 0xDF, 0x22, 0xE0, 0x22, 0xE1, 0x22, 0xE2, 0x22, 0xE3, 0x22, 0xE4, 0x22, 0xE5, 0x22, 0xE6, 0x22, 0xE7, 0x22, 0xE9, 0x22, 0xEA, 0x22, 0xEB,\n\t0x22, 0xEC, 0x22, 0xED, 0x22, 0xEE, 0x22, 0xEF, 0x22, 0xF0, 0x22, 0xF1, 0x22, 0xF2, 0x22, 0xF3, 0x22, 0xF4, 0x22, 0xF5, 0x22, 0xF6, 0x22, 0xF7, 0x22, 0xF8, 0x22, 0xF9, 0x22, 0xFA, 0x22, 0xFB,\n\t0x22, 0xFC, 0x22, 0xFD, 0x22, 0xFE, 0x22, 0xFF, 0x22, 0x00, 0x23, 0x02, 0x23, 0x03, 0x23, 0x04, 0x23, 0x05, 0x23, 0x06, 0x23, 0x07, 0x23, 0x08, 0x23, 0x09, 0x23, 0x0A, 0x23, 0x0B, 0x23, 0x0C,\n\t0x23, 0x0D, 0x23, 0x0E, 0x23, 0x0F, 0x23, 0x10, 0x23, 0x11, 0x23, 0x12, 0x23, 0x13, 0x23, 0x14, 0x23, 0x15, 0x23, 0x16, 0x23, 0x17, 0x23, 0x18, 0x23, 0x1A, 0x23, 0x1B, 0x23, 0x1C, 0x23, 0x1D,\n\t0x23, 0x1E, 0x23, 0x1F, 0x23, 0x20, 0x23, 0x21, 0x23, 0x22, 0x23, 0x23, 0x23, 0x24, 0x23, 0x25, 0x23, 0x26, 0x23, 0x27, 0x23, 0x28, 0x23, 0x29, 0x23, 0x2A, 0x23, 0x2B, 0x23, 0x2C, 0x23, 0x2D,\n\t0x23, 0x2E, 0x23, 0x2F, 0x23, 0x30, 0x23, 0x32, 0x23, 0x33, 0x23, 0x34, 0x23, 0x35, 0x23, 0x36, 0x23, 0x37, 0x23, 0x38, 0x23, 0x39, 0x23, 0x3A, 0x23, 0x3B, 0x23, 0x3C, 0x23, 0x3D, 0x23, 0x3E,\n\t0x23, 0x3F, 0x23, 0x40, 0x23, 0x41, 0x23, 0x42, 0x23, 0x43, 0x23, 0x44, 0x23, 0x45, 0x23, 0x46, 0x23, 0x47, 0x23, 0x48, 0x23, 0x4A, 0x23, 0x4B, 0x23, 0x4C, 0x23, 0x4D, 0x23, 0x4E, 0x23, 0x4F,\n\t0x23, 0x50, 0x23, 0x51, 0x23, 0x52, 0x23, 0x53, 0x23, 0x54, 0x23, 0x55, 0x23, 0x56, 0x23, 0x57, 0x23, 0x58, 0x23, 0x59, 0x23, 0x5A, 0x23, 0x5B, 0x23, 0x5C, 0x23, 0x5D, 0x23, 0x5E, 0x23, 0x5F,\n\t0x23, 0x61, 0x23, 0x62, 0x23, 0x63, 0x23, 0x64, 0x23, 0x65, 0x23, 0x66, 0x23, 0x67, 0x23, 0x68, 0x23, 0x69, 0x23, 0x6A, 0x23, 0x6B, 0x23, 0x6C, 0x23, 0x6D, 0x23, 0x6E, 0x23, 0x6F, 0x23, 0x70,\n\t0x23, 0x71, 0x23, 0x72, 0x23, 0x73, 0x23, 0x74, 0x23, 0x75, 0x23, 0x76, 0x23, 0x78, 0x23, 0x79, 0x23, 0x7A, 0x23, 0x7B, 0x23, 0x7C, 0x23, 0x7D, 0x23, 0x7E, 0x23, 0x7F, 0x23, 0x80, 0x23, 0x81,\n\t0x23, 0x82, 0x23, 0x83, 0x23, 0x84, 0x23, 0x85, 0x23, 0x86, 0x23, 0x87, 0x23, 0x88, 0x23, 0x89, 0x23, 0x8A, 0x23, 0x8B, 0x23, 0x8C, 0x23, 0x8D, 0x23, 0x8F, 0x23, 0x90, 0x23, 0x91, 0x23, 0x92,\n\t0x23, 0x93, 0x23, 0x94, 0x23, 0x95, 0x23, 0x96, 0x23, 0x97, 0x23, 0x98, 0x23, 0x99, 0x23, 0x9A, 0x23, 0x9B, 0x23, 0x9C, 0x23, 0x9D, 0x23, 0x9E, 0x23, 0x9F, 0x23, 0xA0, 0x23, 0xA1, 0x23, 0xA2,\n\t0x23, 0xA3, 0x23, 0xA5, 0x23, 0xA6, 0x23, 0xA7, 0x23, 0xA8, 0x23, 0xA9, 0x23, 0xAA, 0x23, 0xAB, 0x23, 0xAC, 0x23, 0xAD, 0x23, 0xAE, 0x23, 0xAF, 0x23, 0xB0, 0x23, 0xB1, 0x23, 0xB2, 0x23, 0xB3,\n\t0x23, 0xB4, 0x23, 0xB5, 0x23, 0xB6, 0x23, 0xB7, 0x23, 0xB8, 0x23, 0xB9, 0x23, 0xBA, 0x23, 0xBC, 0x23, 0xBD, 0x23, 0xBE, 0x23, 0xBF, 0x23, 0xC0, 0x23, 0xC1, 0x23, 0xC2, 0x23, 0xC3, 0x23, 0xC4,\n\t0x23, 0xC5, 0x23, 0xC6, 0x23, 0xC7, 0x23, 0xC8, 0x23, 0xC9, 0x23, 0xCA, 0x23, 0xCB, 0x23, 0xCC, 0x23, 0xCD, 0x23, 0xCE, 0x23, 0xCF, 0x23, 0xD0, 0x23, 0xD2, 0x23, 0xD3, 0x23, 0xD4, 0x23, 0xD5,\n\t0x23, 0xD6, 0x23, 0xD7, 0x23, 0xD8, 0x23, 0xD9, 0x23, 0xDA, 0x23, 0xDB, 0x23, 0xDC, 0x23, 0xDD, 0x23, 0xDE, 0x23, 0xDF, 0x23, 0xE0, 0x23, 0xE1, 0x23, 0xE2, 0x23, 0xE3, 0x23, 0xE4, 0x23, 0xE5,\n\t0x23, 0xE6, 0x23, 0xE8, 0x23, 0xE9, 0x23, 0xEA, 0x23, 0xEB, 0x23, 0xEC, 0x23, 0xED, 0x23, 0xEE, 0x23, 0xEF, 0x23, 0xF0, 0x23, 0xF1, 0x23, 0xF2, 0x23, 0xF3, 0x23, 0xF4, 0x23, 0xF5, 0x23, 0xF6,\n\t0x23, 0xF7, 0x23, 0xF8, 0x23, 0xF9, 0x23, 0xFA, 0x23, 0xFB, 0x23, 0xFC, 0x23, 0xFE, 0x23, 0xFF, 0x11, 0x4F, 0x00, 0x15, 0x4B, 0x00, 0x19, 0x47, 0x00, 0x1D, 0x43, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF,\n\t0xFF, 0xFF, 0xFF, 0x92, 0x04, 0x24, 0x05, 0x24, 0x05, 0x24, 0x06, 0x24, 0x06, 0x24, 0x07, 0x24, 0x07, 0x24, 0x08, 0x24, 0x08, 0x24, 0x09, 0x24, 0x09, 0x24, 0x0A, 0x24, 0x0A, 0x24, 0x0B, 0x24,\n\t0x0B, 0x24, 0x0C, 0x24, 0x0C, 0x24, 0x0D, 0x24, 0x0D, 0x24, 0x0E, 0x24, 0x0F, 0x24, 0x0F, 0x24, 0x10, 0x24, 0x10, 0x24, 0x11, 0x24, 0x11, 0x24, 0x12, 0x24, 0x12, 0x24, 0x13, 0x24, 0x13, 0x24,\n\t0x14, 0x24, 0x14, 0x24, 0x15, 0x24, 0x15, 0x24, 0x16, 0x24, 0x16, 0x24, 0x17, 0x24, 0x17, 0x24, 0x18, 0x24, 0x18, 0x24, 0x19, 0x24, 0x1A, 0x24, 0x1B, 0x24, 0x1C, 0x24, 0x1D, 0x24, 0x1E, 0x24,\n\t0x1F, 0x24, 0x20, 0x24, 0x21, 0x24, 0x22, 0x24, 0x23, 0x24, 0x24, 0x24, 0x25, 0x24, 0x26, 0x24, 0x27, 0x24, 0x28, 0x24, 0x29, 0x24, 0x2A, 0x24, 0x2B, 0x24, 0x2C, 0x24, 0x2D, 0x24, 0x2F, 0x24,\n\t0x30, 0x24, 0x31, 0x24, 0x32, 0x24, 0x33, 0x24, 0x34, 0x24, 0x35, 0x24, 0x36, 0x24, 0x37, 0x24, 0x38, 0x24, 0x39, 0x24, 0x3A, 0x24, 0x3B, 0x24, 0x3C, 0x24, 0x3D, 0x24, 0x3E, 0x24, 0x3F, 0x24,\n\t0x40, 0x24, 0x41, 0x24, 0x43, 0x24, 0x44, 0x24, 0x45, 0x24, 0x46, 0x24, 0x47, 0x24, 0x48, 0x24, 0x49, 0x24, 0x4A, 0x24, 0x4B, 0x24, 0x4C, 0x24, 0x4D, 0x24, 0x4E, 0x24, 0x4F, 0x24, 0x50, 0x24,\n\t0x51, 0x24, 0x52, 0x24, 0x53, 0x24, 0x54, 0x24, 0x55, 0x24, 0x57, 0x24, 0x58, 0x24, 0x59, 0x24, 0x5A, 0x24, 0x5B, 0x24, 0x5C, 0x24, 0x5D, 0x24, 0x5E, 0x24, 0x5F, 0x24, 0x60, 0x24, 0x61, 0x24,\n\t0x62, 0x24, 0x63, 0x24, 0x64, 0x24, 0x65, 0x24, 0x66, 0x24, 0x67, 0x24, 0x68, 0x24, 0x69, 0x24, 0x6B, 0x24, 0x6C, 0x24, 0x6D, 0x24, 0x6E, 0x24, 0x6F, 0x24, 0x70, 0x24, 0x71, 0x24, 0x72, 0x24,\n\t0x73, 0x24, 0x74, 0x24, 0x75, 0x24, 0x76, 0x24, 0x77, 0x24, 0x78, 0x24, 0x79, 0x24, 0x7A, 0x24, 0x7B, 0x24, 0x7C, 0x24, 0x7D, 0x24, 0x7F, 0x24, 0x80, 0x24, 0x81, 0x24, 0x82, 0x24, 0x83, 0x24,\n\t0x84, 0x24, 0x85, 0x24, 0x86, 0x24, 0x87, 0x24, 0x88, 0x24, 0x89, 0x24, 0x8A, 0x24, 0x8B, 0x24, 0x8C, 0x24, 0x8D, 0x24, 0x8E, 0x24, 0x8F, 0x24, 0x90, 0x24, 0x92, 0x24, 0x93, 0x24, 0x94, 0x24,\n\t0x95, 0x24, 0x96, 0x24, 0x97, 0x24, 0x98, 0x24, 0x99, 0x24, 0x9A, 0x24, 0x9B, 0x24, 0x9C, 0x24, 0x9D, 0x24, 0x9E, 0x24, 0x9F, 0x24, 0xA0, 0x24, 0xA1, 0x24, 0xA2, 0x24, 0xA3, 0x24, 0xA5, 0x24,\n\t0xA6, 0x24, 0xA7, 0x24, 0xA8, 0x24, 0xA9, 0x24, 0xAA, 0x24, 0xAB, 0x24, 0xAC, 0x24, 0xAD, 0x24, 0xAE, 0x24, 0xAF, 0x24, 0xB0, 0x24, 0xB1, 0x24, 0xB2, 0x24, 0xB3, 0x24, 0xB4, 0x24, 0xB5, 0x24,\n\t0xB6, 0x24, 0xB8, 0x24, 0xB9, 0x24, 0xBA, 0x24, 0xBB, 0x24, 0xBC, 0x24, 0xBD, 0x24, 0xBE, 0x24, 0xBF, 0x24, 0xC0, 0x24, 0xC1, 0x24, 0xC2, 0x24, 0xC3, 0x24, 0xC4, 0x24, 0xC5, 0x24, 0xC6, 0x24,\n\t0xC7, 0x24, 0xC8, 0x24, 0xCA, 0x24, 0xCB, 0x24, 0xCC, 0x24, 0xCD, 0x24, 0xCE, 0x24, 0xCF, 0x24, 0xD0, 0x24, 0xD1, 0x24, 0xD2, 0x24, 0xD3, 0x24, 0xD4, 0x24, 0xD5, 0x24, 0xD6, 0x24, 0xD7, 0x24,\n\t0xD8, 0x24, 0xD9, 0x24, 0xDA, 0x24, 0xDC, 0x24, 0xDD, 0x24, 0xDE, 0x24, 0xDF, 0x24, 0xE0, 0x24, 0xE1, 0x24, 0xE2, 0x24, 0xE3, 0x24, 0xE4, 0x24, 0xE5, 0x24, 0xE6, 0x24, 0xE7, 0x24, 0xE8, 0x24,\n\t0xE9, 0x24, 0xEA, 0x24, 0xEB, 0x24, 0xEC, 0x24, 0xEE, 0x24, 0xEF, 0x24, 0xF0, 0x24, 0xF1, 0x24, 0xF2, 0x24, 0xF3, 0x24, 0xF4, 0x24, 0xF5, 0x24, 0xF6, 0x24, 0xF7, 0x24, 0xF8, 0x24, 0xF9, 0x24,\n\t0xFA, 0x24, 0xFB, 0x24, 0xFC, 0x24, 0xFD, 0x24, 0xFE, 0x24, 0x00, 0x25, 0x01, 0x25, 0x02, 0x25, 0x03, 0x25, 0x04, 0x25, 0x05, 0x25, 0x06, 0x25, 0x07, 0x25, 0x08, 0x25, 0x09, 0x25, 0x0A, 0x25,\n\t0x0B, 0x25, 0x0C, 0x25, 0x0D, 0x25, 0x0E, 0x25, 0x0F, 0x25, 0x11, 0x25, 0x12, 0x25, 0x13, 0x25, 0x14, 0x25, 0x15, 0x25, 0x16, 0x25, 0x17, 0x25, 0x18, 0x25, 0x19, 0x25, 0x1A, 0x25, 0x1B, 0x25,\n\t0x1C, 0x25, 0x1D, 0x25, 0x1E, 0x25, 0x1F, 0x25, 0x20, 0x25, 0x22, 0x25, 0x23, 0x25, 0x24, 0x25, 0x25, 0x25, 0x26, 0x25, 0x27, 0x25, 0x28, 0x25, 0x29, 0x25, 0x2A, 0x25, 0x2B, 0x25, 0x2C, 0x25,\n\t0x2D, 0x25, 0x2E, 0x25, 0x2F, 0x25, 0x30, 0x25, 0x31, 0x25, 0x33, 0x25, 0x34, 0x25, 0x35, 0x25, 0x36, 0x25, 0x37, 0x25, 0x38, 0x25, 0x39, 0x25, 0x3A, 0x25, 0x3B, 0x25, 0x3C, 0x25, 0x3D, 0x25,\n\t0x3E, 0x25, 0x3F, 0x25, 0x40, 0x25, 0x41, 0x25, 0x42, 0x25, 0x44, 0x25, 0x45, 0x25, 0x46, 0x25, 0x47, 0x25, 0x48, 0x25, 0x49, 0x25, 0x4A, 0x25, 0x4B, 0x25, 0x4C, 0x25, 0x4D, 0x25, 0x4E, 0x25,\n\t0x4F, 0x25, 0x50, 0x25, 0x51, 0x25, 0x52, 0x25, 0x53, 0x25, 0x55, 0x25, 0x56, 0x25, 0x57, 0x25, 0x58, 0x25, 0x59, 0x25, 0x5A, 0x25, 0x5B, 0x25, 0x5C, 0x25, 0x5D, 0x25, 0x5E, 0x25, 0x5F, 0x25,\n\t0x60, 0x25, 0x61, 0x25, 0x62, 0x25, 0x63, 0x25, 0x65, 0x25, 0x66, 0x25, 0x67, 0x25, 0x68, 0x25, 0x69, 0x25, 0x6A, 0x25, 0x6B, 0x25, 0x6C, 0x25, 0x6D, 0x25, 0x6E, 0x25, 0x6F, 0x25, 0x70, 0x25,\n\t0x71, 0x25, 0x72, 0x25, 0x73, 0x25, 0x74, 0x25, 0x76, 0x25, 0x77, 0x25, 0x78, 0x25, 0x79, 0x25, 0x7A, 0x25, 0x7B, 0x25, 0x7C, 0x25, 0x7D, 0x25, 0x7E, 0x25, 0x7F, 0x25, 0x80, 0x25, 0x81, 0x25,\n\t0x82, 0x25, 0x83, 0x25, 0x84, 0x25, 0x86, 0x25, 0x87, 0x25, 0x88, 0x25, 0x89, 0x25, 0x8A, 0x25, 0x8B, 0x25, 0x8C, 0x25, 0x8D, 0x25, 0x8E, 0x25, 0x8F, 0x25, 0x90, 0x25, 0x91, 0x25, 0x92, 0x25,\n\t0x93, 0x25, 0x94, 0x25, 0x96, 0x25, 0x97, 0x25, 0x98, 0x25, 0x99, 0x25, 0x9A, 0x25, 0x9B, 0x25, 0x9C, 0x25, 0x9D, 0x25, 0x9E, 0x25, 0x9F, 0x25, 0xA0, 0x25, 0xA1, 0x25, 0xA2, 0x25, 0xA3, 0x25,\n\t0xA5, 0x25, 0xA6, 0x25, 0xA7, 0x25, 0xA8, 0x25, 0xA9, 0x25, 0xAA, 0x25, 0xAB, 0x25, 0xAC, 0x25, 0xAD, 0x25, 0xAE, 0x25, 0xAF, 0x25, 0xB0, 0x25, 0xB1, 0x25, 0xB2, 0x25, 0xB3, 0x25, 0xB5, 0x25,\n\t0xB6, 0x25, 0xB7, 0x25, 0xB8, 0x25, 0xB9, 0x25, 0xBA, 0x25, 0xBB, 0x25, 0xBC, 0x25, 0xBD, 0x25, 0xBE, 0x25, 0xBF, 0x25, 0xC0, 0x25, 0xC1, 0x25, 0xC2, 0x25, 0xC3, 0x25, 0xC5, 0x25, 0xC6, 0x25,\n\t0xC7, 0x25, 0xC8, 0x25, 0xC9, 0x25, 0xCA, 0x25, 0xCB, 0x25, 0xCC, 0x25, 0xCD, 0x25, 0xCE, 0x25, 0xCF, 0x25, 0xD0, 0x25, 0xD1, 0x25, 0xD2, 0x25, 0xD4, 0x25, 0xD5, 0x25, 0xD6, 0x25, 0xD7, 0x25,\n\t0xD8, 0x25, 0xD9, 0x25, 0xDA, 0x25, 0xDB, 0x25, 0xDC, 0x25, 0xDD, 0x25, 0xDE, 0x25, 0xDF, 0x25, 0xE0, 0x25, 0xE1, 0x25, 0xE3, 0x25, 0xE4, 0x25, 0xE5, 0x25, 0xE6, 0x25, 0xE7, 0x25, 0xE8, 0x25,\n\t0xE9, 0x25, 0xEA, 0x25, 0xEB, 0x25, 0xEC, 0x25, 0xED, 0x25, 0xEE, 0x25, 0xEF, 0x25, 0xF0, 0x25, 0xF2, 0x25, 0xF3, 0x25, 0xF4, 0x25, 0xF5, 0x25, 0xF6, 0x25, 0xF7, 0x25, 0xF8, 0x25, 0xF9, 0x25,\n\t0xFA, 0x25, 0xFB, 0x25, 0xFC, 0x25, 0xFD, 0x25, 0xFE, 0x25, 0xFF, 0x25, 0x01, 0x26, 0x02, 0x26, 0x03, 0x26, 0x04, 0x26, 0x05, 0x26, 0x06, 0x26, 0x07, 0x26, 0x08, 0x26, 0x09, 0x26, 0x0A, 0x26,\n\t0x0B, 0x26, 0x0C, 0x26, 0x0D, 0x26, 0x0E, 0x26, 0x10, 0x26, 0x11, 0x26, 0x12, 0x26, 0x13, 0x26, 0x14, 0x26, 0x15, 0x26, 0x16, 0x26, 0x17, 0x26, 0x18, 0x26, 0x19, 0x26, 0x1A, 0x26, 0x1B, 0x26,\n\t0x1C, 0x26, 0x1D, 0x26, 0x1F, 0x26, 0x20, 0x26, 0x21, 0x26, 0x22, 0x26, 0x23, 0x26, 0x24, 0x26, 0x25, 0x26, 0x26, 0x26, 0x27, 0x26, 0x28, 0x26, 0x29, 0x26, 0x2A, 0x26, 0x2B, 0x26, 0x2D, 0x26,\n\t0x2E, 0x26, 0x2F, 0x26, 0x30, 0x26, 0x31, 0x26, 0x32, 0x26, 0x33, 0x26, 0x34, 0x26, 0x35, 0x26, 0x36, 0x26, 0x37, 0x26, 0x38, 0x26, 0x39, 0x26, 0x3B, 0x26, 0x3C, 0x26, 0x3D, 0x26, 0x3E, 0x26,\n\t0x3F, 0x26, 0x40, 0x26, 0x41, 0x26, 0x42, 0x26, 0x43, 0x26, 0x44, 0x26, 0x45, 0x26, 0x46, 0x26, 0x47, 0x26, 0x48, 0x26, 0x4A, 0x26, 0x4B, 0x26, 0x4C, 0x26, 0x4D, 0x26, 0x4E, 0x26, 0x4F, 0x26,\n\t0x50, 0x26, 0x51, 0x26, 0x52, 0x26, 0x53, 0x26, 0x54, 0x26, 0x55, 0x26, 0x56, 0x26, 0x58, 0x26, 0x59, 0x26, 0x5A, 0x26, 0x5B, 0x26, 0x5C, 0x26, 0x5D, 0x26, 0x5E, 0x26, 0x5F, 0x26, 0x60, 0x26,\n\t0x61, 0x26, 0x62, 0x26, 0x63, 0x26, 0x64, 0x26, 0x66, 0x26, 0x67, 0x26, 0x68, 0x26, 0x69, 0x26, 0x6A, 0x26, 0x6B, 0x26, 0x6C, 0x26, 0x6D, 0x26, 0x6E, 0x26, 0x6F, 0x26, 0x70, 0x26, 0x71, 0x26,\n\t0x72, 0x26, 0x74, 0x26, 0x75, 0x26, 0x76, 0x26, 0x77, 0x26, 0x78, 0x26, 0x79, 0x26, 0x7A, 0x26, 0x7B, 0x26, 0x7C, 0x26, 0x7D, 0x26, 0x7E, 0x26, 0x7F, 0x26, 0x80, 0x26, 0x82, 0x26, 0x83, 0x26,\n\t0x84, 0x26, 0x85, 0x26, 0x86, 0x26, 0x87, 0x26, 0x88, 0x26, 0x89, 0x26, 0x8A, 0x26, 0x8B, 0x26, 0x8C, 0x26, 0x8D, 0x26, 0x8E, 0x26, 0x90, 0x26, 0x91, 0x26, 0x92, 0x26, 0x93, 0x26, 0x94, 0x26,\n\t0x95, 0x26, 0x96, 0x26, 0x97, 0x26, 0x98, 0x26, 0x99, 0x26, 0x9A, 0x26, 0x9B, 0x26, 0x9D, 0x26, 0x9E, 0x26, 0x9F, 0x26, 0xA0, 0x26, 0xA1, 0x26, 0xA2, 0x26, 0xA3, 0x26, 0xA4, 0x26, 0xA5, 0x26,\n\t0xA6, 0x26, 0xA7, 0x26, 0xA8, 0x26, 0xA9, 0x26, 0xAB, 0x26, 0xAC, 0x26, 0xAD, 0x26, 0xAE, 0x26, 0xAF, 0x26, 0xB0, 0x26, 0xB1, 0x26, 0xB2, 0x26, 0xB3, 0x26, 0xB4, 0x26, 0xB5, 0x26, 0xB6, 0x26,\n\t0xB8, 0x26, 0xB9, 0x26, 0xBA, 0x26, 0xBB, 0x26, 0xBC, 0x26, 0xBD, 0x26, 0xBE, 0x26, 0xBF, 0x26, 0xC0, 0x26, 0xC1, 0x26, 0xC2, 0x26, 0xC3, 0x26, 0xC4, 0x26, 0xC6, 0x26, 0xC7, 0x26, 0xC8, 0x26,\n\t0xC9, 0x26, 0xCA, 0x26, 0xCB, 0x26, 0xCC, 0x26, 0xCD, 0x26, 0xCE, 0x26, 0xCF, 0x26, 0xD0, 0x26, 0xD1, 0x26, 0xD3, 0x26, 0xD4, 0x26, 0xD5, 0x26, 0xD6, 0x26, 0xD7, 0x26, 0xD8, 0x26, 0xD9, 0x26,\n\t0xDA, 0x26, 0xDB, 0x26, 0xDC, 0x26, 0xDD, 0x26, 0xDE, 0x26, 0xE0, 0x26, 0xE1, 0x26, 0xE2, 0x26, 0xE3, 0x26, 0xE4, 0x26, 0xE5, 0x26, 0xE6, 0x26, 0xE7, 0x26, 0xE8, 0x26, 0xE9, 0x26, 0xEA, 0x26,\n\t0xEB, 0x26, 0xED, 0x26, 0xEE, 0x26, 0xEF, 0x26, 0xF0, 0x26, 0xF1, 0x26, 0xF2, 0x26, 0xF3, 0x26, 0xF4, 0x26, 0xF5, 0x26, 0xF6, 0x26, 0xF7, 0x26, 0xF8, 0x26, 0xFA, 0x26, 0xFB, 0x26, 0xFC, 0x26,\n\t0xFD, 0x26, 0xFE, 0x26, 0xFF, 0x26, 0x00, 0x27, 0x01, 0x27, 0x02, 0x27, 0x03, 0x27, 0x04, 0x27, 0x05, 0x27, 0x07, 0x27, 0x08, 0x27, 0x09, 0x27, 0x0A, 0x27, 0x0B, 0x27, 0x0C, 0x27, 0x0D, 0x27,\n\t0x0E, 0x27, 0x0F, 0x27, 0x10, 0x27, 0x11, 0x27, 0x12, 0x27, 0x14, 0x27, 0x15, 0x27, 0x16, 0x27, 0x17, 0x27, 0x18, 0x27, 0x19, 0x27, 0x1A, 0x27, 0x1B, 0x27, 0x1C, 0x27, 0x1D, 0x27, 0x1E, 0x27,\n\t0x1F, 0x27, 0x21, 0x27, 0x22, 0x27, 0x23, 0x27, 0x24, 0x27, 0x25, 0x27, 0x26, 0x27, 0x27, 0x27, 0x28, 0x27, 0x29, 0x27, 0x2A, 0x27, 0x2B, 0x27, 0x2C, 0x27, 0x2E, 0x27, 0x2F, 0x27, 0x30, 0x27,\n\t0x31, 0x27, 0x32, 0x27, 0x33, 0x27, 0x34, 0x27, 0x35, 0x27, 0x36, 0x27, 0x37, 0x27, 0x38, 0x27, 0x3A, 0x27, 0x3B, 0x27, 0x3C, 0x27, 0x3D, 0x27, 0x3E, 0x27, 0x3F, 0x27, 0x40, 0x27, 0x41, 0x27,\n\t0x42, 0x27, 0x43, 0x27, 0x44, 0x27, 0x45, 0x27, 0x47, 0x27, 0x48, 0x27, 0x49, 0x27, 0x4A, 0x27, 0x4B, 0x27, 0x4C, 0x27, 0x4D, 0x27, 0x4E, 0x27, 0x4F, 0x27, 0x50, 0x27, 0x51, 0x27, 0x53, 0x27,\n\t0x54, 0x27, 0x55, 0x27, 0x56, 0x27, 0x57, 0x27, 0x58, 0x27, 0x59, 0x27, 0x5A, 0x27, 0x5B, 0x27, 0x5C, 0x27, 0x5D, 0x27, 0x5E, 0x27, 0x60, 0x27, 0x61, 0x27, 0x62, 0x27, 0x63, 0x27, 0x64, 0x27,\n\t0x65, 0x27, 0x66, 0x27, 0x67, 0x27, 0x68, 0x27, 0x69, 0x27, 0x6A, 0x27, 0x6C, 0x27, 0x6D, 0x27, 0x6E, 0x27, 0x6F, 0x27, 0x70, 0x27, 0x71, 0x27, 0x72, 0x27, 0x73, 0x27, 0x74, 0x27, 0x75, 0x27,\n\t0x76, 0x27, 0x78, 0x27, 0x79, 0x27, 0x7A, 0x27, 0x7B, 0x27, 0x7C, 0x27, 0x7D, 0x27, 0x7E, 0x27, 0x7F, 0x27, 0x80, 0x27, 0x81, 0x27, 0x82, 0x27, 0x83, 0x27, 0x85, 0x27, 0x86, 0x27, 0x87, 0x27,\n\t0x88, 0x27, 0x89, 0x27, 0x8A, 0x27, 0x8B, 0x27, 0x8C, 0x27, 0x8D, 0x27, 0x8E, 0x27, 0x8F, 0x27, 0x91, 0x27, 0x92, 0x27, 0x93, 0x27, 0x94, 0x27, 0x95, 0x27, 0x96, 0x27, 0x97, 0x27, 0x98, 0x27,\n\t0x99, 0x27, 0x9A, 0x27, 0x9B, 0x27, 0x9D, 0x27, 0x9E, 0x27, 0x9F, 0x27, 0xA0, 0x27, 0xA1, 0x27, 0xA2, 0x27, 0xA3, 0x27, 0xA4, 0x27, 0xA5, 0x27, 0xA6, 0x27, 0xA7, 0x27, 0xA9, 0x27, 0xAA, 0x27,\n\t0xAB, 0x27, 0xAC, 0x27, 0xAD, 0x27, 0xAE, 0x27, 0xAF, 0x27, 0xB0, 0x27, 0xB1, 0x27, 0xB2, 0x27, 0xB3, 0x27, 0xB5, 0x27, 0xB6, 0x27, 0xB7, 0x27, 0xB8, 0x27, 0xB9, 0x27, 0xBA, 0x27, 0xBB, 0x27,\n\t0xBC, 0x27, 0xBD, 0x27, 0xBE, 0x27, 0xBF, 0x27, 0xC1, 0x27, 0xC2, 0x27, 0xC3, 0x27, 0xC4, 0x27, 0xC5, 0x27, 0xC6, 0x27, 0xC7, 0x27, 0xC8, 0x27, 0xC9, 0x27, 0xCA, 0x27, 0xCC, 0x27, 0xCD, 0x27,\n\t0xCE, 0x27, 0xCF, 0x27, 0xD0, 0x27, 0xD1, 0x27, 0xD2, 0x27, 0xD3, 0x27, 0xD4, 0x27, 0xD5, 0x27, 0xD6, 0x27, 0xD8, 0x27, 0xD9, 0x27, 0xDA, 0x27, 0xDB, 0x27, 0xDC, 0x27, 0xDD, 0x27, 0xDE, 0x27,\n\t0xDF, 0x27, 0xE0, 0x27, 0xE1, 0x27, 0xE2, 0x27, 0xE4, 0x27, 0xE5, 0x27, 0xE6, 0x27, 0xE7, 0x27, 0xE8, 0x27, 0xE9, 0x27, 0xEA, 0x27, 0xEB, 0x27, 0xEC, 0x27, 0xED, 0x27, 0xEE, 0x27, 0xF0, 0x27,\n\t0xF1, 0x27, 0xF2, 0x27, 0xF3, 0x27, 0xF4, 0x27, 0xF5, 0x27, 0xF6, 0x27, 0xF7, 0x27, 0xF8, 0x27, 0xF9, 0x27, 0xFB, 0x27, 0xFC, 0x27, 0xFD, 0x27, 0xFE, 0x27, 0xFF, 0x27, 0x00, 0x28, 0xAB, 0x52,\n\t0x00, 0xAF, 0x4E, 0x00, 0xB3, 0x4A, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x50, 0x04, 0x28, 0x04, 0x28, 0x05, 0x28, 0x05, 0x28, 0x06, 0x28, 0x07, 0x28, 0x07, 0x28, 0x08, 0x28, 0x08,\n\t0x28, 0x09, 0x28, 0x09, 0x28, 0x0A, 0x28, 0x0A, 0x28, 0x0B, 0x28, 0x0C, 0x28, 0x0C, 0x28, 0x0D, 0x28, 0x0D, 0x28, 0x0E, 0x28, 0x0E, 0x28, 0x0F, 0x28, 0x0F, 0x28, 0x10, 0x28, 0x10, 0x28, 0x11,\n\t0x28, 0x12, 0x28, 0x12, 0x28, 0x13, 0x28, 0x13, 0x28, 0x14, 0x28, 0x14, 0x28, 0x15, 0x28, 0x15, 0x28, 0x16, 0x28, 0x16, 0x28, 0x17, 0x28, 0x18, 0x28, 0x18, 0x28, 0x19, 0x28, 0x19, 0x28, 0x1A,\n\t0x28, 0x1A, 0x28, 0x1B, 0x28, 0x1B, 0x28, 0x1C, 0x28, 0x1D, 0x28, 0x1D, 0x28, 0x1E, 0x28, 0x1E, 0x28, 0x1F, 0x28, 0x1F, 0x28, 0x20, 0x28, 0x20, 0x28, 0x21, 0x28, 0x21, 0x28, 0x22, 0x28, 0x23,\n\t0x28, 0x23, 0x28, 0x24, 0x28, 0x24, 0x28, 0x25, 0x28, 0x25, 0x28, 0x26, 0x28, 0x26, 0x28, 0x27, 0x28, 0x27, 0x28, 0x28, 0x28, 0x29, 0x28, 0x29, 0x28, 0x2A, 0x28, 0x2A, 0x28, 0x2B, 0x28, 0x2B,\n\t0x28, 0x2C, 0x28, 0x2C, 0x28, 0x2D, 0x28, 0x2E, 0x28, 0x2E, 0x28, 0x2F, 0x28, 0x2F, 0x28, 0x30, 0x28, 0x30, 0x28, 0x31, 0x28, 0x31, 0x28, 0x32, 0x28, 0x32, 0x28, 0x33, 0x28, 0x34, 0x28, 0x35,\n\t0x28, 0x36, 0x28, 0x37, 0x28, 0x39, 0x28, 0x3A, 0x28, 0x3B, 0x28, 0x3C, 0x28, 0x3D, 0x28, 0x3E, 0x28, 0x3F, 0x28, 0x40, 0x28, 0x41, 0x28, 0x42, 0x28, 0x44, 0x28, 0x45, 0x28, 0x46, 0x28, 0x47,\n\t0x28, 0x48, 0x28, 0x49, 0x28, 0x4A, 0x28, 0x4B, 0x28, 0x4C, 0x28, 0x4D, 0x28, 0x4F, 0x28, 0x50, 0x28, 0x51, 0x28, 0x52, 0x28, 0x53, 0x28, 0x54, 0x28, 0x55, 0x28, 0x56, 0x28, 0x57, 0x28, 0x58,\n\t0x28, 0x5A, 0x28, 0x5B, 0x28, 0x5C, 0x28, 0x5D, 0x28, 0x5E, 0x28, 0x5F, 0x28, 0x60, 0x28, 0x61, 0x28, 0x62, 0x28, 0x64, 0x28, 0x65, 0x28, 0x66, 0x28, 0x67, 0x28, 0x68, 0x28, 0x69, 0x28, 0x6A,\n\t0x28, 0x6B, 0x28, 0x6C, 0x28, 0x6D, 0x28, 0x6F, 0x28, 0x70, 0x28, 0x71, 0x28, 0x72, 0x28, 0x73, 0x28, 0x74, 0x28, 0x75, 0x28, 0x76, 0x28, 0x77, 0x28, 0x78, 0x28, 0x7A, 0x28, 0x7B, 0x28, 0x7C,\n\t0x28, 0x7D, 0x28, 0x7E, 0x28, 0x7F, 0x28, 0x80, 0x28, 0x81, 0x28, 0x82, 0x28, 0x84, 0x28, 0x85, 0x28, 0x86, 0x28, 0x87, 0x28, 0x88, 0x28, 0x89, 0x28, 0x8A, 0x28, 0x8B, 0x28, 0x8C, 0x28, 0x8E,\n\t0x28, 0x8F, 0x28, 0x90, 0x28, 0x91, 0x28, 0x92, 0x28, 0x93, 0x28, 0x94, 0x28, 0x95, 0x28, 0x96, 0x28, 0x97, 0x28, 0x99, 0x28, 0x9A, 0x28, 0x9B, 0x28, 0x9C, 0x28, 0x9D, 0x28, 0x9E, 0x28, 0x9F,\n\t0x28, 0xA0, 0x28, 0xA1, 0x28, 0xA3, 0x28, 0xA4, 0x28, 0xA5, 0x28, 0xA6, 0x28, 0xA7, 0x28, 0xA8, 0x28, 0xA9, 0x28, 0xAA, 0x28, 0xAB, 0x28, 0xAD, 0x28, 0xAE, 0x28, 0xAF, 0x28, 0xB0, 0x28, 0xB1,\n\t0x28, 0xB2, 0x28, 0xB3, 0x28, 0xB4, 0x28, 0xB5, 0x28, 0xB7, 0x28, 0xB8, 0x28, 0xB9, 0x28, 0xBA, 0x28, 0xBB, 0x28, 0xBC, 0x28, 0xBD, 0x28, 0xBE, 0x28, 0xBF, 0x28, 0xC1, 0x28, 0xC2, 0x28, 0xC3,\n\t0x28, 0xC4, 0x28, 0xC5, 0x28, 0xC6, 0x28, 0xC7, 0x28, 0xC8, 0x28, 0xC9, 0x28, 0xCB, 0x28, 0xCC, 0x28, 0xCD, 0x28, 0xCE, 0x28, 0xCF, 0x28, 0xD0, 0x28, 0xD1, 0x28, 0xD2, 0x28, 0xD3, 0x28, 0xD5,\n\t0x28, 0xD6, 0x28, 0xD7, 0x28, 0xD8, 0x28, 0xD9, 0x28, 0xDA, 0x28, 0xDB, 0x28, 0xDC, 0x28, 0xDE, 0x28, 0xDF, 0x28, 0xE0, 0x28, 0xE1, 0x28, 0xE2, 0x28, 0xE3, 0x28, 0xE4, 0x28, 0xE5, 0x28, 0xE6,\n\t0x28, 0xE8, 0x28, 0xE9, 0x28, 0xEA, 0x28, 0xEB, 0x28, 0xEC, 0x28, 0xED, 0x28, 0xEE, 0x28, 0xEF, 0x28, 0xF0, 0x28, 0xF2, 0x28, 0xF3, 0x28, 0xF4, 0x28, 0xF5, 0x28, 0xF6, 0x28, 0xF7, 0x28, 0xF8,\n\t0x28, 0xF9, 0x28, 0xFB, 0x28, 0xFC, 0x28, 0xFD, 0x28, 0xFE, 0x28, 0xFF, 0x28, 0x00, 0x29, 0x01, 0x29, 0x02, 0x29, 0x03, 0x29, 0x05, 0x29, 0x06, 0x29, 0x07, 0x29, 0x08, 0x29, 0x09, 0x29, 0x0A,\n\t0x29, 0x0B, 0x29, 0x0C, 0x29, 0x0E, 0x29, 0x0F, 0x29, 0x10, 0x29, 0x11, 0x29, 0x12, 0x29, 0x13, 0x29, 0x14, 0x29, 0x15, 0x29, 0x16, 0x29, 0x18, 0x29, 0x19, 0x29, 0x1A, 0x29, 0x1B, 0x29, 0x1C,\n\t0x29, 0x1D, 0x29, 0x1E, 0x29, 0x1F, 0x29, 0x21, 0x29, 0x22, 0x29, 0x23, 0x29, 0x24, 0x29, 0x25, 0x29, 0x26, 0x29, 0x27, 0x29, 0x28, 0x29, 0x2A, 0x29, 0x2B, 0x29, 0x2C, 0x29, 0x2D, 0x29, 0x2E,\n\t0x29, 0x2F, 0x29, 0x30, 0x29, 0x31, 0x29, 0x33, 0x29, 0x34, 0x29, 0x35, 0x29, 0x36, 0x29, 0x37, 0x29, 0x38, 0x29, 0x39, 0x29, 0x3A, 0x29, 0x3B, 0x29, 0x3D, 0x29, 0x3E, 0x29, 0x3F, 0x29, 0x40,\n\t0x29, 0x41, 0x29, 0x42, 0x29, 0x43, 0x29, 0x44, 0x29, 0x46, 0x29, 0x47, 0x29, 0x48, 0x29, 0x49, 0x29, 0x4A, 0x29, 0x4B, 0x29, 0x4C, 0x29, 0x4D, 0x29, 0x4F, 0x29, 0x50, 0x29, 0x51, 0x29, 0x52,\n\t0x29, 0x53, 0x29, 0x54, 0x29, 0x55, 0x29, 0x56, 0x29, 0x58, 0x29, 0x59, 0x29, 0x5A, 0x29, 0x5B, 0x29, 0x5C, 0x29, 0x5D, 0x29, 0x5E, 0x29, 0x5F, 0x29, 0x61, 0x29, 0x62, 0x29, 0x63, 0x29, 0x64,\n\t0x29, 0x65, 0x29, 0x66, 0x29, 0x67, 0x29, 0x68, 0x29, 0x6A, 0x29, 0x6B, 0x29, 0x6C, 0x29, 0x6D, 0x29, 0x6E, 0x29, 0x6F, 0x29, 0x70, 0x29, 0x71, 0x29, 0x73, 0x29, 0x74, 0x29, 0x75, 0x29, 0x76,\n\t0x29, 0x77, 0x29, 0x78, 0x29, 0x79, 0x29, 0x7B, 0x29, 0x7C, 0x29, 0x7D, 0x29, 0x7E, 0x29, 0x7F, 0x29, 0x80, 0x29, 0x81, 0x29, 0x82, 0x29, 0x84, 0x29, 0x85, 0x29, 0x86, 0x29, 0x87, 0x29, 0x88,\n\t0x29, 0x89, 0x29, 0x8A, 0x29, 0x8B, 0x29, 0x8D, 0x29, 0x8E, 0x29, 0x8F, 0x29, 0x90, 0x29, 0x91, 0x29, 0x92, 0x29, 0x93, 0x29, 0x94, 0x29, 0x96, 0x29, 0x97, 0x29, 0x98, 0x29, 0x99, 0x29, 0x9A,\n\t0x29, 0x9B, 0x29, 0x9C, 0x29, 0x9E, 0x29, 0x9F, 0x29, 0xA0, 0x29, 0xA1, 0x29, 0xA2, 0x29, 0xA3, 0x29, 0xA4, 0x29, 0xA5, 0x29, 0xA7, 0x29, 0xA8, 0x29, 0xA9, 0x29, 0xAA, 0x29, 0xAB, 0x29, 0xAC,\n\t0x29, 0xAD, 0x29, 0xAE, 0x29, 0xB0, 0x29, 0xB1, 0x29, 0xB2, 0x29, 0xB3, 0x29, 0xB4, 0x29, 0xB5, 0x29, 0xB6, 0x29, 0xB8, 0x29, 0xB9, 0x29, 0xBA, 0x29, 0xBB, 0x29, 0xBC, 0x29, 0xBD, 0x29, 0xBE,\n\t0x29, 0xBF, 0x29, 0xC1, 0x29, 0xC2, 0x29, 0xC3, 0x29, 0xC4, 0x29, 0xC5, 0x29, 0xC6, 0x29, 0xC7, 0x29, 0xC9, 0x29, 0xCA, 0x29, 0xCB, 0x29, 0xCC, 0x29, 0xCD, 0x29, 0xCE, 0x29, 0xCF, 0x29, 0xD1,\n\t0x29, 0xD2, 0x29, 0xD3, 0x29, 0xD4, 0x29, 0xD5, 0x29, 0xD6, 0x29, 0xD7, 0x29, 0xD8, 0x29, 0xDA, 0x29, 0xDB, 0x29, 0xDC, 0x29, 0xDD, 0x29, 0xDE, 0x29, 0xDF, 0x29, 0xE0, 0x29, 0xE2, 0x29, 0xE3,\n\t0x29, 0xE4, 0x29, 0xE5, 0x29, 0xE6, 0x29, 0xE7, 0x29, 0xE8, 0x29, 0xEA, 0x29, 0xEB, 0x29, 0xEC, 0x29, 0xED, 0x29, 0xEE, 0x29, 0xEF, 0x29, 0xF0, 0x29, 0xF1, 0x29, 0xF3, 0x29, 0xF4, 0x29, 0xF5,\n\t0x29, 0xF6, 0x29, 0xF7, 0x29, 0xF8, 0x29, 0xF9, 0x29, 0xFB, 0x29, 0xFC, 0x29, 0xFD, 0x29, 0xFE, 0x29, 0xFF, 0x29, 0x00, 0x2A, 0x01, 0x2A, 0x03, 0x2A, 0x04, 0x2A, 0x05, 0x2A, 0x06, 0x2A, 0x07,\n\t0x2A, 0x08, 0x2A, 0x09, 0x2A, 0x0B, 0x2A, 0x0C, 0x2A, 0x0D, 0x2A, 0x0E, 0x2A, 0x0F, 0x2A, 0x10, 0x2A, 0x11, 0x2A, 0x13, 0x2A, 0x14, 0x2A, 0x15, 0x2A, 0x16, 0x2A, 0x17, 0x2A, 0x18, 0x2A, 0x19,\n\t0x2A, 0x1B, 0x2A, 0x1C, 0x2A, 0x1D, 0x2A, 0x1E, 0x2A, 0x1F, 0x2A, 0x20, 0x2A, 0x21, 0x2A, 0x23, 0x2A, 0x24, 0x2A, 0x25, 0x2A, 0x26, 0x2A, 0x27, 0x2A, 0x28, 0x2A, 0x29, 0x2A, 0x2B, 0x2A, 0x2C,\n\t0x2A, 0x2D, 0x2A, 0x2E, 0x2A, 0x2F, 0x2A, 0x30, 0x2A, 0x31, 0x2A, 0x33, 0x2A, 0x34, 0x2A, 0x35, 0x2A, 0x36, 0x2A, 0x37, 0x2A, 0x38, 0x2A, 0x39, 0x2A, 0x3B, 0x2A, 0x3C, 0x2A, 0x3D, 0x2A, 0x3E,\n\t0x2A, 0x3F, 0x2A, 0x40, 0x2A, 0x41, 0x2A, 0x43, 0x2A, 0x44, 0x2A, 0x45, 0x2A, 0x46, 0x2A, 0x47, 0x2A, 0x48, 0x2A, 0x49, 0x2A, 0x4B, 0x2A, 0x4C, 0x2A, 0x4D, 0x2A, 0x4E, 0x2A, 0x4F, 0x2A, 0x50,\n\t0x2A, 0x51, 0x2A, 0x53, 0x2A, 0x54, 0x2A, 0x55, 0x2A, 0x56, 0x2A, 0x57, 0x2A, 0x58, 0x2A, 0x59, 0x2A, 0x5B, 0x2A, 0x5C, 0x2A, 0x5D, 0x2A, 0x5E, 0x2A, 0x5F, 0x2A, 0x60, 0x2A, 0x61, 0x2A, 0x63,\n\t0x2A, 0x64, 0x2A, 0x65, 0x2A, 0x66, 0x2A, 0x67, 0x2A, 0x68, 0x2A, 0x6A, 0x2A, 0x6B, 0x2A, 0x6C, 0x2A, 0x6D, 0x2A, 0x6E, 0x2A, 0x6F, 0x2A, 0x70, 0x2A, 0x72, 0x2A, 0x73, 0x2A, 0x74, 0x2A, 0x75,\n\t0x2A, 0x76, 0x2A, 0x77, 0x2A, 0x78, 0x2A, 0x7A, 0x2A, 0x7B, 0x2A, 0x7C, 0x2A, 0x7D, 0x2A, 0x7E, 0x2A, 0x7F, 0x2A, 0x81, 0x2A, 0x82, 0x2A, 0x83, 0x2A, 0x84, 0x2A, 0x85, 0x2A, 0x86, 0x2A, 0x87,\n\t0x2A, 0x89, 0x2A, 0x8A, 0x2A, 0x8B, 0x2A, 0x8C, 0x2A, 0x8D, 0x2A, 0x8E, 0x2A, 0x8F, 0x2A, 0x91, 0x2A, 0x92, 0x2A, 0x93, 0x2A, 0x94, 0x2A, 0x95, 0x2A, 0x96, 0x2A, 0x98, 0x2A, 0x99, 0x2A, 0x9A,\n\t0x2A, 0x9B, 0x2A, 0x9C, 0x2A, 0x9D, 0x2A, 0x9E, 0x2A, 0xA0, 0x2A, 0xA1, 0x2A, 0xA2, 0x2A, 0xA3, 0x2A, 0xA4, 0x2A, 0xA5, 0x2A, 0xA7, 0x2A, 0xA8, 0x2A, 0xA9, 0x2A, 0xAA, 0x2A, 0xAB, 0x2A, 0xAC,\n\t0x2A, 0xAD, 0x2A, 0xAF, 0x2A, 0xB0, 0x2A, 0xB1, 0x2A, 0xB2, 0x2A, 0xB3, 0x2A, 0xB4, 0x2A, 0xB6, 0x2A, 0xB7, 0x2A, 0xB8, 0x2A, 0xB9, 0x2A, 0xBA, 0x2A, 0xBB, 0x2A, 0xBC, 0x2A, 0xBE, 0x2A, 0xBF,\n\t0x2A, 0xC0, 0x2A, 0xC1, 0x2A, 0xC2, 0x2A, 0xC3, 0x2A, 0xC5, 0x2A, 0xC6, 0x2A, 0xC7, 0x2A, 0xC8, 0x2A, 0xC9, 0x2A, 0xCA, 0x2A, 0xCB, 0x2A, 0xCD, 0x2A, 0xCE, 0x2A, 0xCF, 0x2A, 0xD0, 0x2A, 0xD1,\n\t0x2A, 0xD2, 0x2A, 0xD4, 0x2A, 0xD5, 0x2A, 0xD6, 0x2A, 0xD7, 0x2A, 0xD8, 0x2A, 0xD9, 0x2A, 0xDB, 0x2A, 0xDC, 0x2A, 0xDD, 0x2A, 0xDE, 0x2A, 0xDF, 0x2A, 0xE0, 0x2A, 0xE1, 0x2A, 0xE3, 0x2A, 0xE4,\n\t0x2A, 0xE5, 0x2A, 0xE6, 0x2A, 0xE7, 0x2A, 0xE8, 0x2A, 0xEA, 0x2A, 0xEB, 0x2A, 0xEC, 0x2A, 0xED, 0x2A, 0xEE, 0x2A, 0xEF, 0x2A, 0xF1, 0x2A, 0xF2, 0x2A, 0xF3, 0x2A, 0xF4, 0x2A, 0xF5, 0x2A, 0xF6,\n\t0x2A, 0xF8, 0x2A, 0xF9, 0x2A, 0xFA, 0x2A, 0xFB, 0x2A, 0xFC, 0x2A, 0xFD, 0x2A, 0xFE, 0x2A, 0x00, 0x2B, 0x01, 0x2B, 0x02, 0x2B, 0x03, 0x2B, 0x04, 0x2B, 0x05, 0x2B, 0x07, 0x2B, 0x08, 0x2B, 0x09,\n\t0x2B, 0x0A, 0x2B, 0x0B, 0x2B, 0x0C, 0x2B, 0x0E, 0x2B, 0x0F, 0x2B, 0x10, 0x2B, 0x11, 0x2B, 0x12, 0x2B, 0x13, 0x2B, 0x15, 0x2B, 0x16, 0x2B, 0x17, 0x2B, 0x18, 0x2B, 0x19, 0x2B, 0x1A, 0x2B, 0x1C,\n\t0x2B, 0x1D, 0x2B, 0x1E, 0x2B, 0x1F, 0x2B, 0x20, 0x2B, 0x21, 0x2B, 0x23, 0x2B, 0x24, 0x2B, 0x25, 0x2B, 0x26, 0x2B, 0x27, 0x2B, 0x28, 0x2B, 0x29, 0x2B, 0x2B, 0x2B, 0x2C, 0x2B, 0x2D, 0x2B, 0x2E,\n\t0x2B, 0x2F, 0x2B, 0x30, 0x2B, 0x32, 0x2B, 0x33, 0x2B, 0x34, 0x2B, 0x35, 0x2B, 0x36, 0x2B, 0x37, 0x2B, 0x39, 0x2B, 0x3A, 0x2B, 0x3B, 0x2B, 0x3C, 0x2B, 0x3D, 0x2B, 0x3E, 0x2B, 0x40, 0x2B, 0x41,\n\t0x2B, 0x42, 0x2B, 0x43, 0x2B, 0x44, 0x2B, 0x45, 0x2B, 0x47, 0x2B, 0x48, 0x2B, 0x49, 0x2B, 0x4A, 0x2B, 0x4B, 0x2B, 0x4C, 0x2B, 0x4E, 0x2B, 0x4F, 0x2B, 0x50, 0x2B, 0x51, 0x2B, 0x52, 0x2B, 0x53,\n\t0x2B, 0x55, 0x2B, 0x56, 0x2B, 0x57, 0x2B, 0x58, 0x2B, 0x59, 0x2B, 0x5A, 0x2B, 0x5C, 0x2B, 0x5D, 0x2B, 0x5E, 0x2B, 0x5F, 0x2B, 0x60, 0x2B, 0x62, 0x2B, 0x63, 0x2B, 0x64, 0x2B, 0x65, 0x2B, 0x66,\n\t0x2B, 0x67, 0x2B, 0x69, 0x2B, 0x6A, 0x2B, 0x6B, 0x2B, 0x6C, 0x2B, 0x6D, 0x2B, 0x6E, 0x2B, 0x70, 0x2B, 0x71, 0x2B, 0x72, 0x2B, 0x73, 0x2B, 0x74, 0x2B, 0x75, 0x2B, 0x77, 0x2B, 0x78, 0x2B, 0x79,\n\t0x2B, 0x7A, 0x2B, 0x7B, 0x2B, 0x7C, 0x2B, 0x7E, 0x2B, 0x7F, 0x2B, 0x80, 0x2B, 0x81, 0x2B, 0x82, 0x2B, 0x83, 0x2B, 0x85, 0x2B, 0x86, 0x2B, 0x87, 0x2B, 0x88, 0x2B, 0x89, 0x2B, 0x8A, 0x2B, 0x8C,\n\t0x2B, 0x8D, 0x2B, 0x8E, 0x2B, 0x8F, 0x2B, 0x90, 0x2B, 0x92, 0x2B, 0x93, 0x2B, 0x94, 0x2B, 0x95, 0x2B, 0x96, 0x2B, 0x97, 0x2B, 0x99, 0x2B, 0x9A, 0x2B, 0x9B, 0x2B, 0x9C, 0x2B, 0x9D, 0x2B, 0x9E,\n\t0x2B, 0xA0, 0x2B, 0xA1, 0x2B, 0xA2, 0x2B, 0xA3, 0x2B, 0xA4, 0x2B, 0xA5, 0x2B, 0xA7, 0x2B, 0xA8, 0x2B, 0xA9, 0x2B, 0xAA, 0x2B, 0xAB, 0x2B, 0xAD, 0x2B, 0xAE, 0x2B, 0xAF, 0x2B, 0xB0, 0x2B, 0xB1,\n\t0x2B, 0xB2, 0x2B, 0xB4, 0x2B, 0xB5, 0x2B, 0xB6, 0x2B, 0xB7, 0x2B, 0xB8, 0x2B, 0xB9, 0x2B, 0xBB, 0x2B, 0xBC, 0x2B, 0xBD, 0x2B, 0xBE, 0x2B, 0xBF, 0x2B, 0xC1, 0x2B, 0xC2, 0x2B, 0xC3, 0x2B, 0xC4,\n\t0x2B, 0xC5, 0x2B, 0xC6, 0x2B, 0xC8, 0x2B, 0xC9, 0x2B, 0xCA, 0x2B, 0xCB, 0x2B, 0xCC, 0x2B, 0xCD, 0x2B, 0xCF, 0x2B, 0xD0, 0x2B, 0xD1, 0x2B, 0xD2, 0x2B, 0xD3, 0x2B, 0xD5, 0x2B, 0xD6, 0x2B, 0xD7,\n\t0x2B, 0xD8, 0x2B, 0xD9, 0x2B, 0xDA, 0x2B, 0xDC, 0x2B, 0xDD, 0x2B, 0xDE, 0x2B, 0xDF, 0x2B, 0xE0, 0x2B, 0xE1, 0x2B, 0xE3, 0x2B, 0xE4, 0x2B, 0xE5, 0x2B, 0xE6, 0x2B, 0xE7, 0x2B, 0xE9, 0x2B, 0xEA,\n\t0x2B, 0xEB, 0x2B, 0xEC, 0x2B, 0xED, 0x2B, 0xEE, 0x2B, 0xF0, 0x2B, 0xF1, 0x2B, 0xF2, 0x2B, 0xF3, 0x2B, 0xF4, 0x2B, 0xF6, 0x2B, 0xF7, 0x2B, 0xF8, 0x2B, 0xF9, 0x2B, 0xFA, 0x2B, 0xFB, 0x2B, 0xFD,\n\t0x2B, 0xFE, 0x2B, 0xFF, 0x2B, 0x00, 0x2C, 0xFF, 0x59, 0x00, 0x03, 0x56, 0xF0, 0x6D, 0x03, 0x2C, 0x04, 0x2C, 0x04, 0x2C, 0x05, 0x2C, 0x05, 0x2C, 0x06, 0x2C, 0x07, 0x2C, 0x07, 0x2C, 0x08, 0x2C,\n\t0x08, 0x2C, 0x09, 0x2C, 0x0A, 0x2C, 0x0A, 0x2C, 0x0B, 0x2C, 0x0B, 0x2C, 0x0C, 0x2C, 0x0D, 0x2C, 0x0D, 0x2C, 0x0E, 0x2C, 0x0E, 0x2C, 0x0F, 0x2C, 0x0F, 0x2C, 0x10, 0x2C, 0x11, 0x2C, 0x11, 0x2C,\n\t0x12, 0x2C, 0x12, 0x2C, 0x13, 0x2C, 0x14, 0x2C, 0x14, 0x2C, 0x15, 0x2C, 0x15, 0x2C, 0x16, 0x2C, 0x17, 0x2C, 0x17, 0x2C, 0x18, 0x2C, 0x18, 0x2C, 0x19, 0x2C, 0x1A, 0x2C, 0x1A, 0x2C, 0x1B, 0x2C,\n\t0x1B, 0x2C, 0x1C, 0x2C, 0x1D, 0x2C, 0x1D, 0x2C, 0x1E, 0x2C, 0x1E, 0x2C, 0x1F, 0x2C, 0x1F, 0x2C, 0x20, 0x2C, 0x21, 0x2C, 0x21, 0x2C, 0x22, 0x2C, 0x22, 0x2C, 0x23, 0x2C, 0x24, 0x2C, 0x24, 0x2C,\n\t0x25, 0x2C, 0x25, 0x2C, 0x26, 0x2C, 0x27, 0x2C, 0x27, 0x2C, 0x4B, 0x07, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x77, 0x29, 0x2C, 0x2A, 0x2C, 0x2A, 0x2C, 0x2B, 0x2C, 0x2B, 0x2C, 0x2C, 0x2C,\n\t0x2D, 0x2C, 0x2D, 0x2C, 0x2E, 0x2C, 0x2E, 0x2C, 0x2F, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x31, 0x2C, 0x31, 0x2C, 0x32, 0x2C, 0x33, 0x2C, 0x33, 0x2C, 0x34, 0x2C, 0x34, 0x2C, 0x35, 0x2C, 0x35, 0x2C,\n\t0x36, 0x2C, 0x37, 0x2C, 0x37, 0x2C, 0x38, 0x2C, 0x38, 0x2C, 0x39, 0x2C, 0x3A, 0x2C, 0x3A, 0x2C, 0x3B, 0x2C, 0x3B, 0x2C, 0x3C, 0x2C, 0x3D, 0x2C, 0x3D, 0x2C, 0x3E, 0x2C, 0x3E, 0x2C, 0x3F, 0x2C,\n\t0x40, 0x2C, 0x40, 0x2C, 0x41, 0x2C, 0x41, 0x2C, 0x42, 0x2C, 0x43, 0x2C, 0x43, 0x2C, 0x44, 0x2C, 0x44, 0x2C, 0x45, 0x2C, 0x46, 0x2C, 0x46, 0x2C, 0x47, 0x2C, 0x47, 0x2C, 0x48, 0x2C, 0x49, 0x2C,\n\t0x49, 0x2C, 0x4A, 0x2C, 0x4A, 0x2C, 0x4B, 0x2C, 0x4C, 0x2C, 0x4C, 0x2C, 0x4D, 0x2C, 0x4D, 0x2C, 0x4E, 0x2C, 0x4F, 0x2C, 0x4F, 0x2C, 0x50, 0x2C, 0x50, 0x2C, 0x51, 0x2C, 0x52, 0x2C, 0x52, 0x2C,\n\t0x53, 0x2C, 0x53, 0x2C, 0x54, 0x2C, 0x55, 0x2C, 0x55, 0x2C, 0x56, 0x2C, 0x56, 0x2C, 0x57, 0x2C, 0x57, 0x2C, 0x58, 0x2C, 0x59, 0x2C, 0x59, 0x2C, 0x5A, 0x2C, 0x5A, 0x2C, 0x5B, 0x2C, 0x5C, 0x2C,\n\t0x5C, 0x2C, 0x5D, 0x2C, 0x5D, 0x2C, 0x5E, 0x2C, 0x5F, 0x2C, 0x5F, 0x2C, 0x60, 0x2C, 0x60, 0x2C, 0x61, 0x2C, 0x62, 0x2C, 0x62, 0x2C, 0x63, 0x2C, 0x63, 0x2C, 0x64, 0x2C, 0x65, 0x2C, 0x65, 0x2C,\n\t0x66, 0x2C, 0x67, 0x2C, 0x68, 0x2C, 0x69, 0x2C, 0x6B, 0x2C, 0x6C, 0x2C, 0x6D, 0x2C, 0x6E, 0x2C, 0x6F, 0x2C, 0x71, 0x2C, 0x72, 0x2C, 0x73, 0x2C, 0x74, 0x2C, 0x75, 0x2C, 0x77, 0x2C, 0x78, 0x2C,\n\t0x79, 0x2C, 0x7A, 0x2C, 0x7B, 0x2C, 0x7D, 0x2C, 0x7E, 0x2C, 0x7F, 0x2C, 0x80, 0x2C, 0x81, 0x2C, 0x83, 0x2C, 0x84, 0x2C, 0x85, 0x2C, 0x86, 0x2C, 0x87, 0x2C, 0x89, 0x2C, 0x8A, 0x2C, 0x8B, 0x2C,\n\t0x8C, 0x2C, 0x8E, 0x2C, 0x8F, 0x2C, 0x90, 0x2C, 0x91, 0x2C, 0x92, 0x2C, 0x94, 0x2C, 0x95, 0x2C, 0x96, 0x2C, 0x97, 0x2C, 0x98, 0x2C, 0x9A, 0x2C, 0x9B, 0x2C, 0x9C, 0x2C, 0x9D, 0x2C, 0x9E, 0x2C,\n\t0xA0, 0x2C, 0xA1, 0x2C, 0xA2, 0x2C, 0xA3, 0x2C, 0xA4, 0x2C, 0xA6, 0x2C, 0xA7, 0x2C, 0xA8, 0x2C, 0xA9, 0x2C, 0xAA, 0x2C, 0xAC, 0x2C, 0xAD, 0x2C, 0xAE, 0x2C, 0xAF, 0x2C, 0xB1, 0x2C, 0xB2, 0x2C,\n\t0xB3, 0x2C, 0xB4, 0x2C, 0xB5, 0x2C, 0xB7, 0x2C, 0xB8, 0x2C, 0xB9, 0x2C, 0xBA, 0x2C, 0xBB, 0x2C, 0xBD, 0x2C, 0xBE, 0x2C, 0xBF, 0x2C, 0xC0, 0x2C, 0xC1, 0x2C, 0xC3, 0x2C, 0xC4, 0x2C, 0xC5, 0x2C,\n\t0xC6, 0x2C, 0xC8, 0x2C, 0xC9, 0x2C, 0xCA, 0x2C, 0xCB, 0x2C, 0xCC, 0x2C, 0xCE, 0x2C, 0xCF, 0x2C, 0xD0, 0x2C, 0xD1, 0x2C, 0xD2, 0x2C, 0xD4, 0x2C, 0xD5, 0x2C, 0xD6, 0x2C, 0xD7, 0x2C, 0xD9, 0x2C,\n\t0xDA, 0x2C, 0xDB, 0x2C, 0xDC, 0x2C, 0xDD, 0x2C, 0xDF, 0x2C, 0xE0, 0x2C, 0xE1, 0x2C, 0xE2, 0x2C, 0xE4, 0x2C, 0xE5, 0x2C, 0xE6, 0x2C, 0xE7, 0x2C, 0xE8, 0x2C, 0xEA, 0x2C, 0xEB, 0x2C, 0xEC, 0x2C,\n\t0xED, 0x2C, 0xEF, 0x2C, 0xF0, 0x2C, 0xF1, 0x2C, 0xF2, 0x2C, 0xF3, 0x2C, 0xF5, 0x2C, 0xF6, 0x2C, 0xF7, 0x2C, 0xF8, 0x2C, 0xF9, 0x2C, 0xFB, 0x2C, 0xFC, 0x2C, 0xFD, 0x2C, 0xFE, 0x2C, 0x00, 0x2D,\n\t0x01, 0x2D, 0x02, 0x2D, 0x03, 0x2D, 0x05, 0x2D, 0x06, 0x2D, 0x07, 0x2D, 0x08, 0x2D, 0x09, 0x2D, 0x0B, 0x2D, 0x0C, 0x2D, 0x0D, 0x2D, 0x0E, 0x2D, 0x10, 0x2D, 0x11, 0x2D, 0x12, 0x2D, 0x13, 0x2D,\n\t0x14, 0x2D, 0x16, 0x2D, 0x17, 0x2D, 0x18, 0x2D, 0x19, 0x2D, 0x1B, 0x2D, 0x1C, 0x2D, 0x1D, 0x2D, 0x1E, 0x2D, 0x1F, 0x2D, 0x21, 0x2D, 0x22, 0x2D, 0x23, 0x2D, 0x24, 0x2D, 0x26, 0x2D, 0x27, 0x2D,\n\t0x28, 0x2D, 0x29, 0x2D, 0x2B, 0x2D, 0x2C, 0x2D, 0x2D, 0x2D, 0x2E, 0x2D, 0x2F, 0x2D, 0x31, 0x2D, 0x32, 0x2D, 0x33, 0x2D, 0x34, 0x2D, 0x36, 0x2D, 0x37, 0x2D, 0x38, 0x2D, 0x39, 0x2D, 0x3B, 0x2D,\n\t0x3C, 0x2D, 0x3D, 0x2D, 0x3E, 0x2D, 0x3F, 0x2D, 0x41, 0x2D, 0x42, 0x2D, 0x43, 0x2D, 0x44, 0x2D, 0x46, 0x2D, 0x47, 0x2D, 0x48, 0x2D, 0x49, 0x2D, 0x4B, 0x2D, 0x4C, 0x2D, 0x4D, 0x2D, 0x4E, 0x2D,\n\t0x4F, 0x2D, 0x51, 0x2D, 0x52, 0x2D, 0x53, 0x2D, 0x54, 0x2D, 0x56, 0x2D, 0x57, 0x2D, 0x58, 0x2D, 0x59, 0x2D, 0x5B, 0x2D, 0x5C, 0x2D, 0x5D, 0x2D, 0x5E, 0x2D, 0x60, 0x2D, 0x61, 0x2D, 0x62, 0x2D,\n\t0x63, 0x2D, 0x65, 0x2D, 0x66, 0x2D, 0x67, 0x2D, 0x68, 0x2D, 0x69, 0x2D, 0x6B, 0x2D, 0x6C, 0x2D, 0x6D, 0x2D, 0x6E, 0x2D, 0x70, 0x2D, 0x71, 0x2D, 0x72, 0x2D, 0x73, 0x2D, 0x75, 0x2D, 0x76, 0x2D,\n\t0x77, 0x2D, 0x78, 0x2D, 0x7A, 0x2D, 0x7B, 0x2D, 0x7C, 0x2D, 0x7D, 0x2D, 0x7F, 0x2D, 0x80, 0x2D, 0x81, 0x2D, 0x82, 0x2D, 0x84, 0x2D, 0x85, 0x2D, 0x86, 0x2D, 0x87, 0x2D, 0x89, 0x2D, 0x8A, 0x2D,\n\t0x8B, 0x2D, 0x8C, 0x2D, 0x8D, 0x2D, 0x8F, 0x2D, 0x90, 0x2D, 0x91, 0x2D, 0x92, 0x2D, 0x94, 0x2D, 0x95, 0x2D, 0x96, 0x2D, 0x97, 0x2D, 0x99, 0x2D, 0x9A, 0x2D, 0x9B, 0x2D, 0x9C, 0x2D, 0x9E, 0x2D,\n\t0x9F, 0x2D, 0xA0, 0x2D, 0xA1, 0x2D, 0xA3, 0x2D, 0xA4, 0x2D, 0xA5, 0x2D, 0xA6, 0x2D, 0xA8, 0x2D, 0xA9, 0x2D, 0xAA, 0x2D, 0xAB, 0x2D, 0xAD, 0x2D, 0xAE, 0x2D, 0xAF, 0x2D, 0xB0, 0x2D, 0xB2, 0x2D,\n\t0xB3, 0x2D, 0xB4, 0x2D, 0xB5, 0x2D, 0xB7, 0x2D, 0xB8, 0x2D, 0xB9, 0x2D, 0xBA, 0x2D, 0xBC, 0x2D, 0xBD, 0x2D, 0xBE, 0x2D, 0xBF, 0x2D, 0xC1, 0x2D, 0xC2, 0x2D, 0xC3, 0x2D, 0xC4, 0x2D, 0xC6, 0x2D,\n\t0xC7, 0x2D, 0xC8, 0x2D, 0xC9, 0x2D, 0xCB, 0x2D, 0xCC, 0x2D, 0xCD, 0x2D, 0xCE, 0x2D, 0xD0, 0x2D, 0xD1, 0x2D, 0xD2, 0x2D, 0xD3, 0x2D, 0xD5, 0x2D, 0xD6, 0x2D, 0xD7, 0x2D, 0xD9, 0x2D, 0xDA, 0x2D,\n\t0xDB, 0x2D, 0xDC, 0x2D, 0xDE, 0x2D, 0xDF, 0x2D, 0xE0, 0x2D, 0xE1, 0x2D, 0xE3, 0x2D, 0xE4, 0x2D, 0xE5, 0x2D, 0xE6, 0x2D, 0xE8, 0x2D, 0xE9, 0x2D, 0xEA, 0x2D, 0xEB, 0x2D, 0xED, 0x2D, 0xEE, 0x2D,\n\t0xEF, 0x2D, 0xF0, 0x2D, 0xF2, 0x2D, 0xF3, 0x2D, 0xF4, 0x2D, 0xF5, 0x2D, 0xF7, 0x2D, 0xF8, 0x2D, 0xF9, 0x2D, 0xFA, 0x2D, 0xFC, 0x2D, 0xFD, 0x2D, 0xFE, 0x2D, 0x00, 0x2E, 0x01, 0x2E, 0x02, 0x2E,\n\t0x03, 0x2E, 0x05, 0x2E, 0x06, 0x2E, 0x07, 0x2E, 0x08, 0x2E, 0x0A, 0x2E, 0x0B, 0x2E, 0x0C, 0x2E, 0x0D, 0x2E, 0x0F, 0x2E, 0x10, 0x2E, 0x11, 0x2E, 0x12, 0x2E, 0x14, 0x2E, 0x15, 0x2E, 0x16, 0x2E,\n\t0x18, 0x2E, 0x19, 0x2E, 0x1A, 0x2E, 0x1B, 0x2E, 0x1D, 0x2E, 0x1E, 0x2E, 0x1F, 0x2E, 0x20, 0x2E, 0x22, 0x2E, 0x23, 0x2E, 0x24, 0x2E, 0x25, 0x2E, 0x27, 0x2E, 0x28, 0x2E, 0x29, 0x2E, 0x2B, 0x2E,\n\t0x2C, 0x2E, 0x2D, 0x2E, 0x2E, 0x2E, 0x30, 0x2E, 0x31, 0x2E, 0x32, 0x2E, 0x33, 0x2E, 0x35, 0x2E, 0x36, 0x2E, 0x37, 0x2E, 0x39, 0x2E, 0x3A, 0x2E, 0x3B, 0x2E, 0x3C, 0x2E, 0x3E, 0x2E, 0x3F, 0x2E,\n\t0x40, 0x2E, 0x41, 0x2E, 0x43, 0x2E, 0x44, 0x2E, 0x45, 0x2E, 0x46, 0x2E, 0x48, 0x2E, 0x49, 0x2E, 0x4A, 0x2E, 0x4C, 0x2E, 0x4D, 0x2E, 0x4E, 0x2E, 0x4F, 0x2E, 0x51, 0x2E, 0x52, 0x2E, 0x53, 0x2E,\n\t0x55, 0x2E, 0x56, 0x2E, 0x57, 0x2E, 0x58, 0x2E, 0x5A, 0x2E, 0x5B, 0x2E, 0x5C, 0x2E, 0x5D, 0x2E, 0x5F, 0x2E, 0x60, 0x2E, 0x61, 0x2E, 0x63, 0x2E, 0x64, 0x2E, 0x65, 0x2E, 0x66, 0x2E, 0x68, 0x2E,\n\t0x69, 0x2E, 0x6A, 0x2E, 0x6B, 0x2E, 0x6D, 0x2E, 0x6E, 0x2E, 0x6F, 0x2E, 0x71, 0x2E, 0x72, 0x2E, 0x73, 0x2E, 0x74, 0x2E, 0x76, 0x2E, 0x77, 0x2E, 0x78, 0x2E, 0x7A, 0x2E, 0x7B, 0x2E, 0x7C, 0x2E,\n\t0x7D, 0x2E, 0x7F, 0x2E, 0x80, 0x2E, 0x81, 0x2E, 0x83, 0x2E, 0x84, 0x2E, 0x85, 0x2E, 0x86, 0x2E, 0x88, 0x2E, 0x89, 0x2E, 0x8A, 0x2E, 0x8B, 0x2E, 0x8D, 0x2E, 0x8E, 0x2E, 0x8F, 0x2E, 0x91, 0x2E,\n\t0x92, 0x2E, 0x93, 0x2E, 0x94, 0x2E, 0x96, 0x2E, 0x97, 0x2E, 0x98, 0x2E, 0x9A, 0x2E, 0x9B, 0x2E, 0x9C, 0x2E, 0x9D, 0x2E, 0x9F, 0x2E, 0xA0, 0x2E, 0xA1, 0x2E, 0xA3, 0x2E, 0xA4, 0x2E, 0xA5, 0x2E,\n\t0xA6, 0x2E, 0xA8, 0x2E, 0xA9, 0x2E, 0xAA, 0x2E, 0xAC, 0x2E, 0xAD, 0x2E, 0xAE, 0x2E, 0xAF, 0x2E, 0xB1, 0x2E, 0xB2, 0x2E, 0xB3, 0x2E, 0xB5, 0x2E, 0xB6, 0x2E, 0xB7, 0x2E, 0xB8, 0x2E, 0xBA, 0x2E,\n\t0xBB, 0x2E, 0xBC, 0x2E, 0xBE, 0x2E, 0xBF, 0x2E, 0xC0, 0x2E, 0xC2, 0x2E, 0xC3, 0x2E, 0xC4, 0x2E, 0xC5, 0x2E, 0xC7, 0x2E, 0xC8, 0x2E, 0xC9, 0x2E, 0xCB, 0x2E, 0xCC, 0x2E, 0xCD, 0x2E, 0xCE, 0x2E,\n\t0xD0, 0x2E, 0xD1, 0x2E, 0xD2, 0x2E, 0xD4, 0x2E, 0xD5, 0x2E, 0xD6, 0x2E, 0xD7, 0x2E, 0xD9, 0x2E, 0xDA, 0x2E, 0xDB, 0x2E, 0xDD, 0x2E, 0xDE, 0x2E, 0xDF, 0x2E, 0xE1, 0x2E, 0xE2, 0x2E, 0xE3, 0x2E,\n\t0xE4, 0x2E, 0xE6, 0x2E, 0xE7, 0x2E, 0xE8, 0x2E, 0xEA, 0x2E, 0xEB, 0x2E, 0xEC, 0x2E, 0xEE, 0x2E, 0xEF, 0x2E, 0xF0, 0x2E, 0xF1, 0x2E, 0xF3, 0x2E, 0xF4, 0x2E, 0xF5, 0x2E, 0xF7, 0x2E, 0xF8, 0x2E,\n\t0xF9, 0x2E, 0xFA, 0x2E, 0xFC, 0x2E, 0xFD, 0x2E, 0xFE, 0x2E, 0x00, 0x2F, 0x01, 0x2F, 0x02, 0x2F, 0x04, 0x2F, 0x05, 0x2F, 0x06, 0x2F, 0x07, 0x2F, 0x09, 0x2F, 0x0A, 0x2F, 0x0B, 0x2F, 0x0D, 0x2F,\n\t0x0E, 0x2F, 0x0F, 0x2F, 0x11, 0x2F, 0x12, 0x2F, 0x13, 0x2F, 0x15, 0x2F, 0x16, 0x2F, 0x17, 0x2F, 0x18, 0x2F, 0x1A, 0x2F, 0x1B, 0x2F, 0x1C, 0x2F, 0x1E, 0x2F, 0x1F, 0x2F, 0x20, 0x2F, 0x22, 0x2F,\n\t0x23, 0x2F, 0x24, 0x2F, 0x25, 0x2F, 0x27, 0x2F, 0x28, 0x2F, 0x29, 0x2F, 0x2B, 0x2F, 0x2C, 0x2F, 0x2D, 0x2F, 0x2F, 0x2F, 0x30, 0x2F, 0x31, 0x2F, 0x33, 0x2F, 0x34, 0x2F, 0x35, 0x2F, 0x36, 0x2F,\n\t0x38, 0x2F, 0x39, 0x2F, 0x3A, 0x2F, 0x3C, 0x2F, 0x3D, 0x2F, 0x3E, 0x2F, 0x40, 0x2F, 0x41, 0x2F, 0x42, 0x2F, 0x44, 0x2F, 0x45, 0x2F, 0x46, 0x2F, 0x47, 0x2F, 0x49, 0x2F, 0x4A, 0x2F, 0x4B, 0x2F,\n\t0x4D, 0x2F, 0x4E, 0x2F, 0x4F, 0x2F, 0x51, 0x2F, 0x52, 0x2F, 0x53, 0x2F, 0x55, 0x2F, 0x56, 0x2F, 0x57, 0x2F, 0x59, 0x2F, 0x5A, 0x2F, 0x5B, 0x2F, 0x5C, 0x2F, 0x5E, 0x2F, 0x5F, 0x2F, 0x60, 0x2F,\n\t0x62, 0x2F, 0x63, 0x2F, 0x64, 0x2F, 0x66, 0x2F, 0x67, 0x2F, 0x68, 0x2F, 0x6A, 0x2F, 0x6B, 0x2F, 0x6C, 0x2F, 0x6E, 0x2F, 0x6F, 0x2F, 0x70, 0x2F, 0x71, 0x2F, 0x73, 0x2F, 0x74, 0x2F, 0x75, 0x2F,\n\t0x77, 0x2F, 0x78, 0x2F, 0x79, 0x2F, 0x7B, 0x2F, 0x7C, 0x2F, 0x7D, 0x2F, 0x7F, 0x2F, 0x80, 0x2F, 0x81, 0x2F, 0x83, 0x2F, 0x84, 0x2F, 0x85, 0x2F, 0x87, 0x2F, 0x88, 0x2F, 0x89, 0x2F, 0x8B, 0x2F,\n\t0x8C, 0x2F, 0x8D, 0x2F, 0x8E, 0x2F, 0x90, 0x2F, 0x91, 0x2F, 0x92, 0x2F, 0x94, 0x2F, 0x95, 0x2F, 0x96, 0x2F, 0x98, 0x2F, 0x99, 0x2F, 0x9A, 0x2F, 0x9C, 0x2F, 0x9D, 0x2F, 0x9E, 0x2F, 0xA0, 0x2F,\n\t0xA1, 0x2F, 0xA2, 0x2F, 0xA4, 0x2F, 0xA5, 0x2F, 0xA6, 0x2F, 0xA8, 0x2F, 0xA9, 0x2F, 0xAA, 0x2F, 0xAC, 0x2F, 0xAD, 0x2F, 0xAE, 0x2F, 0xB0, 0x2F, 0xB1, 0x2F, 0xB2, 0x2F, 0xB4, 0x2F, 0xB5, 0x2F,\n\t0xB6, 0x2F, 0xB7, 0x2F, 0xB9, 0x2F, 0xBA, 0x2F, 0xBB, 0x2F, 0xBD, 0x2F, 0xBE, 0x2F, 0xBF, 0x2F, 0xC1, 0x2F, 0xC2, 0x2F, 0xC3, 0x2F, 0xC5, 0x2F, 0xC6, 0x2F, 0xC7, 0x2F, 0xC9, 0x2F, 0xCA, 0x2F,\n\t0xCB, 0x2F, 0xCD, 0x2F, 0xCE, 0x2F, 0xCF, 0x2F, 0xD1, 0x2F, 0xD2, 0x2F, 0xD3, 0x2F, 0xD5, 0x2F, 0xD6, 0x2F, 0xD7, 0x2F, 0xD9, 0x2F, 0xDA, 0x2F, 0xDB, 0x2F, 0xDD, 0x2F, 0xDE, 0x2F, 0xDF, 0x2F,\n\t0xE1, 0x2F, 0xE2, 0x2F, 0xE3, 0x2F, 0xE5, 0x2F, 0xE6, 0x2F, 0xE7, 0x2F, 0xE9, 0x2F, 0xEA, 0x2F, 0xEB, 0x2F, 0xED, 0x2F, 0xEE, 0x2F, 0xEF, 0x2F, 0xF1, 0x2F, 0xF2, 0x2F, 0xF3, 0x2F, 0xF5, 0x2F,\n\t0xF6, 0x2F, 0xF7, 0x2F, 0xF9, 0x2F, 0xFA, 0x2F, 0xFB, 0x2F, 0xFD, 0x2F, 0xFE, 0x2F, 0xFF, 0x2F, 0x00, 0x30, 0x01, 0x30, 0xF7, 0x5C, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x93, 0x03, 0x30,\n\t0x04, 0x30, 0x04, 0x30, 0x05, 0x30, 0x06, 0x30, 0x06, 0x30, 0x07, 0x30, 0x08, 0x30, 0x08, 0x30, 0x09, 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0B, 0x30, 0x0C, 0x30, 0x0C, 0x30, 0x0D, 0x30, 0x0E, 0x30,\n\t0x0E, 0x30, 0x0F, 0x30, 0x10, 0x30, 0x10, 0x30, 0x11, 0x30, 0x12, 0x30, 0x12, 0x30, 0x13, 0x30, 0x14, 0x30, 0x14, 0x30, 0x15, 0x30, 0x16, 0x30, 0x16, 0x30, 0x17, 0x30, 0x18, 0x30, 0x18, 0x30,\n\t0x19, 0x30, 0x1A, 0x30, 0x1A, 0x30, 0x1B, 0x30, 0x1C, 0x30, 0x1C, 0x30, 0x1D, 0x30, 0x1E, 0x30, 0x1F, 0x30, 0x1F, 0x30, 0x20, 0x30, 0x21, 0x30, 0x21, 0x30, 0x22, 0x30, 0x23, 0x30, 0x23, 0x30,\n\t0x24, 0x30, 0x25, 0x30, 0x25, 0x30, 0x26, 0x30, 0x27, 0x30, 0x27, 0x30, 0x28, 0x30, 0x29, 0x30, 0x29, 0x30, 0x2A, 0x30, 0x2B, 0x30, 0x2B, 0x30, 0x2C, 0x30, 0x2D, 0x30, 0x2D, 0x30, 0x2E, 0x30,\n\t0x2F, 0x30, 0x2F, 0x30, 0x30, 0x30, 0x31, 0x30, 0x31, 0x30, 0x32, 0x30, 0x33, 0x30, 0x33, 0x30, 0x34, 0x30, 0x35, 0x30, 0x35, 0x30, 0x36, 0x30, 0x37, 0x30, 0x37, 0x30, 0x38, 0x30, 0x39, 0x30,\n\t0x39, 0x30, 0x3A, 0x30, 0x3B, 0x30, 0x3B, 0x30, 0x3C, 0x30, 0x3D, 0x30, 0x3E, 0x30, 0x3E, 0x30, 0x3F, 0x30, 0x40, 0x30, 0x40, 0x30, 0x41, 0x30, 0x42, 0x30, 0x42, 0x30, 0x43, 0x30, 0x44, 0x30,\n\t0x44, 0x30, 0x45, 0x30, 0x46, 0x30, 0x46, 0x30, 0x47, 0x30, 0x48, 0x30, 0x48, 0x30, 0x49, 0x30, 0x4A, 0x30, 0x4A, 0x30, 0x4B, 0x30, 0x4C, 0x30, 0x4C, 0x30, 0x4D, 0x30, 0x4E, 0x30, 0x4E, 0x30,\n\t0x4F, 0x30, 0x50, 0x30, 0x51, 0x30, 0x51, 0x30, 0x52, 0x30, 0x53, 0x30, 0x53, 0x30, 0x54, 0x30, 0x55, 0x30, 0x55, 0x30, 0x56, 0x30, 0x57, 0x30, 0x57, 0x30, 0x58, 0x30, 0x59, 0x30, 0x59, 0x30,\n\t0x5A, 0x30, 0x5B, 0x30, 0x5B, 0x30, 0x5C, 0x30, 0x5D, 0x30, 0x5D, 0x30, 0x5E, 0x30, 0x5F, 0x30, 0x5F, 0x30, 0x60, 0x30, 0x61, 0x30, 0x62, 0x30, 0x62, 0x30, 0x63, 0x30, 0x64, 0x30, 0x64, 0x30,\n\t0x65, 0x30, 0x66, 0x30, 0x66, 0x30, 0x67, 0x30, 0x68, 0x30, 0x68, 0x30, 0x69, 0x30, 0x6A, 0x30, 0x6A, 0x30, 0x6B, 0x30, 0x6C, 0x30, 0x6C, 0x30, 0x6D, 0x30, 0x6E, 0x30, 0x6E, 0x30, 0x6F, 0x30,\n\t0x70, 0x30, 0x71, 0x30, 0x71, 0x30, 0x72, 0x30, 0x73, 0x30, 0x73, 0x30, 0x74, 0x30, 0x75, 0x30, 0x75, 0x30, 0x76, 0x30, 0x77, 0x30, 0x77, 0x30, 0x78, 0x30, 0x79, 0x30, 0x79, 0x30, 0x7A, 0x30,\n\t0x7B, 0x30, 0x7B, 0x30, 0x7C, 0x30, 0x7D, 0x30, 0x7E, 0x30, 0x7E, 0x30, 0x7F, 0x30, 0x80, 0x30, 0x80, 0x30, 0x81, 0x30, 0x82, 0x30, 0x82, 0x30, 0x83, 0x30, 0x84, 0x30, 0x84, 0x30, 0x85, 0x30,\n\t0x86, 0x30, 0x86, 0x30, 0x87, 0x30, 0x88, 0x30, 0x88, 0x30, 0x89, 0x30, 0x8A, 0x30, 0x8B, 0x30, 0x8B, 0x30, 0x8C, 0x30, 0x8D, 0x30, 0x8D, 0x30, 0x8E, 0x30, 0x8F, 0x30, 0x8F, 0x30, 0x90, 0x30,\n\t0x91, 0x30, 0x91, 0x30, 0x92, 0x30, 0x93, 0x30, 0x93, 0x30, 0x94, 0x30, 0x95, 0x30, 0x96, 0x30, 0x96, 0x30, 0x97, 0x30, 0x98, 0x30, 0x98, 0x30, 0x99, 0x30, 0x9A, 0x30, 0x9A, 0x30, 0x9B, 0x30,\n\t0x9C, 0x30, 0x9C, 0x30, 0x9D, 0x30, 0x9E, 0x30, 0x9F, 0x30, 0x9F, 0x30, 0xA0, 0x30, 0xA1, 0x30, 0xA1, 0x30, 0xA2, 0x30, 0xA3, 0x30, 0xA3, 0x30, 0xA4, 0x30, 0xA5, 0x30, 0xA5, 0x30, 0xA6, 0x30,\n\t0xA7, 0x30, 0xA7, 0x30, 0xA8, 0x30, 0xA9, 0x30, 0xAA, 0x30, 0xAA, 0x30, 0xAB, 0x30, 0xAC, 0x30, 0xAC, 0x30, 0xAD, 0x30, 0xAE, 0x30, 0xAE, 0x30, 0xAF, 0x30, 0xB0, 0x30, 0xB0, 0x30, 0xB1, 0x30,\n\t0xB2, 0x30, 0xB3, 0x30, 0xB3, 0x30, 0xB4, 0x30, 0xB5, 0x30, 0xB5, 0x30, 0xB6, 0x30, 0xB7, 0x30, 0xB7, 0x30, 0xB8, 0x30, 0xB9, 0x30, 0xB9, 0x30, 0xBA, 0x30, 0xBB, 0x30, 0xBC, 0x30, 0xBC, 0x30,\n\t0xBD, 0x30, 0xBE, 0x30, 0xBE, 0x30, 0xBF, 0x30, 0xC0, 0x30, 0xC0, 0x30, 0xC1, 0x30, 0xC2, 0x30, 0xC2, 0x30, 0xC3, 0x30, 0xC4, 0x30, 0xC5, 0x30, 0xC5, 0x30, 0xC6, 0x30, 0xC7, 0x30, 0xC7, 0x30,\n\t0xC8, 0x30, 0xC9, 0x30, 0xC9, 0x30, 0xCA, 0x30, 0xCC, 0x30, 0xCD, 0x30, 0xCE, 0x30, 0xD0, 0x30, 0xD1, 0x30, 0xD2, 0x30, 0xD4, 0x30, 0xD5, 0x30, 0xD7, 0x30, 0xD8, 0x30, 0xD9, 0x30, 0xDB, 0x30,\n\t0xDC, 0x30, 0xDE, 0x30, 0xDF, 0x30, 0xE0, 0x30, 0xE2, 0x30, 0xE3, 0x30, 0xE5, 0x30, 0xE6, 0x30, 0xE7, 0x30, 0xE9, 0x30, 0xEA, 0x30, 0xEC, 0x30, 0xED, 0x30, 0xEE, 0x30, 0xF0, 0x30, 0xF1, 0x30,\n\t0xF3, 0x30, 0xF4, 0x30, 0xF5, 0x30, 0xF7, 0x30, 0xF8, 0x30, 0xFA, 0x30, 0xFB, 0x30, 0xFC, 0x30, 0xFE, 0x30, 0xFF, 0x30, 0x01, 0x31, 0x02, 0x31, 0x03, 0x31, 0x05, 0x31, 0x06, 0x31, 0x08, 0x31,\n\t0x09, 0x31, 0x0A, 0x31, 0x0C, 0x31, 0x0D, 0x31, 0x0F, 0x31, 0x10, 0x31, 0x12, 0x31, 0x13, 0x31, 0x14, 0x31, 0x16, 0x31, 0x17, 0x31, 0x19, 0x31, 0x1A, 0x31, 0x1B, 0x31, 0x1D, 0x31, 0x1E, 0x31,\n\t0x20, 0x31, 0x21, 0x31, 0x22, 0x31, 0x24, 0x31, 0x25, 0x31, 0x27, 0x31, 0x28, 0x31, 0x2A, 0x31, 0x2B, 0x31, 0x2C, 0x31, 0x2E, 0x31, 0x2F, 0x31, 0x31, 0x31, 0x32, 0x31, 0x33, 0x31, 0x35, 0x31,\n\t0x36, 0x31, 0x38, 0x31, 0x39, 0x31, 0x3B, 0x31, 0x3C, 0x31, 0x3D, 0x31, 0x3F, 0x31, 0x40, 0x31, 0x42, 0x31, 0x43, 0x31, 0x45, 0x31, 0x46, 0x31, 0x47, 0x31, 0x49, 0x31, 0x4A, 0x31, 0x4C, 0x31,\n\t0x4D, 0x31, 0x4E, 0x31, 0x50, 0x31, 0x51, 0x31, 0x53, 0x31, 0x54, 0x31, 0x56, 0x31, 0x57, 0x31, 0x58, 0x31, 0x5A, 0x31, 0x5B, 0x31, 0x5D, 0x31, 0x5E, 0x31, 0x60, 0x31, 0x61, 0x31, 0x62, 0x31,\n\t0x64, 0x31, 0x65, 0x31, 0x67, 0x31, 0x68, 0x31, 0x6A, 0x31, 0x6B, 0x31, 0x6C, 0x31, 0x6E, 0x31, 0x6F, 0x31, 0x71, 0x31, 0x72, 0x31, 0x74, 0x31, 0x75, 0x31, 0x77, 0x31, 0x78, 0x31, 0x79, 0x31,\n\t0x7B, 0x31, 0x7C, 0x31, 0x7E, 0x31, 0x7F, 0x31, 0x81, 0x31, 0x82, 0x31, 0x83, 0x31, 0x85, 0x31, 0x86, 0x31, 0x88, 0x31, 0x89, 0x31, 0x8B, 0x31, 0x8C, 0x31, 0x8E, 0x31, 0x8F, 0x31, 0x90, 0x31,\n\t0x92, 0x31, 0x93, 0x31, 0x95, 0x31, 0x96, 0x31, 0x98, 0x31, 0x99, 0x31, 0x9B, 0x31, 0x9C, 0x31, 0x9D, 0x31, 0x9F, 0x31, 0xA0, 0x31, 0xA2, 0x31, 0xA3, 0x31, 0xA5, 0x31, 0xA6, 0x31, 0xA8, 0x31,\n\t0xA9, 0x31, 0xAA, 0x31, 0xAC, 0x31, 0xAD, 0x31, 0xAF, 0x31, 0xB0, 0x31, 0xB2, 0x31, 0xB3, 0x31, 0xB5, 0x31, 0xB6, 0x31, 0xB7, 0x31, 0xB9, 0x31, 0xBA, 0x31, 0xBC, 0x31, 0xBD, 0x31, 0xBF, 0x31,\n\t0xC0, 0x31, 0xC2, 0x31, 0xC3, 0x31, 0xC5, 0x31, 0xC6, 0x31, 0xC7, 0x31, 0xC9, 0x31, 0xCA, 0x31, 0xCC, 0x31, 0xCD, 0x31, 0xCF, 0x31, 0xD0, 0x31, 0xD2, 0x31, 0xD3, 0x31, 0xD5, 0x31, 0xD6, 0x31,\n\t0xD7, 0x31, 0xD9, 0x31, 0xDA, 0x31, 0xDC, 0x31, 0xDD, 0x31, 0xDF, 0x31, 0xE0, 0x31, 0xE2, 0x31, 0xE3, 0x31, 0xE5, 0x31, 0xE6, 0x31, 0xE8, 0x31, 0xE9, 0x31, 0xEA, 0x31, 0xEC, 0x31, 0xED, 0x31,\n\t0xEF, 0x31, 0xF0, 0x31, 0xF2, 0x31, 0xF3, 0x31, 0xF5, 0x31, 0xF6, 0x31, 0xF8, 0x31, 0xF9, 0x31, 0xFB, 0x31, 0xFC, 0x31, 0xFE, 0x31, 0xFF, 0x31, 0x00, 0x32, 0x02, 0x32, 0x03, 0x32, 0x05, 0x32,\n\t0x06, 0x32, 0x08, 0x32, 0x09, 0x32, 0x0B, 0x32, 0x0C, 0x32, 0x0E, 0x32, 0x0F, 0x32, 0x11, 0x32, 0x12, 0x32, 0x14, 0x32, 0x15, 0x32, 0x17, 0x32, 0x18, 0x32, 0x19, 0x32, 0x1B, 0x32, 0x1C, 0x32,\n\t0x1E, 0x32, 0x1F, 0x32, 0x21, 0x32, 0x22, 0x32, 0x24, 0x32, 0x25, 0x32, 0x27, 0x32, 0x28, 0x32, 0x2A, 0x32, 0x2B, 0x32, 0x2D, 0x32, 0x2E, 0x32, 0x30, 0x32, 0x31, 0x32, 0x33, 0x32, 0x34, 0x32,\n\t0x36, 0x32, 0x37, 0x32, 0x39, 0x32, 0x3A, 0x32, 0x3B, 0x32, 0x3D, 0x32, 0x3E, 0x32, 0x40, 0x32, 0x41, 0x32, 0x43, 0x32, 0x44, 0x32, 0x46, 0x32, 0x47, 0x32, 0x49, 0x32, 0x4A, 0x32, 0x4C, 0x32,\n\t0x4D, 0x32, 0x4F, 0x32, 0x50, 0x32, 0x52, 0x32, 0x53, 0x32, 0x55, 0x32, 0x56, 0x32, 0x58, 0x32, 0x59, 0x32, 0x5B, 0x32, 0x5C, 0x32, 0x5E, 0x32, 0x5F, 0x32, 0x61, 0x32, 0x62, 0x32, 0x64, 0x32,\n\t0x65, 0x32, 0x67, 0x32, 0x68, 0x32, 0x6A, 0x32, 0x6B, 0x32, 0x6D, 0x32, 0x6E, 0x32, 0x70, 0x32, 0x71, 0x32, 0x73, 0x32, 0x74, 0x32, 0x76, 0x32, 0x77, 0x32, 0x79, 0x32, 0x7A, 0x32, 0x7C, 0x32,\n\t0x7D, 0x32, 0x7F, 0x32, 0x80, 0x32, 0x82, 0x32, 0x83, 0x32, 0x85, 0x32, 0x86, 0x32, 0x88, 0x32, 0x89, 0x32, 0x8B, 0x32, 0x8C, 0x32, 0x8E, 0x32, 0x8F, 0x32, 0x91, 0x32, 0x92, 0x32, 0x94, 0x32,\n\t0x95, 0x32, 0x97, 0x32, 0x98, 0x32, 0x9A, 0x32, 0x9B, 0x32, 0x9D, 0x32, 0x9E, 0x32, 0xA0, 0x32, 0xA1, 0x32, 0xA3, 0x32, 0xA4, 0x32, 0xA6, 0x32, 0xA7, 0x32, 0xA9, 0x32, 0xAA, 0x32, 0xAC, 0x32,\n\t0xAD, 0x32, 0xAF, 0x32, 0xB0, 0x32, 0xB2, 0x32, 0xB3, 0x32, 0xB5, 0x32, 0xB6, 0x32, 0xB8, 0x32, 0xB9, 0x32, 0xBB, 0x32, 0xBC, 0x32, 0xBE, 0x32, 0xBF, 0x32, 0xC1, 0x32, 0xC2, 0x32, 0xC4, 0x32,\n\t0xC5, 0x32, 0xC7, 0x32, 0xC8, 0x32, 0xCA, 0x32, 0xCB, 0x32, 0xCD, 0x32, 0xCF, 0x32, 0xD0, 0x32, 0xD2, 0x32, 0xD3, 0x32, 0xD5, 0x32, 0xD6, 0x32, 0xD8, 0x32, 0xD9, 0x32, 0xDB, 0x32, 0xDC, 0x32,\n\t0xDE, 0x32, 0xDF, 0x32, 0xE1, 0x32, 0xE2, 0x32, 0xE4, 0x32, 0xE5, 0x32, 0xE7, 0x32, 0xE8, 0x32, 0xEA, 0x32, 0xEB, 0x32, 0xED, 0x32, 0xEE, 0x32, 0xF0, 0x32, 0xF2, 0x32, 0xF3, 0x32, 0xF5, 0x32,\n\t0xF6, 0x32, 0xF8, 0x32, 0xF9, 0x32, 0xFB, 0x32, 0xFC, 0x32, 0xFE, 0x32, 0xFF, 0x32, 0x01, 0x33, 0x02, 0x33, 0x04, 0x33, 0x05, 0x33, 0x07, 0x33, 0x08, 0x33, 0x0A, 0x33, 0x0B, 0x33, 0x0D, 0x33,\n\t0x0F, 0x33, 0x10, 0x33, 0x12, 0x33, 0x13, 0x33, 0x15, 0x33, 0x16, 0x33, 0x18, 0x33, 0x19, 0x33, 0x1B, 0x33, 0x1C, 0x33, 0x1E, 0x33, 0x1F, 0x33, 0x21, 0x33, 0x22, 0x33, 0x24, 0x33, 0x26, 0x33,\n\t0x27, 0x33, 0x29, 0x33, 0x2A, 0x33, 0x2C, 0x33, 0x2D, 0x33, 0x2F, 0x33, 0x30, 0x33, 0x32, 0x33, 0x33, 0x33, 0x35, 0x33, 0x36, 0x33, 0x38, 0x33, 0x3A, 0x33, 0x3B, 0x33, 0x3D, 0x33, 0x3E, 0x33,\n\t0x40, 0x33, 0x41, 0x33, 0x43, 0x33, 0x44, 0x33, 0x46, 0x33, 0x47, 0x33, 0x49, 0x33, 0x4B, 0x33, 0x4C, 0x33, 0x4E, 0x33, 0x4F, 0x33, 0x51, 0x33, 0x52, 0x33, 0x54, 0x33, 0x55, 0x33, 0x57, 0x33,\n\t0x58, 0x33, 0x5A, 0x33, 0x5C, 0x33, 0x5D, 0x33, 0x5F, 0x33, 0x60, 0x33, 0x62, 0x33, 0x63, 0x33, 0x65, 0x33, 0x66, 0x33, 0x68, 0x33, 0x6A, 0x33, 0x6B, 0x33, 0x6D, 0x33, 0x6E, 0x33, 0x70, 0x33,\n\t0x71, 0x33, 0x73, 0x33, 0x74, 0x33, 0x76, 0x33, 0x78, 0x33, 0x79, 0x33, 0x7B, 0x33, 0x7C, 0x33, 0x7E, 0x33, 0x7F, 0x33, 0x81, 0x33, 0x82, 0x33, 0x84, 0x33, 0x86, 0x33, 0x87, 0x33, 0x89, 0x33,\n\t0x8A, 0x33, 0x8C, 0x33, 0x8D, 0x33, 0x8F, 0x33, 0x90, 0x33, 0x92, 0x33, 0x94, 0x33, 0x95, 0x33, 0x97, 0x33, 0x98, 0x33, 0x9A, 0x33, 0x9B, 0x33, 0x9D, 0x33, 0x9F, 0x33, 0xA0, 0x33, 0xA2, 0x33,\n\t0xA3, 0x33, 0xA5, 0x33, 0xA6, 0x33, 0xA8, 0x33, 0xA9, 0x33, 0xAB, 0x33, 0xAD, 0x33, 0xAE, 0x33, 0xB0, 0x33, 0xB1, 0x33, 0xB3, 0x33, 0xB4, 0x33, 0xB6, 0x33, 0xB8, 0x33, 0xB9, 0x33, 0xBB, 0x33,\n\t0xBC, 0x33, 0xBE, 0x33, 0xBF, 0x33, 0xC1, 0x33, 0xC3, 0x33, 0xC4, 0x33, 0xC6, 0x33, 0xC7, 0x33, 0xC9, 0x33, 0xCA, 0x33, 0xCC, 0x33, 0xCE, 0x33, 0xCF, 0x33, 0xD1, 0x33, 0xD2, 0x33, 0xD4, 0x33,\n\t0xD5, 0x33, 0xD7, 0x33, 0xD9, 0x33, 0xDA, 0x33, 0xDC, 0x33, 0xDD, 0x33, 0xDF, 0x33, 0xE0, 0x33, 0xE2, 0x33, 0xE4, 0x33, 0xE5, 0x33, 0xE7, 0x33, 0xE8, 0x33, 0xEA, 0x33, 0xEC, 0x33, 0xED, 0x33,\n\t0xEF, 0x33, 0xF0, 0x33, 0xF2, 0x33, 0xF3, 0x33, 0xF5, 0x33, 0xF7, 0x33, 0xF8, 0x33, 0xFA, 0x33, 0xFB, 0x33, 0xFD, 0x33, 0xFF, 0x33, 0x00, 0x34, 0x01, 0x34, 0x87, 0x63, 0xF0, 0xFF, 0xFF, 0x63,\n\t0x03, 0x34, 0x04, 0x34, 0x05, 0x34, 0x06, 0x34, 0x06, 0x34, 0x07, 0x34, 0x08, 0x34, 0x09, 0x34, 0x0A, 0x34, 0x0A, 0x34, 0x0B, 0x34, 0x0C, 0x34, 0x0D, 0x34, 0x0E, 0x34, 0x0E, 0x34, 0x0F, 0x34,\n\t0x10, 0x34, 0x11, 0x34, 0x12, 0x34, 0x12, 0x34, 0x13, 0x34, 0x14, 0x34, 0x15, 0x34, 0x16, 0x34, 0x16, 0x34, 0x17, 0x34, 0x18, 0x34, 0x19, 0x34, 0x19, 0x34, 0x1A, 0x34, 0x1B, 0x34, 0x1C, 0x34,\n\t0x1D, 0x34, 0x1D, 0x34, 0x1E, 0x34, 0x1F, 0x34, 0x20, 0x34, 0x21, 0x34, 0x21, 0x34, 0x22, 0x34, 0x23, 0x34, 0x24, 0x34, 0x25, 0x34, 0x25, 0x34, 0x26, 0x34, 0x27, 0x34, 0x28, 0x34, 0x29, 0x34,\n\t0x29, 0x34, 0x2A, 0x34, 0x2B, 0x34, 0x2C, 0x34, 0x2D, 0x34, 0x2D, 0x34, 0x2E, 0x34, 0x2F, 0x34, 0x30, 0x34, 0x31, 0x34, 0x31, 0x34, 0x32, 0x34, 0x33, 0x34, 0x34, 0x34, 0x35, 0x34, 0x36, 0x34,\n\t0x36, 0x34, 0x37, 0x34, 0x38, 0x34, 0x39, 0x34, 0x3A, 0x34, 0x3A, 0x34, 0x3B, 0x34, 0x3C, 0x34, 0x3D, 0x34, 0x3E, 0x34, 0x3E, 0x34, 0x3F, 0x34, 0x40, 0x34, 0x41, 0x34, 0x42, 0x34, 0x42, 0x34,\n\t0x43, 0x34, 0x44, 0x34, 0x45, 0x34, 0x46, 0x34, 0x46, 0x34, 0x47, 0x34, 0x48, 0x34, 0x49, 0x34, 0x4A, 0x34, 0x4A, 0x34, 0x4B, 0x34, 0x4C, 0x34, 0x4D, 0x34, 0x4E, 0x34, 0x4E, 0x34, 0x4F, 0x34,\n\t0x50, 0x34, 0x51, 0x34, 0x52, 0x34, 0x53, 0x34, 0x53, 0x34, 0x54, 0x34, 0x55, 0x34, 0x56, 0x34, 0x57, 0x34, 0x57, 0x34, 0x58, 0x34, 0x59, 0x34, 0x5A, 0x34, 0x5B, 0x34, 0x5B, 0x34, 0x5C, 0x34,\n\t0x5D, 0x34, 0x5E, 0x34, 0x5F, 0x34, 0x60, 0x34, 0x60, 0x34, 0x61, 0x34, 0x62, 0x34, 0x63, 0x34, 0x64, 0x34, 0x64, 0x34, 0x65, 0x34, 0x66, 0x34, 0x67, 0x34, 0x68, 0x34, 0x68, 0x34, 0x69, 0x34,\n\t0x6A, 0x34, 0x6B, 0x34, 0x6C, 0x34, 0x6D, 0x34, 0x6D, 0x34, 0x6E, 0x34, 0x6F, 0x34, 0x70, 0x34, 0x71, 0x34, 0x71, 0x34, 0x72, 0x34, 0x73, 0x34, 0x74, 0x34, 0x75, 0x34, 0x75, 0x34, 0x76, 0x34,\n\t0x77, 0x34, 0x78, 0x34, 0x79, 0x34, 0x7A, 0x34, 0x7A, 0x34, 0x7B, 0x34, 0x7C, 0x34, 0x7D, 0x34, 0x7E, 0x34, 0x7E, 0x34, 0x7F, 0x34, 0x80, 0x34, 0x81, 0x34, 0x82, 0x34, 0x83, 0x34, 0x83, 0x34,\n\t0x84, 0x34, 0x85, 0x34, 0x86, 0x34, 0x87, 0x34, 0x87, 0x34, 0x88, 0x34, 0x89, 0x34, 0x8A, 0x34, 0x8B, 0x34, 0x8C, 0x34, 0x8C, 0x34, 0x8D, 0x34, 0x8E, 0x34, 0x8F, 0x34, 0x90, 0x34, 0x91, 0x34,\n\t0x91, 0x34, 0x92, 0x34, 0x93, 0x34, 0x94, 0x34, 0x95, 0x34, 0x95, 0x34, 0x96, 0x34, 0x97, 0x34, 0x98, 0x34, 0x99, 0x34, 0x9A, 0x34, 0x9A, 0x34, 0x9B, 0x34, 0x9C, 0x34, 0x9D, 0x34, 0x9E, 0x34,\n\t0x9F, 0x34, 0x9F, 0x34, 0xA0, 0x34, 0xA1, 0x34, 0xA2, 0x34, 0xA3, 0x34, 0xA3, 0x34, 0xA4, 0x34, 0xA5, 0x34, 0xA6, 0x34, 0xA7, 0x34, 0xA8, 0x34, 0xA8, 0x34, 0xA9, 0x34, 0xAA, 0x34, 0xAB, 0x34,\n\t0xAC, 0x34, 0xAD, 0x34, 0xAD, 0x34, 0xAE, 0x34, 0xAF, 0x34, 0xB0, 0x34, 0xB1, 0x34, 0xB2, 0x34, 0xB2, 0x34, 0xB3, 0x34, 0xB4, 0x34, 0xB5, 0x34, 0xB6, 0x34, 0xB6, 0x34, 0xB7, 0x34, 0xB8, 0x34,\n\t0xB9, 0x34, 0xBA, 0x34, 0xBB, 0x34, 0xBB, 0x34, 0xBC, 0x34, 0xBD, 0x34, 0xBE, 0x34, 0xBF, 0x34, 0xC0, 0x34, 0xC0, 0x34, 0xC1, 0x34, 0xC2, 0x34, 0xC3, 0x34, 0xC4, 0x34, 0xC5, 0x34, 0xC5, 0x34,\n\t0xC6, 0x34, 0xC7, 0x34, 0xC8, 0x34, 0xC9, 0x34, 0xCA, 0x34, 0xCA, 0x34, 0xCB, 0x34, 0xCC, 0x34, 0xCD, 0x34, 0xCE, 0x34, 0xCF, 0x34, 0xCF, 0x34, 0xD0, 0x34, 0xD1, 0x34, 0xD2, 0x34, 0xD3, 0x34,\n\t0xD4, 0x34, 0xD4, 0x34, 0xD5, 0x34, 0xD6, 0x34, 0xD7, 0x34, 0xD8, 0x34, 0xD9, 0x34, 0xD9, 0x34, 0xDA, 0x34, 0xDB, 0x34, 0xDC, 0x34, 0xDD, 0x34, 0xDE, 0x34, 0xDE, 0x34, 0xDF, 0x34, 0xE0, 0x34,\n\t0xE1, 0x34, 0xE2, 0x34, 0xE3, 0x34, 0xE3, 0x34, 0xE4, 0x34, 0xE5, 0x34, 0xE6, 0x34, 0xE7, 0x34, 0xE8, 0x34, 0xE9, 0x34, 0xE9, 0x34, 0xEA, 0x34, 0xEB, 0x34, 0xEC, 0x34, 0xED, 0x34, 0xEE, 0x34,\n\t0xEE, 0x34, 0xEF, 0x34, 0xF0, 0x34, 0xF1, 0x34, 0xF2, 0x34, 0xF3, 0x34, 0xF3, 0x34, 0xF4, 0x34, 0xF5, 0x34, 0xF6, 0x34, 0xF7, 0x34, 0xF8, 0x34, 0xF8, 0x34, 0xF9, 0x34, 0xFA, 0x34, 0xFB, 0x34,\n\t0xFC, 0x34, 0xFD, 0x34, 0xFE, 0x34, 0xFE, 0x34, 0xFF, 0x34, 0x00, 0x35, 0x01, 0x35, 0x02, 0x35, 0xF7, 0x61, 0xF0, 0x57, 0x04, 0x35, 0x05, 0x35, 0x06, 0x35, 0x07, 0x35, 0x08, 0x35, 0x08, 0x35,\n\t0x09, 0x35, 0x0A, 0x35, 0x0B, 0x35, 0x0C, 0x35, 0x0D, 0x35, 0x0E, 0x35, 0x0E, 0x35, 0x0F, 0x35, 0x10, 0x35, 0x11, 0x35, 0x12, 0x35, 0x13, 0x35, 0x13, 0x35, 0x14, 0x35, 0x15, 0x35, 0x16, 0x35,\n\t0x17, 0x35, 0x18, 0x35, 0x19, 0x35, 0x19, 0x35, 0x1A, 0x35, 0x1B, 0x35, 0x1C, 0x35, 0x1D, 0x35, 0x1E, 0x35, 0x1F, 0x35, 0x1F, 0x35, 0x20, 0x35, 0x21, 0x35, 0x22, 0x35, 0x23, 0x35, 0x24, 0x35,\n\t0x24, 0x35, 0x25, 0x35, 0x26, 0x35, 0x27, 0x35, 0x28, 0x35, 0x29, 0x35, 0x2A, 0x35, 0x2A, 0x35, 0x2B, 0x35, 0x2C, 0x35, 0x2D, 0x35, 0x2E, 0x35, 0x2F, 0x35, 0xE7, 0x08, 0x80, 0x31, 0x35, 0x32,\n\t0x35, 0x33, 0x35, 0x34, 0x35, 0x01, 0x00, 0xF0, 0xFF, 0xFF, 0xFF, 0xBE, 0x36, 0x35, 0x37, 0x35, 0x38, 0x35, 0x39, 0x35, 0x3A, 0x35, 0x3B, 0x35, 0x3B, 0x35, 0x3C, 0x35, 0x3D, 0x35, 0x3E, 0x35,\n\t0x3F, 0x35, 0x40, 0x35, 0x41, 0x35, 0x41, 0x35, 0x42, 0x35, 0x43, 0x35, 0x44, 0x35, 0x45, 0x35, 0x46, 0x35, 0x47, 0x35, 0x47, 0x35, 0x48, 0x35, 0x49, 0x35, 0x4A, 0x35, 0x4B, 0x35, 0x4C, 0x35,\n\t0x4D, 0x35, 0x4D, 0x35, 0x4E, 0x35, 0x4F, 0x35, 0x50, 0x35, 0x51, 0x35, 0x52, 0x35, 0x53, 0x35, 0x53, 0x35, 0x54, 0x35, 0x55, 0x35, 0x56, 0x35, 0x57, 0x35, 0x58, 0x35, 0x59, 0x35, 0x59, 0x35,\n\t0x5A, 0x35, 0x5B, 0x35, 0x5C, 0x35, 0x5D, 0x35, 0x5E, 0x35, 0x5F, 0x35, 0x5F, 0x35, 0x60, 0x35, 0x61, 0x35, 0x62, 0x35, 0x63, 0x35, 0x64, 0x35, 0x65, 0x35, 0x65, 0x35, 0x66, 0x35, 0x67, 0x35,\n\t0x68, 0x35, 0x69, 0x35, 0x6A, 0x35, 0x6B, 0x35, 0x6C, 0x35, 0x6C, 0x35, 0x6D, 0x35, 0x6E, 0x35, 0x6F, 0x35, 0x70, 0x35, 0x71, 0x35, 0x72, 0x35, 0x72, 0x35, 0x73, 0x35, 0x74, 0x35, 0x75, 0x35,\n\t0x76, 0x35, 0x77, 0x35, 0x78, 0x35, 0x78, 0x35, 0x79, 0x35, 0x7A, 0x35, 0x7B, 0x35, 0x7C, 0x35, 0x7D, 0x35, 0x7E, 0x35, 0x7F, 0x35, 0x7F, 0x35, 0x80, 0x35, 0x81, 0x35, 0x82, 0x35, 0x83, 0x35,\n\t0x84, 0x35, 0x85, 0x35, 0x85, 0x35, 0x86, 0x35, 0x87, 0x35, 0x88, 0x35, 0x8A, 0x35, 0x8C, 0x35, 0x8D, 0x35, 0x8F, 0x35, 0x91, 0x35, 0x92, 0x35, 0x94, 0x35, 0x96, 0x35, 0x98, 0x35, 0x99, 0x35,\n\t0x9B, 0x35, 0x9D, 0x35, 0x9F, 0x35, 0xA0, 0x35, 0xA2, 0x35, 0xA4, 0x35, 0xA6, 0x35, 0xA7, 0x35, 0xA9, 0x35, 0xAB, 0x35, 0xAD, 0x35, 0xAE, 0x35, 0xB0, 0x35, 0xB2, 0x35, 0xB4, 0x35, 0xB5, 0x35,\n\t0xB7, 0x35, 0xB9, 0x35, 0xBB, 0x35, 0xBC, 0x35, 0xBE, 0x35, 0xC0, 0x35, 0xC2, 0x35, 0xC3, 0x35, 0xC5, 0x35, 0xC7, 0x35, 0xC9, 0x35, 0xCA, 0x35, 0xCC, 0x35, 0xCE, 0x35, 0xD0, 0x35, 0xD1, 0x35,\n\t0xD3, 0x35, 0xD5, 0x35, 0xD7, 0x35, 0xD8, 0x35, 0xDA, 0x35, 0xDC, 0x35, 0xDE, 0x35, 0xE0, 0x35, 0xE1, 0x35, 0xE3, 0x35, 0xE5, 0x35, 0xE7, 0x35, 0xE8, 0x35, 0xEA, 0x35, 0xEC, 0x35, 0xEE, 0x35,\n\t0xEF, 0x35, 0xF1, 0x35, 0xF3, 0x35, 0xF5, 0x35, 0xF7, 0x35, 0xF8, 0x35, 0xFA, 0x35, 0xFC, 0x35, 0xFE, 0x35, 0xFF, 0x35, 0x01, 0x36, 0x03, 0x36, 0x05, 0x36, 0x06, 0x36, 0x08, 0x36, 0x0A, 0x36,\n\t0x0C, 0x36, 0x0E, 0x36, 0x0F, 0x36, 0x11, 0x36, 0x13, 0x36, 0x15, 0x36, 0x17, 0x36, 0x18, 0x36, 0x1A, 0x36, 0x1C, 0x36, 0x1E, 0x36, 0x1F, 0x36, 0x21, 0x36, 0x23, 0x36, 0x25, 0x36, 0x27, 0x36,\n\t0x28, 0x36, 0x2A, 0x36, 0x2C, 0x36, 0x2E, 0x36, 0x30, 0x36, 0x31, 0x36, 0x33, 0x36, 0x35, 0x36, 0x37, 0x36, 0x38, 0x36, 0x3A, 0x36, 0x3C, 0x36, 0x3E, 0x36, 0x40, 0x36, 0x41, 0x36, 0x43, 0x36,\n\t0x45, 0x36, 0x47, 0x36, 0x49, 0x36, 0x4A, 0x36, 0x4C, 0x36, 0x4E, 0x36, 0x50, 0x36, 0x52, 0x36, 0x53, 0x36, 0x55, 0x36, 0x57, 0x36, 0x59, 0x36, 0x5B, 0x36, 0x5C, 0x36, 0x5E, 0x36, 0x60, 0x36,\n\t0x62, 0x36, 0x64, 0x36, 0x65, 0x36, 0x67, 0x36, 0x69, 0x36, 0x6B, 0x36, 0x6D, 0x36, 0x6F, 0x36, 0x70, 0x36, 0x72, 0x36, 0x74, 0x36, 0x76, 0x36, 0x78, 0x36, 0x79, 0x36, 0x7B, 0x36, 0x7D, 0x36,\n\t0x7F, 0x36, 0x81, 0x36, 0x82, 0x36, 0x84, 0x36, 0x86, 0x36, 0x88, 0x36, 0x8A, 0x36, 0x8C, 0x36, 0x8D, 0x36, 0x8F, 0x36, 0x91, 0x36, 0x93, 0x36, 0x95, 0x36, 0x96, 0x36, 0x98, 0x36, 0x9A, 0x36,\n\t0x9C, 0x36, 0x9E, 0x36, 0xA0, 0x36, 0xA1, 0x36, 0xA3, 0x36, 0xA5, 0x36, 0xA7, 0x36, 0xA9, 0x36, 0xAB, 0x36, 0xAC, 0x36, 0xAE, 0x36, 0xB0, 0x36, 0xB2, 0x36, 0xB4, 0x36, 0xB6, 0x36, 0xB7, 0x36,\n\t0xB9, 0x36, 0xBB, 0x36, 0xBD, 0x36, 0xBF, 0x36, 0xC1, 0x36, 0xC2, 0x36, 0xC4, 0x36, 0xC6, 0x36, 0xC8, 0x36, 0xCA, 0x36, 0xCC, 0x36, 0xCD, 0x36, 0xCF, 0x36, 0xD1, 0x36, 0xD3, 0x36, 0xD5, 0x36,\n\t0xD7, 0x36, 0xD8, 0x36, 0xDA, 0x36, 0xDC, 0x36, 0xDE, 0x36, 0xE0, 0x36, 0xE2, 0x36, 0xE4, 0x36, 0xE5, 0x36, 0xE7, 0x36, 0xE9, 0x36, 0xEB, 0x36, 0xED, 0x36, 0xEF, 0x36, 0xF0, 0x36, 0xF2, 0x36,\n\t0xF4, 0x36, 0xF6, 0x36, 0xF8, 0x36, 0xFA, 0x36, 0xFC, 0x36, 0xFD, 0x36, 0xFF, 0x36, 0x01, 0x37, 0x03, 0x37, 0x05, 0x37, 0x07, 0x37, 0x09, 0x37, 0x0A, 0x37, 0x0C, 0x37, 0x0E, 0x37, 0x10, 0x37,\n\t0x12, 0x37, 0x14, 0x37, 0x16, 0x37, 0x17, 0x37, 0x19, 0x37, 0x1B, 0x37, 0x1D, 0x37, 0x1F, 0x37, 0x21, 0x37, 0x23, 0x37, 0x24, 0x37, 0x26, 0x37, 0x28, 0x37, 0x2A, 0x37, 0x2C, 0x37, 0x2E, 0x37,\n\t0x30, 0x37, 0x31, 0x37, 0x33, 0x37, 0x35, 0x37, 0x37, 0x37, 0x39, 0x37, 0x3B, 0x37, 0x3D, 0x37, 0x3F, 0x37, 0x40, 0x37, 0x42, 0x37, 0x44, 0x37, 0x46, 0x37, 0x48, 0x37, 0x4A, 0x37, 0x4C, 0x37,\n\t0x4E, 0x37, 0x4F, 0x37, 0x51, 0x37, 0x53, 0x37, 0x55, 0x37, 0x57, 0x37, 0x59, 0x37, 0x5B, 0x37, 0x5D, 0x37, 0x5E, 0x37, 0x60, 0x37, 0x62, 0x37, 0x64, 0x37, 0x66, 0x37, 0x68, 0x37, 0x6A, 0x37,\n\t0x6C, 0x37, 0x6D, 0x37, 0x6F, 0x37, 0x71, 0x37, 0x73, 0x37, 0x75, 0x37, 0x77, 0x37, 0x79, 0x37, 0x7B, 0x37, 0x7D, 0x37, 0x7E, 0x37, 0x80, 0x37, 0x82, 0x37, 0x84, 0x37, 0x86, 0x37, 0x88, 0x37,\n\t0x8A, 0x37, 0x8C, 0x37, 0x8E, 0x37, 0x8F, 0x37, 0x91, 0x37, 0x93, 0x37, 0x95, 0x37, 0x97, 0x37, 0x99, 0x37, 0x9B, 0x37, 0x9D, 0x37, 0x9F, 0x37, 0xA1, 0x37, 0xA2, 0x37, 0xA4, 0x37, 0xA6, 0x37,\n\t0xA8, 0x37, 0xAA, 0x37, 0xAC, 0x37, 0xAE, 0x37, 0xB0, 0x37, 0xB2, 0x37, 0xB4, 0x37, 0xB5, 0x37, 0xB7, 0x37, 0xB9, 0x37, 0xBB, 0x37, 0xBD, 0x37, 0xBF, 0x37, 0xC1, 0x37, 0xC3, 0x37, 0xC5, 0x37,\n\t0xC7, 0x37, 0xC9, 0x37, 0xCA, 0x37, 0xCC, 0x37, 0xCE, 0x37, 0xD0, 0x37, 0xD2, 0x37, 0xD4, 0x37, 0xD6, 0x37, 0xD8, 0x37, 0xDA, 0x37, 0xDC, 0x37, 0xDE, 0x37, 0xDF, 0x37, 0xE1, 0x37, 0xE3, 0x37,\n\t0xE5, 0x37, 0xE7, 0x37, 0xE9, 0x37, 0xEB, 0x37, 0xED, 0x37, 0xEF, 0x37, 0xF1, 0x37, 0xF3, 0x37, 0xF5, 0x37, 0xF6, 0x37, 0xF8, 0x37, 0xFA, 0x37, 0xFC, 0x37, 0xFE, 0x37, 0x00, 0x38, 0x01, 0x38,\n\t0x02, 0x38, 0x03, 0x38, 0x04, 0x38, 0x05, 0x38, 0x06, 0x38, 0x07, 0x38, 0x08, 0x38, 0x09, 0x38, 0x0A, 0x38, 0x0B, 0x38, 0x0C, 0x38, 0x0D, 0x38, 0x0E, 0x38, 0x0E, 0x38, 0x0F, 0x38, 0x10, 0x38,\n\t0x11, 0x38, 0x12, 0x38, 0x13, 0x38, 0x14, 0x38, 0x15, 0x38, 0x16, 0x38, 0x17, 0x38, 0x18, 0x38, 0x19, 0x38, 0x1A, 0x38, 0x1B, 0x38, 0x1C, 0x38, 0x1D, 0x38, 0x1E, 0x38, 0x1F, 0x38, 0x20, 0x38,\n\t0x21, 0x38, 0x22, 0x38, 0x23, 0x38, 0x24, 0x38, 0x25, 0x38, 0x26, 0x38, 0x27, 0x38, 0x28, 0x38, 0x29, 0x38, 0x2A, 0x38, 0x2B, 0x38, 0xA7, 0x13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,\n\t0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,\n\t0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,\n\t0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,\n\t0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,\n\t0xFF, 0xFF, 0x6C, 0x2D, 0x38, 0x2E, 0x38, 0x2F, 0x38, 0x30, 0x38, 0x31, 0x38, 0x32, 0x38, 0x33, 0x38, 0x34, 0x38, 0x35, 0x38, 0x36, 0x38, 0x37, 0x38, 0x38, 0x38, 0x39, 0x38, 0x3A, 0x38, 0x3B,\n\t0x38, 0x3C, 0x38, 0x3D, 0x38, 0x3E, 0x38, 0x3F, 0x38, 0x40, 0x38, 0x41, 0x38, 0x42, 0x38, 0x43, 0x38, 0x44, 0x38, 0x45, 0x38, 0x46, 0x38, 0x47, 0x38, 0x48, 0x38, 0x49, 0x38, 0x4A, 0x38, 0x4B,\n\t0x38, 0x4C, 0x38, 0x4D, 0x38, 0x4E, 0x38, 0x4F, 0x38, 0x50, 0x38, 0x51, 0x38, 0x52, 0x38, 0x53, 0x38, 0x54, 0x38, 0x55, 0x38, 0x56, 0x38, 0x56, 0x38, 0x57, 0x38, 0x58, 0x38, 0x59, 0x38, 0x5A,\n\t0x38, 0x5B, 0x38, 0x5C, 0x38, 0x5D, 0x38, 0x5E, 0x38, 0x5F, 0x38, 0x60, 0x38, 0x61, 0x38, 0x62, 0x38, 0x63, 0x38, 0x64, 0x38, 0x65, 0x38, 0x66, 0x38, 0x67, 0x38, 0x68, 0x38, 0x69, 0x38, 0x6A,\n\t0x38, 0x6B, 0x38, 0x6C, 0x38, 0x6D, 0x38, 0x6E, 0x38, 0x6F, 0x38, 0x70, 0x38, 0x71, 0x38, 0x72, 0x38, 0x73, 0x38, 0x74, 0x38, 0x75, 0x38, 0x76, 0x38, 0x77, 0x38, 0x78, 0x38, 0x79, 0x38, 0x7A,\n\t0x38, 0x7B, 0x38, 0x7C, 0x38, 0x7D, 0x38, 0x7E, 0x38, 0x7F, 0x38, 0x80, 0x38, 0x81, 0x38, 0x82, 0x38, 0x83, 0x38, 0x84, 0x38, 0x85, 0x38, 0x86, 0x38, 0x87, 0x38, 0x88, 0x38, 0x89, 0x38, 0x8A,\n\t0x38, 0x8B, 0x38, 0x8C, 0x38, 0x8D, 0x38, 0x8E, 0x38, 0x8F, 0x38, 0x90, 0x38, 0x91, 0x38, 0x92, 0x38, 0x93, 0x38, 0x94, 0x38, 0x95, 0x38, 0x96, 0x38, 0x97, 0x38, 0x98, 0x38, 0x99, 0x38, 0x9A,\n\t0x38, 0x9B, 0x38, 0x9C, 0x38, 0x9D, 0x38, 0x9E, 0x38, 0x9F, 0x38, 0xA0, 0x38, 0xA1, 0x38, 0xA2, 0x38, 0xA3, 0x38, 0xA4, 0x38, 0xA5, 0x38, 0xA6, 0x38, 0xA7, 0x38, 0xA8, 0x38, 0xA9, 0x38, 0xAA,\n\t0x38, 0xAB, 0x38, 0xAC, 0x38, 0xAD, 0x38, 0xAE, 0x38, 0xAF, 0x38, 0xB0, 0x38, 0xB1, 0x38, 0xB2, 0x38, 0xB3, 0x38, 0xB4, 0x38, 0xB5, 0x38, 0xB6, 0x38, 0xB7, 0x38, 0xB8, 0x38, 0xB9, 0x38, 0xBA,\n\t0x38, 0xBB, 0x38, 0xBC, 0x38, 0xBD, 0x38, 0xBE, 0x38, 0xBF, 0x38, 0xC0, 0x38, 0xC1, 0x38, 0xC2, 0x38, 0xC3, 0x38, 0xC4, 0x38, 0xC5, 0x38, 0xC6, 0x38, 0xC7, 0x38, 0xC8, 0x38, 0xC9, 0x38, 0xCA,\n\t0x38, 0xCB, 0x38, 0xCC, 0x38, 0xCD, 0x38, 0xCE, 0x38, 0xCF, 0x38, 0xD0, 0x38, 0xD1, 0x38, 0xD2, 0x38, 0xD3, 0x38, 0xD4, 0x38, 0xD5, 0x38, 0xD6, 0x38, 0xD7, 0x38, 0xD8, 0x38, 0xD9, 0x38, 0xDA,\n\t0x38, 0xDB, 0x38, 0xDC, 0x38, 0xDD, 0x38, 0xDE, 0x38, 0xDF, 0x38, 0xE0, 0x38, 0xE1, 0x38, 0xE2, 0x38, 0xE3, 0x38, 0xE4, 0x38, 0xE5, 0x38, 0xE6, 0x38, 0xE7, 0x38, 0xE8, 0x38, 0xE9, 0x38, 0xEA,\n\t0x38, 0xEB, 0x38, 0xEC, 0x38, 0xED, 0x38, 0xEE, 0x38, 0xEF, 0x38, 0xF0, 0x38, 0xF1, 0x38, 0xF2, 0x38, 0xF3, 0x38, 0xF4, 0x38, 0xF5, 0x38, 0xF6, 0x38, 0xF7, 0x38, 0xF8, 0x38, 0xF9, 0x38, 0xFA,\n\t0x38, 0xFB, 0x38, 0xFC, 0x38, 0xFD, 0x38, 0xFE, 0x38, 0x00, 0x39, 0x01, 0x39, 0x02, 0x39, 0x03, 0x39, 0x04, 0x39, 0x05, 0x39, 0x06, 0x39, 0x07, 0x39, 0x08, 0x39, 0x09, 0x39, 0x0A, 0x39, 0x0B,\n\t0x39, 0x0C, 0x39, 0x0D, 0x39, 0x0E, 0x39, 0x0F, 0x39, 0x10, 0x39, 0x11, 0x39, 0x12, 0x39, 0x13, 0x39, 0x14, 0x39, 0x15, 0x39, 0x16, 0x39, 0x17, 0x39, 0x18, 0x39, 0x19, 0x39, 0x1A, 0x39, 0x1B,\n\t0x39, 0x1C, 0x39, 0x1D, 0x39, 0x1E, 0x39, 0x1F, 0x39, 0x20, 0x39, 0x21, 0x39, 0x22, 0x39, 0x23, 0x39, 0x24, 0x39, 0x25, 0x39, 0x26, 0x39, 0x27, 0x39, 0x28, 0x39, 0x29, 0x39, 0x2A, 0x39, 0x2B,\n\t0x39, 0x2C, 0x39, 0x2D, 0x39, 0x2F, 0x39, 0x30, 0x39, 0x31, 0x39, 0x32, 0x39, 0x33, 0x39, 0x34, 0x39, 0x35, 0x39, 0x36, 0x39, 0x37, 0x39, 0x38, 0x39, 0x39, 0x39, 0x3A, 0x39, 0x3B, 0x39, 0x3C,\n\t0x39, 0x3D, 0x39, 0x3E, 0x39, 0x3F, 0x39, 0x40, 0x39, 0x41, 0x39, 0x42, 0x39, 0x43, 0x39, 0x44, 0x39, 0x45, 0x39, 0x46, 0x39, 0x47, 0x39, 0x48, 0x39, 0x49, 0x39, 0x4A, 0x39, 0x4B, 0x39, 0x4C,\n\t0x39, 0x4D, 0x39, 0x4E, 0x39, 0x4F, 0x39, 0x50, 0x39, 0x52, 0x39, 0x53, 0x39, 0x54, 0x39, 0x55, 0x39, 0x56, 0x39, 0x57, 0x39, 0x58, 0x39, 0x59, 0x39, 0x5A, 0x39, 0x5B, 0x39, 0x5C, 0x39, 0x5D,\n\t0x39, 0x5E, 0x39, 0x5F, 0x39, 0x60, 0x39, 0x61, 0x39, 0x62, 0x39, 0x63, 0x39, 0x64, 0x39, 0x65, 0x39, 0x66, 0x39, 0x67, 0x39, 0x68, 0x39, 0x69, 0x39, 0x6A, 0x39, 0x6B, 0x39, 0x6C, 0x39, 0x6D,\n\t0x39, 0x6E, 0x39, 0x70, 0x39, 0x71, 0x39, 0x72, 0x39, 0x73, 0x39, 0x74, 0x39, 0x75, 0x39, 0x76, 0x39, 0x77, 0x39, 0x78, 0x39, 0x79, 0x39, 0x7A, 0x39, 0x7B, 0x39, 0x7C, 0x39, 0x7D, 0x39, 0x7E,\n\t0x39, 0x7F, 0x39, 0x80, 0x39, 0x81, 0x39, 0x82, 0x39, 0x83, 0x39, 0x84, 0x39, 0x85, 0x39, 0x86, 0x39, 0x87, 0x39, 0x88, 0x39, 0x8A, 0x39, 0x8B, 0x39, 0x8C, 0x39, 0x8D, 0x39, 0x8E, 0x39, 0x8F,\n\t0x39, 0x90, 0x39, 0x91, 0x39, 0x92, 0x39, 0x93, 0x39, 0x94, 0x39, 0x95, 0x39, 0x96, 0x39, 0x97, 0x39, 0x98, 0x39, 0x99, 0x39, 0x9A, 0x39, 0x9B, 0x39, 0x9C, 0x39, 0x9D, 0x39, 0x9E, 0x39, 0x9F,\n\t0x39, 0xA0, 0x39, 0xA2, 0x39, 0xA3, 0x39, 0xA4, 0x39, 0xA5, 0x39, 0xA6, 0x39, 0xA7, 0x39, 0xA8, 0x39, 0xA9, 0x39, 0xAA, 0x39, 0xAB, 0x39, 0xAC, 0x39, 0xAD, 0x39, 0xAE, 0x39, 0xAF, 0x39, 0xB0,\n\t0x39, 0xB1, 0x39, 0xB2, 0x39, 0xB3, 0x39, 0xB4, 0x39, 0xB5, 0x39, 0xB6, 0x39, 0xB8, 0x39, 0xB9, 0x39, 0xBA, 0x39, 0xBB, 0x39, 0xBC, 0x39, 0xBD, 0x39, 0xBE, 0x39, 0xBF, 0x39, 0xC0, 0x39, 0xC1,\n\t0x39, 0xC2, 0x39, 0xC3, 0x39, 0xC4, 0x39, 0xC5, 0x39, 0xC6, 0x39, 0xC7, 0x39, 0xC8, 0x39, 0xC9, 0x39, 0xCA, 0x39, 0xCB, 0x39, 0xCD, 0x39, 0xCE, 0x39, 0xCF, 0x39, 0xD0, 0x39, 0xD1, 0x39, 0xD2,\n\t0x39, 0xD3, 0x39, 0xD4, 0x39, 0xD5, 0x39, 0xD6, 0x39, 0xD7, 0x39, 0xD8, 0x39, 0xD9, 0x39, 0xDA, 0x39, 0xDB, 0x39, 0xDC, 0x39, 0xDD, 0x39, 0xDE, 0x39, 0xDF, 0x39, 0xE1, 0x39, 0xE2, 0x39, 0xE3,\n\t0x39, 0xE4, 0x39, 0xE5, 0x39, 0xE6, 0x39, 0xE7, 0x39, 0xE8, 0x39, 0xE9, 0x39, 0xEA, 0x39, 0xEB, 0x39, 0xEC, 0x39, 0xED, 0x39, 0xEE, 0x39, 0xEF, 0x39, 0xF0, 0x39, 0xF1, 0x39, 0xF3, 0x39, 0xF4,\n\t0x39, 0xF5, 0x39, 0xF6, 0x39, 0xF7, 0x39, 0xF8, 0x39, 0xF9, 0x39, 0xFA, 0x39, 0xFB, 0x39, 0xFC, 0x39, 0xFD, 0x39, 0xFE, 0x39, 0xFF, 0x39, 0x00, 0x3A, 0x01, 0x3A, 0x02, 0x3A, 0x03, 0x3A, 0x05,\n\t0x3A, 0x06, 0x3A, 0x07, 0x3A, 0x08, 0x3A, 0x09, 0x3A, 0x0A, 0x3A, 0x0B, 0x3A, 0x0C, 0x3A, 0x0D, 0x3A, 0x0E, 0x3A, 0x0F, 0x3A, 0x10, 0x3A, 0x11, 0x3A, 0x12, 0x3A, 0x13, 0x3A, 0x14, 0x3A, 0x15,\n\t0x3A, 0x17, 0x3A, 0x18, 0x3A, 0x19, 0x3A, 0x1A, 0x3A, 0x1B, 0x3A, 0x1C, 0x3A, 0x1D, 0x3A, 0x1E, 0x3A, 0x1F, 0x3A, 0x20, 0x3A, 0x21, 0x3A, 0x22, 0x3A, 0x23, 0x3A, 0x24, 0x3A, 0x25, 0x3A, 0x27,\n\t0x3A, 0x28, 0x3A, 0x29, 0x3A, 0x2A, 0x3A, 0x2B, 0x3A, 0x2C, 0x3A, 0x2D, 0x3A, 0x2E, 0x3A, 0x2F, 0x3A, 0x30, 0x3A, 0x31, 0x3A, 0x32, 0x3A, 0x33, 0x3A, 0x34, 0x3A, 0x35, 0x3A, 0x37, 0x3A, 0x38,\n\t0x3A, 0x39, 0x3A, 0x3A, 0x3A, 0x3B, 0x3A, 0x3C, 0x3A, 0x3D, 0x3A, 0x3E, 0x3A, 0x3F, 0x3A, 0x40, 0x3A, 0x41, 0x3A, 0x42, 0x3A, 0x43, 0x3A, 0x44, 0x3A, 0x45, 0x3A, 0x47, 0x3A, 0x48, 0x3A, 0x49,\n\t0x3A, 0x4A, 0x3A, 0x4B, 0x3A, 0x4C, 0x3A, 0x4D, 0x3A, 0x4E, 0x3A, 0x4F, 0x3A, 0x50, 0x3A, 0x51, 0x3A, 0x52, 0x3A, 0x53, 0x3A, 0x54, 0x3A, 0x56, 0x3A, 0x57, 0x3A, 0x58, 0x3A, 0x59, 0x3A, 0x5A,\n\t0x3A, 0x5B, 0x3A, 0x5C, 0x3A, 0x5D, 0x3A, 0x5E, 0x3A, 0x5F, 0x3A, 0x60, 0x3A, 0x61, 0x3A, 0x62, 0x3A, 0x63, 0x3A, 0x65, 0x3A, 0x66, 0x3A, 0x67, 0x3A, 0x68, 0x3A, 0x69, 0x3A, 0x6A, 0x3A, 0x6B,\n\t0x3A, 0x6C, 0x3A, 0x6D, 0x3A, 0x6E, 0x3A, 0x6F, 0x3A, 0x70, 0x3A, 0x71, 0x3A, 0x72, 0x3A, 0x74, 0x3A, 0x75, 0x3A, 0x76, 0x3A, 0x77, 0x3A, 0x78, 0x3A, 0x79, 0x3A, 0x7A, 0x3A, 0x7B, 0x3A, 0x7C,\n\t0x3A, 0x7D, 0x3A, 0x7E, 0x3A, 0x7F, 0x3A, 0x80, 0x3A, 0x82, 0x3A, 0x83, 0x3A, 0x84, 0x3A, 0x85, 0x3A, 0x86, 0x3A, 0x87, 0x3A, 0x88, 0x3A, 0x89, 0x3A, 0x8A, 0x3A, 0x8B, 0x3A, 0x8C, 0x3A, 0x8D,\n\t0x3A, 0x8E, 0x3A, 0x90, 0x3A, 0x91, 0x3A, 0x92, 0x3A, 0x93, 0x3A, 0x94, 0x3A, 0x95, 0x3A, 0x96, 0x3A, 0x97, 0x3A, 0x98, 0x3A, 0x99, 0x3A, 0x9A, 0x3A, 0x9B, 0x3A, 0x9C, 0x3A, 0x9E, 0x3A, 0x9F,\n\t0x3A, 0xA0, 0x3A, 0xA1, 0x3A, 0xA2, 0x3A, 0xA3, 0x3A, 0xA4, 0x3A, 0xA5, 0x3A, 0xA6, 0x3A, 0xA7, 0x3A, 0xA8, 0x3A, 0xA9, 0x3A, 0xAB, 0x3A, 0xAC, 0x3A, 0xAD, 0x3A, 0xAE, 0x3A, 0xAF, 0x3A, 0xB0,\n\t0x3A, 0xB1, 0x3A, 0xB2, 0x3A, 0xB3, 0x3A, 0xB4, 0x3A, 0xB5, 0x3A, 0xB6, 0x3A, 0xB8, 0x3A, 0xB9, 0x3A, 0xBA, 0x3A, 0xBB, 0x3A, 0xBD, 0x3A, 0xBF, 0x3A, 0xC1, 0x3A, 0xC3, 0x3A, 0xC6, 0x3A, 0xC8,\n\t0x3A, 0xCA, 0x3A, 0xCC, 0x3A, 0xCE, 0x3A, 0xD0, 0x3A, 0xD3, 0x3A, 0xD5, 0x3A, 0xD7, 0x3A, 0xD9, 0x3A, 0xDB, 0x3A, 0xDD, 0x3A, 0xE0, 0x3A, 0xE2, 0x3A, 0xE4, 0x3A, 0xE6, 0x3A, 0xE8, 0x3A, 0xEB,\n\t0x3A, 0xED, 0x3A, 0xEF, 0x3A, 0xF1, 0x3A, 0xF3, 0x3A, 0xF5, 0x3A, 0xF8, 0x3A, 0xFA, 0x3A, 0xFC, 0x3A, 0xFE, 0x3A, 0x00, 0x3B, 0x02, 0x3B, 0x05, 0x3B, 0x07, 0x3B, 0x09, 0x3B, 0x0B, 0x3B, 0x0D,\n\t0x3B, 0x10, 0x3B, 0x12, 0x3B, 0x14, 0x3B, 0x16, 0x3B, 0x18, 0x3B, 0x1B, 0x3B, 0x1D, 0x3B, 0x1F, 0x3B, 0x21, 0x3B, 0x23, 0x3B, 0x25, 0x3B, 0x28, 0x3B, 0x2A, 0x3B, 0x2C, 0x3B, 0x2E, 0x3B, 0x30,\n\t0x3B, 0x33, 0x3B, 0x35, 0x3B, 0x37, 0x3B, 0x39, 0x3B, 0x3B, 0x3B, 0x3E, 0x3B, 0x40, 0x3B, 0x42, 0x3B, 0x44, 0x3B, 0x46, 0x3B, 0x48, 0x3B, 0x4B, 0x3B, 0x4D, 0x3B, 0x4F, 0x3B, 0x51, 0x3B, 0x53,\n\t0x3B, 0x56, 0x3B, 0x58, 0x3B, 0x5A, 0x3B, 0x5C, 0x3B, 0x5E, 0x3B, 0x61, 0x3B, 0x63, 0x3B, 0x65, 0x3B, 0x67, 0x3B, 0x69, 0x3B, 0x6C, 0x3B, 0x6E, 0x3B, 0x70, 0x3B, 0x72, 0x3B, 0x74, 0x3B, 0x77,\n\t0x3B, 0x79, 0x3B, 0x7B, 0x3B, 0x7D, 0x3B, 0x7F, 0x3B, 0x82, 0x3B, 0x84, 0x3B, 0x86, 0x3B, 0x88, 0x3B, 0x8A, 0x3B, 0x8D, 0x3B, 0x8F, 0x3B, 0x91, 0x3B, 0x93, 0x3B, 0x95, 0x3B, 0x98, 0x3B, 0x9A,\n\t0x3B, 0x9C, 0x3B, 0x9E, 0x3B, 0xA1, 0x3B, 0xA3, 0x3B, 0xA5, 0x3B, 0xA7, 0x3B, 0xA9, 0x3B, 0xAC, 0x3B, 0xAE, 0x3B, 0xB0, 0x3B, 0xB2, 0x3B, 0xB4, 0x3B, 0xB7, 0x3B, 0xB9, 0x3B, 0xBB, 0x3B, 0xBD,\n\t0x3B, 0xBF, 0x3B, 0xC2, 0x3B, 0xC4, 0x3B, 0xC6, 0x3B, 0xC8, 0x3B, 0xCB, 0x3B, 0xCD, 0x3B, 0xCF, 0x3B, 0xD1, 0x3B, 0xD3, 0x3B, 0xD6, 0x3B, 0xD8, 0x3B, 0xDA, 0x3B, 0xDC, 0x3B, 0xDE, 0x3B, 0xE1,\n\t0x3B, 0xE3, 0x3B, 0xE5, 0x3B, 0xE7, 0x3B, 0xEA, 0x3B, 0xEC, 0x3B, 0xEE, 0x3B, 0xF0, 0x3B, 0xF2, 0x3B, 0xF5, 0x3B, 0xF7, 0x3B, 0xF9, 0x3B, 0xFB, 0x3B, 0xFE, 0x3B, 0x00, 0x3C, 0x01, 0x3C, 0x02,\n\t0x3C, 0x03, 0x3C, 0x04, 0x3C, 0x05, 0x3C, 0x07, 0x3C, 0x08, 0x3C, 0x09, 0x3C, 0x0A, 0x3C, 0x0B, 0x3C, 0x0C, 0x3C, 0x0D, 0x3C, 0x0E, 0x3C, 0x0F, 0x3C, 0x11, 0x3C, 0x12, 0x3C, 0x13, 0x3C, 0x14,\n\t0x3C, 0x15, 0x3C, 0x16, 0x3C, 0x17, 0x3C, 0x18, 0x3C, 0x19, 0x3C, 0x1B, 0x3C, 0x1C, 0x3C, 0x1D, 0x3C, 0x1E, 0x3C, 0x1F, 0x3C, 0x20, 0x3C, 0x21, 0x3C, 0x22, 0x3C, 0x23, 0x3C, 0x25, 0x3C, 0x26,\n\t0x3C, 0x27, 0x3C, 0x28, 0x3C, 0x29, 0x3C, 0x2A, 0x3C, 0x2B, 0x3C, 0x2C, 0x3C, 0x2E, 0x3C, 0x2F, 0x3C, 0x30, 0x3C, 0x31, 0x3C, 0x32, 0x3C, 0x33, 0x3C, 0x34, 0x3C, 0x35, 0x3C, 0x36, 0x3C, 0x38,\n\t0x3C, 0x39, 0x3C, 0x3A, 0x3C, 0x3B, 0x3C, 0x3C, 0x3C, 0x3D, 0x3C, 0x3E, 0x3C, 0x3F, 0x3C, 0x41, 0x3C, 0x42, 0x3C, 0x43, 0x3C, 0x44, 0x3C, 0x45, 0x3C, 0x46, 0x3C, 0x47, 0x3C, 0x48, 0x3C, 0x49,\n\t0x3C, 0x4B, 0x3C, 0x4C, 0x3C, 0x4D, 0x3C, 0x4E, 0x3C, 0x4F, 0x3C, 0x50, 0x3C, 0x51, 0x3C, 0x52, 0x3C, 0x54, 0x3C, 0x55, 0x3C, 0x56, 0x3C, 0x57, 0x3C, 0x58, 0x3C, 0x59, 0x3C, 0x5A, 0x3C, 0x5B,\n\t0x3C, 0x5D, 0x3C, 0x5E, 0x3C, 0x5F, 0x3C, 0x60, 0x3C, 0x61, 0x3C, 0x62, 0x3C, 0x63, 0x3C, 0x64, 0x3C, 0x65, 0x3C, 0x67, 0x3C, 0x68, 0x3C, 0x69, 0x3C, 0x6A, 0x3C, 0x6B, 0x3C, 0x6C, 0x3C, 0x6D,\n\t0x3C, 0x6E, 0x3C, 0x70, 0x3C, 0x71, 0x3C, 0x72, 0x3C, 0x73, 0x3C, 0x74, 0x3C, 0x75, 0x3C, 0x76, 0x3C, 0x77, 0x3C, 0x79, 0x3C, 0x7A, 0x3C, 0x7B, 0x3C, 0x7C, 0x3C, 0x7D, 0x3C, 0x7E, 0x3C, 0x7F,\n\t0x3C, 0x80, 0x3C, 0x82, 0x3C, 0x83, 0x3C, 0x84, 0x3C, 0x85, 0x3C, 0x86, 0x3C, 0x87, 0x3C, 0x88, 0x3C, 0x89, 0x3C, 0x8B, 0x3C, 0x8C, 0x3C, 0x8D, 0x3C, 0x8E, 0x3C, 0x8F, 0x3C, 0x90, 0x3C, 0x91,\n\t0x3C, 0x92, 0x3C, 0x94, 0x3C, 0x95, 0x3C, 0x96, 0x3C, 0x97, 0x3C, 0x98, 0x3C, 0x99, 0x3C, 0x9A, 0x3C, 0x9B, 0x3C, 0x9C, 0x3C, 0x9E, 0x3C, 0x9F, 0x3C, 0xA0, 0x3C, 0xA1, 0x3C, 0xA2, 0x3C, 0xA3,\n\t0x3C, 0xA4, 0x3C, 0xA5, 0x3C, 0xA7, 0x3C, 0xA8, 0x3C, 0xA9, 0x3C, 0xAA, 0x3C, 0xAB, 0x3C, 0xAC, 0x3C, 0xAD, 0x3C, 0xAF, 0x3C, 0xB0, 0x3C, 0xB1, 0x3C, 0xB2, 0x3C, 0xB3, 0x3C, 0xB4, 0x3C, 0xB5,\n\t0x3C, 0xB6, 0x3C, 0xB8, 0x3C, 0xB9, 0x3C, 0xBA, 0x3C, 0xBB, 0x3C, 0xBC, 0x3C, 0xBD, 0x3C, 0xBE, 0x3C, 0xBF, 0x3C, 0xC1, 0x3C, 0xC2, 0x3C, 0xC3, 0x3C, 0xC4, 0x3C, 0xC5, 0x3C, 0xC6, 0x3C, 0xC7,\n\t0x3C, 0xC8, 0x3C, 0xCA, 0x3C, 0xCB, 0x3C, 0xCC, 0x3C, 0xCD, 0x3C, 0xCE, 0x3C, 0xCF, 0x3C, 0xD0, 0x3C, 0xD1, 0x3C, 0xD3, 0x3C, 0xD4, 0x3C, 0xD5, 0x3C, 0xD6, 0x3C, 0xD7, 0x3C, 0xD8, 0x3C, 0xD9,\n\t0x3C, 0xDA, 0x3C, 0xDC, 0x3C, 0xDD, 0x3C, 0xDE, 0x3C, 0xDF, 0x3C, 0xE0, 0x3C, 0xE1, 0x3C, 0xE2, 0x3C, 0xE3, 0x3C, 0xE5, 0x3C, 0xE6, 0x3C, 0xE7, 0x3C, 0xE8, 0x3C, 0xE9, 0x3C, 0xEA, 0x3C, 0xEB,\n\t0x3C, 0xEC, 0x3C, 0xEE, 0x3C, 0xEF, 0x3C, 0xF0, 0x3C, 0xF1, 0x3C, 0xF2, 0x3C, 0xF3, 0x3C, 0xF4, 0x3C, 0xF6, 0x3C, 0xF7, 0x3C, 0xF8, 0x3C, 0xF9, 0x3C, 0xFA, 0x3C, 0xFB, 0x3C, 0xFC, 0x3C, 0xFD,\n\t0x3C, 0xFF, 0x3C, 0x00, 0x3D, 0x01, 0x3D, 0x02, 0x3D, 0x03, 0x3D, 0x04, 0x3D, 0x05, 0x3D, 0x06, 0x3D, 0x08, 0x3D, 0x09, 0x3D, 0x0A, 0x3D, 0x0B, 0x3D, 0x0C, 0x3D, 0x0D, 0x3D, 0x0E, 0x3D, 0x0F,\n\t0x3D, 0x11, 0x3D, 0x12, 0x3D, 0x13, 0x3D, 0x14, 0x3D, 0x15, 0x3D, 0x16, 0x3D, 0x17, 0x3D, 0x18, 0x3D, 0x1A, 0x3D, 0x1B, 0x3D, 0x1C, 0x3D, 0x1D, 0x3D, 0x1E, 0x3D, 0x1F, 0x3D, 0x20, 0x3D, 0x22,\n\t0x3D, 0x23, 0x3D, 0x24, 0x3D, 0x25, 0x3D, 0x26, 0x3D, 0x27, 0x3D, 0x28, 0x3D, 0x29, 0x3D, 0x2B, 0x3D, 0x2C, 0x3D, 0x2D, 0x3D, 0x2E, 0x3D, 0x2F, 0x3D, 0x30, 0x3D, 0x31, 0x3D, 0x32, 0x3D, 0x34,\n\t0x3D, 0x35, 0x3D, 0x36, 0x3D, 0x37, 0x3D, 0x38, 0x3D, 0x39, 0x3D, 0x3A, 0x3D, 0x3B, 0x3D, 0x3D, 0x3D, 0x3E, 0x3D, 0x3F, 0x3D, 0x40, 0x3D, 0x41, 0x3D, 0x42, 0x3D, 0x43, 0x3D, 0x45, 0x3D, 0x46,\n\t0x3D, 0x47, 0x3D, 0x48, 0x3D, 0x49, 0x3D, 0x4A, 0x3D, 0x4B, 0x3D, 0x4C, 0x3D, 0x4E, 0x3D, 0x4F, 0x3D, 0x50, 0x3D, 0x51, 0x3D, 0x52, 0x3D, 0x53, 0x3D, 0x54, 0x3D, 0x55, 0x3D, 0x57, 0x3D, 0x58,\n\t0x3D, 0x59, 0x3D, 0x5A, 0x3D, 0x5B, 0x3D, 0x5C, 0x3D, 0x5D, 0x3D, 0x5E, 0x3D, 0x60, 0x3D, 0x61, 0x3D, 0x62, 0x3D, 0x63, 0x3D, 0x64, 0x3D, 0x65, 0x3D, 0x66, 0x3D, 0x68, 0x3D, 0x69, 0x3D, 0x6A,\n\t0x3D, 0x6B, 0x3D, 0x6C, 0x3D, 0x6D, 0x3D, 0x6E, 0x3D, 0x6F, 0x3D, 0x71, 0x3D, 0x72, 0x3D, 0x73, 0x3D, 0x74, 0x3D, 0x75, 0x3D, 0x76, 0x3D, 0x77, 0x3D, 0x78, 0x3D, 0x7A, 0x3D, 0x7B, 0x3D, 0x7C,\n\t0x3D, 0x7D, 0x3D, 0x7E, 0x3D, 0x7F, 0x3D, 0x80, 0x3D, 0x81, 0x3D, 0x83, 0x3D, 0x84, 0x3D, 0x85, 0x3D, 0x86, 0x3D, 0x87, 0x3D, 0x88, 0x3D, 0x89, 0x3D, 0x8A, 0x3D, 0x8C, 0x3D, 0x8D, 0x3D, 0x8E,\n\t0x3D, 0x8F, 0x3D, 0x90, 0x3D, 0x91, 0x3D, 0x92, 0x3D, 0x94, 0x3D, 0x95, 0x3D, 0x96, 0x3D, 0x97, 0x3D, 0x98, 0x3D, 0x99, 0x3D, 0x9A, 0x3D, 0x9B, 0x3D, 0x9D, 0x3D, 0x9E, 0x3D, 0x9F, 0x3D, 0xA0,\n\t0x3D, 0xA1, 0x3D, 0xA2, 0x3D, 0xA3, 0x3D, 0xA4, 0x3D, 0xA6, 0x3D, 0xA7, 0x3D, 0xA8, 0x3D, 0xA9, 0x3D, 0xAA, 0x3D, 0xAB, 0x3D, 0xAC, 0x3D, 0xAD, 0x3D, 0xAF, 0x3D, 0xB0, 0x3D, 0xB1, 0x3D, 0xB2,\n\t0x3D, 0xB3, 0x3D, 0xB4, 0x3D, 0xB5, 0x3D, 0xB6, 0x3D, 0xB8, 0x3D, 0xB9, 0x3D, 0xBA, 0x3D, 0xBB, 0x3D, 0xBC, 0x3D, 0xBD, 0x3D, 0xBE, 0x3D, 0xBF, 0x3D, 0xC1, 0x3D, 0xC2, 0x3D, 0xC3, 0x3D, 0xC4,\n\t0x3D, 0xC5, 0x3D, 0xC6, 0x3D, 0xC7, 0x3D, 0xC8, 0x3D, 0xCA, 0x3D, 0xCB, 0x3D, 0xCC, 0x3D, 0xCD, 0x3D, 0xCE, 0x3D, 0xCF, 0x3D, 0xD0, 0x3D, 0xD2, 0x3D, 0xD3, 0x3D, 0xD4, 0x3D, 0xD5, 0x3D, 0xD6,\n\t0x3D, 0xD7, 0x3D, 0xD8, 0x3D, 0xD9, 0x3D, 0xDB, 0x3D, 0xDC, 0x3D, 0xDD, 0x3D, 0xDE, 0x3D, 0xDF, 0x3D, 0xE0, 0x3D, 0xE1, 0x3D, 0xE2, 0x3D, 0xE4, 0x3D, 0xE5, 0x3D, 0xE6, 0x3D, 0xE7, 0x3D, 0xE8,\n\t0x3D, 0xE9, 0x3D, 0xEA, 0x3D, 0xEB, 0x3D, 0xED, 0x3D, 0xEE, 0x3D, 0xEF, 0x3D, 0xF0, 0x3D, 0xF1, 0x3D, 0xF2, 0x3D, 0xF3, 0x3D, 0xF4, 0x3D, 0xF6, 0x3D, 0xF7, 0x3D, 0xF8, 0x3D, 0xF9, 0x3D, 0xFA,\n\t0x3D, 0xFB, 0x3D, 0xFC, 0x3D, 0xFD, 0x3D, 0xFE, 0x3D, 0x00, 0x3E, 0x01, 0x3E, 0x02, 0x3E, 0x03, 0x3E, 0x04, 0x3E, 0x05, 0x3E, 0x06, 0x3E, 0x07, 0x3E, 0x09, 0x3E, 0x0A, 0x3E, 0x0B, 0x3E, 0x0C,\n\t0x3E, 0x0D, 0x3E, 0x0E, 0x3E, 0x0F, 0x3E, 0x10, 0x3E, 0x12, 0x3E, 0x13, 0x3E, 0x14, 0x3E, 0x15, 0x3E, 0x16, 0x3E, 0x17, 0x3E, 0x18, 0x3E, 0x19, 0x3E, 0x1B, 0x3E, 0x1C, 0x3E, 0x1D, 0x3E, 0x1E,\n\t0x3E, 0x1F, 0x3E, 0x20, 0x3E, 0x21, 0x3E, 0x22, 0x3E, 0x24, 0x3E, 0x25, 0x3E, 0x26, 0x3E, 0x27, 0x3E, 0x28, 0x3E, 0x29, 0x3E, 0x2A, 0x3E, 0x2B, 0x3E, 0x2D, 0x3E, 0x2E, 0x3E, 0x2F, 0x3E, 0x30,\n\t0x3E, 0x31, 0x3E, 0x32, 0x3E, 0x33, 0x3E, 0x34, 0x3E, 0x35, 0x3E, 0x37, 0x3E, 0x38, 0x3E, 0x39, 0x3E, 0x3A, 0x3E, 0x3B, 0x3E, 0x3C, 0x3E, 0x3D, 0x3E, 0x3E, 0x3E, 0x40, 0x3E, 0x41, 0x3E, 0x42,\n\t0x3E, 0x43, 0x3E, 0x44, 0x3E, 0x45, 0x3E, 0x46, 0x3E, 0x47, 0x3E, 0x49, 0x3E, 0x4A, 0x3E, 0x4B, 0x3E, 0x4C, 0x3E, 0x4D, 0x3E, 0x4E, 0x3E, 0x4F, 0x3E, 0x50, 0x3E, 0x51, 0x3E, 0x53, 0x3E, 0x54,\n\t0x3E, 0x55, 0x3E, 0x56, 0x3E, 0x57, 0x3E, 0x58, 0x3E, 0x59, 0x3E, 0x5A, 0x3E, 0x5C, 0x3E, 0x5D, 0x3E, 0x5E, 0x3E, 0x5F, 0x3E, 0x60, 0x3E, 0x61, 0x3E, 0x62, 0x3E, 0x63, 0x3E, 0x65, 0x3E, 0x66,\n\t0x3E, 0x67, 0x3E, 0x68, 0x3E, 0x69, 0x3E, 0x6A, 0x3E, 0x6B, 0x3E, 0x6C, 0x3E, 0x6D, 0x3E, 0x6F, 0x3E, 0x70, 0x3E, 0x71, 0x3E, 0x72, 0x3E, 0x73, 0x3E, 0x74, 0x3E, 0x75, 0x3E, 0x76, 0x3E, 0x77,\n\t0x3E, 0x79, 0x3E, 0x7A, 0x3E, 0x7B, 0x3E, 0x7C, 0x3E, 0x7D, 0x3E, 0x7E, 0x3E, 0x7F, 0x3E, 0x80, 0x3E, 0x82, 0x3E, 0x83, 0x3E, 0x84, 0x3E, 0x85, 0x3E, 0x86, 0x3E, 0x87, 0x3E, 0x88, 0x3E, 0x89,\n\t0x3E, 0x8A, 0x3E, 0x8C, 0x3E, 0x8D, 0x3E, 0x8E, 0x3E, 0x8F, 0x3E, 0x90, 0x3E, 0x91, 0x3E, 0x92, 0x3E, 0x93, 0x3E, 0x94, 0x3E, 0x96, 0x3E, 0x97, 0x3E, 0x98, 0x3E, 0x99, 0x3E, 0x9A, 0x3E, 0x9B,\n\t0x3E, 0x9C, 0x3E, 0x9D, 0x3E, 0x9F, 0x3E, 0xA0, 0x3E, 0xA1, 0x3E, 0xA2, 0x3E, 0xA3, 0x3E, 0xA4, 0x3E, 0xA5, 0x3E, 0xA6, 0x3E, 0xA7, 0x3E, 0xA9, 0x3E, 0xAA, 0x3E, 0xAB, 0x3E, 0xAC, 0x3E, 0xAD,\n\t0x3E, 0xAE, 0x3E, 0xAF, 0x3E, 0xB0, 0x3E, 0xB1, 0x3E, 0xB3, 0x3E, 0xB4, 0x3E, 0xB5, 0x3E, 0xB6, 0x3E, 0xB7, 0x3E, 0xB8, 0x3E, 0xB9, 0x3E, 0xBA, 0x3E, 0xBB, 0x3E, 0xBD, 0x3E, 0xBE, 0x3E, 0xBF,\n\t0x3E, 0xC0, 0x3E, 0xC1, 0x3E, 0xC2, 0x3E, 0xC3, 0x3E, 0xC4, 0x3E, 0xC5, 0x3E, 0xC7, 0x3E, 0xC8, 0x3E, 0xC9, 0x3E, 0xCA, 0x3E, 0xCB, 0x3E, 0xCC, 0x3E, 0xCD, 0x3E, 0xCE, 0x3E, 0xCF, 0x3E, 0xD1,\n\t0x3E, 0xD2, 0x3E, 0xD3, 0x3E, 0xD4, 0x3E, 0xD5, 0x3E, 0xD6, 0x3E, 0xD7, 0x3E, 0xD8, 0x3E, 0xD9, 0x3E, 0xDB, 0x3E, 0xDC, 0x3E, 0xDD, 0x3E, 0xDE, 0x3E, 0xDF, 0x3E, 0xE0, 0x3E, 0xE1, 0x3E, 0xE2,\n\t0x3E, 0xE3, 0x3E, 0xE5, 0x3E, 0xE6, 0x3E, 0xE7, 0x3E, 0xE8, 0x3E, 0xE9, 0x3E, 0xEA, 0x3E, 0xEB, 0x3E, 0xEC, 0x3E, 0xED, 0x3E, 0xEE, 0x3E, 0xF0, 0x3E, 0xF1, 0x3E, 0xF2, 0x3E, 0xF3, 0x3E, 0xF4,\n\t0x3E, 0xF5, 0x3E, 0xF6, 0x3E, 0xF7, 0x3E, 0xF8, 0x3E, 0xFA, 0x3E, 0xFB, 0x3E, 0xFC, 0x3E, 0xFD, 0x3E, 0xFE, 0x3E, 0xFF, 0x3E, 0x00, 0x3F, 0x01, 0x3F, 0x02, 0x3F, 0x03, 0x3F, 0x05, 0x3F, 0x06,\n\t0x3F, 0x07, 0x3F, 0x08, 0x3F, 0x09, 0x3F, 0x0A, 0x3F, 0x0B, 0x3F, 0x0C, 0x3F, 0x0D, 0x3F, 0x0F, 0x3F, 0x10, 0x3F, 0x11, 0x3F, 0x12, 0x3F, 0x13, 0x3F, 0x14, 0x3F, 0x15, 0x3F, 0x16, 0x3F, 0x17,\n\t0x3F, 0x18, 0x3F, 0x1A, 0x3F, 0x1B, 0x3F, 0x1C, 0x3F, 0x1D, 0x3F, 0x1E, 0x3F, 0x1F, 0x3F, 0x20, 0x3F, 0x21, 0x3F, 0x22, 0x3F, 0x24, 0x3F, 0x25, 0x3F, 0x26, 0x3F, 0x27, 0x3F, 0x28, 0x3F, 0x29,\n\t0x3F, 0x2A, 0x3F, 0x2B, 0x3F, 0x2C, 0x3F, 0x2D, 0x3F, 0x2F, 0x3F, 0x30, 0x3F, 0x31, 0x3F, 0x32, 0x3F, 0x33, 0x3F, 0x34, 0x3F, 0x35, 0x3F, 0x36, 0x3F, 0x37, 0x3F, 0x38, 0x3F, 0x3A, 0x3F, 0x3B,\n\t0x3F, 0x3C, 0x3F, 0x3D, 0x3F, 0x3E, 0x3F, 0x3F, 0x3F, 0x40, 0x3F, 0x41, 0x3F, 0x42, 0x3F, 0x43, 0x3F, 0x45, 0x3F, 0x46, 0x3F, 0x47, 0x3F, 0x48, 0x3F, 0x49, 0x3F, 0x4A, 0x3F, 0x4B, 0x3F, 0x4C,\n\t0x3F, 0x4D, 0x3F, 0x4E, 0x3F, 0x50, 0x3F, 0x51, 0x3F, 0x52, 0x3F, 0x53, 0x3F, 0x54, 0x3F, 0x55, 0x3F, 0x56, 0x3F, 0x57, 0x3F, 0x58, 0x3F, 0x59, 0x3F, 0x5B, 0x3F, 0x5C, 0x3F, 0x5D, 0x3F, 0x5E,\n\t0x3F, 0x5F, 0x3F, 0x60, 0x3F, 0x61, 0x3F, 0x62, 0x3F, 0x63, 0x3F, 0x64, 0x3F, 0x65, 0x3F, 0x67, 0x3F, 0x68, 0x3F, 0x69, 0x3F, 0x6A, 0x3F, 0x6B, 0x3F, 0x6C, 0x3F, 0x6D, 0x3F, 0x6E, 0x3F, 0x6F,\n\t0x3F, 0x70, 0x3F, 0x72, 0x3F, 0x73, 0x3F, 0x74, 0x3F, 0x75, 0x3F, 0x76, 0x3F, 0x77, 0x3F, 0x78, 0x3F, 0x79, 0x3F, 0x7A, 0x3F, 0x7B, 0x3F, 0x7C, 0x3F, 0x7E, 0x3F, 0x7F, 0x3F, 0x80, 0x3F, 0x81,\n\t0x3F, 0x82, 0x3F, 0x83, 0x3F, 0x84, 0x3F, 0x85, 0x3F, 0x86, 0x3F, 0x87, 0x3F, 0x89, 0x3F, 0x8A, 0x3F, 0x8B, 0x3F, 0x8C, 0x3F, 0x8D, 0x3F, 0x8E, 0x3F, 0x8F, 0x3F, 0x90, 0x3F, 0x91, 0x3F, 0x92,\n\t0x3F, 0x93, 0x3F, 0x95, 0x3F, 0x96, 0x3F, 0x97, 0x3F, 0x98, 0x3F, 0x99, 0x3F, 0x9A, 0x3F, 0x9B, 0x3F, 0x9C, 0x3F, 0x9D, 0x3F, 0x9E, 0x3F, 0x9F, 0x3F, 0xA1, 0x3F, 0xA2, 0x3F, 0xA3, 0x3F, 0xA4,\n\t0x3F, 0xA5, 0x3F, 0xA6, 0x3F, 0xA7, 0x3F, 0xA8, 0x3F, 0xA9, 0x3F, 0xAA, 0x3F, 0xAB, 0x3F, 0xAD, 0x3F, 0xAE, 0x3F, 0xAF, 0x3F, 0xB0, 0x3F, 0xB1, 0x3F, 0xB2, 0x3F, 0xB3, 0x3F, 0xB4, 0x3F, 0xB5,\n\t0x3F, 0xB6, 0x3F, 0xB7, 0x3F, 0xB8, 0x3F, 0xBA, 0x3F, 0xBB, 0x3F, 0xBC, 0x3F, 0xBD, 0x3F, 0xBE, 0x3F, 0xBF, 0x3F, 0xC0, 0x3F, 0xC1, 0x3F, 0xC2, 0x3F, 0xC3, 0x3F, 0xC4, 0x3F, 0xC6, 0x3F, 0xC7,\n\t0x3F, 0xC8, 0x3F, 0xC9, 0x3F, 0xCA, 0x3F, 0xCB, 0x3F, 0xCC, 0x3F, 0xCD, 0x3F, 0xCE, 0x3F, 0xCF, 0x3F, 0xD0, 0x3F, 0xD2, 0x3F, 0xD4, 0x3F, 0xD6, 0x3F, 0xD8, 0x3F, 0xDA, 0x3F, 0xDC, 0x3F, 0xDF,\n\t0x3F, 0xE1, 0x3F, 0xE3, 0x3F, 0xE5, 0x3F, 0xE7, 0x3F, 0xE9, 0x3F, 0xEC, 0x3F, 0xEE, 0x3F, 0xF0, 0x3F, 0xF2, 0x3F, 0xF4, 0x3F, 0xF6, 0x3F, 0xF9, 0x3F, 0xFB, 0x3F, 0xFD, 0x3F, 0xFF, 0x3F, 0x01,\n\t0x40, 0x02, 0x40, 0x03, 0x40, 0x04, 0x40, 0x05, 0x40, 0x06, 0x40, 0x07, 0x40, 0x08, 0x40, 0x09, 0x40, 0x0A, 0x40, 0x0B, 0x40, 0x0C, 0x40, 0x0E, 0x40, 0x0F, 0x40, 0x10, 0x40, 0x11, 0x40, 0x12,\n\t0x40, 0x13, 0x40, 0x14, 0x40, 0x15, 0x40, 0x16, 0x40, 0x17, 0x40, 0x18, 0x40, 0x19, 0x40, 0x1A, 0x40, 0x1C, 0x40, 0x1D, 0x40, 0x1E, 0x40, 0x1F, 0x40, 0x20, 0x40, 0x21, 0x40, 0x22, 0x40, 0x23,\n\t0x40, 0x24, 0x40, 0x25, 0x40, 0x26, 0x40, 0x27, 0x40, 0x28, 0x40, 0x2A, 0x40, 0x2B, 0x40, 0x2C, 0x40, 0x2D, 0x40, 0x2E, 0x40, 0x2F, 0x40, 0x30, 0x40, 0x31, 0x40, 0x32, 0x40, 0x33, 0x40, 0x34,\n\t0x40, 0x35, 0x40, 0x36, 0x40, 0x37, 0x40, 0x39, 0x40, 0x3A, 0x40, 0x3B, 0x40, 0x3C, 0x40, 0x3D, 0x40, 0x3E, 0x40, 0x3F, 0x40, 0x40, 0x40, 0x41, 0x40, 0x42, 0x40, 0x43, 0x40, 0x44, 0x40, 0x45,\n\t0x40, 0x46, 0x40, 0x48, 0x40, 0x49, 0x40, 0x4A, 0x40, 0x4B, 0x40, 0x4C, 0x40, 0x4D, 0x40, 0x4E, 0x40, 0x4F, 0x40, 0x50, 0x40, 0x51, 0x40, 0x52, 0x40, 0x53, 0x40, 0x54, 0x40, 0x55, 0x40, 0x56,\n\t0x40, 0x58, 0x40, 0x59, 0x40, 0x5A, 0x40, 0x5B, 0x40, 0x5C, 0x40, 0x5D, 0x40, 0x5E, 0x40, 0x5F, 0x40, 0x60, 0x40, 0x61, 0x40, 0x62, 0x40, 0x63, 0x40, 0x64, 0x40, 0x65, 0x40, 0x66, 0x40, 0x67,\n\t0x40, 0x69, 0x40, 0x6A, 0x40, 0x6B, 0x40, 0x6C, 0x40, 0x6D, 0x40, 0x6E, 0x40, 0x6F, 0x40, 0x70, 0x40, 0x71, 0x40, 0x72, 0x40, 0x73, 0x40, 0x74, 0x40, 0x75, 0x40, 0x76, 0x40, 0x77, 0x40, 0x78,\n\t0x40, 0x79, 0x40, 0x7B, 0x40, 0x7C, 0x40, 0x7D, 0x40, 0x7E, 0x40, 0x7F, 0x40, 0x80, 0x40, 0x81, 0x40, 0x82, 0x40, 0x83, 0x40, 0x84, 0x40, 0x85, 0x40, 0x86, 0x40, 0x87, 0x40, 0x88, 0x40, 0x89,\n\t0x40, 0x8A, 0x40, 0x8B, 0x40, 0x8D, 0x40, 0x8E, 0x40, 0x8F, 0x40, 0x90, 0x40, 0x91, 0x40, 0x92, 0x40, 0x93, 0x40, 0x94, 0x40, 0x95, 0x40, 0x96, 0x40, 0x97, 0x40, 0x98, 0x40, 0x99, 0x40, 0x9A,\n\t0x40, 0x9B, 0x40, 0x9C, 0x40, 0x9D, 0x40, 0x9E, 0x40, 0x9F, 0x40, 0xA1, 0x40, 0xA2, 0x40, 0xA3, 0x40, 0xA4, 0x40, 0xA5, 0x40, 0xA6, 0x40, 0xA7, 0x40, 0xA8, 0x40, 0xA9, 0x40, 0xAA, 0x40, 0xAB,\n\t0x40, 0xAC, 0x40, 0xAD, 0x40, 0xAE, 0x40, 0xAF, 0x40, 0xB0, 0x40, 0xB1, 0x40, 0xB2, 0x40, 0xB3, 0x40, 0xB4, 0x40, 0xB6, 0x40, 0xB7, 0x40, 0xB8, 0x40, 0xB9, 0x40, 0xBA, 0x40, 0xBB, 0x40, 0xBC,\n\t0x40, 0xBD, 0x40, 0xBE, 0x40, 0xBF, 0x40, 0xC0, 0x40, 0xC1, 0x40, 0xC2, 0x40, 0xC3, 0x40, 0xC4, 0x40, 0xC5, 0x40, 0xC6, 0x40, 0xC7, 0x40, 0xC8, 0x40, 0xC9, 0x40, 0xCA, 0x40, 0xCC, 0x40, 0xCD,\n\t0x40, 0xCE, 0x40, 0xCF, 0x40, 0xD0, 0x40, 0xD1, 0x40, 0xD2, 0x40, 0xD3, 0x40, 0xD4, 0x40, 0xD5, 0x40, 0xD6, 0x40, 0xD7, 0x40, 0xD8, 0x40, 0xD9, 0x40, 0xDA, 0x40, 0xDB, 0x40, 0xDC, 0x40, 0xDD,\n\t0x40, 0xDE, 0x40, 0xDF, 0x40, 0xE0, 0x40, 0xE1, 0x40, 0xE2, 0x40, 0xE3, 0x40, 0xE5, 0x40, 0xE6, 0x40, 0xE7, 0x40, 0xE8, 0x40, 0xE9, 0x40, 0xEA, 0x40, 0xEB, 0x40, 0xEC, 0x40, 0xED, 0x40, 0xEE,\n\t0x40, 0xEF, 0x40, 0xF0, 0x40, 0xF1, 0x40, 0xF2, 0x40, 0xF3, 0x40, 0xF4, 0x40, 0xF5, 0x40, 0xF6, 0x40, 0xF7, 0x40, 0xF8, 0x40, 0xF9, 0x40, 0xFA, 0x40, 0xFB, 0x40, 0xFC, 0x40, 0xFD, 0x40, 0xFF,\n\t0x40, 0x00, 0x41, 0x01, 0x41, 0x02, 0x41, 0x03, 0x41, 0x04, 0x41, 0x05, 0x41, 0x06, 0x41, 0x07, 0x41, 0x08, 0x41, 0x09, 0x41, 0x0A, 0x41, 0x0B, 0x41, 0x0C, 0x41, 0x0D, 0x41, 0x0E, 0x41, 0x0F,\n\t0x41, 0x10, 0x41, 0x11, 0x41, 0x12, 0x41, 0x13, 0x41, 0x14, 0x41, 0x15, 0x41, 0x16, 0x41, 0x17, 0x41, 0x18, 0x41, 0x19, 0x41, 0x1A, 0x41, 0x1B, 0x41, 0x1D, 0x41, 0x1E, 0x41, 0x1F, 0x41, 0x20,\n\t0x41, 0x21, 0x41, 0x22, 0x41, 0x23, 0x41, 0x24, 0x41, 0x25, 0x41, 0x26, 0x41, 0x27, 0x41, 0x28, 0x41, 0x29, 0x41, 0x2A, 0x41, 0x2B, 0x41, 0x2C, 0x41, 0x2D, 0x41, 0x2E, 0x41, 0x2F, 0x41, 0x30,\n\t0x41, 0x31, 0x41, 0x32, 0x41, 0x33, 0x41, 0x34, 0x41, 0x35, 0x41, 0x36, 0x41, 0x37, 0x41, 0x38, 0x41, 0x39, 0x41, 0x3A, 0x41, 0x3B, 0x41, 0x3C, 0x41, 0x3E, 0x41, 0x3F, 0x41, 0x40, 0x41, 0x41,\n\t0x41, 0x42, 0x41, 0x43, 0x41, 0x44, 0x41, 0x45, 0x41, 0x46, 0x41, 0x47, 0x41, 0x48, 0x41, 0x49, 0x41, 0x4A, 0x41, 0x4B, 0x41, 0x4C, 0x41, 0x4D, 0x41, 0x4E, 0x41, 0x4F, 0x41, 0x50, 0x41, 0x51,\n\t0x41, 0x52, 0x41, 0x53, 0x41, 0x54, 0x41, 0x55, 0x41, 0x56, 0x41, 0x57, 0x41, 0x58, 0x41, 0x59, 0x41, 0x5A, 0x41, 0x5B, 0x41, 0x5C, 0x41, 0x5D, 0x41, 0x5E, 0x41, 0x5F, 0x41, 0x60, 0x41, 0x61,\n\t0x41, 0x62, 0x41, 0x63, 0x41, 0x65, 0x41, 0x66, 0x41, 0x67, 0x41, 0x68, 0x41, 0x69, 0x41, 0x6A, 0x41, 0x6B, 0x41, 0x6C, 0x41, 0x6D, 0x41, 0x6E, 0x41, 0x6F, 0x41, 0x70, 0x41, 0x71, 0x41, 0x72,\n\t0x41, 0x73, 0x41, 0x74, 0x41, 0x75, 0x41, 0x76, 0x41, 0x77, 0x41, 0x78, 0x41, 0x79, 0x41, 0x7A, 0x41, 0x7B, 0x41, 0x7C, 0x41, 0x7D, 0x41, 0x7E, 0x41, 0x7F, 0x41, 0x80, 0x41, 0x81, 0x41, 0x82,\n\t0x41, 0x83, 0x41, 0x84, 0x41, 0x85, 0x41, 0x86, 0x41, 0x87, 0x41, 0x88, 0x41, 0x89, 0x41, 0x8A, 0x41, 0x8B, 0x41, 0x8C, 0x41, 0x8D, 0x41, 0x8E, 0x41, 0x8F, 0x41, 0x90, 0x41, 0x91, 0x41, 0x93,\n\t0x41, 0x94, 0x41, 0x95, 0x41, 0x96, 0x41, 0x97, 0x41, 0x98, 0x41, 0x99, 0x41, 0x9A, 0x41, 0x9B, 0x41, 0x9C, 0x41, 0x9D, 0x41, 0x9E, 0x41, 0x9F, 0x41, 0xA0, 0x41, 0xA1, 0x41, 0xA2, 0x41, 0xA3,\n\t0x41, 0xA4, 0x41, 0xA5, 0x41, 0xA6, 0x41, 0xA7, 0x41, 0xA8, 0x41, 0xA9, 0x41, 0xAA, 0x41, 0xAB, 0x41, 0xAC, 0x41, 0xAD, 0x41, 0xAE, 0x41, 0xAF, 0x41, 0xB0, 0x41, 0xB1, 0x41, 0xB2, 0x41, 0xB3,\n\t0x41, 0xB4, 0x41, 0xB5, 0x41, 0xB6, 0x41, 0xB7, 0x41, 0xB8, 0x41, 0xB9, 0x41, 0xBA, 0x41, 0xBB, 0x41, 0xBC, 0x41, 0xBD, 0x41, 0xBE, 0x41, 0xBF, 0x41, 0xC0, 0x41, 0xC1, 0x41, 0xC2, 0x41, 0xC3,\n\t0x41, 0xC4, 0x41, 0xC5, 0x41, 0xC6, 0x41, 0xC7, 0x41, 0xC8, 0x41, 0xC9, 0x41, 0xCA, 0x41, 0xCB, 0x41, 0xCC, 0x41, 0xCE, 0x41, 0xCF, 0x41, 0xD0, 0x41, 0xD1, 0x41, 0xD2, 0x41, 0xD3, 0x41, 0xD4,\n\t0x41, 0xD5, 0x41, 0xD6, 0x41, 0xD7, 0x41, 0xD8, 0x41, 0xD9, 0x41, 0xDA, 0x41, 0xDB, 0x41, 0xDC, 0x41, 0xDD, 0x41, 0xDE, 0x41, 0xDF, 0x41, 0xE0, 0x41, 0xE1, 0x41, 0xE2, 0x41, 0xE3, 0x41, 0xE4,\n\t0x41, 0xE5, 0x41, 0xE6, 0x41, 0xE7, 0x41, 0xE8, 0x41, 0xE9, 0x41, 0xEA, 0x41, 0xEB, 0x41, 0xEC, 0x41, 0xED, 0x41, 0xEE, 0x41, 0xEF, 0x41, 0xF0, 0x41, 0xF1, 0x41, 0xF2, 0x41, 0xF3, 0x41, 0xF4,\n\t0x41, 0xF5, 0x41, 0xF6, 0x41, 0xF7, 0x41, 0xF8, 0x41, 0xF9, 0x41, 0xFA, 0x41, 0xFB, 0x41, 0xFC, 0x41, 0xFD, 0x41, 0xFE, 0x41, 0xFF, 0x41, 0x00, 0x42, 0x01, 0x42, 0x02, 0x42, 0x03, 0x42, 0x04,\n\t0x42, 0x05, 0x42, 0x06, 0x42, 0x07, 0x42, 0x08, 0x42, 0x09, 0x42, 0x0A, 0x42, 0x0B, 0x42, 0x0C, 0x42, 0x0D, 0x42, 0x0E, 0x42, 0x0F, 0x42, 0x10, 0x42, 0x11, 0x42, 0x12, 0x42, 0x13, 0x42, 0x14,\n\t0x42, 0x15, 0x42, 0x16, 0x42, 0x17, 0x42, 0x18, 0x42, 0x19, 0x42, 0x1A, 0x42, 0x1B, 0x42, 0x1C, 0x42, 0x1D, 0x42, 0x1E, 0x42, 0x1F, 0x42, 0x20, 0x42, 0x22, 0x42, 0x23, 0x42, 0x24, 0x42, 0x25,\n\t0x42, 0x26, 0x42, 0x27, 0x42, 0x28, 0x42, 0x29, 0x42, 0x2A, 0x42, 0x2B, 0x42, 0x2C, 0x42, 0x2D, 0x42, 0x2E, 0x42, 0x2F, 0x42, 0x30, 0x42, 0x31, 0x42, 0x32, 0x42, 0x33, 0x42, 0x34, 0x42, 0x35,\n\t0x42, 0x36, 0x42, 0x37, 0x42, 0x38, 0x42, 0x39, 0x42, 0x3A, 0x42, 0x3B, 0x42, 0x3C, 0x42, 0x3D, 0x42, 0x3E, 0x42, 0x3F, 0x42, 0x40, 0x42, 0x41, 0x42, 0x42, 0x42, 0x43, 0x42, 0x44, 0x42, 0x45,\n\t0x42, 0x46, 0x42, 0x47, 0x42, 0x48, 0x42, 0x49, 0x42, 0x4A, 0x42, 0x4B, 0x42, 0x4C, 0x42, 0x4D, 0x42, 0x4E, 0x42, 0x4F, 0x42, 0x50, 0x42, 0x51, 0x42, 0x52, 0x42, 0x53, 0x42, 0x54, 0x42, 0x55,\n\t0x42, 0x56, 0x42, 0x57, 0x42, 0x58, 0x42, 0x59, 0x42, 0x5A, 0x42, 0x5B, 0x42, 0x5C, 0x42, 0x5D, 0x42, 0x5E, 0x42, 0x5F, 0x42, 0x60, 0x42, 0x61, 0x42, 0x62, 0x42, 0x63, 0x42, 0x64, 0x42, 0x65,\n\t0x42, 0x66, 0x42, 0x67, 0x42, 0x68, 0x42, 0x69, 0x42, 0x6A, 0x42, 0x6B, 0x42, 0x6C, 0x42, 0x6D, 0x42, 0x6E, 0x42, 0x6F, 0x42, 0x70, 0x42, 0x71, 0x42, 0x72, 0x42, 0x73, 0x42, 0x74, 0x42, 0x75,\n\t0x42, 0x76, 0x42, 0x77, 0x42, 0x78, 0x42, 0x79, 0x42, 0x7A, 0x42, 0x7B, 0x42, 0x7C, 0x42, 0x7D, 0x42, 0x7E, 0x42, 0x7F, 0x42, 0x80, 0x42, 0x81, 0x42, 0x82, 0x42, 0x83, 0x42, 0x84, 0x42, 0x85,\n\t0x42, 0x86, 0x42, 0x87, 0x42, 0x88, 0x42, 0x89, 0x42, 0x8A, 0x42, 0x8B, 0x42, 0x8C, 0x42, 0x8D, 0x42, 0x8E, 0x42, 0x8F, 0x42, 0x90, 0x42, 0x91, 0x42, 0x92, 0x42, 0x93, 0x42, 0x94, 0x42, 0x95,\n\t0x42, 0x96, 0x42, 0x97, 0x42, 0x98, 0x42, 0x99, 0x42, 0x9A, 0x42, 0x9B, 0x42, 0x9C, 0x42, 0x9D, 0x42, 0x9E, 0x42, 0x9F, 0x42, 0xA0, 0x42, 0xA1, 0x42, 0xA2, 0x42, 0xA3, 0x42, 0xA4, 0x42, 0xA5,\n\t0x42, 0xA6, 0x42, 0xA7, 0x42, 0xA8, 0x42, 0xA9, 0x42, 0xAA, 0x42, 0xAB, 0x42, 0xAC, 0x42, 0xAD, 0x42, 0xAE, 0x42, 0xAF, 0x42, 0xB0, 0x42, 0xB1, 0x42, 0xB2, 0x42, 0xB3, 0x42, 0xB4, 0x42, 0xB5,\n\t0x42, 0xB6, 0x42, 0xB7, 0x42, 0xB8, 0x42, 0xB9, 0x42, 0xBA, 0x42, 0xBB, 0x42, 0xBC, 0x42, 0xBD, 0x42, 0xBE, 0x42, 0xBF, 0x42, 0xC0, 0x42, 0xC1, 0x42, 0xC3, 0x42, 0xC4, 0x42, 0xC5, 0x42, 0xC6,\n\t0x42, 0xC7, 0x42, 0xC8, 0x42, 0xC9, 0x42, 0xCA, 0x42, 0xCB, 0x42, 0xCC, 0x42, 0xCD, 0x42, 0xCE, 0x42, 0xCF, 0x42, 0xD0, 0x42, 0xD1, 0x42, 0xD2, 0x42, 0xD3, 0x42, 0xD4, 0x42, 0xD5, 0x42, 0xD6,\n\t0x42, 0xD7, 0x42, 0xD8, 0x42, 0xD9, 0x42, 0xDA, 0x42, 0xDB, 0x42, 0xDC, 0x42, 0xDD, 0x42, 0xDE, 0x42, 0xDF, 0x42, 0xE0, 0x42, 0xE1, 0x42, 0xE2, 0x42, 0xE3, 0x42, 0xE4, 0x42, 0xE5, 0x42, 0xE6,\n\t0x42, 0xE7, 0x42, 0xE8, 0x42, 0xE9, 0x42, 0xEA, 0x42, 0xEB, 0x42, 0xEC, 0x42, 0xED, 0x42, 0xEE, 0x42, 0xEF, 0x42, 0xF0, 0x42, 0xF1, 0x42, 0xF2, 0x42, 0xF3, 0x42, 0xF4, 0x42, 0xF5, 0x42, 0xF6,\n\t0x42, 0xF7, 0x42, 0xF8, 0x42, 0xF9, 0x42, 0xFA, 0x42, 0xFB, 0x42, 0xFC, 0x42, 0xFD, 0x42, 0xFE, 0x42, 0xFF, 0x42, 0x00, 0x43, 0x01, 0x43, 0x02, 0x43, 0x03, 0x43, 0x04, 0x43, 0x05, 0x43, 0x06,\n\t0x43, 0x07, 0x43, 0x08, 0x43, 0x09, 0x43, 0x0A, 0x43, 0x0B, 0x43, 0x0C, 0x43, 0x0D, 0x43, 0x0E, 0x43, 0x0F, 0x43, 0x10, 0x43, 0x11, 0x43, 0x12, 0x43, 0x13, 0x43, 0x14, 0x43, 0x15, 0x43, 0x16,\n\t0x43, 0x17, 0x43, 0x18, 0x43, 0x19, 0x43, 0x1A, 0x43, 0x1B, 0x43, 0x1C, 0x43, 0x1D, 0x43, 0x1E, 0x43, 0x1F, 0x43, 0x20, 0x43, 0x21, 0x43, 0x22, 0x43, 0x23, 0x43, 0x24, 0x43, 0x25, 0x43, 0x26,\n\t0x43, 0x27, 0x43, 0x28, 0x43, 0x29, 0x43, 0x2A, 0x43, 0x2B, 0x43, 0x2C, 0x43, 0x2D, 0x43, 0x2E, 0x43, 0x2F, 0x43, 0x30, 0x43, 0x31, 0x43, 0x32, 0x43, 0x33, 0x43, 0x34, 0x43, 0x35, 0x43, 0x36,\n\t0x43, 0x37, 0x43, 0x38, 0x43, 0x39, 0x43, 0x3A, 0x43, 0x3B, 0x43, 0x3C, 0x43, 0x3D, 0x43, 0x3E, 0x43, 0x3F, 0x43, 0x40, 0x43, 0x41, 0x43, 0x42, 0x43, 0x43, 0x43, 0x44, 0x43, 0x45, 0x43, 0x46,\n\t0x43, 0x47, 0x43, 0x48, 0x43, 0x49, 0x43, 0x4A, 0x43, 0x4B, 0x43, 0x4C, 0x43, 0x4D, 0x43, 0x4E, 0x43, 0x4F, 0x43, 0x50, 0x43, 0x51, 0x43, 0x52, 0x43, 0x53, 0x43, 0x54, 0x43, 0x55, 0x43, 0x56,\n\t0x43, 0x57, 0x43, 0x58, 0x43, 0x59, 0x43, 0x5A, 0x43, 0x5B, 0x43, 0x5C, 0x43, 0x5D, 0x43, 0x5E, 0x43, 0x5F, 0x43, 0x60, 0x43, 0x61, 0x43, 0x62, 0x43, 0x63, 0x43, 0x64, 0x43, 0x65, 0x43, 0x66,\n\t0x43, 0x67, 0x43, 0x68, 0x43, 0x69, 0x43, 0x6A, 0x43, 0x6B, 0x43, 0x6C, 0x43, 0x6D, 0x43, 0x6E, 0x43, 0x6F, 0x43, 0x70, 0x43, 0x71, 0x43, 0x72, 0x43, 0x73, 0x43, 0x74, 0x43, 0x75, 0x43, 0x76,\n\t0x43, 0x77, 0x43, 0x78, 0x43, 0x79, 0x43, 0x7A, 0x43, 0x7B, 0x43, 0x7C, 0x43, 0x7D, 0x43, 0x7E, 0x43, 0x7F, 0x43, 0x80, 0x43, 0x81, 0x43, 0x82, 0x43, 0x83, 0x43, 0x84, 0x43, 0x85, 0x43, 0x86,\n\t0x43, 0x87, 0x43, 0x88, 0x43, 0x89, 0x43, 0x8A, 0x43, 0x8B, 0x43, 0x8C, 0x43, 0x8D, 0x43, 0x8E, 0x43, 0x8F, 0x43, 0x90, 0x43, 0x91, 0x43, 0x92, 0x43, 0x93, 0x43, 0x94, 0x43, 0x95, 0x43, 0x96,\n\t0x43, 0x97, 0x43, 0x98, 0x43, 0x99, 0x43, 0x9A, 0x43, 0x9B, 0x43, 0x9C, 0x43, 0x9D, 0x43, 0x9E, 0x43, 0x9F, 0x43, 0xA0, 0x43, 0xA1, 0x43, 0xA2, 0x43, 0xA3, 0x43, 0xA4, 0x43, 0xA5, 0x43, 0xA6,\n\t0x43, 0xA7, 0x43, 0xA8, 0x43, 0xA9, 0x43, 0xAA, 0x43, 0xAB, 0x43, 0xAC, 0x43, 0xAD, 0x43, 0xAE, 0x43, 0xAF, 0x43, 0xB0, 0x43, 0xB1, 0x43, 0xB2, 0x43, 0xB3, 0x43, 0xB4, 0x43, 0xB5, 0x43, 0xB6,\n\t0x43, 0xB7, 0x43, 0xB8, 0x43, 0xB9, 0x43, 0xBA, 0x43, 0xBB, 0x43, 0xBC, 0x43, 0xBD, 0x43, 0xBE, 0x43, 0xBF, 0x43, 0xC0, 0x43, 0xC1, 0x43, 0xC2, 0x43, 0xC3, 0x43, 0xC4, 0x43, 0xC5, 0x43, 0xC6,\n\t0x43, 0xC7, 0x43, 0xC8, 0x43, 0xC9, 0x43, 0xCA, 0x43, 0xCB, 0x43, 0xCC, 0x43, 0xCD, 0x43, 0xCE, 0x43, 0xCF, 0x43, 0xD0, 0x43, 0xD1, 0x43, 0xD2, 0x43, 0xD3, 0x43, 0xD4, 0x43, 0xD5, 0x43, 0xD6,\n\t0x43, 0xD7, 0x43, 0xD8, 0x43, 0xD9, 0x43, 0xDA, 0x43, 0xDB, 0x43, 0xDC, 0x43, 0xDD, 0x43, 0xDE, 0x43, 0xDF, 0x43, 0xE0, 0x43, 0xE1, 0x43, 0xE2, 0x43, 0xE3, 0x43, 0xE4, 0x43, 0xE5, 0x43, 0xE6,\n\t0x43, 0xE7, 0x43, 0xE8, 0x43, 0xE9, 0x43, 0xEA, 0x43, 0xEB, 0x43, 0xEC, 0x43, 0xED, 0x43, 0xEE, 0x43, 0xEF, 0x43, 0xF0, 0x43, 0xF1, 0x43, 0xF2, 0x43, 0xF3, 0x43, 0xF4, 0x43, 0xF5, 0x43, 0xF6,\n\t0x43, 0xF7, 0x43, 0xF8, 0x43, 0xF9, 0x43, 0xFA, 0x43, 0xFB, 0x43, 0xFC, 0x43, 0xFD, 0x43, 0xFE, 0x43, 0xFF, 0x43, 0x00, 0x44, 0x01, 0x44, 0x02, 0x44, 0x03, 0x44, 0x04, 0x44, 0x05, 0x44, 0x06,\n\t0x44, 0x07, 0x44, 0x08, 0x44, 0x09, 0x44, 0x0A, 0x44, 0x0B, 0x44, 0x0C, 0x44, 0x0D, 0x44, 0x0E, 0x44, 0x0F, 0x44, 0x10, 0x44, 0x11, 0x44, 0x12, 0x44, 0x13, 0x44, 0x14, 0x44, 0x15, 0x44, 0x16,\n\t0x44, 0x17, 0x44, 0x18, 0x44, 0x19, 0x44, 0x1A, 0x44, 0x1B, 0x44, 0x1C, 0x44, 0x1D, 0x44, 0x1E, 0x44, 0x1F, 0x44, 0x20, 0x44, 0x21, 0x44, 0x22, 0x44, 0x23, 0x44, 0x24, 0x44, 0x25, 0x44, 0x26,\n\t0x44, 0x27, 0x44, 0x28, 0x44, 0x29, 0x44, 0x2A, 0x44, 0x2B, 0x44, 0x2C, 0x44, 0x2D, 0x44, 0x2E, 0x44, 0x2F, 0x44, 0x30, 0x44, 0x31, 0x44, 0x32, 0x44, 0x33, 0x44, 0x34, 0x44, 0x35, 0x44, 0x36,\n\t0x44, 0x37, 0x44, 0x38, 0x44, 0x39, 0x44, 0x3A, 0x44, 0x3B, 0x44, 0x3C, 0x44, 0x3D, 0x44, 0x3E, 0x44, 0x3F, 0x44, 0x40, 0x44, 0x41, 0x44, 0x42, 0x44, 0x43, 0x44, 0x44, 0x44, 0x45, 0x44, 0x46,\n\t0x44, 0x47, 0x44, 0x48, 0x44, 0x49, 0x44, 0x4A, 0x44, 0x4B, 0x44, 0x4C, 0x44, 0x4D, 0x44, 0x4E, 0x44, 0x4F, 0x44, 0x50, 0x44, 0x51, 0x44, 0x52, 0x44, 0x53, 0x44, 0x54, 0x44, 0x55, 0x44, 0x56,\n\t0x44, 0x57, 0x44, 0x58, 0x44, 0x59, 0x44, 0x5A, 0x44, 0x5B, 0x44, 0x5C, 0x44, 0x5D, 0x44, 0x5E, 0x44, 0x5F, 0x44, 0x60, 0x44, 0x61, 0x44, 0x62, 0x44, 0x63, 0x44, 0x64, 0x44, 0x65, 0x44, 0x66,\n\t0x44, 0x67, 0x44, 0x68, 0x44, 0x69, 0x44, 0x6A, 0x44, 0x6B, 0x44, 0x6C, 0x44, 0x6D, 0x44, 0x6E, 0x44, 0x6F, 0x44, 0x70, 0x44, 0x71, 0x44, 0x72, 0x44, 0x73, 0x44, 0x74, 0x44, 0x75, 0x44, 0x76,\n\t0x44, 0x77, 0x44, 0x78, 0x44, 0x79, 0x44, 0x7A, 0x44, 0x7B, 0x44, 0x7C, 0x44, 0x7D, 0x44, 0x7E, 0x44, 0x7F, 0x44, 0x80, 0x44, 0x81, 0x44, 0x82, 0x44, 0x83, 0x44, 0x84, 0x44, 0x85, 0x44, 0x86,\n\t0x44, 0x87, 0x44, 0x88, 0x44, 0x89, 0x44, 0x8A, 0x44, 0x8B, 0x44, 0x8C, 0x44, 0x8D, 0x44, 0x8E, 0x44, 0x8F, 0x44, 0x90, 0x44, 0x91, 0x44, 0x92, 0x44, 0x93, 0x44, 0x94, 0x44, 0x95, 0x44, 0x96,\n\t0x44, 0x97, 0x44, 0x98, 0x44, 0x99, 0x44, 0x9A, 0x44, 0x9B, 0x44, 0x9C, 0x44, 0x9D, 0x44, 0x9E, 0x44, 0x9F, 0x44, 0xA0, 0x44, 0xA1, 0x44, 0xA2, 0x44, 0xA3, 0x44, 0xA4, 0x44, 0xA5, 0x44, 0xA6,\n\t0x44, 0xA7, 0x44, 0xA8, 0x44, 0xA9, 0x44, 0xAA, 0x44, 0xAB, 0x44, 0xAC, 0x44, 0xAD, 0x44, 0xAE, 0x44, 0xAF, 0x44, 0xB0, 0x44, 0xB1, 0x44, 0xB2, 0x44, 0xB3, 0x44, 0xB4, 0x44, 0xB5, 0x44, 0xB6,\n\t0x44, 0xB7, 0x44, 0xB8, 0x44, 0xB9, 0x44, 0xBA, 0x44, 0xBB, 0x44, 0xBC, 0x44, 0xBD, 0x44, 0xBE, 0x44, 0xBF, 0x44, 0xC0, 0x44, 0xC1, 0x44, 0xC2, 0x44, 0xC3, 0x44, 0xC4, 0x44, 0xC5, 0x44, 0xC6,\n\t0x44, 0xC7, 0x44, 0xC8, 0x44, 0xC9, 0x44, 0xCA, 0x44, 0xCB, 0x44, 0xCC, 0x44, 0xCD, 0x44, 0xCE, 0x44, 0xCF, 0x44, 0xD0, 0x44, 0xD1, 0x44, 0xD2, 0x44, 0xD3, 0x44, 0xD4, 0x44, 0xD5, 0x44, 0xD6,\n\t0x44, 0xD7, 0x44, 0xD8, 0x44, 0xD9, 0x44, 0xDA, 0x44, 0xDB, 0x44, 0xDC, 0x44, 0xDD, 0x44, 0xDE, 0x44, 0xDF, 0x44, 0xE0, 0x44, 0xE1, 0x44, 0xE2, 0x44, 0xE3, 0x44, 0xE4, 0x44, 0xE5, 0x44, 0xE6,\n\t0x44, 0xE7, 0x44, 0xE8, 0x44, 0xE9, 0x44, 0xEA, 0x44, 0xEB, 0x44, 0xEC, 0x44, 0xED, 0x44, 0xEE, 0x44, 0xEF, 0x44, 0xF0, 0x44, 0xF1, 0x44, 0xF2, 0x44, 0xF3, 0x44, 0xF4, 0x44, 0xF5, 0x44, 0xF6,\n\t0x44, 0xF7, 0x44, 0xF8, 0x44, 0xF9, 0x44, 0xFA, 0x44, 0xFB, 0x44, 0xFC, 0x44, 0xFD, 0x44, 0xFE, 0x44, 0xFF, 0x44, 0x00, 0x45, 0x01, 0x45, 0x02, 0x45, 0x03, 0x45, 0x04, 0x45, 0x05, 0x45, 0x06,\n\t0x45, 0x07, 0x45, 0x08, 0x45, 0x09, 0x45, 0x0A, 0x45, 0x0B, 0x45, 0x0C, 0x45, 0x0D, 0x45, 0x0E, 0x45, 0x0F, 0x45, 0x10, 0x45, 0x11, 0x45, 0x12, 0x45, 0x13, 0x45, 0x14, 0x45, 0x15, 0x45, 0x16,\n\t0x45, 0x17, 0x45, 0x18, 0x45, 0x19, 0x45, 0x1A, 0x45, 0x1B, 0x45, 0x1C, 0x45, 0x1D, 0x45, 0x1E, 0x45, 0x1F, 0x45, 0x20, 0x45, 0x21, 0x45, 0x22, 0x45, 0x23, 0x45, 0x24, 0x45, 0x25, 0x45, 0x26,\n\t0x45, 0x27, 0x45, 0x28, 0x45, 0x29, 0x45, 0x2A, 0x45, 0x2B, 0x45, 0x2C, 0x45, 0x2D, 0x45, 0x2E, 0x45, 0x2F, 0x45, 0x30, 0x45, 0x31, 0x45, 0x32, 0x45, 0x33, 0x45, 0x34, 0x45, 0x35, 0x45, 0x36,\n\t0x45, 0x37, 0x45, 0x38, 0x45, 0x39, 0x45, 0x3A, 0x45, 0x3B, 0x45, 0x3C, 0x45, 0x3D, 0x45, 0x3E, 0x45, 0x3F, 0x45, 0x40, 0x45, 0x41, 0x45, 0x42, 0x45, 0x43, 0x45, 0x44, 0x45, 0x45, 0x45, 0x46,\n\t0x45, 0x47, 0x45, 0x48, 0x45, 0x49, 0x45, 0x4A, 0x45, 0x4B, 0x45, 0x4C, 0x45, 0x4D, 0x45, 0x4E, 0x45, 0x4F, 0x45, 0x50, 0x45, 0x51, 0x45, 0x52, 0x45, 0x53, 0x45, 0x54, 0x45, 0x55, 0x45, 0x56,\n\t0x45, 0x57, 0x45, 0x58, 0x45, 0x59, 0x45, 0x5A, 0x45, 0x5B, 0x45, 0x5C, 0x45, 0x5D, 0x45, 0x5E, 0x45, 0x5F, 0x45, 0x60, 0x45, 0x61, 0x45, 0x62, 0x45, 0x63, 0x45, 0x64, 0x45, 0x65, 0x45, 0x66,\n\t0x45, 0x67, 0x45, 0x68, 0x45, 0x69, 0x45, 0x6A, 0x45, 0x6B, 0x45, 0x6C, 0x45, 0x6D, 0x45, 0x6E, 0x45, 0x6F, 0x45, 0x70, 0x45, 0x71, 0x45, 0x72, 0x45, 0x73, 0x45, 0x74, 0x45, 0x75, 0x45, 0x76,\n\t0x45, 0x77, 0x45, 0x78, 0x45, 0x79, 0x45, 0x7A, 0x45, 0x7B, 0x45, 0x7C, 0x45, 0x7D, 0x45, 0x7E, 0x45, 0x7F, 0x45, 0x80, 0x45, 0x81, 0x45, 0x82, 0x45, 0x83, 0x45, 0x84, 0x45, 0x85, 0x45, 0x86,\n\t0x45, 0x87, 0x45, 0x88, 0x45, 0x89, 0x45, 0x8A, 0x45, 0x8B, 0x45, 0x8C, 0x45, 0x8D, 0x45, 0x8E, 0x45, 0x8F, 0x45, 0x90, 0x45, 0x91, 0x45, 0x92, 0x45, 0x93, 0x45, 0x94, 0x45, 0x95, 0x45, 0x96,\n\t0x45, 0x97, 0x45, 0x98, 0x45, 0x99, 0x45, 0x9A, 0x45, 0x9B, 0x45, 0x9C, 0x45, 0x9D, 0x45, 0x9E, 0x45, 0x9F, 0x45, 0xA0, 0x45, 0xA1, 0x45, 0xA2, 0x45, 0xA3, 0x45, 0xA4, 0x45, 0xA5, 0x45, 0xA6,\n\t0x45, 0xA7, 0x45, 0xA8, 0x45, 0xA9, 0x45, 0xAA, 0x45, 0xAB, 0x45, 0xAC, 0x45, 0xAD, 0x45, 0xAE, 0x45, 0xAF, 0x45, 0xB0, 0x45, 0xB1, 0x45, 0xB2, 0x45, 0xB3, 0x45, 0xB4, 0x45, 0xB5, 0x45, 0xB6,\n\t0x45, 0xB7, 0x45, 0xB8, 0x45, 0xB9, 0x45, 0xBA, 0x45, 0xBB, 0x45, 0xBC, 0x45, 0xBD, 0x45, 0xBE, 0x45, 0xBF, 0x45, 0xC0, 0x45, 0xC1, 0x45, 0xC2, 0x45, 0xC3, 0x45, 0xC4, 0x45, 0xC5, 0x45, 0xC6,\n\t0x45, 0xC7, 0x45, 0xC8, 0x45, 0xC9, 0x45, 0xCA, 0x45, 0xCB, 0x45, 0xCC, 0x45, 0xCD, 0x45, 0xCE, 0x45, 0xCF, 0x45, 0xD0, 0x45, 0xD1, 0x45, 0xD2, 0x45, 0xD3, 0x45, 0xD4, 0x45, 0xD5, 0x45, 0xD6,\n\t0x45, 0xD7, 0x45, 0xD8, 0x45, 0xD9, 0x45, 0xDA, 0x45, 0xDB, 0x45, 0xDC, 0x45, 0xDD, 0x45, 0xDE, 0x45, 0xDF, 0x45, 0xE0, 0x45, 0xE1, 0x45, 0xE2, 0x45, 0xE3, 0x45, 0xE4, 0x45, 0xE5, 0x45, 0xE6,\n\t0x45, 0xE7, 0x45, 0xE8, 0x45, 0xE9, 0x45, 0xEA, 0x45, 0xEB, 0x45, 0xEC, 0x45, 0xED, 0x45, 0xEE, 0x45, 0xEF, 0x45, 0xF0, 0x45, 0xF1, 0x45, 0xF2, 0x45, 0xF3, 0x45, 0xF4, 0x45, 0xF5, 0x45, 0xF6,\n\t0x45, 0xF7, 0x45, 0xF8, 0x45, 0xF9, 0x45, 0xFA, 0x45, 0xFB, 0x45, 0xFC, 0x45, 0xFD, 0x45, 0xFE, 0x45, 0xFF, 0x45, 0x00, 0x46, 0x01, 0x46, 0x02, 0x46, 0x03, 0x46, 0x04, 0x46, 0x05, 0x46, 0x06,\n\t0x46, 0x07, 0x46, 0x08, 0x46, 0x09, 0x46, 0x0A, 0x46, 0x0B, 0x46, 0x0C, 0x46, 0x0D, 0x46, 0x0E, 0x46, 0x0F, 0x46, 0x10, 0x46, 0x11, 0x46, 0x12, 0x46, 0x13, 0x46, 0x14, 0x46, 0x15, 0x46, 0x16,\n\t0x46, 0x17, 0x46, 0x18, 0x46, 0x19, 0x46, 0x1A, 0x46, 0x1B, 0x46, 0x1C, 0x46, 0x1D, 0x46, 0x1E, 0x46, 0x1F, 0x46, 0x20, 0x46, 0x21, 0x46, 0x22, 0x46, 0x23, 0x46, 0x24, 0x46, 0x25, 0x46, 0x26,\n\t0x46, 0x27, 0x46, 0x28, 0x46, 0x29, 0x46, 0x2A, 0x46, 0x2B, 0x46, 0x2C, 0x46, 0x2D, 0x46, 0x2E, 0x46, 0x2F, 0x46, 0x30, 0x46, 0x31, 0x46, 0x32, 0x46, 0x33, 0x46, 0x34, 0x46, 0x35, 0x46, 0x36,\n\t0x46, 0x37, 0x46, 0x38, 0x46, 0x39, 0x46, 0x3A, 0x46, 0x3B, 0x46, 0x3C, 0x46, 0x3D, 0x46, 0x3E, 0x46, 0x3F, 0x46, 0x40, 0x46, 0x41, 0x46, 0x42, 0x46, 0x43, 0x46, 0x44, 0x46, 0x45, 0x46, 0x46,\n\t0x46, 0x47, 0x46, 0x48, 0x46, 0x49, 0x46, 0x4A, 0x46, 0x4B, 0x46, 0x4C, 0x46, 0x4D, 0x46, 0x4E, 0x46, 0x4F, 0x46, 0x50, 0x46, 0x51, 0x46, 0x52, 0x46, 0x53, 0x46, 0x54, 0x46, 0x55, 0x46, 0x56,\n\t0x46, 0x57, 0x46, 0x58, 0x46, 0x59, 0x46, 0x5A, 0x46, 0x5B, 0x46, 0x5C, 0x46, 0x5D, 0x46, 0x5E, 0x46, 0x5F, 0x46, 0x60, 0x46, 0x61, 0x46, 0x62, 0x46, 0x63, 0x46, 0x64, 0x46, 0x65, 0x46, 0x66,\n\t0x46, 0x67, 0x46, 0x68, 0x46, 0x69, 0x46, 0x6A, 0x46, 0x6B, 0x46, 0x6C, 0x46, 0x6D, 0x46, 0x6E, 0x46, 0x6F, 0x46, 0x70, 0x46, 0x71, 0x46, 0x72, 0x46, 0x73, 0x46, 0x74, 0x46, 0x75, 0x46, 0x76,\n\t0x46, 0x77, 0x46, 0x78, 0x46, 0x79, 0x46, 0x7A, 0x46, 0x7B, 0x46, 0x7C, 0x46, 0x7D, 0x46, 0x7E, 0x46, 0x7F, 0x46, 0x80, 0x46, 0x81, 0x46, 0x82, 0x46, 0x83, 0x46, 0x84, 0x46, 0x85, 0x46, 0x86,\n\t0x46, 0x87, 0x46, 0x88, 0x46, 0x89, 0x46, 0x8A, 0x46, 0x8B, 0x46, 0x8C, 0x46, 0x8D, 0x46, 0x8E, 0x46, 0x8F, 0x46, 0x90, 0x46, 0x91, 0x46, 0x92, 0x46, 0x93, 0x46, 0x94, 0x46, 0x95, 0x46, 0x96,\n\t0x46, 0x97, 0x46, 0x98, 0x46, 0x99, 0x46, 0x9A, 0x46, 0x9B, 0x46, 0x9C, 0x46, 0x9D, 0x46, 0x9E, 0x46, 0x9F, 0x46, 0xA0, 0x46, 0xA1, 0x46, 0xA2, 0x46, 0xA3, 0x46, 0xA4, 0x46, 0xA5, 0x46, 0xA6,\n\t0x46, 0xA7, 0x46, 0xA8, 0x46, 0xA9, 0x46, 0xAA, 0x46, 0xAB, 0x46, 0xAC, 0x46, 0xAD, 0x46, 0xAE, 0x46, 0xAF, 0x46, 0xB0, 0x46, 0xB1, 0x46, 0xB2, 0x46, 0xB3, 0x46, 0xB4, 0x46, 0xB5, 0x46, 0xB6,\n\t0x46, 0xB7, 0x46, 0xB8, 0x46, 0xB9, 0x46, 0xBA, 0x46, 0xBB, 0x46, 0xBC, 0x46, 0xBD, 0x46, 0xBE, 0x46, 0xBF, 0x46, 0xC0, 0x46, 0xC1, 0x46, 0xC2, 0x46, 0xC3, 0x46, 0xC4, 0x46, 0xC5, 0x46, 0xC6,\n\t0x46, 0xC7, 0x46, 0xC8, 0x46, 0xC9, 0x46, 0xCA, 0x46, 0xCB, 0x46, 0xCC, 0x46, 0xCD, 0x46, 0xCE, 0x46, 0xCF, 0x46, 0xD0, 0x46, 0xD1, 0x46, 0xD2, 0x46, 0xD3, 0x46, 0xD4, 0x46, 0xD5, 0x46, 0xD6,\n\t0x46, 0xD7, 0x46, 0xD8, 0x46, 0xD9, 0x46, 0xDA, 0x46, 0xDB, 0x46, 0xDC, 0x46, 0xDD, 0x46, 0xDE, 0x46, 0xDF, 0x46, 0xE0, 0x46, 0xE1, 0x46, 0xE2, 0x46, 0xE3, 0x46, 0xE4, 0x46, 0xE5, 0x46, 0xE6,\n\t0x46, 0xE7, 0x46, 0xE8, 0x46, 0xE9, 0x46, 0xEA, 0x46, 0xEB, 0x46, 0xEC, 0x46, 0xED, 0x46, 0xEE, 0x46, 0xEF, 0x46, 0xF0, 0x46, 0xF1, 0x46, 0xF2, 0x46, 0xF3, 0x46, 0xF4, 0x46, 0xF5, 0x46, 0xF6,\n\t0x46, 0xF7, 0x46, 0xF8, 0x46, 0xF9, 0x46, 0xFA, 0x46, 0xFB, 0x46, 0xFC, 0x46, 0xFD, 0x46, 0xFE, 0x46, 0xFF, 0x46, 0x00, 0x47, 0x01, 0x47, 0x02, 0x47, 0x03, 0x47, 0x04, 0x47, 0x05, 0x47, 0x06,\n\t0x47, 0x07, 0x47, 0x08, 0x47, 0x09, 0x47, 0x0A, 0x47, 0x0B, 0x47, 0x0C, 0x47, 0x0D, 0x47, 0x0E, 0x47, 0x0F, 0x47, 0x10, 0x47, 0x11, 0x47, 0x12, 0x47, 0x13, 0x47, 0x14, 0x47, 0x15, 0x47, 0x16,\n\t0x47, 0x17, 0x47, 0x18, 0x47, 0x19, 0x47, 0x1A, 0x47, 0x1B, 0x47, 0x1C, 0x47, 0x1D, 0x47, 0x1E, 0x47, 0x1F, 0x47, 0x20, 0x47, 0x21, 0x47, 0x22, 0x47, 0x23, 0x47, 0x24, 0x47, 0x25, 0x47, 0x26,\n\t0x47, 0x27, 0x47, 0x28, 0x47, 0x29, 0x47, 0x2A, 0x47, 0x2B, 0x47, 0x2C, 0x47, 0x2D, 0x47, 0x2E, 0x47, 0x2F, 0x47, 0x30, 0x47, 0x31, 0x47, 0x32, 0x47, 0x33, 0x47, 0x34, 0x47, 0x35, 0x47, 0x36,\n\t0x47, 0x37, 0x47, 0x38, 0x47, 0x39, 0x47, 0x3A, 0x47, 0x3B, 0x47, 0x3C, 0x47, 0x3D, 0x47, 0x3E, 0x47, 0x3F, 0x47, 0x40, 0x47, 0x41, 0x47, 0x42, 0x47, 0x43, 0x47, 0x44, 0x47, 0x45, 0x47, 0x46,\n\t0x47, 0x47, 0x47, 0x48, 0x47, 0x49, 0x47, 0x4A, 0x47, 0x4B, 0x47, 0x4C, 0x47, 0x4D, 0x47, 0x4E, 0x47, 0x4F, 0x47, 0x50, 0x47, 0x51, 0x47, 0x52, 0x47, 0x53, 0x47, 0x54, 0x47, 0x55, 0x47, 0x56,\n\t0x47, 0x57, 0x47, 0x58, 0x47, 0x59, 0x47, 0x5A, 0x47, 0x5B, 0x47, 0x5C, 0x47, 0x5D, 0x47, 0x5E, 0x47, 0x5F, 0x47, 0x60, 0x47, 0x61, 0x47, 0x62, 0x47, 0x63, 0x47, 0x64, 0x47, 0x65, 0x47, 0x66,\n\t0x47, 0x67, 0x47, 0x68, 0x47, 0x69, 0x47, 0x6A, 0x47, 0x6B, 0x47, 0x6C, 0x47, 0x6D, 0x47, 0x6E, 0x47, 0x6F, 0x47, 0x70, 0x47, 0x71, 0x47, 0x72, 0x47, 0x73, 0x47, 0x74, 0x47, 0x75, 0x47, 0x76,\n\t0x47, 0x77, 0x47, 0x78, 0x47, 0x79, 0x47, 0x7A, 0x47, 0x7B, 0x47, 0x7C, 0x47, 0x7D, 0x47, 0x7E, 0x47, 0x7F, 0x47, 0x80, 0x47, 0x81, 0x47, 0x82, 0x47, 0x83, 0x47, 0x84, 0x47, 0x85, 0x47, 0x86,\n\t0x47, 0x87, 0x47, 0x88, 0x47, 0x89, 0x47, 0x8A, 0x47, 0x8B, 0x47, 0x8C, 0x47, 0x8D, 0x47, 0x8E, 0x47, 0x8F, 0x47, 0x90, 0x47, 0x91, 0x47, 0x92, 0x47, 0x93, 0x47, 0x94, 0x47, 0x95, 0x47, 0x96,\n\t0x47, 0x97, 0x47, 0x98, 0x47, 0x99, 0x47, 0x9A, 0x47, 0x9B, 0x47, 0x9C, 0x47, 0x9D, 0x47, 0x9E, 0x47, 0x9F, 0x47, 0xA0, 0x47, 0xA1, 0x47, 0xA2, 0x47, 0xA3, 0x47, 0xA4, 0x47, 0xA5, 0x47, 0xA6,\n\t0x47, 0xA7, 0x47, 0xA8, 0x47, 0xA9, 0x47, 0xAA, 0x47, 0xAB, 0x47, 0xAC, 0x47, 0xAD, 0x47, 0xAE, 0x47, 0xAF, 0x47, 0xB0, 0x47, 0xB1, 0x47, 0xB2, 0x47, 0xB3, 0x47, 0xB4, 0x47, 0xB5, 0x47, 0xB6,\n\t0x47, 0xB7, 0x47, 0xB8, 0x47, 0xB9, 0x47, 0xBA, 0x47, 0xBB, 0x47, 0xBC, 0x47, 0xBD, 0x47, 0xBE, 0x47, 0xBF, 0x47, 0xC0, 0x47, 0xC1, 0x47, 0xC2, 0x47, 0xC3, 0x47, 0xC4, 0x47, 0xC5, 0x47, 0xC6,\n\t0x47, 0xC7, 0x47, 0xC8, 0x47, 0xC9, 0x47, 0xCA, 0x47, 0xCB, 0x47, 0xCC, 0x47, 0xCD, 0x47, 0xCE, 0x47, 0xCF, 0x47, 0xD0, 0x47, 0xD1, 0x47, 0xD2, 0x47, 0xD3, 0x47, 0xD4, 0x47, 0xD5, 0x47, 0xD6,\n\t0x47, 0xD7, 0x47, 0xD8, 0x47, 0xD9, 0x47, 0xDA, 0x47, 0xDB, 0x47, 0xDC, 0x47, 0xDD, 0x47, 0xDE, 0x47, 0xDF, 0x47, 0xE0, 0x47, 0xE1, 0x47, 0xE2, 0x47, 0xE3, 0x47, 0xE4, 0x47, 0xE5, 0x47, 0xE6,\n\t0x47, 0xE7, 0x47, 0xE8, 0x47, 0xE9, 0x47, 0xEA, 0x47, 0xEB, 0x47, 0xEC, 0x47, 0xED, 0x47, 0xEE, 0x47, 0xEF, 0x47, 0xF0, 0x47, 0xF1, 0x47, 0xF2, 0x47, 0xF3, 0x47, 0xF4, 0x47, 0xF5, 0x47, 0xF6,\n\t0x47, 0xF7, 0x47, 0xF8, 0x47, 0xF9, 0x47, 0xFA, 0x47, 0xFB, 0x47, 0xFC, 0x47, 0xFD, 0x47, 0xFE, 0x47, 0xFF, 0x47, 0x00, 0x48, 0x01, 0x48, 0x02, 0x48, 0x03, 0x48, 0x04, 0x48, 0x05, 0x48, 0x06,\n\t0x48, 0x07, 0x48, 0x08, 0x48, 0x09, 0x48, 0x0A, 0x48, 0x0B, 0x48, 0x0C, 0x48, 0x0D, 0x48, 0x0E, 0x48, 0x0F, 0x48, 0x10, 0x48, 0x11, 0x48, 0x12, 0x48, 0x13, 0x48, 0x14, 0x48, 0x15, 0x48, 0x16,\n\t0x48, 0x17, 0x48, 0x18, 0x48, 0x19, 0x48, 0x1A, 0x48, 0x1B, 0x48, 0x1C, 0x48, 0x1D, 0x48, 0x1E, 0x48, 0x1F, 0x48, 0x20, 0x48, 0x21, 0x48, 0x22, 0x48, 0x23, 0x48, 0x24, 0x48, 0x25, 0x48, 0x26,\n\t0x48, 0x27, 0x48, 0x28, 0x48, 0x29, 0x48, 0x2A, 0x48, 0x2B, 0x48, 0x2C, 0x48, 0x2D, 0x48, 0x2E, 0x48, 0x2F, 0x48, 0x30, 0x48, 0x31, 0x48, 0x32, 0x48, 0x33, 0x48, 0x34, 0x48, 0x35, 0x48, 0x36,\n\t0x48, 0x37, 0x48, 0x38, 0x48, 0x39, 0x48, 0x3A, 0x48, 0x3B, 0x48, 0x3C, 0x48, 0x3D, 0x48, 0x3E, 0x48, 0x3F, 0x48, 0x40, 0x48, 0x41, 0x48, 0x42, 0x48, 0x43, 0x48, 0x44, 0x48, 0x45, 0x48, 0x46,\n\t0x48, 0x47, 0x48, 0x48, 0x48, 0x49, 0x48, 0x4A, 0x48, 0x4B, 0x48, 0x4C, 0x48, 0x4D, 0x48, 0x4E, 0x48, 0x4F, 0x48, 0x50, 0x48, 0x51, 0x48, 0x52, 0x48, 0x53, 0x48, 0x54, 0x48, 0x55, 0x48, 0x56,\n\t0x48, 0x57, 0x48, 0x58, 0x48, 0x59, 0x48, 0x5A, 0x48, 0x5B, 0x48, 0x5C, 0x48, 0x5D, 0x48, 0x5E, 0x48, 0x5F, 0x48, 0x60, 0x48, 0x61, 0x48, 0x62, 0x48, 0x63, 0x48, 0x64, 0x48, 0x65, 0x48, 0x66,\n\t0x48, 0x67, 0x48, 0x68, 0x48, 0x69, 0x48, 0x6A, 0x48, 0x6B, 0x48, 0x6C, 0x48, 0x6D, 0x48, 0x6E, 0x48, 0x6F, 0x48, 0x70, 0x48, 0x71, 0x48, 0x72, 0x48, 0x73, 0x48, 0x74, 0x48, 0x75, 0x48, 0x76,\n\t0x48, 0x77, 0x48, 0x78, 0x48, 0x79, 0x48, 0x7A, 0x48, 0x7B, 0x48, 0x7C, 0x48, 0x7D, 0x48, 0x7E, 0x48, 0x7F, 0x48, 0x80, 0x48, 0x81, 0x48, 0x82, 0x48, 0x83, 0x48, 0x84, 0x48, 0x85, 0x48, 0x86,\n\t0x48, 0x87, 0x48, 0x88, 0x48, 0x89, 0x48, 0x8A, 0x48, 0x8B, 0x48, 0x8C, 0x48, 0x8D, 0x48, 0x8E, 0x48, 0x8F, 0x48, 0x90, 0x48, 0x91, 0x48, 0x92, 0x48, 0x93, 0x48, 0x94, 0x48, 0x95, 0x48, 0x96,\n\t0x48, 0x97, 0x48, 0x98, 0x48, 0x99, 0x48, 0x9A, 0x48, 0x9B, 0x48, 0x9C, 0x48, 0x9D, 0x48, 0x9E, 0x48, 0x9F, 0x48, 0xA0, 0x48, 0xA1, 0x48, 0xA2, 0x48, 0xA3, 0x48, 0xA4, 0x48, 0xA5, 0x48, 0xA6,\n\t0x48, 0xA7, 0x48, 0xA8, 0x48, 0xA9, 0x48, 0xAA, 0x48, 0xAB, 0x48, 0xAC, 0x48, 0xAD, 0x48, 0xAE, 0x48, 0xAF, 0x48, 0xB0, 0x48, 0xB1, 0x48, 0xB2, 0x48, 0xB3, 0x48, 0xB4, 0x48, 0xB5, 0x48, 0xB6,\n\t0x48, 0xB7, 0x48, 0xB8, 0x48, 0xB9, 0x48, 0xBA, 0x48, 0xBB, 0x48, 0xBC, 0x48, 0xBD, 0x48, 0xBE, 0x48, 0xBF, 0x48, 0xC0, 0x48, 0xC1, 0x48, 0xC2, 0x48, 0xC3, 0x48, 0xC4, 0x48, 0xC5, 0x48, 0xC6,\n\t0x48, 0xC7, 0x48, 0xC8, 0x48, 0xC9, 0x48, 0xCA, 0x48, 0xCB, 0x48, 0xCC, 0x48, 0xCD, 0x48, 0xCE, 0x48, 0xCF, 0x48, 0xD0, 0x48, 0xD1, 0x48, 0xD2, 0x48, 0xD3, 0x48, 0xD4, 0x48, 0xD5, 0x48, 0xD6,\n\t0x48, 0xD7, 0x48, 0xD8, 0x48, 0xD9, 0x48, 0xDA, 0x48, 0xDB, 0x48, 0xDC, 0x48, 0xDD, 0x48, 0xDE, 0x48, 0xDF, 0x48, 0xE0, 0x48, 0xE1, 0x48, 0xE2, 0x48, 0xE3, 0x48, 0xE4, 0x48, 0xE5, 0x48, 0xE6,\n\t0x48, 0xE7, 0x48, 0xE8, 0x48, 0xE9, 0x48, 0xEA, 0x48, 0xEB, 0x48, 0xEC, 0x48, 0xED, 0x48, 0xEE, 0x48, 0xEF, 0x48, 0xF0, 0x48, 0xF1, 0x48, 0xF2, 0x48, 0xF3, 0x48, 0xF4, 0x48, 0xF5, 0x48, 0xF6,\n\t0x48, 0xF7, 0x48, 0xF8, 0x48, 0xF9, 0x48, 0xFA, 0x48, 0xFB, 0x48, 0xFC, 0x48, 0xFD, 0x48, 0xFE, 0x48, 0xFF, 0x48, 0x00, 0x49, 0x01, 0x49, 0x02, 0x49, 0x03, 0x49, 0x04, 0x49, 0x05, 0x49, 0x06,\n\t0x49, 0x07, 0x49, 0x08, 0x49, 0x09, 0x49, 0x0A, 0x49, 0x0B, 0x49, 0x0C, 0x49, 0x0D, 0x49, 0x0E, 0x49, 0x0F, 0x49, 0x10, 0x49, 0x11, 0x49, 0x12, 0x49, 0x13, 0x49, 0x14, 0x49, 0x15, 0x49, 0x16,\n\t0x49, 0x17, 0x49, 0x18, 0x49, 0x19, 0x49, 0x1A, 0x49, 0x1B, 0x49, 0x1C, 0x49, 0x1D, 0x49, 0x1E, 0x49, 0x1F, 0x49, 0x20, 0x49, 0x21, 0x49, 0x22, 0x49, 0x23, 0x49, 0x24, 0x49, 0x25, 0x49, 0x26,\n\t0x49, 0x27, 0x49, 0x28, 0x49, 0x29, 0x49, 0x2A, 0x49, 0x2B, 0x49, 0x2C, 0x49, 0x2D, 0x49, 0x2E, 0x49, 0x2F, 0x49, 0x30, 0x49, 0x31, 0x49, 0x32, 0x49, 0x33, 0x49, 0x34, 0x49, 0x35, 0x49, 0x36,\n\t0x49, 0x37, 0x49, 0x38, 0x49, 0x39, 0x49, 0x3A, 0x49, 0x3B, 0x49, 0x3C, 0x49, 0x3D, 0x49, 0x3E, 0x49, 0x3F, 0x49, 0x40, 0x49, 0x41, 0x49, 0x42, 0x49, 0x43, 0x49, 0x44, 0x49, 0x45, 0x49, 0x46,\n\t0x49, 0x47, 0x49, 0x48, 0x49, 0x49, 0x49, 0x4A, 0x49, 0x4B, 0x49, 0x4C, 0x49, 0x4D, 0x49, 0x4E, 0x49, 0x4F, 0x49, 0x50, 0x49, 0x51, 0x49, 0x52, 0x49, 0x53, 0x49, 0x54, 0x49, 0x55, 0x49, 0x56,\n\t0x49, 0x57, 0x49, 0x58, 0x49, 0x59, 0x49, 0x5A, 0x49, 0x5B, 0x49, 0x5C, 0x49, 0x5D, 0x49, 0x5E, 0x49, 0x5F, 0x49, 0x60, 0x49, 0x61, 0x49, 0x62, 0x49, 0x63, 0x49, 0x64, 0x49, 0x65, 0x49, 0x66,\n\t0x49, 0x67, 0x49, 0x68, 0x49, 0x69, 0x49, 0x6A, 0x49, 0x6B, 0x49, 0x6C, 0x49, 0x6D, 0x49, 0x6E, 0x49, 0x6F, 0x49, 0x70, 0x49, 0x71, 0x49, 0x72, 0x49, 0x73, 0x49, 0x74, 0x49, 0x75, 0x49, 0x76,\n\t0x49, 0x77, 0x49, 0x78, 0x49, 0x79, 0x49, 0x7A, 0x49, 0x7B, 0x49, 0x7C, 0x49, 0x7D, 0x49, 0x7E, 0x49, 0x7F, 0x49, 0x80, 0x49, 0x81, 0x49, 0x82, 0x49, 0x83, 0x49, 0x84, 0x49, 0x85, 0x49, 0x86,\n\t0x49, 0x87, 0x49, 0x88, 0x49, 0x89, 0x49, 0x8A, 0x49, 0x8B, 0x49, 0x8C, 0x49, 0x8D, 0x49, 0x8E, 0x49, 0x8F, 0x49, 0x90, 0x49, 0x91, 0x49, 0x92, 0x49, 0x93, 0x49, 0x94, 0x49, 0x95, 0x49, 0x96,\n\t0x49, 0x97, 0x49, 0x98, 0x49, 0x99, 0x49, 0x9A, 0x49, 0x9B, 0x49, 0x9C, 0x49, 0x9D, 0x49, 0x9E, 0x49, 0x9F, 0x49, 0xA0, 0x49, 0xA1, 0x49, 0xA2, 0x49, 0xA3, 0x49, 0xA4, 0x49, 0xA5, 0x49, 0xA6,\n\t0x49, 0xA7, 0x49, 0xA8, 0x49, 0xA9, 0x49, 0xAA, 0x49, 0xAB, 0x49, 0xAC, 0x49, 0xAD, 0x49, 0xAE, 0x49, 0xAF, 0x49, 0xB0, 0x49, 0xB1, 0x49, 0xB2, 0x49, 0xB3, 0x49, 0xB4, 0x49, 0xB5, 0x49, 0xB6,\n\t0x49, 0xB7, 0x49, 0xB8, 0x49, 0xB9, 0x49, 0xBA, 0x49, 0xBB, 0x49, 0xBC, 0x49, 0xBD, 0x49, 0xBE, 0x49, 0xBF, 0x49, 0xC0, 0x49, 0xC1, 0x49, 0xC2, 0x49, 0xC3, 0x49, 0xC4, 0x49, 0xC5, 0x49, 0xC6,\n\t0x49, 0xC7, 0x49, 0xC8, 0x49, 0xC9, 0x49, 0xCA, 0x49, 0xCB, 0x49, 0xCC, 0x49, 0xCD, 0x49, 0xCE, 0x49, 0xCF, 0x49, 0xD0, 0x49, 0xD1, 0x49, 0xD2, 0x49, 0xD3, 0x49, 0xD4, 0x49, 0xD5, 0x49, 0xD6,\n\t0x49, 0xD7, 0x49, 0xD8, 0x49, 0xD9, 0x49, 0xDA, 0x49, 0xDB, 0x49, 0xDC, 0x49, 0xDD, 0x49, 0xDE, 0x49, 0xDF, 0x49, 0xE0, 0x49, 0xE1, 0x49, 0xE2, 0x49, 0xE3, 0x49, 0xE4, 0x49, 0xE5, 0x49, 0xE6,\n\t0x49, 0xE7, 0x49, 0xE8, 0x49, 0xE9, 0x49, 0xEA, 0x49, 0xEB, 0x49, 0xEC, 0x49, 0xED, 0x49, 0xEE, 0x49, 0xEF, 0x49, 0xF0, 0x49, 0xF1, 0x49, 0xF2, 0x49, 0xF3, 0x49, 0xF4, 0x49, 0xF5, 0x49, 0xF6,\n\t0x49, 0xF7, 0x49, 0xF8, 0x49, 0xF9, 0x49, 0xFA, 0x49, 0xFB, 0x49, 0xFC, 0x49, 0xFD, 0x49, 0xFE, 0x49, 0xFF, 0x49, 0x00, 0x4A, 0x01, 0x4A, 0x02, 0x4A, 0x03, 0x4A, 0x04, 0x4A, 0x05, 0x4A, 0x06,\n\t0x4A, 0x07, 0x4A, 0x08, 0x4A, 0x09, 0x4A, 0x0A, 0x4A, 0x0B, 0x4A, 0x0C, 0x4A, 0x0D, 0x4A, 0x0E, 0x4A, 0x0F, 0x4A, 0x10, 0x4A, 0x11, 0x4A, 0x12, 0x4A, 0x13, 0x4A, 0x14, 0x4A, 0x15, 0x4A, 0x16,\n\t0x4A, 0x17, 0x4A, 0x18, 0x4A, 0x19, 0x4A, 0x1A, 0x4A, 0x1B, 0x4A, 0x1C, 0x4A, 0x1D, 0x4A, 0x1E, 0x4A, 0x1F, 0x4A, 0x20, 0x4A, 0x21, 0x4A, 0x22, 0x4A, 0x23, 0x4A, 0x24, 0x4A, 0x25, 0x4A, 0x26,\n\t0x4A, 0x27, 0x4A, 0x28, 0x4A, 0x29, 0x4A, 0x2A, 0x4A, 0x2B, 0x4A, 0x2C, 0x4A, 0x2D, 0x4A, 0x2E, 0x4A, 0x2F, 0x4A, 0x30, 0x4A, 0x31, 0x4A, 0x32, 0x4A, 0x33, 0x4A, 0x34, 0x4A, 0x35, 0x4A, 0x36,\n\t0x4A, 0x37, 0x4A, 0x38, 0x4A, 0x39, 0x4A, 0x3A, 0x4A, 0x3B, 0x4A, 0x3C, 0x4A, 0x3D, 0x4A, 0x3E, 0x4A, 0x3F, 0x4A, 0x40, 0x4A, 0x41, 0x4A, 0x42, 0x4A, 0x43, 0x4A, 0x44, 0x4A, 0x45, 0x4A, 0x46,\n\t0x4A, 0x47, 0x4A, 0x48, 0x4A, 0x49, 0x4A, 0x4A, 0x4A, 0x4B, 0x4A, 0x4C, 0x4A, 0x4D, 0x4A, 0x4E, 0x4A, 0x4F, 0x4A, 0x50, 0x4A, 0x51, 0x4A, 0x52, 0x4A, 0x53, 0x4A, 0x54, 0x4A, 0x55, 0x4A, 0x56,\n\t0x4A, 0x57, 0x4A, 0x58, 0x4A, 0x59, 0x4A, 0x5A, 0x4A, 0x5B, 0x4A, 0x5C, 0x4A, 0x5D, 0x4A, 0x5E, 0x4A, 0x5F, 0x4A, 0x60, 0x4A, 0x61, 0x4A, 0x62, 0x4A, 0x63, 0x4A, 0x64, 0x4A, 0x65, 0x4A, 0x66,\n\t0x4A, 0x67, 0x4A, 0x68, 0x4A, 0x69, 0x4A, 0x6A, 0x4A, 0x6B, 0x4A, 0x6C, 0x4A, 0x6D, 0x4A, 0x6E, 0x4A, 0x6F, 0x4A, 0x70, 0x4A, 0x71, 0x4A, 0x72, 0x4A, 0x73, 0x4A, 0x74, 0x4A, 0x75, 0x4A, 0x76,\n\t0x4A, 0x77, 0x4A, 0x78, 0x4A, 0x79, 0x4A, 0x7A, 0x4A, 0x7B, 0x4A, 0x7C, 0x4A, 0x7D, 0x4A, 0x7E, 0x4A, 0x7F, 0x4A, 0x80, 0x4A, 0x81, 0x4A, 0x82, 0x4A, 0x83, 0x4A, 0x84, 0x4A, 0x85, 0x4A, 0x86,\n\t0x4A, 0x87, 0x4A, 0x88, 0x4A, 0x89, 0x4A, 0x8A, 0x4A, 0x8B, 0x4A, 0x8C, 0x4A, 0x8D, 0x4A, 0x8E, 0x4A, 0x8F, 0x4A, 0x90, 0x4A, 0x91, 0x4A, 0x92, 0x4A, 0x93, 0x4A, 0x94, 0x4A, 0x95, 0x4A, 0x96,\n\t0x4A, 0x97, 0x4A, 0x98, 0x4A, 0x99, 0x4A, 0x9A, 0x4A, 0x9B, 0x4A, 0x9C, 0x4A, 0x9D, 0x4A, 0x9E, 0x4A, 0x9F, 0x4A, 0xA0, 0x4A, 0xA1, 0x4A, 0xA2, 0x4A, 0xA3, 0x4A, 0xA4, 0x4A, 0xA5, 0x4A, 0xA6,\n\t0x4A, 0xA7, 0x4A, 0xA8, 0x4A, 0xA9, 0x4A, 0xAA, 0x4A, 0xAB, 0x4A, 0xAC, 0x4A, 0xAD, 0x4A, 0xAE, 0x4A, 0xAF, 0x4A, 0xB0, 0x4A, 0xB1, 0x4A, 0xB2, 0x4A, 0xB3, 0x4A, 0xB4, 0x4A, 0xB5, 0x4A, 0xB6,\n\t0x4A, 0xB7, 0x4A, 0xB8, 0x4A, 0xB9, 0x4A, 0xBA, 0x4A, 0xBB, 0x4A, 0xBC, 0x4A, 0xBD, 0x4A, 0xBE, 0x4A, 0xBF, 0x4A, 0xC0, 0x4A, 0xC1, 0x4A, 0xC2, 0x4A, 0xC3, 0x4A, 0xC4, 0x4A, 0xC5, 0x4A, 0xC6,\n\t0x4A, 0xC7, 0x4A, 0xC8, 0x4A, 0xC9, 0x4A, 0xCA, 0x4A, 0xCB, 0x4A, 0xCC, 0x4A, 0xCD, 0x4A, 0xCE, 0x4A, 0xCF, 0x4A, 0xD0, 0x4A, 0xD1, 0x4A, 0xD2, 0x4A, 0xD3, 0x4A, 0xD4, 0x4A, 0xD5, 0x4A, 0xD6,\n\t0x4A, 0xD7, 0x4A, 0xD8, 0x4A, 0xD9, 0x4A, 0xDA, 0x4A, 0xDB, 0x4A, 0xDC, 0x4A, 0xDD, 0x4A, 0xDE, 0x4A, 0xDF, 0x4A, 0xE0, 0x4A, 0xE1, 0x4A, 0xE2, 0x4A, 0xE3, 0x4A, 0xE4, 0x4A, 0xE5, 0x4A, 0xE6,\n\t0x4A, 0xE7, 0x4A, 0xE8, 0x4A, 0xE9, 0x4A, 0xEA, 0x4A, 0xEB, 0x4A, 0xEC, 0x4A, 0xED, 0x4A, 0xEE, 0x4A, 0xEF, 0x4A, 0xF0, 0x4A, 0xF1, 0x4A, 0xF2, 0x4A, 0xF3, 0x4A, 0xF4, 0x4A, 0xF5, 0x4A, 0xF6,\n\t0x4A, 0xF7, 0x4A, 0xF8, 0x4A, 0xF9, 0x4A, 0xFA, 0x4A, 0xFB, 0x4A, 0xFC, 0x4A, 0xFD, 0x4A, 0xFE, 0x4A, 0xFF, 0x4A, 0x00, 0x4B, 0x01, 0x4B, 0x02, 0x4B, 0x03, 0x4B, 0x04, 0x4B, 0x05, 0x4B, 0x06,\n\t0x4B, 0x07, 0x4B, 0x08, 0x4B, 0x09, 0x4B, 0x0A, 0x4B, 0x0B, 0x4B, 0x0C, 0x4B, 0x0D, 0x4B, 0x0E, 0x4B, 0x0F, 0x4B, 0x10, 0x4B, 0x11, 0x4B, 0x12, 0x4B, 0x13, 0x4B, 0x14, 0x4B, 0x15, 0x4B, 0x16,\n\t0x4B, 0x17, 0x4B, 0x18, 0x4B, 0x19, 0x4B, 0x1A, 0x4B, 0x1B, 0x4B, 0x1C, 0x4B, 0x1D, 0x4B, 0x1E, 0x4B, 0x1F, 0x4B, 0x20, 0x4B, 0x21, 0x4B, 0x22, 0x4B, 0x23, 0x4B, 0x24, 0x4B, 0x25, 0x4B, 0x26,\n\t0x4B, 0x27, 0x4B, 0x28, 0x4B, 0x29, 0x4B, 0x2A, 0x4B, 0x2B, 0x4B, 0x2C, 0x4B, 0x2D, 0x4B, 0x2E, 0x4B, 0x2F, 0x4B, 0x30, 0x4B, 0x31, 0x4B, 0x32, 0x4B, 0x33, 0x4B, 0x34, 0x4B, 0x35, 0x4B, 0x36,\n\t0x4B, 0x37, 0x4B, 0x38, 0x4B, 0x39, 0x4B, 0x3A, 0x4B, 0x3B, 0x4B, 0x3C, 0x4B, 0x3D, 0x4B, 0x3E, 0x4B, 0x3F, 0x4B, 0x40, 0x4B, 0x41, 0x4B, 0x42, 0x4B, 0x43, 0x4B, 0x44, 0x4B, 0x45, 0x4B, 0x46,\n\t0x4B, 0x47, 0x4B, 0x48, 0x4B, 0x49, 0x4B, 0x4A, 0x4B, 0x4B, 0x4B, 0x4C, 0x4B, 0x4D, 0x4B, 0x4E, 0x4B, 0x4F, 0x4B, 0x50, 0x4B, 0x51, 0x4B, 0x52, 0x4B, 0x53, 0x4B, 0x54, 0x4B, 0x55, 0x4B, 0x56,\n\t0x4B, 0x57, 0x4B, 0x58, 0x4B, 0x59, 0x4B, 0x5A, 0x4B, 0x5B, 0x4B, 0x5C, 0x4B, 0x5D, 0x4B, 0x5E, 0x4B, 0x5F, 0x4B, 0x60, 0x4B, 0x61, 0x4B, 0x62, 0x4B, 0x63, 0x4B, 0x64, 0x4B, 0x65, 0x4B, 0x66,\n\t0x4B, 0x67, 0x4B, 0x68, 0x4B, 0x69, 0x4B, 0x6A, 0x4B, 0x6B, 0x4B, 0x6C, 0x4B, 0x6D, 0x4B, 0x6E, 0x4B, 0x6F, 0x4B, 0x70, 0x4B, 0x71, 0x4B, 0x72, 0x4B, 0x73, 0x4B, 0x74, 0x4B, 0x75, 0x4B, 0x76,\n\t0x4B, 0x77, 0x4B, 0x78, 0x4B, 0x79, 0x4B, 0x7A, 0x4B, 0x7B, 0x4B, 0x7C, 0x4B, 0x7D, 0x4B, 0x7E, 0x4B, 0x7F, 0x4B, 0x80, 0x4B, 0x81, 0x4B, 0x82, 0x4B, 0x83, 0x4B, 0x84, 0x4B, 0x85, 0x4B, 0x86,\n\t0x4B, 0x87, 0x4B, 0x88, 0x4B, 0x89, 0x4B, 0x8A, 0x4B, 0x8B, 0x4B, 0x8C, 0x4B, 0x8D, 0x4B, 0x8E, 0x4B, 0x8F, 0x4B, 0x90, 0x4B, 0x91, 0x4B, 0x92, 0x4B, 0x93, 0x4B, 0x94, 0x4B, 0x95, 0x4B, 0x96,\n\t0x4B, 0x97, 0x4B, 0x98, 0x4B, 0x99, 0x4B, 0x9A, 0x4B, 0x9B, 0x4B, 0x9C, 0x4B, 0x9D, 0x4B, 0x9E, 0x4B, 0x9F, 0x4B, 0xA0, 0x4B, 0xA1, 0x4B, 0xA2, 0x4B, 0xA3, 0x4B, 0xA4, 0x4B, 0xA5, 0x4B, 0xA6,\n\t0x4B, 0xA7, 0x4B, 0xA8, 0x4B, 0xA9, 0x4B, 0xAA, 0x4B, 0xAB, 0x4B, 0xAC, 0x4B, 0xAD, 0x4B, 0xAE, 0x4B, 0xAF, 0x4B, 0xB0, 0x4B, 0xB1, 0x4B, 0xB2, 0x4B, 0xB3, 0x4B, 0xB4, 0x4B, 0xB5, 0x4B, 0xB6,\n\t0x4B, 0xB7, 0x4B, 0xB8, 0x4B, 0xB9, 0x4B, 0xBA, 0x4B, 0xBB, 0x4B, 0xBC, 0x4B, 0xBD, 0x4B, 0xBE, 0x4B, 0xBF, 0x4B, 0xC0, 0x4B, 0xC1, 0x4B, 0xC2, 0x4B, 0xC3, 0x4B, 0xC4, 0x4B, 0xC5, 0x4B, 0xC6,\n\t0x4B, 0xC7, 0x4B, 0xC8, 0x4B, 0xC9, 0x4B, 0xCA, 0x4B, 0xCB, 0x4B, 0xCC, 0x4B, 0xCD, 0x4B, 0xCE, 0x4B, 0xCF, 0x4B, 0xD0, 0x4B, 0xD1, 0x4B, 0xD2, 0x4B, 0xD3, 0x4B, 0xD4, 0x4B, 0xD5, 0x4B, 0xD6,\n\t0x4B, 0xD7, 0x4B, 0xD8, 0x4B, 0xD9, 0x4B, 0xDA, 0x4B, 0xDB, 0x4B, 0xDC, 0x4B, 0xDD, 0x4B, 0xDE, 0x4B, 0xDF, 0x4B, 0xE0, 0x4B, 0xE1, 0x4B, 0xE2, 0x4B, 0xE3, 0x4B, 0xE4, 0x4B, 0xE5, 0x4B, 0xE6,\n\t0x4B, 0xE7, 0x4B, 0xE8, 0x4B, 0xE9, 0x4B, 0xEA, 0x4B, 0xEB, 0x4B, 0xEC, 0x4B, 0xED, 0x4B, 0xEE, 0x4B, 0xEF, 0x4B, 0xF0, 0x4B, 0xF1, 0x4B, 0xF2, 0x4B, 0xF3, 0x4B, 0xF4, 0x4B, 0xF5, 0x4B, 0xF6,\n\t0x4B, 0xF7, 0x4B, 0xF8, 0x4B, 0xF9, 0x4B, 0xFA, 0x4B, 0xFB, 0x4B, 0xFC, 0x4B, 0xFD, 0x4B, 0xFE, 0x4B, 0xFF, 0x4B, 0x00, 0x4C, 0x01, 0x4C, 0x02, 0x4C, 0x03, 0x4C, 0x04, 0x4C, 0x05, 0x4C, 0x06,\n\t0x4C, 0x07, 0x4C, 0x08, 0x4C, 0x09, 0x4C, 0x0A, 0x4C, 0x0B, 0x4C, 0x0C, 0x4C, 0x0D, 0x4C, 0x0E, 0x4C, 0x0F, 0x4C, 0x10, 0x4C, 0x11, 0x4C, 0x12, 0x4C, 0x13, 0x4C, 0x14, 0x4C, 0x15, 0x4C, 0x16,\n\t0x4C, 0x17, 0x4C, 0x18, 0x4C, 0x19, 0x4C, 0x1A, 0x4C, 0x1B, 0x4C, 0x1C, 0x4C, 0x1D, 0x4C, 0x1E, 0x4C, 0x1F, 0x4C, 0x20, 0x4C, 0x21, 0x4C, 0x22, 0x4C, 0x23, 0x4C, 0x24, 0x4C, 0x25, 0x4C, 0x26,\n\t0x4C, 0x27, 0x4C, 0x28, 0x4C, 0x29, 0x4C, 0x2A, 0x4C, 0x2B, 0x4C, 0x2C, 0x4C, 0x2D, 0x4C, 0x2E, 0x4C, 0x2F, 0x4C, 0x30, 0x4C, 0x31, 0x4C, 0x32, 0x4C, 0x33, 0x4C, 0x34, 0x4C, 0x35, 0x4C, 0x36,\n\t0x4C, 0x37, 0x4C, 0x38, 0x4C, 0x39, 0x4C, 0x3A, 0x4C, 0x3B, 0x4C, 0x3C, 0x4C, 0x3D, 0x4C, 0x3E, 0x4C, 0x3F, 0x4C, 0x40, 0x4C, 0x41, 0x4C, 0x42, 0x4C, 0x43, 0x4C, 0x44, 0x4C, 0x45, 0x4C, 0x46,\n\t0x4C, 0x47, 0x4C, 0x48, 0x4C, 0x49, 0x4C, 0x4A, 0x4C, 0x4B, 0x4C, 0x4C, 0x4C, 0x4D, 0x4C, 0x4E, 0x4C, 0x4F, 0x4C, 0x50, 0x4C, 0x51, 0x4C, 0x52, 0x4C, 0x53, 0x4C, 0x54, 0x4C, 0x55, 0x4C, 0x56,\n\t0x4C, 0x57, 0x4C, 0x58, 0x4C, 0x59, 0x4C, 0x5A, 0x4C, 0x5B, 0x4C, 0x5C, 0x4C, 0x5D, 0x4C, 0x5E, 0x4C, 0x5F, 0x4C, 0x60, 0x4C, 0x61, 0x4C, 0x62, 0x4C, 0x63, 0x4C, 0x64, 0x4C, 0x65, 0x4C, 0x66,\n\t0x4C, 0x67, 0x4C, 0x68, 0x4C, 0x69, 0x4C, 0x6A, 0x4C, 0x6B, 0x4C, 0x6C, 0x4C, 0x6D, 0x4C, 0x6E, 0x4C, 0x6F, 0x4C, 0x70, 0x4C, 0x71, 0x4C, 0x72, 0x4C, 0x73, 0x4C, 0x74, 0x4C, 0x75, 0x4C, 0x76,\n\t0x4C, 0x77, 0x4C, 0x78, 0x4C, 0x79, 0x4C, 0x7A, 0x4C, 0x7B, 0x4C, 0x7C, 0x4C, 0x7D, 0x4C, 0x7E, 0x4C, 0x7F, 0x4C, 0x80, 0x4C, 0x81, 0x4C, 0x82, 0x4C, 0x83, 0x4C, 0x84, 0x4C, 0x85, 0x4C, 0x86,\n\t0x4C, 0x87, 0x4C, 0x88, 0x4C, 0x89, 0x4C, 0x8A, 0x4C, 0x8B, 0x4C, 0x8C, 0x4C, 0x8D, 0x4C, 0x8E, 0x4C, 0x8F, 0x4C, 0x90, 0x4C, 0x91, 0x4C, 0x92, 0x4C, 0x93, 0x4C, 0x94, 0x4C, 0x95, 0x4C, 0x96,\n\t0x4C, 0x97, 0x4C, 0x98, 0x4C, 0x99, 0x4C, 0x9A, 0x4C, 0x9B, 0x4C, 0x9C, 0x4C, 0x9D, 0x4C, 0x9E, 0x4C, 0x9F, 0x4C, 0xA0, 0x4C, 0xA1, 0x4C, 0xA2, 0x4C, 0xA3, 0x4C, 0xA4, 0x4C, 0xA5, 0x4C, 0xA6,\n\t0x4C, 0xA7, 0x4C, 0xA8, 0x4C, 0xA9, 0x4C, 0xAA, 0x4C, 0xAB, 0x4C, 0xAC, 0x4C, 0xAD, 0x4C, 0xAE, 0x4C, 0xAF, 0x4C, 0xB0, 0x4C, 0xB1, 0x4C, 0xB2, 0x4C, 0xB3, 0x4C, 0xB4, 0x4C, 0xB5, 0x4C, 0xB6,\n\t0x4C, 0xB7, 0x4C, 0xB8, 0x4C, 0xB9, 0x4C, 0xBA, 0x4C, 0xBB, 0x4C, 0xBC, 0x4C, 0xBD, 0x4C, 0xBE, 0x4C, 0xBF, 0x4C, 0xC0, 0x4C, 0xC1, 0x4C, 0xC2, 0x4C, 0xC3, 0x4C, 0xC4, 0x4C, 0xC5, 0x4C, 0xC6,\n\t0x4C, 0xC7, 0x4C, 0xC8, 0x4C, 0xC9, 0x4C, 0xCA, 0x4C, 0xCB, 0x4C, 0xCC, 0x4C, 0xCD, 0x4C, 0xCE, 0x4C, 0xCF, 0x4C, 0xD0, 0x4C, 0xD1, 0x4C, 0xD2, 0x4C, 0xD3, 0x4C, 0xD4, 0x4C, 0xD5, 0x4C, 0xD6,\n\t0x4C, 0xD7, 0x4C, 0xD8, 0x4C, 0xD9, 0x4C, 0xDA, 0x4C, 0xDB, 0x4C, 0xDC, 0x4C, 0xDD, 0x4C, 0xDE, 0x4C, 0xDF, 0x4C, 0xE0, 0x4C, 0xE1, 0x4C, 0xE2, 0x4C, 0xE3, 0x4C, 0xE4, 0x4C, 0xE5, 0x4C, 0xE6,\n\t0x4C, 0xE7, 0x4C, 0xE8, 0x4C, 0xE9, 0x4C, 0xEA, 0x4C, 0xEB, 0x4C, 0xEC, 0x4C, 0xED, 0x4C, 0xEE, 0x4C, 0xEF, 0x4C, 0xF0, 0x4C, 0xF1, 0x4C, 0xF2, 0x4C, 0xF3, 0x4C, 0xF4, 0x4C, 0xF5, 0x4C, 0xF6,\n\t0x4C, 0xF7, 0x4C, 0xF8, 0x4C, 0xF9, 0x4C, 0xFA, 0x4C, 0xFB, 0x4C, 0xFC, 0x4C, 0xFD, 0x4C, 0xFE, 0x4C, 0xFF, 0x4C, 0x00, 0x4D, 0x01, 0x4D, 0x02, 0x4D, 0x03, 0x4D, 0x04, 0x4D, 0x05, 0x4D, 0x06,\n\t0x4D, 0x07, 0x4D, 0x08, 0x4D, 0x09, 0x4D, 0x0A, 0x4D, 0x0B, 0x4D, 0x0C, 0x4D, 0x0D, 0x4D, 0x0E, 0x4D, 0x0F, 0x4D, 0x10, 0x4D, 0x11, 0x4D, 0x12, 0x4D, 0x13, 0x4D, 0x14, 0x4D, 0x15, 0x4D, 0x16,\n\t0x4D, 0x17, 0x4D, 0x18, 0x4D, 0x19, 0x4D, 0x1A, 0x4D, 0x1B, 0x4D, 0x1C, 0x4D, 0x1D, 0x4D, 0x1E, 0x4D, 0x1F, 0x4D, 0x20, 0x4D, 0x21, 0x4D, 0x22, 0x4D, 0x23, 0x4D, 0x24, 0x4D, 0x25, 0x4D, 0x26,\n\t0x4D, 0x27, 0x4D, 0x28, 0x4D, 0x29, 0x4D, 0x2A, 0x4D, 0x2B, 0x4D, 0x2C, 0x4D, 0x2D, 0x4D, 0x2E, 0x4D, 0x2F, 0x4D, 0x30, 0x4D, 0x31, 0x4D, 0x32, 0x4D, 0x33, 0x4D, 0x34, 0x4D, 0x35, 0x4D, 0x36,\n\t0x4D, 0x37, 0x4D, 0x38, 0x4D, 0x39, 0x4D, 0x3A, 0x4D, 0x3B, 0x4D, 0x3C, 0x4D, 0x3D, 0x4D, 0x3E, 0x4D, 0x3F, 0x4D, 0x40, 0x4D, 0x41, 0x4D, 0x42, 0x4D, 0x43, 0x4D, 0x44, 0x4D, 0x45, 0x4D, 0x46,\n\t0x4D, 0x47, 0x4D, 0x48, 0x4D, 0x49, 0x4D, 0x4A, 0x4D, 0x4B, 0x4D, 0x4C, 0x4D, 0x4D, 0x4D, 0x4E, 0x4D, 0x4F, 0x4D, 0x50, 0x4D, 0x51, 0x4D, 0x52, 0x4D, 0x53, 0x4D, 0x54, 0x4D, 0x55, 0x4D, 0x56,\n\t0x4D, 0x57, 0x4D, 0x58, 0x4D, 0x59, 0x4D, 0x5A, 0x4D, 0x5B, 0x4D, 0x5C, 0x4D, 0x5D, 0x4D, 0x5E, 0x4D, 0x5F, 0x4D, 0x60, 0x4D, 0x61, 0x4D, 0x62, 0x4D, 0x63, 0x4D, 0x64, 0x4D, 0x65, 0x4D, 0x66,\n\t0x4D, 0x67, 0x4D, 0x68, 0x4D, 0x69, 0x4D, 0x6A, 0x4D, 0x6B, 0x4D, 0x6C, 0x4D, 0x6D, 0x4D, 0x6E, 0x4D, 0x6F, 0x4D, 0x70, 0x4D, 0x71, 0x4D, 0x72, 0x4D, 0x73, 0x4D, 0x74, 0x4D, 0x75, 0x4D, 0x76,\n\t0x4D, 0x77, 0x4D, 0x78, 0x4D, 0x79, 0x4D, 0x7A, 0x4D, 0x7B, 0x4D, 0x7C, 0x4D, 0x7D, 0x4D, 0x7E, 0x4D, 0x7F, 0x4D, 0x80, 0x4D, 0x81, 0x4D, 0x82, 0x4D, 0x83, 0x4D, 0x84, 0x4D, 0x85, 0x4D, 0x86,\n\t0x4D, 0x87, 0x4D, 0x88, 0x4D, 0x89, 0x4D, 0x8A, 0x4D, 0x8B, 0x4D, 0x8C, 0x4D, 0x8D, 0x4D, 0x8E, 0x4D, 0x8F, 0x4D, 0x90, 0x4D, 0x91, 0x4D, 0x92, 0x4D, 0x93, 0x4D, 0x94, 0x4D, 0x95, 0x4D, 0x96,\n\t0x4D, 0x97, 0x4D, 0x98, 0x4D, 0x99, 0x4D, 0x9A, 0x4D, 0x9B, 0x4D, 0x9C, 0x4D, 0x9D, 0x4D, 0x9E, 0x4D, 0x9F, 0x4D, 0xA0, 0x4D, 0xA1, 0x4D, 0xA2, 0x4D, 0xA3, 0x4D, 0xA4, 0x4D, 0xA5, 0x4D, 0xA6,\n\t0x4D, 0xA7, 0x4D, 0xA8, 0x4D, 0xA9, 0x4D, 0xAA, 0x4D, 0xAB, 0x4D, 0xAC, 0x4D, 0xAD, 0x4D, 0xAE, 0x4D, 0xAF, 0x4D, 0xB0, 0x4D, 0xB1, 0x4D, 0xB2, 0x4D, 0xB3, 0x4D, 0xB4, 0x4D, 0xB5, 0x4D, 0xB6,\n\t0x4D, 0xB7, 0x4D, 0xB8, 0x4D, 0xB9, 0x4D, 0xBA, 0x4D, 0xBB, 0x4D, 0xBC, 0x4D, 0xBD, 0x4D, 0xBE, 0x4D, 0xBF, 0x4D, 0xC0, 0x4D, 0xC1, 0x4D, 0xC2, 0x4D, 0xC3, 0x4D, 0xC4, 0x4D, 0xC5, 0x4D, 0xC6,\n\t0x4D, 0xC7, 0x4D, 0xC8, 0x4D, 0xC9, 0x4D, 0xCA, 0x4D, 0xCB, 0x4D, 0xCC, 0x4D, 0xCD, 0x4D, 0xCE, 0x4D, 0xCF, 0x4D, 0xD0, 0x4D, 0xD1, 0x4D, 0xD2, 0x4D, 0xD3, 0x4D, 0xD4, 0x4D, 0xD5, 0x4D, 0xD6,\n\t0x4D, 0xD7, 0x4D, 0xD8, 0x4D, 0xD9, 0x4D, 0xDA, 0x4D, 0xDB, 0x4D, 0xDC, 0x4D, 0xDD, 0x4D, 0xDE, 0x4D, 0xDF, 0x4D, 0xE0, 0x4D, 0xE1, 0x4D, 0xE2, 0x4D, 0xE3, 0x4D, 0xE4, 0x4D, 0xE5, 0x4D, 0xE6,\n\t0x4D, 0xE7, 0x4D, 0xE8, 0x4D, 0xE9, 0x4D, 0xEA, 0x4D, 0xEB, 0x4D, 0xEC, 0x4D, 0xED, 0x4D, 0xEE, 0x4D, 0xEF, 0x4D, 0xF0, 0x4D, 0xF1, 0x4D, 0xF2, 0x4D, 0xF3, 0x4D, 0xF4, 0x4D, 0xF5, 0x4D, 0xF6,\n\t0x4D, 0xF7, 0x4D, 0xF8, 0x4D, 0xF9, 0x4D, 0xFA, 0x4D, 0xFB, 0x4D, 0xFC, 0x4D, 0xFD, 0x4D, 0xFE, 0x4D, 0xFF, 0x4D, 0x00, 0x4E, 0x01, 0x4E, 0x02, 0x4E, 0x03, 0x4E, 0x04, 0x4E, 0x05, 0x4E, 0x06,\n\t0x4E, 0x07, 0x4E, 0x08, 0x4E, 0x09, 0x4E, 0x0A, 0x4E, 0x0B, 0x4E, 0x0C, 0x4E, 0x0D, 0x4E, 0x0E, 0x4E, 0x0F, 0x4E, 0x10, 0x4E, 0x11, 0x4E, 0x12, 0x4E, 0x13, 0x4E, 0x14, 0x4E, 0x15, 0x4E, 0x16,\n\t0x4E, 0x17, 0x4E, 0x18, 0x4E, 0x19, 0x4E, 0x1A, 0x4E, 0x1B, 0x4E, 0x1C, 0x4E, 0x1D, 0x4E, 0x1E, 0x4E, 0x1F, 0x4E, 0x20, 0x4E, 0x21, 0x4E, 0x22, 0x4E, 0x23, 0x4E, 0x24, 0x4E, 0x25, 0x4E, 0x26,\n\t0x4E, 0x27, 0x4E, 0x28, 0x4E, 0x29, 0x4E, 0x2A, 0x4E, 0x2B, 0x4E, 0x2C, 0x4E, 0x2D, 0x4E, 0x2E, 0x4E, 0x2F, 0x4E, 0x30, 0x4E, 0x31, 0x4E, 0x32, 0x4E, 0x33, 0x4E, 0x34, 0x4E, 0x35, 0x4E, 0x36,\n\t0x4E, 0x37, 0x4E, 0x38, 0x4E, 0x39, 0x4E, 0x3A, 0x4E, 0x3B, 0x4E, 0x3C, 0x4E, 0x3D, 0x4E, 0x3E, 0x4E, 0x3F, 0x4E, 0x40, 0x4E, 0x41, 0x4E, 0x42, 0x4E, 0x43, 0x4E, 0x44, 0x4E, 0x45, 0x4E, 0x46,\n\t0x4E, 0x47, 0x4E, 0x48, 0x4E, 0x49, 0x4E, 0x4A, 0x4E, 0x4B, 0x4E, 0x4C, 0x4E, 0x4D, 0x4E, 0x4E, 0x4E, 0x4F, 0x4E, 0x50, 0x4E, 0x51, 0x4E, 0x52, 0x4E, 0x53, 0x4E, 0x54, 0x4E, 0x55, 0x4E, 0x56,\n\t0x4E, 0x57, 0x4E, 0x58, 0x4E, 0x59, 0x4E, 0x5A, 0x4E, 0x5B, 0x4E, 0x5C, 0x4E, 0x5D, 0x4E, 0x5E, 0x4E, 0x5F, 0x4E, 0x60, 0x4E, 0x61, 0x4E, 0x62, 0x4E, 0x63, 0x4E, 0x64, 0x4E, 0x65, 0x4E, 0x66,\n\t0x4E, 0x67, 0x4E, 0x68, 0x4E, 0x69, 0x4E, 0x6A, 0x4E, 0x6B, 0x4E, 0x6C, 0x4E, 0x6D, 0x4E, 0x6E, 0x4E, 0x6F, 0x4E, 0x70, 0x4E, 0x71, 0x4E, 0x72, 0x4E, 0x73, 0x4E, 0x74, 0x4E, 0x75, 0x4E, 0x76,\n\t0x4E, 0x77, 0x4E, 0x78, 0x4E, 0x79, 0x4E, 0x7A, 0x4E, 0x7B, 0x4E, 0x7C, 0x4E, 0x7D, 0x4E, 0x7E, 0x4E, 0x7F, 0x4E, 0x80, 0x4E, 0x81, 0x4E, 0x82, 0x4E, 0x83, 0x4E, 0x84, 0x4E, 0x85, 0x4E, 0x86,\n\t0x4E, 0x87, 0x4E, 0x88, 0x4E, 0x89, 0x4E, 0x8A, 0x4E, 0x8B, 0x4E, 0x8C, 0x4E, 0x8D, 0x4E, 0x8E, 0x4E, 0x8F, 0x4E, 0x90, 0x4E, 0x91, 0x4E, 0x92, 0x4E, 0x93, 0x4E, 0x94, 0x4E, 0x95, 0x4E, 0x96,\n\t0x4E, 0x97, 0x4E, 0x98, 0x4E, 0x99, 0x4E, 0x9A, 0x4E, 0x9B, 0x4E, 0x9C, 0x4E, 0x9D, 0x4E, 0x9E, 0x4E, 0x9F, 0x4E, 0xA0, 0x4E, 0xA1, 0x4E, 0xA2, 0x4E, 0xA3, 0x4E, 0xA4, 0x4E, 0xA5, 0x4E, 0xA6,\n\t0x4E, 0xA7, 0x4E, 0xA8, 0x4E, 0xA9, 0x4E, 0xAA, 0x4E, 0xAB, 0x4E, 0xAC, 0x4E, 0xAD, 0x4E, 0xAE, 0x4E, 0xAF, 0x4E, 0xB0, 0x4E, 0xB1, 0x4E, 0xB2, 0x4E, 0xB3, 0x4E, 0xB4, 0x4E, 0xB5, 0x4E, 0xB6,\n\t0x4E, 0xB7, 0x4E, 0xB8, 0x4E, 0xB9, 0x4E, 0xBA, 0x4E, 0xBB, 0x4E, 0xBC, 0x4E, 0xBD, 0x4E, 0xBE, 0x4E, 0xBF, 0x4E, 0xC0, 0x4E, 0xC1, 0x4E, 0xC2, 0x4E, 0xC3, 0x4E, 0xC4, 0x4E, 0xC5, 0x4E, 0xC6,\n\t0x4E, 0xC7, 0x4E, 0xC8, 0x4E, 0xC9, 0x4E, 0xCA, 0x4E, 0xCB, 0x4E, 0xCC, 0x4E, 0xCD, 0x4E, 0xCE, 0x4E, 0xCF, 0x4E, 0xD0, 0x4E, 0xD1, 0x4E, 0xD2, 0x4E, 0xD3, 0x4E, 0xD4, 0x4E, 0xD5, 0x4E, 0xD6,\n\t0x4E, 0xD7, 0x4E, 0xD8, 0x4E, 0xD9, 0x4E, 0xDA, 0x4E, 0xDB, 0x4E, 0xDC, 0x4E, 0xDD, 0x4E, 0xDE, 0x4E, 0xDF, 0x4E, 0xE0, 0x4E, 0xE1, 0x4E, 0xE2, 0x4E, 0xE3, 0x4E, 0xE4, 0x4E, 0xE5, 0x4E, 0xE6,\n\t0x4E, 0xE7, 0x4E, 0xE8, 0x4E, 0xE9, 0x4E, 0xEA, 0x4E, 0xEB, 0x4E, 0xEC, 0x4E, 0xED, 0x4E, 0xEE, 0x4E, 0xEF, 0x4E, 0xF0, 0x4E, 0xF1, 0x4E, 0xF2, 0x4E, 0xF3, 0x4E, 0xF4, 0x4E, 0xF5, 0x4E, 0xF6,\n\t0x4E, 0xF7, 0x4E, 0xF8, 0x4E, 0xF9, 0x4E, 0xFA, 0x4E, 0xFB, 0x4E, 0xFC, 0x4E, 0xFD, 0x4E, 0xFE, 0x4E, 0xFF, 0x4E, 0x00, 0x4F, 0x01, 0x4F, 0x02, 0x4F, 0x03, 0x4F, 0x04, 0x4F, 0x05, 0x4F, 0x06,\n\t0x4F, 0x07, 0x4F, 0x08, 0x4F, 0x09, 0x4F, 0x0A, 0x4F, 0x0B, 0x4F, 0x0C, 0x4F, 0x0D, 0x4F, 0x0E, 0x4F, 0x0F, 0x4F, 0x10, 0x4F, 0x11, 0x4F, 0x12, 0x4F, 0x13, 0x4F, 0x14, 0x4F, 0x15, 0x4F, 0x16,\n\t0x4F, 0x17, 0x4F, 0x18, 0x4F, 0x19, 0x4F, 0x1A, 0x4F, 0x1B, 0x4F, 0x1C, 0x4F, 0x1D, 0x4F, 0x1E, 0x4F, 0x1F, 0x4F, 0x20, 0x4F, 0x21, 0x4F, 0x22, 0x4F, 0x23, 0x4F, 0x24, 0x4F, 0x25, 0x4F, 0x26,\n\t0x4F, 0x27, 0x4F, 0x28, 0x4F, 0x29, 0x4F, 0x2A, 0x4F, 0x2B, 0x4F, 0x2C, 0x4F, 0x2D, 0x4F, 0x2E, 0x4F, 0x2F, 0x4F, 0x30, 0x4F, 0x31, 0x4F, 0x32, 0x4F, 0x33, 0x4F, 0x34, 0x4F, 0x35, 0x4F, 0x36,\n\t0x4F, 0x37, 0x4F, 0x38, 0x4F, 0x39, 0x4F, 0x3A, 0x4F, 0x3B, 0x4F, 0x3C, 0x4F, 0x3D, 0x4F, 0x3E, 0x4F, 0x3F, 0x4F, 0x40, 0x4F, 0x41, 0x4F, 0x42, 0x4F, 0x43, 0x4F, 0x44, 0x4F, 0x45, 0x4F, 0x46,\n\t0x4F, 0x47, 0x4F, 0x48, 0x4F, 0x49, 0x4F, 0x4A, 0x4F, 0x4B, 0x4F, 0x4C, 0x4F, 0x4D, 0x4F, 0x4E, 0x4F, 0x4F, 0x4F, 0x50, 0x4F, 0x51, 0x4F, 0x52, 0x4F, 0x53, 0x4F, 0x54, 0x4F, 0x55, 0x4F, 0x56,\n\t0x4F, 0x57, 0x4F, 0x58, 0x4F, 0x59, 0x4F, 0x5A, 0x4F, 0x5B, 0x4F, 0x5C, 0x4F, 0x5D, 0x4F, 0x5E, 0x4F, 0x5F, 0x4F, 0x60, 0x4F, 0x61, 0x4F, 0x62, 0x4F, 0x63, 0x4F, 0x64, 0x4F, 0x65, 0x4F, 0x66,\n\t0x4F, 0x67, 0x4F, 0x68, 0x4F, 0x69, 0x4F, 0x6A, 0x4F, 0x6B, 0x4F, 0x6C, 0x4F, 0x6D, 0x4F, 0x6E, 0x4F, 0x6F, 0x4F, 0x70, 0x4F, 0x71, 0x4F, 0x72, 0x4F, 0x73, 0x4F, 0x74, 0x4F, 0x75, 0x4F, 0x76,\n\t0x4F, 0x77, 0x4F, 0x78, 0x4F, 0x79, 0x4F, 0x7A, 0x4F, 0x7B, 0x4F, 0x7C, 0x4F, 0x7D, 0x4F, 0x7E, 0x4F, 0x7F, 0x4F, 0x80, 0x4F, 0x81, 0x4F, 0x82, 0x4F, 0x83, 0x4F, 0x84, 0x4F, 0x85, 0x4F, 0x86,\n\t0x4F, 0x87, 0x4F, 0x88, 0x4F, 0x89, 0x4F, 0x8A, 0x4F, 0x8B, 0x4F, 0x8C, 0x4F, 0x8D, 0x4F, 0x8E, 0x4F, 0x8F, 0x4F, 0x90, 0x4F, 0x91, 0x4F, 0x92, 0x4F, 0x93, 0x4F, 0x94, 0x4F, 0x95, 0x4F, 0x96,\n\t0x4F, 0x97, 0x4F, 0x98, 0x4F, 0x99, 0x4F, 0x9A, 0x4F, 0x9B, 0x4F, 0x9C, 0x4F, 0x9D, 0x4F, 0x9E, 0x4F, 0x9F, 0x4F, 0xA0, 0x4F, 0xA1, 0x4F, 0xA2, 0x4F, 0xA3, 0x4F, 0xA4, 0x4F, 0xA5, 0x4F, 0xA6,\n\t0x4F, 0xA7, 0x4F, 0xA8, 0x4F, 0xA9, 0x4F, 0xAA, 0x4F, 0xAB, 0x4F, 0xAC, 0x4F, 0xAD, 0x4F, 0xAE, 0x4F, 0xAF, 0x4F, 0xB0, 0x4F, 0xB1, 0x4F, 0xB2, 0x4F, 0xB3, 0x4F, 0xB4, 0x4F, 0xB5, 0x4F, 0xB6,\n\t0x4F, 0xB7, 0x4F, 0xB8, 0x4F, 0xB9, 0x4F, 0xBA, 0x4F, 0xBB, 0x4F, 0xBC, 0x4F, 0xBD, 0x4F, 0xBE, 0x4F, 0xBF, 0x4F, 0xC0, 0x4F, 0xC1, 0x4F, 0xC2, 0x4F, 0xC3, 0x4F, 0xC4, 0x4F, 0xC5, 0x4F, 0xC6,\n\t0x4F, 0xC7, 0x4F, 0xC8, 0x4F, 0xC9, 0x4F, 0xCA, 0x4F, 0xCB, 0x4F, 0xCC, 0x4F, 0xCD, 0x4F, 0xCE, 0x4F, 0xCF, 0x4F, 0xD0, 0x4F, 0xD1, 0x4F, 0xD2, 0x4F, 0xD3, 0x4F, 0xD4, 0x4F, 0xD5, 0x4F, 0xD6,\n\t0x4F, 0xD7, 0x4F, 0xD8, 0x4F, 0xD9, 0x4F, 0xDA, 0x4F, 0xDB, 0x4F, 0xDC, 0x4F, 0xDD, 0x4F, 0xDE, 0x4F, 0xDF, 0x4F, 0xE0, 0x4F, 0xE1, 0x4F, 0xE2, 0x4F, 0xE3, 0x4F, 0xE4, 0x4F, 0xE5, 0x4F, 0xE6,\n\t0x4F, 0xE7, 0x4F, 0xE8, 0x4F, 0xE9, 0x4F, 0xEA, 0x4F, 0xEB, 0x4F, 0xEC, 0x4F, 0xED, 0x4F, 0xEE, 0x4F, 0xEF, 0x4F, 0xF0, 0x4F, 0xF1, 0x4F, 0xF2, 0x4F, 0xF3, 0x4F, 0xF4, 0x4F, 0xF5, 0x4F, 0xF6,\n\t0x4F, 0xF7, 0x4F, 0xF8, 0x4F, 0xF9, 0x4F, 0xFA, 0x4F, 0xFB, 0x4F, 0xFC, 0x4F, 0xFD, 0x4F, 0xFE, 0x4F, 0xFF, 0x4F, 0x00, 0x50, 0x01, 0x50, 0x02, 0x50, 0x03, 0x50, 0x04, 0x50, 0x05, 0x50, 0x06,\n\t0x50, 0x07, 0x50, 0x08, 0x50, 0x09, 0x50, 0x0A, 0x50, 0x0B, 0x50, 0x0C, 0x50, 0x0D, 0x50, 0x0E, 0x50, 0x0F, 0x50, 0x10, 0x50, 0x11, 0x50, 0x12, 0x50, 0x13, 0x50, 0x14, 0x50, 0x15, 0x50, 0x16,\n\t0x50, 0x17, 0x50, 0x18, 0x50, 0x19, 0x50, 0x1A, 0x50, 0x1B, 0x50, 0x1C, 0x50, 0x1D, 0x50, 0x1E, 0x50, 0x1F, 0x50, 0x20, 0x50, 0x21, 0x50, 0x22, 0x50, 0x23, 0x50, 0x24, 0x50, 0x25, 0x50, 0x26,\n\t0x50, 0x27, 0x50, 0x28, 0x50, 0x29, 0x50, 0x2A, 0x50, 0x2B, 0x50, 0x2C, 0x50, 0x2D, 0x50, 0x2E, 0x50, 0x2F, 0x50, 0x30, 0x50, 0x31, 0x50, 0x32, 0x50, 0x33, 0x50, 0x34, 0x50, 0x35, 0x50, 0x36,\n\t0x50, 0x37, 0x50, 0x38, 0x50, 0x39, 0x50, 0x3A, 0x50, 0x3B, 0x50, 0x3C, 0x50, 0x3D, 0x50, 0x3E, 0x50, 0x3F, 0x50, 0x40, 0x50, 0x41, 0x50, 0x42, 0x50, 0x43, 0x50, 0x44, 0x50, 0x45, 0x50, 0x46,\n\t0x50, 0x47, 0x50, 0x48, 0x50, 0x49, 0x50, 0x4A, 0x50, 0x4B, 0x50, 0x4C, 0x50, 0x4D, 0x50, 0x4E, 0x50, 0x4F, 0x50, 0x50, 0x50, 0x51, 0x50, 0x52, 0x50, 0x53, 0x50, 0x54, 0x50, 0x55, 0x50, 0x56,\n\t0x50, 0x57, 0x50, 0x58, 0x50, 0x59, 0x50, 0x5A, 0x50, 0x5B, 0x50, 0x5C, 0x50, 0x5D, 0x50, 0x5E, 0x50, 0x5F, 0x50, 0x60, 0x50, 0x61, 0x50, 0x62, 0x50, 0x63, 0x50, 0x64, 0x50, 0x65, 0x50, 0x66,\n\t0x50, 0x67, 0x50, 0x68, 0x50, 0x69, 0x50, 0x6A, 0x50, 0x6B, 0x50, 0x6C, 0x50, 0x6D, 0x50, 0x6E, 0x50, 0x6F, 0x50, 0x70, 0x50, 0x71, 0x50, 0x72, 0x50, 0x73, 0x50, 0x74, 0x50, 0x75, 0x50, 0x76,\n\t0x50, 0x77, 0x50, 0x78, 0x50, 0x79, 0x50, 0x7A, 0x50, 0x7B, 0x50, 0x7C, 0x50, 0x7D, 0x50, 0x7E, 0x50, 0x7F, 0x50, 0x80, 0x50, 0x81, 0x50, 0x82, 0x50, 0x83, 0x50, 0x84, 0x50, 0x85, 0x50, 0x86,\n\t0x50, 0x87, 0x50, 0x88, 0x50, 0x89, 0x50, 0x8A, 0x50, 0x8B, 0x50, 0x8C, 0x50, 0x8D, 0x50, 0x8E, 0x50, 0x8F, 0x50, 0x90, 0x50, 0x91, 0x50, 0x92, 0x50, 0x93, 0x50, 0x94, 0x50, 0x95, 0x50, 0x96,\n\t0x50, 0x97, 0x50, 0x98, 0x50, 0x99, 0x50, 0x9A, 0x50, 0x9B, 0x50, 0x9C, 0x50, 0x9D, 0x50, 0x9E, 0x50, 0x9F, 0x50, 0xA0, 0x50, 0xA1, 0x50, 0xA2, 0x50, 0xA3, 0x50, 0xA4, 0x50, 0xA5, 0x50, 0xA6,\n\t0x50, 0xA7, 0x50, 0xA8, 0x50, 0xA9, 0x50, 0xAA, 0x50, 0xAB, 0x50, 0xAC, 0x50, 0xAD, 0x50, 0xAE, 0x50, 0xAF, 0x50, 0xB0, 0x50, 0xB1, 0x50, 0xB2, 0x50, 0xB3, 0x50, 0xB4, 0x50, 0xB5, 0x50, 0xB6,\n\t0x50, 0xB7, 0x50, 0xB8, 0x50, 0xB9, 0x50, 0xBA, 0x50, 0xBB, 0x50, 0xBC, 0x50, 0xBD, 0x50, 0xBE, 0x50, 0xBF, 0x50, 0xC0, 0x50, 0xC1, 0x50, 0xC2, 0x50, 0xC3, 0x50, 0xC4, 0x50, 0xC5, 0x50, 0xC6,\n\t0x50, 0xC7, 0x50, 0xC8, 0x50, 0xC9, 0x50, 0xCA, 0x50, 0xCB, 0x50, 0xCC, 0x50, 0xCD, 0x50, 0xCE, 0x50, 0xCF, 0x50, 0xD0, 0x50, 0xD1, 0x50, 0xD2, 0x50, 0xD3, 0x50, 0xD4, 0x50, 0xD5, 0x50, 0xD6,\n\t0x50, 0xD7, 0x50, 0xD8, 0x50, 0xD9, 0x50, 0xDA, 0x50, 0xDB, 0x50, 0xDC, 0x50, 0xDD, 0x50, 0xDE, 0x50, 0xDF, 0x50, 0xE0, 0x50, 0xE1, 0x50, 0xE2, 0x50, 0xE3, 0x50, 0xE4, 0x50, 0xE5, 0x50, 0xE6,\n\t0x50, 0xE7, 0x50, 0xE8, 0x50, 0xE9, 0x50, 0xEA, 0x50, 0xEB, 0x50, 0xEC, 0x50, 0xED, 0x50, 0xEE, 0x50, 0xEF, 0x50, 0xF0, 0x50, 0xF1, 0x50, 0xF2, 0x50, 0xF3, 0x50, 0xF4, 0x50, 0xF5, 0x50, 0xF6,\n\t0x50, 0xF7, 0x50, 0xF8, 0x50, 0xF9, 0x50, 0xFA, 0x50, 0xFB, 0x50, 0xFC, 0x50, 0xFD, 0x50, 0xFE, 0x50, 0xFF, 0x50, 0x00, 0x51, 0x01, 0x51, 0x02, 0x51, 0x03, 0x51, 0x04, 0x51, 0x05, 0x51, 0x06,\n\t0x51, 0x07, 0x51, 0x08, 0x51, 0x09, 0x51, 0x0A, 0x51, 0x0B, 0x51, 0x0C, 0x51, 0x0D, 0x51, 0x0E, 0x51, 0x0F, 0x51, 0x10, 0x51, 0x11, 0x51, 0x12, 0x51, 0x13, 0x51, 0x14, 0x51, 0x15, 0x51, 0x16,\n\t0x51, 0x17, 0x51, 0x18, 0x51, 0x19, 0x51, 0x1A, 0x51, 0x1B, 0x51, 0x1C, 0x51, 0x1D, 0x51, 0x1E, 0x51, 0x1F, 0x51, 0x20, 0x51, 0x21, 0x51, 0x22, 0x51, 0x23, 0x51, 0x24, 0x51, 0x25, 0x51, 0x26,\n\t0x51, 0x27, 0x51, 0x28, 0x51, 0x29, 0x51, 0x2A, 0x51, 0x2B, 0x51, 0x2C, 0x51, 0x2D, 0x51, 0x2E, 0x51, 0x2F, 0x51, 0x30, 0x51, 0x31, 0x51, 0x32, 0x51, 0x33, 0x51, 0x34, 0x51, 0x35, 0x51, 0x36,\n\t0x51, 0x37, 0x51, 0x38, 0x51, 0x39, 0x51, 0x3A, 0x51, 0x3B, 0x51, 0x3C, 0x51, 0x3D, 0x51, 0x3E, 0x51, 0x3F, 0x51, 0x40, 0x51, 0x41, 0x51, 0x42, 0x51, 0x43, 0x51, 0x44, 0x51, 0x45, 0x51, 0x46,\n\t0x51, 0x47, 0x51, 0x48, 0x51, 0x49, 0x51, 0x4A, 0x51, 0x4B, 0x51, 0x4C, 0x51, 0x4D, 0x51, 0x4E, 0x51, 0x4F, 0x51, 0x50, 0x51, 0x51, 0x51, 0x52, 0x51, 0x53, 0x51, 0x54, 0x51, 0x55, 0x51, 0x56,\n\t0x51, 0x57, 0x51, 0x58, 0x51, 0x59, 0x51, 0x5A, 0x51, 0x5B, 0x51, 0x5C, 0x51, 0x5D, 0x51, 0x5E, 0x51, 0x5F, 0x51, 0x60, 0x51, 0x61, 0x51, 0x62, 0x51, 0x63, 0x51, 0x64, 0x51, 0x65, 0x51, 0x66,\n\t0x51, 0x67, 0x51, 0x68, 0x51, 0x69, 0x51, 0x6A, 0x51, 0x6B, 0x51, 0x6C, 0x51, 0x6D, 0x51, 0x6E, 0x51, 0x6F, 0x51, 0x70, 0x51, 0x71, 0x51, 0x72, 0x51, 0x73, 0x51, 0x74, 0x51, 0x75, 0x51, 0x76,\n\t0x51, 0x77, 0x51, 0x78, 0x51, 0x79, 0x51, 0x7A, 0x51, 0x7B, 0x51, 0x7C, 0x51, 0x7D, 0x51, 0x7E, 0x51, 0x7F, 0x51, 0x80, 0x51, 0x81, 0x51, 0x82, 0x51, 0x83, 0x51, 0x84, 0x51, 0x85, 0x51, 0x86,\n\t0x51, 0x87, 0x51, 0x88, 0x51, 0x89, 0x51, 0x8A, 0x51, 0x8B, 0x51, 0x8C, 0x51, 0x8D, 0x51, 0x8E, 0x51, 0x8F, 0x51, 0x90, 0x51, 0x91, 0x51, 0x92, 0x51, 0x93, 0x51, 0x94, 0x51, 0x95, 0x51, 0x96,\n\t0x51, 0x97, 0x51, 0x98, 0x51, 0x99, 0x51, 0x9A, 0x51, 0x9B, 0x51, 0x9C, 0x51, 0x9D, 0x51, 0x9E, 0x51, 0x9F, 0x51, 0xA0, 0x51, 0xA1, 0x51, 0xA2, 0x51, 0xA3, 0x51, 0xA4, 0x51, 0xA5, 0x51, 0xA6,\n\t0x51, 0xA7, 0x51, 0xA8, 0x51, 0xA9, 0x51, 0xAA, 0x51, 0xAB, 0x51, 0xAC, 0x51, 0xAD, 0x51, 0xAE, 0x51, 0xAF, 0x51, 0xB0, 0x51, 0xB1, 0x51, 0xB2, 0x51, 0xB3, 0x51, 0xB4, 0x51, 0xB5, 0x51, 0xB6,\n\t0x51, 0xB7, 0x51, 0xB8, 0x51, 0xB9, 0x51, 0xBA, 0x51, 0xBB, 0x51, 0xBC, 0x51, 0xBD, 0x51, 0xBE, 0x51, 0xBF, 0x51, 0xC0, 0x51, 0xC1, 0x51, 0xC2, 0x51, 0xC3, 0x51, 0xC4, 0x51, 0xC5, 0x51, 0xC6,\n\t0x51, 0xC7, 0x51, 0xC8, 0x51, 0xC9, 0x51, 0xCA, 0x51, 0xCB, 0x51, 0xCC, 0x51, 0xCD, 0x51, 0xCE, 0x51, 0xCF, 0x51, 0xD0, 0x51, 0xD1, 0x51, 0xD2, 0x51, 0xD3, 0x51, 0xD4, 0x51, 0xD5, 0x51, 0xD6,\n\t0x51, 0xD7, 0x51, 0xD8, 0x51, 0xD9, 0x51, 0xDA, 0x51, 0xDB, 0x51, 0xDC, 0x51, 0xDD, 0x51, 0xDE, 0x51, 0xDF, 0x51, 0xE0, 0x51, 0xE1, 0x51, 0xE2, 0x51, 0xE3, 0x51, 0xE4, 0x51, 0xE5, 0x51, 0xE6,\n\t0x51, 0xE7, 0x51, 0xE8, 0x51, 0xE9, 0x51, 0xEA, 0x51, 0xEB, 0x51, 0xEC, 0x51, 0xED, 0x51, 0xEE, 0x51, 0xEF, 0x51, 0xF0, 0x51, 0xF1, 0x51, 0xF2, 0x51, 0xF3, 0x51, 0xF4, 0x51, 0xF5, 0x51, 0xF6,\n\t0x51, 0xF7, 0x51, 0xF8, 0x51, 0xF9, 0x51, 0xFA, 0x51, 0xFB, 0x51, 0xFC, 0x51, 0xFD, 0x51, 0xFE, 0x51, 0xFF, 0x51, 0x00, 0x52, 0x01, 0x52, 0x02, 0x52, 0x03, 0x52, 0x04, 0x52, 0x05, 0x52, 0x06,\n\t0x52, 0x07, 0x52, 0x08, 0x52, 0x09, 0x52, 0x0A, 0x52, 0x0B, 0x52, 0x0C, 0x52, 0x0D, 0x52, 0x0E, 0x52, 0x0F, 0x52, 0x10, 0x52, 0x11, 0x52, 0x12, 0x52, 0x13, 0x52, 0x14, 0x52, 0x15, 0x52, 0x16,\n\t0x52, 0x17, 0x52, 0x18, 0x52, 0x19, 0x52, 0x1A, 0x52, 0x1B, 0x52, 0x1C, 0x52, 0x1D, 0x52, 0x1E, 0x52, 0x1F, 0x52, 0x20, 0x52, 0x21, 0x52, 0x22, 0x52, 0x23, 0x52, 0x24, 0x52, 0x25, 0x52, 0x26,\n\t0x52, 0x27, 0x52, 0x28, 0x52, 0x29, 0x52, 0x2A, 0x52, 0x2B, 0x52, 0x2C, 0x52, 0x2D, 0x52, 0x2E, 0x52, 0x2F, 0x52, 0x30, 0x52, 0x31, 0x52, 0x32, 0x52, 0x33, 0x52, 0x34, 0x52, 0x35, 0x52, 0x36,\n\t0x52, 0x37, 0x52, 0x38, 0x52, 0x39, 0x52, 0x3A, 0x52, 0x3B, 0x52, 0x3C, 0x52, 0x3D, 0x52, 0x3E, 0x52, 0x3F, 0x52, 0x40, 0x52, 0x41, 0x52, 0x42, 0x52, 0x43, 0x52, 0x44, 0x52, 0x45, 0x52, 0x46,\n\t0x52, 0x47, 0x52, 0x48, 0x52, 0x49, 0x52, 0x4A, 0x52, 0x4B, 0x52, 0x4C, 0x52, 0x4D, 0x52, 0x4E, 0x52, 0x4F, 0x52, 0x50, 0x52, 0x51, 0x52, 0x52, 0x52, 0x53, 0x52, 0x54, 0x52, 0x55, 0x52, 0x56,\n\t0x52, 0x57, 0x52, 0x58, 0x52, 0x59, 0x52, 0x5A, 0x52, 0x5B, 0x52, 0x5C, 0x52, 0x5D, 0x52, 0x5E, 0x52, 0x5F, 0x52, 0x60, 0x52, 0x61, 0x52, 0x62, 0x52, 0x63, 0x52, 0x64, 0x52, 0x65, 0x52, 0x66,\n\t0x52, 0x67, 0x52, 0x68, 0x52, 0x69, 0x52, 0x6A, 0x52, 0x6B, 0x52, 0x6C, 0x52, 0x6D, 0x52, 0x6E, 0x52, 0x6F, 0x52, 0x70, 0x52, 0x71, 0x52, 0x72, 0x52, 0x73, 0x52, 0x74, 0x52, 0x75, 0x52, 0x76,\n\t0x52, 0x77, 0x52, 0x78, 0x52, 0x79, 0x52, 0x7A, 0x52, 0x7B, 0x52, 0x7C, 0x52, 0x7D, 0x52, 0x7E, 0x52, 0x7F, 0x52, 0x80, 0x52, 0x81, 0x52, 0x82, 0x52, 0x83, 0x52, 0x84, 0x52, 0x85, 0x52, 0x86,\n\t0x52, 0x87, 0x52, 0x88, 0x52, 0x89, 0x52, 0x8A, 0x52, 0x8B, 0x52, 0x8C, 0x52, 0x8D, 0x52, 0x8E, 0x52, 0x8F, 0x52, 0x90, 0x52, 0x91, 0x52, 0x92, 0x52, 0x93, 0x52, 0x94, 0x52, 0x95, 0x52, 0x96,\n\t0x52, 0x97, 0x52, 0x98, 0x52, 0x99, 0x52, 0x9A, 0x52, 0x9B, 0x52, 0x9C, 0x52, 0x9D, 0x52, 0x9E, 0x52, 0x9F, 0x52, 0xA0, 0x52, 0xA1, 0x52, 0xA2, 0x52, 0xA3, 0x52, 0xA4, 0x52, 0xA5, 0x52, 0xA6,\n\t0x52, 0xA7, 0x52, 0xA8, 0x52, 0xA9, 0x52, 0xAA, 0x52, 0xAB, 0x52, 0xAC, 0x52, 0xAD, 0x52, 0xAE, 0x52, 0xAF, 0x52, 0xB0, 0x52, 0xB1, 0x52, 0xB2, 0x52, 0xB3, 0x52, 0xB4, 0x52, 0xB5, 0x52, 0xB6,\n\t0x52, 0xB7, 0x52, 0xB8, 0x52, 0xB9, 0x52, 0xBA, 0x52, 0xBB, 0x52, 0xBC, 0x52, 0xBD, 0x52, 0xBE, 0x52, 0xBF, 0x52, 0xC0, 0x52, 0xC1, 0x52, 0xC2, 0x52, 0xC3, 0x52, 0xC4, 0x52, 0xC5, 0x52, 0xC6,\n\t0x52, 0xC7, 0x52, 0xC8, 0x52, 0xC9, 0x52, 0xCA, 0x52, 0xCB, 0x52, 0xCC, 0x52, 0xCD, 0x52, 0xCE, 0x52, 0xCF, 0x52, 0xD0, 0x52, 0xD1, 0x52, 0xD2, 0x52, 0xD3, 0x52, 0xD4, 0x52, 0xD5, 0x52, 0xD6,\n\t0x52, 0xD7, 0x52, 0xD8, 0x52, 0xD9, 0x52, 0xDA, 0x52, 0xDB, 0x52, 0xDC, 0x52, 0xDD, 0x52, 0xDE, 0x52, 0xDF, 0x52, 0xE0, 0x52, 0xE1, 0x52, 0xE2, 0x52, 0xE3, 0x52, 0xE4, 0x52, 0xE5, 0x52, 0xE6,\n\t0x52, 0xE7, 0x52, 0xE8, 0x52, 0xE9, 0x52, 0xEA, 0x52, 0xEB, 0x52, 0xEC, 0x52, 0xED, 0x52, 0xEE, 0x52, 0xEF, 0x52, 0xF0, 0x52, 0xF1, 0x52, 0xF2, 0x52, 0xF3, 0x52, 0xF4, 0x52, 0xF5, 0x52, 0xF6,\n\t0x52, 0xF7, 0x52, 0xF8, 0x52, 0xF9, 0x52, 0xFA, 0x52, 0xFB, 0x52, 0xFC, 0x52, 0xFD, 0x52, 0xFE, 0x52, 0xFF, 0x52, 0x00, 0x53, 0x01, 0x53, 0x02, 0x53, 0x03, 0x53, 0x04, 0x53, 0x05, 0x53, 0x06,\n\t0x53, 0x07, 0x53, 0x08, 0x53, 0x09, 0x53, 0x0A, 0x53, 0x0B, 0x53, 0x0C, 0x53, 0x0D, 0x53, 0x0E, 0x53, 0x0F, 0x53, 0x10, 0x53, 0x11, 0x53, 0x12, 0x53, 0x13, 0x53, 0x14, 0x53, 0x15, 0x53, 0x16,\n\t0x53, 0x17, 0x53, 0x18, 0x53, 0x19, 0x53, 0x1A, 0x53, 0x1B, 0x53, 0x1C, 0x53, 0x1D, 0x53, 0x1E, 0x53, 0x1F, 0x53, 0x20, 0x53, 0x21, 0x53, 0x22, 0x53, 0x23, 0x53, 0x24, 0x53, 0x25, 0x53, 0x26,\n\t0x53, 0x27, 0x53, 0x28, 0x53, 0x29, 0x53, 0x2A, 0x53, 0x2B, 0x53, 0x2C, 0x53, 0x2D, 0x53, 0x2E, 0x53, 0x2F, 0x53, 0x30, 0x53, 0x31, 0x53, 0x32, 0x53, 0x33, 0x53, 0x34, 0x53, 0x35, 0x53, 0x36,\n\t0x53, 0x37, 0x53, 0x38, 0x53, 0x39, 0x53, 0x3A, 0x53, 0x3B, 0x53, 0x3C, 0x53, 0x3D, 0x53, 0x3E, 0x53, 0x3F, 0x53, 0x40, 0x53, 0x41, 0x53, 0x42, 0x53, 0x43, 0x53, 0x44, 0x53, 0x45, 0x53, 0x46,\n\t0x53, 0x47, 0x53, 0x48, 0x53, 0x49, 0x53, 0x4A, 0x53, 0x4B, 0x53, 0x4C, 0x53, 0x4D, 0x53, 0x4E, 0x53, 0x4F, 0x53, 0x50, 0x53, 0x51, 0x53, 0x52, 0x53, 0x53, 0x53, 0x54, 0x53, 0x55, 0x53, 0x56,\n\t0x53, 0x57, 0x53, 0x58, 0x53, 0x59, 0x53, 0x5A, 0x53, 0x5B, 0x53, 0x5C, 0x53, 0x5D, 0x53, 0x5E, 0x53, 0x5F, 0x53, 0x60, 0x53, 0x61, 0x53, 0x62, 0x53, 0x63, 0x53, 0x64, 0x53, 0x65, 0x53, 0x66,\n\t0x53, 0x67, 0x53, 0x68, 0x53, 0x69, 0x53, 0x6A, 0x53, 0x6B, 0x53, 0x6C, 0x53, 0x6D, 0x53, 0x6E, 0x53, 0x6F, 0x53, 0x70, 0x53, 0x71, 0x53, 0x72, 0x53, 0x73, 0x53, 0x74, 0x53, 0x75, 0x53, 0x76,\n\t0x53, 0x77, 0x53, 0x78, 0x53, 0x79, 0x53, 0x7A, 0x53, 0x7B, 0x53, 0x7C, 0x53, 0x7D, 0x53, 0x7E, 0x53, 0x7F, 0x53, 0x80, 0x53, 0x81, 0x53, 0x82, 0x53, 0x83, 0x53, 0x84, 0x53, 0x85, 0x53, 0x86,\n\t0x53, 0x87, 0x53, 0x88, 0x53, 0x89, 0x53, 0x8A, 0x53, 0x8B, 0x53, 0x8C, 0x53, 0x8D, 0x53, 0x8E, 0x53, 0x8F, 0x53, 0x90, 0x53, 0x91, 0x53, 0x92, 0x53, 0x93, 0x53, 0x94, 0x53, 0x95, 0x53, 0x96,\n\t0x53, 0x97, 0x53, 0x98, 0x53, 0x99, 0x53, 0x9A, 0x53, 0x9B, 0x53, 0x9C, 0x53, 0x9D, 0x53, 0x9E, 0x53, 0x9F, 0x53, 0xA0, 0x53, 0xA1, 0x53, 0xA2, 0x53, 0xA3, 0x53, 0xA4, 0x53, 0xA5, 0x53, 0xA6,\n\t0x53, 0xA7, 0x53, 0xA8, 0x53, 0xA9, 0x53, 0xAA, 0x53, 0xAB, 0x53, 0xAC, 0x53, 0xAD, 0x53, 0xAE, 0x53, 0xAF, 0x53, 0xB0, 0x53, 0xB1, 0x53, 0xB2, 0x53, 0xB3, 0x53, 0xB4, 0x53, 0xB5, 0x53, 0xB6,\n\t0x53, 0xB7, 0x53, 0xB8, 0x53, 0xB9, 0x53, 0xBA, 0x53, 0xBB, 0x53, 0xBC, 0x53, 0xBD, 0x53, 0xBE, 0x53, 0xBF, 0x53, 0xC0, 0x53, 0xC1, 0x53, 0xC2, 0x53, 0xC3, 0x53, 0xC4, 0x53, 0xC5, 0x53, 0xC6,\n\t0x53, 0xC7, 0x53, 0xC8, 0x53, 0xC9, 0x53, 0xCA, 0x53, 0xCB, 0x53, 0xCC, 0x53, 0xCD, 0x53, 0xCE, 0x53, 0xCF, 0x53, 0xD0, 0x53, 0xD1, 0x53, 0xD2, 0x53, 0xD3, 0x53, 0xD4, 0x53, 0xD5, 0x53, 0xD6,\n\t0x53, 0xD7, 0x53, 0xD8, 0x53, 0xD9, 0x53, 0xDA, 0x53, 0xDB, 0x53, 0xDC, 0x53, 0xDD, 0x53, 0xDE, 0x53, 0xDF, 0x53, 0xE0, 0x53, 0xE1, 0x53, 0xE2, 0x53, 0xE3, 0x53, 0xE4, 0x53, 0xE5, 0x53, 0xE6,\n\t0x53, 0xE7, 0x53, 0xE8, 0x53, 0xE9, 0x53, 0xEA, 0x53, 0xEB, 0x53, 0xEC, 0x53, 0xED, 0x53, 0xEE, 0x53, 0xEF, 0x53, 0xF0, 0x53, 0xF1, 0x53, 0xF2, 0x53, 0xF3, 0x53, 0xF4, 0x53, 0xF5, 0x53, 0xF6,\n\t0x53, 0xF7, 0x53, 0xF8, 0x53, 0xF9, 0x53, 0xFA, 0x53, 0xFB, 0x53, 0xFC, 0x53, 0xFD, 0x53, 0xFE, 0x53, 0xFF, 0x53, 0x00, 0x54, 0x01, 0x54, 0x02, 0x54, 0x03, 0x54, 0x04, 0x54, 0x05, 0x54, 0x06,\n\t0x54, 0x07, 0x54, 0x08, 0x54, 0x09, 0x54, 0x0A, 0x54, 0x0B, 0x54, 0x0C, 0x54, 0x0D, 0x54, 0x0E, 0x54, 0x0F, 0x54, 0x10, 0x54, 0x11, 0x54, 0x12, 0x54, 0x13, 0x54, 0x14, 0x54, 0x15, 0x54, 0x16,\n\t0x54, 0x17, 0x54, 0x18, 0x54, 0x19, 0x54, 0x1A, 0x54, 0x1B, 0x54, 0x1C, 0x54, 0x1D, 0x54, 0x1E, 0x54, 0x1F, 0x54, 0x20, 0x54, 0x21, 0x54, 0x22, 0x54, 0x23, 0x54, 0x24, 0x54, 0x25, 0x54, 0x26,\n\t0x54, 0x27, 0x54, 0x28, 0x54, 0x29, 0x54, 0x2A, 0x54, 0x2B, 0x54, 0x2C, 0x54, 0x2D, 0x54, 0x2E, 0x54, 0x2F, 0x54, 0x30, 0x54, 0x31, 0x54, 0x32, 0x54, 0x33, 0x54, 0x34, 0x54, 0x35, 0x54, 0x36,\n\t0x54, 0x37, 0x54, 0x38, 0x54, 0x39, 0x54, 0x3A, 0x54, 0x3B, 0x54, 0x3C, 0x54, 0x3D, 0x54, 0x3E, 0x54, 0x3F, 0x54, 0x40, 0x54, 0x41, 0x54, 0x42, 0x54, 0x43, 0x54, 0x44, 0x54, 0x45, 0x54, 0x46,\n\t0x54, 0x47, 0x54, 0x48, 0x54, 0x49, 0x54, 0x4A, 0x54, 0x4B, 0x54, 0x4C, 0x54, 0x4D, 0x54, 0x4E, 0x54, 0x4F, 0x54, 0x50, 0x54, 0x51, 0x54, 0x52, 0x54, 0x53, 0x54, 0x54, 0x54, 0x55, 0x54, 0x56,\n\t0x54, 0x57, 0x54, 0x58, 0x54, 0x59, 0x54, 0x5A, 0x54, 0x5B, 0x54, 0x5C, 0x54, 0x5D, 0x54, 0x5E, 0x54, 0x5F, 0x54, 0x60, 0x54, 0x61, 0x54, 0x62, 0x54, 0x63, 0x54, 0x64, 0x54, 0x65, 0x54, 0x66,\n\t0x54, 0x67, 0x54, 0x68, 0x54, 0x69, 0x54, 0x6A, 0x54, 0x6B, 0x54, 0x6C, 0x54, 0x6D, 0x54, 0x6E, 0x54, 0x6F, 0x54, 0x70, 0x54, 0x71, 0x54, 0x72, 0x54, 0x73, 0x54, 0x74, 0x54, 0x75, 0x54, 0x76,\n\t0x54, 0x77, 0x54, 0x78, 0x54, 0x79, 0x54, 0x7A, 0x54, 0x7B, 0x54, 0x7C, 0x54, 0x7D, 0x54, 0x7E, 0x54, 0x7F, 0x54, 0x80, 0x54, 0x81, 0x54, 0x82, 0x54, 0x83, 0x54, 0x84, 0x54, 0x85, 0x54, 0x86,\n\t0x54, 0x87, 0x54, 0x88, 0x54, 0x89, 0x54, 0x8A, 0x54, 0x8B, 0x54, 0x8C, 0x54, 0x8D, 0x54, 0x8E, 0x54, 0x8F, 0x54, 0x90, 0x54, 0x91, 0x54, 0x92, 0x54, 0x93, 0x54, 0x94, 0x54, 0x95, 0x54, 0x96,\n\t0x54, 0x97, 0x54, 0x98, 0x54, 0x99, 0x54, 0x9A, 0x54, 0x9B, 0x54, 0x9C, 0x54, 0x9D, 0x54, 0x9E, 0x54, 0x9F, 0x54, 0xA0, 0x54, 0xA1, 0x54, 0xA2, 0x54, 0xA3, 0x54, 0xA4, 0x54, 0xA5, 0x54, 0xA6,\n\t0x54, 0xA7, 0x54, 0xA8, 0x54, 0xA9, 0x54, 0xAA, 0x54, 0xAB, 0x54, 0xAC, 0x54, 0xAD, 0x54, 0xAE, 0x54, 0xAF, 0x54, 0xB0, 0x54, 0xB1, 0x54, 0xB2, 0x54, 0xB3, 0x54, 0xB4, 0x54, 0xB5, 0x54, 0xB6,\n\t0x54, 0xB7, 0x54, 0xB8, 0x54, 0xB9, 0x54, 0xBA, 0x54, 0xBB, 0x54, 0xBC, 0x54, 0xBD, 0x54, 0xBE, 0x54, 0xBF, 0x54, 0xC0, 0x54, 0xC1, 0x54, 0xC2, 0x54, 0xC3, 0x54, 0xC4, 0x54, 0xC5, 0x54, 0xC6,\n\t0x54, 0xC7, 0x54, 0xC8, 0x54, 0xC9, 0x54, 0xCA, 0x54, 0xCB, 0x54, 0xCC, 0x54, 0xCD, 0x54, 0xCE, 0x54, 0xCF, 0x54, 0xD0, 0x54, 0xD1, 0x54, 0xD2, 0x54, 0xD3, 0x54, 0xD4, 0x54, 0xD5, 0x54, 0xD6,\n\t0x54, 0xD7, 0x54, 0xD8, 0x54, 0xD9, 0x54, 0xDA, 0x54, 0xDB, 0x54, 0xDC, 0x54, 0xDD, 0x54, 0xDE, 0x54, 0xDF, 0x54, 0xE0, 0x54, 0xE1, 0x54, 0xE2, 0x54, 0xE3, 0x54, 0xE4, 0x54, 0xE5, 0x54, 0xE6,\n\t0x54, 0xE7, 0x54, 0xE8, 0x54, 0xE9, 0x54, 0xEA, 0x54, 0xEB, 0x54, 0xEC, 0x54, 0xED, 0x54, 0xEE, 0x54, 0xEF, 0x54, 0xF0, 0x54, 0xF1, 0x54, 0xF2, 0x54, 0xF3, 0x54, 0xF4, 0x54, 0xF5, 0x54, 0xF6,\n\t0x54, 0xF7, 0x54, 0xF8, 0x54, 0xF9, 0x54, 0xFA, 0x54, 0xFB, 0x54, 0xFC, 0x54, 0xFD, 0x54, 0xFE, 0x54, 0xFF, 0x54, 0x00, 0x55, 0x01, 0x55, 0x02, 0x55, 0x03, 0x55, 0x04, 0x55, 0x05, 0x55, 0x06,\n\t0x55, 0x07, 0x55, 0x08, 0x55, 0x09, 0x55, 0x0A, 0x55, 0x0B, 0x55, 0x0C, 0x55, 0x0D, 0x55, 0x0E, 0x55, 0x0F, 0x55, 0x10, 0x55, 0x11, 0x55, 0x12, 0x55, 0x13, 0x55, 0x14, 0x55, 0x15, 0x55, 0x16,\n\t0x55, 0x17, 0x55, 0x18, 0x55, 0x19, 0x55, 0x1A, 0x55, 0x1B, 0x55, 0x1C, 0x55, 0x1D, 0x55, 0x1E, 0x55, 0x1F, 0x55, 0x20, 0x55, 0x21, 0x55, 0x22, 0x55, 0x23, 0x55, 0x24, 0x55, 0x25, 0x55, 0x26,\n\t0x55, 0x27, 0x55, 0x28, 0x55, 0x29, 0x55, 0x2A, 0x55, 0x2B, 0x55, 0x2C, 0x55, 0x2D, 0x55, 0x2E, 0x55, 0x2F, 0x55, 0x30, 0x55, 0x31, 0x55, 0x32, 0x55, 0x33, 0x55, 0x34, 0x55, 0x35, 0x55, 0x36,\n\t0x55, 0x37, 0x55, 0x38, 0x55, 0x39, 0x55, 0x3A, 0x55, 0x3B, 0x55, 0x3C, 0x55, 0x3D, 0x55, 0x3E, 0x55, 0x3F, 0x55, 0x40, 0x55, 0x41, 0x55, 0x42, 0x55, 0x43, 0x55, 0x44, 0x55, 0x45, 0x55, 0x46,\n\t0x55, 0x47, 0x55, 0x48, 0x55, 0x49, 0x55, 0x4A, 0x55, 0x4B, 0x55, 0x4C, 0x55, 0x4D, 0x55, 0x4E, 0x55, 0x4F, 0x55, 0x50, 0x55, 0x51, 0x55, 0x52, 0x55, 0x53, 0x55, 0x54, 0x55, 0x55, 0x55, 0x56,\n\t0x55, 0x57, 0x55, 0x58, 0x55, 0x59, 0x55, 0x5A, 0x55, 0x5B, 0x55, 0x5C, 0x55, 0x5D, 0x55, 0x5E, 0x55, 0x5F, 0x55, 0x60, 0x55, 0x61, 0x55, 0x62, 0x55, 0x63, 0x55, 0x64, 0x55, 0x65, 0x55, 0x66,\n\t0x55, 0x67, 0x55, 0x68, 0x55, 0x69, 0x55, 0x6A, 0x55, 0x6B, 0x55, 0x6C, 0x55, 0x6D, 0x55, 0x6E, 0x55, 0x6F, 0x55, 0x70, 0x55, 0x71, 0x55, 0x72, 0x55, 0x73, 0x55, 0x74, 0x55, 0x75, 0x55, 0x76,\n\t0x55, 0x77, 0x55, 0x78, 0x55, 0x79, 0x55, 0x7A, 0x55, 0x7B, 0x55, 0x7C, 0x55, 0x7D, 0x55, 0x7E, 0x55, 0x7F, 0x55, 0x80, 0x55, 0x81, 0x55, 0x82, 0x55, 0x83, 0x55, 0x84, 0x55, 0x85, 0x55, 0x86,\n\t0x55, 0x87, 0x55, 0x88, 0x55, 0x89, 0x55, 0x8A, 0x55, 0x8B, 0x55, 0x8C, 0x55, 0x8D, 0x55, 0x8E, 0x55, 0x8F, 0x55, 0x90, 0x55, 0x91, 0x55, 0x92, 0x55, 0x93, 0x55, 0x94, 0x55, 0x95, 0x55, 0x96,\n\t0x55, 0x97, 0x55, 0x98, 0x55, 0x99, 0x55, 0x9A, 0x55, 0x9B, 0x55, 0x9C, 0x55, 0x9D, 0x55, 0x9E, 0x55, 0x9F, 0x55, 0xA0, 0x55, 0xA1, 0x55, 0xA2, 0x55, 0xA3, 0x55, 0xA4, 0x55, 0xA5, 0x55, 0xA6,\n\t0x55, 0xA7, 0x55, 0xA8, 0x55, 0xA9, 0x55, 0xAA, 0x55, 0xAB, 0x55, 0xAC, 0x55, 0xAD, 0x55, 0xAE, 0x55, 0xAF, 0x55, 0xB0, 0x55, 0xB1, 0x55, 0xB2, 0x55, 0xB3, 0x55, 0xB4, 0x55, 0xB5, 0x55, 0xB6,\n\t0x55, 0xB7, 0x55, 0xB8, 0x55, 0xB9, 0x55, 0xBA, 0x55, 0xBB, 0x55, 0xBC, 0x55, 0xBD, 0x55, 0xBE, 0x55, 0xBF, 0x55, 0xC0, 0x55, 0xC1, 0x55, 0xC2, 0x55, 0xC3, 0x55, 0xC4, 0x55, 0xC5, 0x55, 0xC6,\n\t0x55, 0xC7, 0x55, 0xC8, 0x55, 0xC9, 0x55, 0xCA, 0x55, 0xCB, 0x55, 0xCC, 0x55, 0xCD, 0x55, 0xCE, 0x55, 0xCF, 0x55, 0xD0, 0x55, 0xD1, 0x55, 0xD2, 0x55, 0xD3, 0x55, 0xD4, 0x55, 0xD5, 0x55, 0xD6,\n\t0x55, 0xD7, 0x55, 0xD8, 0x55, 0xD9, 0x55, 0xDA, 0x55, 0xDB, 0x55, 0xDC, 0x55, 0xDD, 0x55, 0xDE, 0x55, 0xDF, 0x55, 0xE0, 0x55, 0xE1, 0x55, 0xE2, 0x55, 0xE3, 0x55, 0xE4, 0x55, 0xE5, 0x55, 0xE6,\n\t0x55, 0xE7, 0x55, 0xE8, 0x55, 0xE9, 0x55, 0xEA, 0x55, 0xEB, 0x55, 0xEC, 0x55, 0xED, 0x55, 0xEE, 0x55, 0xEF, 0x55, 0xF0, 0x55, 0xF1, 0x55, 0xF2, 0x55, 0xF3, 0x55, 0xF4, 0x55, 0xF5, 0x55, 0xF6,\n\t0x55, 0xF7, 0x55, 0xF8, 0x55, 0xF9, 0x55, 0xFA, 0x55, 0xFB, 0x55, 0xFC, 0x55, 0xFD, 0x55, 0xFE, 0x55, 0xFF, 0x55, 0x00, 0x56, 0x01, 0x56, 0x02, 0x56, 0x03, 0x56, 0x04, 0x56, 0x05, 0x56, 0x06,\n\t0x56, 0x07, 0x56, 0x08, 0x56, 0x09, 0x56, 0x0A, 0x56, 0x0B, 0x56, 0x0C, 0x56, 0x0D, 0x56, 0x0E, 0x56, 0x0F, 0x56, 0x10, 0x56, 0x11, 0x56, 0x12, 0x56, 0x13, 0x56, 0x14, 0x56, 0x15, 0x56, 0x16,\n\t0x56, 0x17, 0x56, 0x18, 0x56, 0x19, 0x56, 0x1A, 0x56, 0x1B, 0x56, 0x1C, 0x56, 0x1D, 0x56, 0x1E, 0x56, 0x1F, 0x56, 0x20, 0x56, 0x21, 0x56, 0x22, 0x56, 0x23, 0x56, 0x24, 0x56, 0x25, 0x56, 0x26,\n\t0x56, 0x27, 0x56, 0x28, 0x56, 0x29, 0x56, 0x2A, 0x56, 0x2B, 0x56, 0x2C, 0x56, 0x2D, 0x56, 0x2E, 0x56, 0x2F, 0x56, 0x30, 0x56, 0x31, 0x56, 0x32, 0x56, 0x33, 0x56, 0x34, 0x56, 0x35, 0x56, 0x36,\n\t0x56, 0x37, 0x56, 0x38, 0x56, 0x39, 0x56, 0x3A, 0x56, 0x3B, 0x56, 0x3C, 0x56, 0x3D, 0x56, 0x3E, 0x56, 0x3F, 0x56, 0x40, 0x56, 0x41, 0x56, 0x42, 0x56, 0x43, 0x56, 0x44, 0x56, 0x45, 0x56, 0x46,\n\t0x56, 0x47, 0x56, 0x48, 0x56, 0x49, 0x56, 0x4A, 0x56, 0x4B, 0x56, 0x4C, 0x56, 0x4D, 0x56, 0x4E, 0x56, 0x4F, 0x56, 0x50, 0x56, 0x51, 0x56, 0x52, 0x56, 0x53, 0x56, 0x54, 0x56, 0x55, 0x56, 0x56,\n\t0x56, 0x57, 0x56, 0x58, 0x56, 0x59, 0x56, 0x5A, 0x56, 0x5B, 0x56, 0x5C, 0x56, 0x5D, 0x56, 0x5E, 0x56, 0x5F, 0x56, 0x60, 0x56, 0x61, 0x56, 0x62, 0x56, 0x63, 0x56, 0x64, 0x56, 0x65, 0x56, 0x66,\n\t0x56, 0x67, 0x56, 0x68, 0x56, 0x69, 0x56, 0x6A, 0x56, 0x6B, 0x56, 0x6C, 0x56, 0x6D, 0x56, 0x6E, 0x56, 0x6F, 0x56, 0x70, 0x56, 0x71, 0x56, 0x72, 0x56, 0x73, 0x56, 0x74, 0x56, 0x75, 0x56, 0x76,\n\t0x56, 0x77, 0x56, 0x78, 0x56, 0x79, 0x56, 0x7A, 0x56, 0x7B, 0x56, 0x7C, 0x56, 0x7D, 0x56, 0x7E, 0x56, 0x7F, 0x56, 0x80, 0x56, 0x81, 0x56, 0x82, 0x56, 0x83, 0x56, 0x84, 0x56, 0x85, 0x56, 0x86,\n\t0x56, 0x87, 0x56, 0x88, 0x56, 0x89, 0x56, 0x8A, 0x56, 0x8B, 0x56, 0x8C, 0x56, 0x8D, 0x56, 0x8E, 0x56, 0x8F, 0x56, 0x90, 0x56, 0x91, 0x56, 0x92, 0x56, 0x93, 0x56, 0x94, 0x56, 0x95, 0x56, 0x96,\n\t0x56, 0x97, 0x56, 0x98, 0x56, 0x99, 0x56, 0x9A, 0x56, 0x9B, 0x56, 0x9C, 0x56, 0x9D, 0x56, 0x9E, 0x56, 0x9F, 0x56, 0xA0, 0x56, 0xA1, 0x56, 0xA2, 0x56, 0xA3, 0x56, 0xA4, 0x56, 0xA5, 0x56, 0xA6,\n\t0x56, 0xA7, 0x56, 0xA8, 0x56, 0xA9, 0x56, 0xAA, 0x56, 0xAB, 0x56, 0xAC, 0x56, 0xAD, 0x56, 0xAE, 0x56, 0xAF, 0x56, 0xB0, 0x56, 0xB1, 0x56, 0xB2, 0x56, 0xB3, 0x56, 0xB4, 0x56, 0xB5, 0x56, 0xB6,\n\t0x56, 0xB7, 0x56, 0xB8, 0x56, 0xB9, 0x56, 0xBA, 0x56, 0xBB, 0x56, 0xBC, 0x56, 0xBD, 0x56, 0xBE, 0x56, 0xBF, 0x56, 0xC0, 0x56, 0xC1, 0x56, 0xC2, 0x56, 0xC3, 0x56, 0xC4, 0x56, 0xC5, 0x56, 0xC6,\n\t0x56, 0xC7, 0x56, 0xC8, 0x56, 0xC9, 0x56, 0xCA, 0x56, 0xCB, 0x56, 0xCC, 0x56, 0xCD, 0x56, 0xCE, 0x56, 0xCF, 0x56, 0xD0, 0x56, 0xD1, 0x56, 0xD2, 0x56, 0xD3, 0x56, 0xD4, 0x56, 0xD5, 0x56, 0xD6,\n\t0x56, 0xD7, 0x56, 0xD8, 0x56, 0xD9, 0x56, 0xDA, 0x56, 0xDB, 0x56, 0xDC, 0x56, 0xDD, 0x56, 0xDE, 0x56, 0xDF, 0x56, 0xE0, 0x56, 0xE1, 0x56, 0xE2, 0x56, 0xE3, 0x56, 0xE4, 0x56, 0xE5, 0x56, 0xE6,\n\t0x56, 0xE7, 0x56, 0xE8, 0x56, 0xE9, 0x56, 0xEA, 0x56, 0xEB, 0x56, 0xEC, 0x56, 0xED, 0x56, 0xEE, 0x56, 0xEF, 0x56, 0xF0, 0x56, 0xF1, 0x56, 0xF2, 0x56, 0xF3, 0x56, 0xF4, 0x56, 0xF5, 0x56, 0xF6,\n\t0x56, 0xF7, 0x56, 0xF8, 0x56, 0xF9, 0x56, 0xFA, 0x56, 0xFB, 0x56, 0xFC, 0x56, 0xFD, 0x56, 0xFE, 0x56, 0xFF, 0x56, 0x00, 0x57, 0x01, 0x57, 0x02, 0x57, 0x03, 0x57, 0x04, 0x57, 0x05, 0x57, 0x06,\n\t0x57, 0x07, 0x57, 0x08, 0x57, 0x09, 0x57, 0x0A, 0x57, 0x0B, 0x57, 0x0C, 0x57, 0x0D, 0x57, 0x0E, 0x57, 0x0F, 0x57, 0x10, 0x57, 0x11, 0x57, 0x12, 0x57, 0x13, 0x57, 0x14, 0x57, 0x15, 0x57, 0x16,\n\t0x57, 0x17, 0x57, 0x18, 0x57, 0x19, 0x57, 0x1A, 0x57, 0x1B, 0x57, 0x1C, 0x57, 0x1D, 0x57, 0x1E, 0x57, 0x1F, 0x57, 0x20, 0x57, 0x21, 0x57, 0x22, 0x57, 0x23, 0x57, 0x24, 0x57, 0x25, 0x57, 0x26,\n\t0x57, 0x27, 0x57, 0x28, 0x57, 0x29, 0x57, 0x2A, 0x57, 0x2B, 0x57, 0x2C, 0x57, 0x2D, 0x57, 0x2E, 0x57, 0x2F, 0x57, 0x30, 0x57, 0x31, 0x57, 0x32, 0x57, 0x33, 0x57, 0x34, 0x57, 0x35, 0x57, 0x36,\n\t0x57, 0x37, 0x57, 0x38, 0x57, 0x39, 0x57, 0x3A, 0x57, 0x3B, 0x57, 0x3C, 0x57, 0x3D, 0x57, 0x3E, 0x57, 0x3F, 0x57, 0x40, 0x57, 0x41, 0x57, 0x42, 0x57, 0x43, 0x57, 0x44, 0x57, 0x45, 0x57, 0x46,\n\t0x57, 0x47, 0x57, 0x48, 0x57, 0x49, 0x57, 0x4A, 0x57, 0x4B, 0x57, 0x4C, 0x57, 0x4D, 0x57, 0x4E, 0x57, 0x4F, 0x57, 0x50, 0x57, 0x51, 0x57, 0x52, 0x57, 0x53, 0x57, 0x54, 0x57, 0x55, 0x57, 0x56,\n\t0x57, 0x57, 0x57, 0x58, 0x57, 0x59, 0x57, 0x5A, 0x57, 0x5B, 0x57, 0x5C, 0x57, 0x5D, 0x57, 0x5E, 0x57, 0x5F, 0x57, 0x60, 0x57, 0x61, 0x57, 0x62, 0x57, 0x63, 0x57, 0x64, 0x57, 0x65, 0x57, 0x66,\n\t0x57, 0x67, 0x57, 0x68, 0x57, 0x69, 0x57, 0x6A, 0x57, 0x6B, 0x57, 0x6C, 0x57, 0x6D, 0x57, 0x6E, 0x57, 0x6F, 0x57, 0x70, 0x57, 0x71, 0x57, 0x72, 0x57, 0x73, 0x57, 0x74, 0x57, 0x75, 0x57, 0x76,\n\t0x57, 0x77, 0x57, 0x78, 0x57, 0x79, 0x57, 0x7A, 0x57, 0x7B, 0x57, 0x7C, 0x57, 0x7D, 0x57, 0x7E, 0x57, 0x7F, 0x57, 0x80, 0x57, 0x81, 0x57, 0x82, 0x57, 0x83, 0x57, 0x84, 0x57, 0x85, 0x57, 0x86,\n\t0x57, 0x87, 0x57, 0x88, 0x57, 0x89, 0x57, 0x8A, 0x57, 0x8B, 0x57, 0x8C, 0x57, 0x8D, 0x57, 0x8E, 0x57, 0x8F, 0x57, 0x90, 0x57, 0x91, 0x57, 0x92, 0x57, 0x93, 0x57, 0x94, 0x57, 0x95, 0x57, 0x96,\n\t0x57, 0x97, 0x57, 0x98, 0x57, 0x99, 0x57, 0x9A, 0x57, 0x9B, 0x57, 0x9C, 0x57, 0x9D, 0x57, 0x9E, 0x57, 0x9F, 0x57, 0xA0, 0x57, 0xA1, 0x57, 0xA2, 0x57, 0xA3, 0x57, 0xA4, 0x57, 0xA5, 0x57, 0xA6,\n\t0x57, 0xA7, 0x57, 0xA8, 0x57, 0xA9, 0x57, 0xAA, 0x57, 0xAB, 0x57, 0xAC, 0x57, 0xAD, 0x57, 0xAE, 0x57, 0xAF, 0x57, 0xB0, 0x57, 0xB1, 0x57, 0xB2, 0x57, 0xB3, 0x57, 0xB4, 0x57, 0xB5, 0x57, 0xB6,\n\t0x57, 0xB7, 0x57, 0xB8, 0x57, 0xB9, 0x57, 0xBA, 0x57, 0xBB, 0x57, 0xBC, 0x57, 0xBD, 0x57, 0xBE, 0x57, 0xBF, 0x57, 0xC0, 0x57, 0xC1, 0x57, 0xC2, 0x57, 0xC3, 0x57, 0xC4, 0x57, 0xC5, 0x57, 0xC6,\n\t0x57, 0xC7, 0x57, 0xC8, 0x57, 0xC9, 0x57, 0xCA, 0x57, 0xCB, 0x57, 0xCC, 0x57, 0xCD, 0x57, 0xCE, 0x57, 0xCF, 0x57, 0xD0, 0x57, 0xD1, 0x57, 0xD2, 0x57, 0xD3, 0x57, 0xD4, 0x57, 0xD5, 0x57, 0xD6,\n\t0x57, 0xD7, 0x57, 0xD8, 0x57, 0xD9, 0x57, 0xDA, 0x57, 0xDB, 0x57, 0xDC, 0x57, 0xDD, 0x57, 0xDE, 0x57, 0xDF, 0x57, 0xE0, 0x57, 0xE1, 0x57, 0xE2, 0x57, 0xE3, 0x57, 0xE4, 0x57, 0xE5, 0x57, 0xE6,\n\t0x57, 0xE7, 0x57, 0xE8, 0x57, 0xE9, 0x57, 0xEA, 0x57, 0xEB, 0x57, 0xEC, 0x57, 0xED, 0x57, 0xEE, 0x57, 0xEF, 0x57, 0xF0, 0x57, 0xF1, 0x57, 0xF2, 0x57, 0xF3, 0x57, 0xF4, 0x57, 0xF5, 0x57, 0xF6,\n\t0x57, 0xF7, 0x57, 0xF8, 0x57, 0xF9, 0x57, 0xFA, 0x57, 0xFB, 0x57, 0xFC, 0x57, 0xFD, 0x57, 0xFE, 0x57, 0xFF, 0x57, 0x00, 0x58, 0x01, 0x58, 0x02, 0x58, 0x03, 0x58, 0x04, 0x58, 0x05, 0x58, 0x06,\n\t0x58, 0x07, 0x58, 0x08, 0x58, 0x09, 0x58, 0x0A, 0x58, 0x0B, 0x58, 0x0C, 0x58, 0x0D, 0x58, 0x0E, 0x58, 0x0F, 0x58, 0x10, 0x58, 0x11, 0x58, 0x12, 0x58, 0x13, 0x58, 0x14, 0x58, 0x15, 0x58, 0x16,\n\t0x58, 0x17, 0x58, 0x18, 0x58, 0x19, 0x58, 0x1A, 0x58, 0x1B, 0x58, 0x1C, 0x58, 0x1D, 0x58, 0x1E, 0x58, 0x1F, 0x58, 0x20, 0x58, 0x21, 0x58, 0x22, 0x58, 0x23, 0x58, 0x24, 0x58, 0x25, 0x58, 0x26,\n\t0x58, 0x27, 0x58, 0x28, 0x58, 0x29, 0x58, 0x2A, 0x58, 0x2B, 0x58, 0x2C, 0x58, 0x2D, 0x58, 0x2E, 0x58, 0x2F, 0x58, 0x30, 0x58, 0x31, 0x58, 0x32, 0x58, 0x33, 0x58, 0x34, 0x58, 0x35, 0x58, 0x36,\n\t0x58, 0x37, 0x58, 0x38, 0x58, 0x39, 0x58, 0x3A, 0x58, 0x3B, 0x58, 0x3C, 0x58, 0x3D, 0x58, 0x3E, 0x58, 0x3F, 0x58, 0x40, 0x58, 0x41, 0x58, 0x42, 0x58, 0x43, 0x58, 0x44, 0x58, 0x45, 0x58, 0x46,\n\t0x58, 0x47, 0x58, 0x48, 0x58, 0x49, 0x58, 0x4A, 0x58, 0x4B, 0x58, 0x4C, 0x58, 0x4D, 0x58, 0x4E, 0x58, 0x4F, 0x58, 0x50, 0x58, 0x51, 0x58, 0x52, 0x58, 0x53, 0x58, 0x54, 0x58, 0x55, 0x58, 0x56,\n\t0x58, 0x57, 0x58, 0x58, 0x58, 0x59, 0x58, 0x5A, 0x58, 0x5B, 0x58, 0x5C, 0x58, 0x5D, 0x58, 0x5E, 0x58, 0x5F, 0x58, 0x60, 0x58, 0x61, 0x58, 0x62, 0x58, 0x63, 0x58, 0x64, 0x58, 0x65, 0x58, 0x66,\n\t0x58, 0x67, 0x58, 0x68, 0x58, 0x69, 0x58, 0x6A, 0x58, 0x6B, 0x58, 0x6C, 0x58, 0x6D, 0x58, 0x6E, 0x58, 0x6F, 0x58, 0x70, 0x58, 0x71, 0x58, 0x72, 0x58, 0x73, 0x58, 0x74, 0x58, 0x75, 0x58, 0x76,\n\t0x58, 0x77, 0x58, 0x78, 0x58, 0x79, 0x58, 0x7A, 0x58, 0x7B, 0x58, 0x7C, 0x58, 0x7D, 0x58, 0x7E, 0x58, 0x7F, 0x58, 0x80, 0x58, 0x81, 0x58, 0x82, 0x58, 0x83, 0x58, 0x84, 0x58, 0x85, 0x58, 0x86,\n\t0x58, 0x87, 0x58, 0x88, 0x58, 0x89, 0x58, 0x8A, 0x58, 0x8B, 0x58, 0x8C, 0x58, 0x8D, 0x58, 0x8E, 0x58, 0x8F, 0x58, 0x90, 0x58, 0x91, 0x58, 0x92, 0x58, 0x93, 0x58, 0x94, 0x58, 0x95, 0x58, 0x96,\n\t0x58, 0x97, 0x58, 0x98, 0x58, 0x99, 0x58, 0x9A, 0x58, 0x9B, 0x58, 0x9C, 0x58, 0x9D, 0x58, 0x9E, 0x58, 0x9F, 0x58, 0xA0, 0x58, 0xA1, 0x58, 0xA2, 0x58, 0xA3, 0x58, 0xA4, 0x58, 0xA5, 0x58, 0xA6,\n\t0x58, 0xA7, 0x58, 0xA8, 0x58, 0xA9, 0x58, 0xAA, 0x58, 0xAB, 0x58, 0xAC, 0x58, 0xAD, 0x58, 0xAE, 0x58, 0xAF, 0x58, 0xB0, 0x58, 0xB1, 0x58, 0xB2, 0x58, 0xB3, 0x58, 0xB4, 0x58, 0xB5, 0x58, 0xB6,\n\t0x58, 0xB7, 0x58, 0xB8, 0x58, 0xB9, 0x58, 0xBA, 0x58, 0xBB, 0x58, 0xBC, 0x58, 0xBD, 0x58, 0xBE, 0x58, 0xBF, 0x58, 0xC0, 0x58, 0xC1, 0x58, 0xC2, 0x58, 0xC3, 0x58, 0xC4, 0x58, 0xC5, 0x58, 0xC6,\n\t0x58, 0xC7, 0x58, 0xC8, 0x58, 0xC9, 0x58, 0xCA, 0x58, 0xCB, 0x58, 0xCC, 0x58, 0xCD, 0x58, 0xCE, 0x58, 0xCF, 0x58, 0xD0, 0x58, 0xD1, 0x58, 0xD2, 0x58, 0xD3, 0x58, 0xD4, 0x58, 0xD5, 0x58, 0xD6,\n\t0x58, 0xD7, 0x58, 0xD8, 0x58, 0xD9, 0x58, 0xDA, 0x58, 0xDB, 0x58, 0xDC, 0x58, 0xDD, 0x58, 0xDE, 0x58, 0xDF, 0x58, 0xE0, 0x58, 0xE1, 0x58, 0xE2, 0x58, 0xE3, 0x58, 0xE4, 0x58, 0xE5, 0x58, 0xE6,\n\t0x58, 0xE7, 0x58, 0xE8, 0x58, 0xE9, 0x58, 0xEA, 0x58, 0xEB, 0x58, 0xEC, 0x58, 0xED, 0x58, 0xEE, 0x58, 0xEF, 0x58, 0xF0, 0x58, 0xF1, 0x58, 0xF2, 0x58, 0xF3, 0x58, 0xF4, 0x58, 0xF5, 0x58, 0xF6,\n\t0x58, 0xF7, 0x58, 0xF8, 0x58, 0xF9, 0x58, 0xFA, 0x58, 0xFB, 0x58, 0xFC, 0x58, 0xFD, 0x58, 0xFE, 0x58, 0xFF, 0x58, 0x00, 0x59, 0x01, 0x59, 0x02, 0x59, 0x03, 0x59, 0x04, 0x59, 0x05, 0x59, 0x06,\n\t0x59, 0x07, 0x59, 0x08, 0x59, 0x09, 0x59, 0x0A, 0x59, 0x0B, 0x59, 0x0C, 0x59, 0x0D, 0x59, 0x0E, 0x59, 0x0F, 0x59, 0x10, 0x59, 0x11, 0x59, 0x12, 0x59, 0x13, 0x59, 0x14, 0x59, 0x15, 0x59, 0x16,\n\t0x59, 0x17, 0x59, 0x18, 0x59, 0x19, 0x59, 0x1A, 0x59, 0x1B, 0x59, 0x1C, 0x59, 0x1D, 0x59, 0x1E, 0x59, 0x1F, 0x59, 0x20, 0x59, 0x21, 0x59, 0x22, 0x59, 0x23, 0x59, 0x24, 0x59, 0x25, 0x59, 0x26,\n\t0x59, 0x27, 0x59, 0x28, 0x59, 0x29, 0x59, 0x2A, 0x59, 0x2B, 0x59, 0x2C, 0x59, 0x2D, 0x59, 0x2E, 0x59, 0x2F, 0x59, 0x30, 0x59, 0x31, 0x59, 0x32, 0x59, 0x33, 0x59, 0x34, 0x59, 0x35, 0x59, 0x36,\n\t0x59, 0x37, 0x59, 0x38, 0x59, 0x39, 0x59, 0x3A, 0x59, 0x3B, 0x59, 0x3C, 0x59, 0x3D, 0x59, 0x3E, 0x59, 0x3F, 0x59, 0x40, 0x59, 0x41, 0x59, 0x42, 0x59, 0x43, 0x59, 0x44, 0x59, 0x45, 0x59, 0x46,\n\t0x59, 0x47, 0x59, 0x48, 0x59, 0x49, 0x59, 0x4A, 0x59, 0x4B, 0x59, 0x4C, 0x59, 0x4D, 0x59, 0x4E, 0x59, 0x4F, 0x59, 0x50, 0x59, 0x51, 0x59, 0x52, 0x59, 0x53, 0x59, 0x54, 0x59, 0x55, 0x59, 0x56,\n\t0x59, 0x57, 0x59, 0x58, 0x59, 0x59, 0x59, 0x5A, 0x59, 0x5B, 0x59, 0x5C, 0x59, 0x5D, 0x59, 0x5E, 0x59, 0x5F, 0x59, 0x60, 0x59, 0x61, 0x59, 0x62, 0x59, 0x63, 0x59, 0x64, 0x59, 0x65, 0x59, 0x66,\n\t0x59, 0x67, 0x59, 0x68, 0x59, 0x69, 0x59, 0x6A, 0x59, 0x6B, 0x59, 0x6C, 0x59, 0x6D, 0x59, 0x6E, 0x59, 0x6F, 0x59, 0x70, 0x59, 0x71, 0x59, 0x72, 0x59, 0x73, 0x59, 0x74, 0x59, 0x75, 0x59, 0x76,\n\t0x59, 0x77, 0x59, 0x78, 0x59, 0x79, 0x59, 0x7A, 0x59, 0x7B, 0x59, 0x7C, 0x59, 0x7D, 0x59, 0x7E, 0x59, 0x7F, 0x59, 0x80, 0x59, 0x81, 0x59, 0x82, 0x59, 0x83, 0x59, 0x84, 0x59, 0x85, 0x59, 0x86,\n\t0x59, 0x87, 0x59, 0x88, 0x59, 0x89, 0x59, 0x8A, 0x59, 0x8B, 0x59, 0x8C, 0x59, 0x8D, 0x59, 0x8E, 0x59, 0x8F, 0x59, 0x90, 0x59, 0x91, 0x59, 0x92, 0x59, 0x93, 0x59, 0x94, 0x59, 0x95, 0x59, 0x96,\n\t0x59, 0x97, 0x59, 0x98, 0x59, 0x99, 0x59, 0x9A, 0x59, 0x9B, 0x59, 0x9C, 0x59, 0x9D, 0x59, 0x9E, 0x59, 0x9F, 0x59, 0xA0, 0x59, 0xA1, 0x59, 0xA2, 0x59, 0xA3, 0x59, 0xA4, 0x59, 0xA5, 0x59, 0xA6,\n\t0x59, 0xA7, 0x59, 0xA8, 0x59, 0xA9, 0x59, 0xAA, 0x59, 0xAB, 0x59, 0xAC, 0x59, 0xAD, 0x59, 0xAE, 0x59, 0xAF, 0x59, 0xB0, 0x59, 0xB1, 0x59, 0xB2, 0x59, 0xB3, 0x59, 0xB4, 0x59, 0xB5, 0x59, 0xB6,\n\t0x59, 0xB7, 0x59, 0xB8, 0x59, 0xB9, 0x59, 0xBA, 0x59, 0xBB, 0x59, 0xBC, 0x59, 0xBD, 0x59, 0xBE, 0x59, 0xBF, 0x59, 0xC0, 0x59, 0xC1, 0x59, 0xC2, 0x59, 0xC3, 0x59, 0xC4, 0x59, 0xC5, 0x59, 0xC6,\n\t0x59, 0xC7, 0x59, 0xC8, 0x59, 0xC9, 0x59, 0xCA, 0x59, 0xCB, 0x59, 0xCC, 0x59, 0xCD, 0x59, 0xCE, 0x59, 0xCF, 0x59, 0xD0, 0x59, 0xD1, 0x59, 0xD2, 0x59, 0xD3, 0x59, 0xD4, 0x59, 0xD5, 0x59, 0xD6,\n\t0x59, 0xD7, 0x59, 0xD8, 0x59, 0xD9, 0x59, 0xDA, 0x59, 0xDB, 0x59, 0xDC, 0x59, 0xDD, 0x59, 0xDE, 0x59, 0xDF, 0x59, 0xE0, 0x59, 0xE1, 0x59, 0xE2, 0x59, 0xE3, 0x59, 0xE4, 0x59, 0xE5, 0x59, 0xE6,\n\t0x59, 0xE7, 0x59, 0xE8, 0x59, 0xE9, 0x59, 0xEA, 0x59, 0xEB, 0x59, 0xEC, 0x59, 0xED, 0x59, 0xEE, 0x59, 0xEF, 0x59, 0xF0, 0x59, 0xF1, 0x59, 0xF2, 0x59, 0xF3, 0x59, 0xF4, 0x59, 0xF5, 0x59, 0xF6,\n\t0x59, 0xF7, 0x59, 0xF8, 0x59, 0xF9, 0x59, 0xFA, 0x59, 0xFB, 0x59, 0xFC, 0x59, 0xFD, 0x59, 0xFE, 0x59, 0xFF, 0x59, 0x00, 0x5A, 0x01, 0x5A, 0x02, 0x5A, 0x03, 0x5A, 0x04, 0x5A, 0x05, 0x5A, 0x06,\n\t0x5A, 0x07, 0x5A, 0x08, 0x5A, 0x09, 0x5A, 0x0A, 0x5A, 0x0B, 0x5A, 0x0C, 0x5A, 0x0D, 0x5A, 0x0E, 0x5A, 0x0F, 0x5A, 0x10, 0x5A, 0x11, 0x5A, 0x12, 0x5A, 0x13, 0x5A, 0x14, 0x5A, 0x15, 0x5A, 0x16,\n\t0x5A, 0x17, 0x5A, 0x18, 0x5A, 0x19, 0x5A, 0x1A, 0x5A, 0x1B, 0x5A, 0x1C, 0x5A, 0x1D, 0x5A, 0x1E, 0x5A, 0x1F, 0x5A, 0x20, 0x5A, 0x21, 0x5A, 0x22, 0x5A, 0x23, 0x5A, 0x24, 0x5A, 0x25, 0x5A, 0x26,\n\t0x5A, 0x27, 0x5A, 0x28, 0x5A, 0x29, 0x5A, 0x2A, 0x5A, 0x2B, 0x5A, 0x2C, 0x5A, 0x2D, 0x5A, 0x2E, 0x5A, 0x2F, 0x5A, 0x30, 0x5A, 0x31, 0x5A, 0x32, 0x5A, 0x33, 0x5A, 0x34, 0x5A, 0x35, 0x5A, 0x36,\n\t0x5A, 0x37, 0x5A, 0x38, 0x5A, 0x39, 0x5A, 0x3A, 0x5A, 0x3B, 0x5A, 0x3C, 0x5A, 0x3D, 0x5A, 0x3E, 0x5A, 0x3F, 0x5A, 0x40, 0x5A, 0x41, 0x5A, 0x42, 0x5A, 0x43, 0x5A, 0x44, 0x5A, 0x45, 0x5A, 0x46,\n\t0x5A, 0x47, 0x5A, 0x48, 0x5A, 0x49, 0x5A, 0x4A, 0x5A, 0x4B, 0x5A, 0x4C, 0x5A, 0x4D, 0x5A, 0x4E, 0x5A, 0x4F, 0x5A, 0x50, 0x5A, 0x51, 0x5A, 0x52, 0x5A, 0x53, 0x5A, 0x54, 0x5A, 0x55, 0x5A, 0x56,\n\t0x5A, 0x57, 0x5A, 0x58, 0x5A, 0x59, 0x5A, 0x5A, 0x5A, 0x5B, 0x5A, 0x5C, 0x5A, 0x5D, 0x5A, 0x5E, 0x5A, 0x5F, 0x5A, 0x60, 0x5A, 0x61, 0x5A, 0x62, 0x5A, 0x63, 0x5A, 0x64, 0x5A, 0x65, 0x5A, 0x66,\n\t0x5A, 0x67, 0x5A, 0x68, 0x5A, 0x69, 0x5A, 0x6A, 0x5A, 0x6B, 0x5A, 0x6C, 0x5A, 0x6D, 0x5A, 0x6E, 0x5A, 0x6F, 0x5A, 0x70, 0x5A, 0x71, 0x5A, 0x72, 0x5A, 0x73, 0x5A, 0x74, 0x5A, 0x75, 0x5A, 0x76,\n\t0x5A, 0x77, 0x5A, 0x78, 0x5A, 0x79, 0x5A, 0x7A, 0x5A, 0x7B, 0x5A, 0x7C, 0x5A, 0x7D, 0x5A, 0x7E, 0x5A, 0x7F, 0x5A, 0x80, 0x5A, 0x81, 0x5A, 0x82, 0x5A, 0x83, 0x5A, 0x84, 0x5A, 0x85, 0x5A, 0x86,\n\t0x5A, 0x87, 0x5A, 0x88, 0x5A, 0x89, 0x5A, 0x8A, 0x5A, 0x8B, 0x5A, 0x8C, 0x5A, 0x8D, 0x5A, 0x8E, 0x5A, 0x8F, 0x5A, 0x90, 0x5A, 0x91, 0x5A, 0x92, 0x5A, 0x93, 0x5A, 0x94, 0x5A, 0x95, 0x5A, 0x96,\n\t0x5A, 0x97, 0x5A, 0x98, 0x5A, 0x99, 0x5A, 0x9A, 0x5A, 0x9B, 0x5A, 0x9C, 0x5A, 0x9D, 0x5A, 0x9E, 0x5A, 0x9F, 0x5A, 0xA0, 0x5A, 0xA1, 0x5A, 0xA2, 0x5A, 0xA3, 0x5A, 0xA4, 0x5A, 0xA5, 0x5A, 0xA6,\n\t0x5A, 0xA7, 0x5A, 0xA8, 0x5A, 0xA9, 0x5A, 0xAA, 0x5A, 0xAB, 0x5A, 0xAC, 0x5A, 0xAD, 0x5A, 0xAE, 0x5A, 0xAF, 0x5A, 0xB0, 0x5A, 0xB1, 0x5A, 0xB2, 0x5A, 0xB3, 0x5A, 0xB4, 0x5A, 0xB5, 0x5A, 0xB6,\n\t0x5A, 0xB7, 0x5A, 0xB8, 0x5A, 0xB9, 0x5A, 0xBA, 0x5A, 0xBB, 0x5A, 0xBC, 0x5A, 0xBD, 0x5A, 0xBE, 0x5A, 0xBF, 0x5A, 0xC0, 0x5A, 0xC1, 0x5A, 0xC2, 0x5A, 0xC3, 0x5A, 0xC4, 0x5A, 0xC5, 0x5A, 0xC6,\n\t0x5A, 0xC7, 0x5A, 0xC8, 0x5A, 0xC9, 0x5A, 0xCA, 0x5A, 0xCB, 0x5A, 0xCC, 0x5A, 0xCD, 0x5A, 0xCE, 0x5A, 0xCF, 0x5A, 0xD0, 0x5A, 0xD1, 0x5A, 0xD2, 0x5A, 0xD3, 0x5A, 0xD4, 0x5A, 0xD5, 0x5A, 0xD6,\n\t0x5A, 0xD7, 0x5A, 0xD8, 0x5A, 0xD9, 0x5A, 0xDA, 0x5A, 0xDB, 0x5A, 0xDC, 0x5A, 0xDD, 0x5A, 0xDE, 0x5A, 0xDF, 0x5A, 0xE0, 0x5A, 0xE1, 0x5A, 0xE2, 0x5A, 0xE3, 0x5A, 0xE4, 0x5A, 0xE5, 0x5A, 0xE6,\n\t0x5A, 0xE7, 0x5A, 0xE8, 0x5A, 0xE9, 0x5A, 0xEA, 0x5A, 0xEB, 0x5A, 0xEC, 0x5A, 0xED, 0x5A, 0xEE, 0x5A, 0xEF, 0x5A, 0xF0, 0x5A, 0xF1, 0x5A, 0xF2, 0x5A, 0xF3, 0x5A, 0xF4, 0x5A, 0xF5, 0x5A, 0xF6,\n\t0x5A, 0xF7, 0x5A, 0xF8, 0x5A, 0xF9, 0x5A, 0xFA, 0x5A, 0xFB, 0x5A, 0xFC, 0x5A, 0xFD, 0x5A, 0xFE, 0x5A, 0xFF, 0x5A, 0x00, 0x5B, 0x01, 0x5B, 0x02, 0x5B, 0x03, 0x5B, 0x04, 0x5B, 0x05, 0x5B, 0x06,\n\t0x5B, 0x07, 0x5B, 0x08, 0x5B, 0x09, 0x5B, 0x0A, 0x5B, 0x0B, 0x5B, 0x0C, 0x5B, 0x0D, 0x5B, 0x0E, 0x5B, 0x0F, 0x5B, 0x10, 0x5B, 0x11, 0x5B, 0x12, 0x5B, 0x13, 0x5B, 0x14, 0x5B, 0x15, 0x5B, 0x16,\n\t0x5B, 0x17, 0x5B, 0x18, 0x5B, 0x19, 0x5B, 0x1A, 0x5B, 0x1B, 0x5B, 0x1C, 0x5B, 0x1D, 0x5B, 0x1E, 0x5B, 0x1F, 0x5B, 0x20, 0x5B, 0x21, 0x5B, 0x22, 0x5B, 0x23, 0x5B, 0x24, 0x5B, 0x25, 0x5B, 0x26,\n\t0x5B, 0x27, 0x5B, 0x28, 0x5B, 0x29, 0x5B, 0x2A, 0x5B, 0x2B, 0x5B, 0x2C, 0x5B, 0x2D, 0x5B, 0x2E, 0x5B, 0x2F, 0x5B, 0x30, 0x5B, 0x31, 0x5B, 0x32, 0x5B, 0x33, 0x5B, 0x34, 0x5B, 0x35, 0x5B, 0x36,\n\t0x5B, 0x37, 0x5B, 0x38, 0x5B, 0x39, 0x5B, 0x3A, 0x5B, 0x3B, 0x5B, 0x3C, 0x5B, 0x3D, 0x5B, 0x3E, 0x5B, 0x3F, 0x5B, 0x40, 0x5B, 0x41, 0x5B, 0x42, 0x5B, 0x43, 0x5B, 0x44, 0x5B, 0x45, 0x5B, 0x46,\n\t0x5B, 0x47, 0x5B, 0x48, 0x5B, 0x49, 0x5B, 0x4A, 0x5B, 0x4B, 0x5B, 0x4C, 0x5B, 0x4D, 0x5B, 0x4E, 0x5B, 0x4F, 0x5B, 0x50, 0x5B, 0x51, 0x5B, 0x52, 0x5B, 0x53, 0x5B, 0x54, 0x5B, 0x55, 0x5B, 0x56,\n\t0x5B, 0x57, 0x5B, 0x58, 0x5B, 0x59, 0x5B, 0x5A, 0x5B, 0x5B, 0x5B, 0x5C, 0x5B, 0x5D, 0x5B, 0x5E, 0x5B, 0x5F, 0x5B, 0x60, 0x5B, 0x61, 0x5B, 0x62, 0x5B, 0x63, 0x5B, 0x64, 0x5B, 0x65, 0x5B, 0x66,\n\t0x5B, 0x67, 0x5B, 0x68, 0x5B, 0x69, 0x5B, 0x6A, 0x5B, 0x6B, 0x5B, 0x6C, 0x5B, 0x6D, 0x5B, 0x6E, 0x5B, 0x6F, 0x5B, 0x70, 0x5B, 0x71, 0x5B, 0x72, 0x5B, 0x73, 0x5B, 0x74, 0x5B, 0x75, 0x5B, 0x76,\n\t0x5B, 0x77, 0x5B, 0x78, 0x5B, 0x79, 0x5B, 0x7A, 0x5B, 0x7B, 0x5B, 0x7C, 0x5B, 0x7D, 0x5B, 0x7E, 0x5B, 0x7F, 0x5B, 0x80, 0x5B, 0x81, 0x5B, 0x82, 0x5B, 0x83, 0x5B, 0x84, 0x5B, 0x85, 0x5B, 0x86,\n\t0x5B, 0x87, 0x5B, 0x88, 0x5B, 0x89, 0x5B, 0x8A, 0x5B, 0x8B, 0x5B, 0x8C, 0x5B, 0x8D, 0x5B, 0x8E, 0x5B, 0x8F, 0x5B, 0x90, 0x5B, 0x91, 0x5B, 0x92, 0x5B, 0x93, 0x5B, 0x94, 0x5B, 0x95, 0x5B, 0x96,\n\t0x5B, 0x97, 0x5B, 0x98, 0x5B, 0x99, 0x5B, 0x9A, 0x5B, 0x9B, 0x5B, 0x9C, 0x5B, 0x9D, 0x5B, 0x9E, 0x5B, 0x9F, 0x5B, 0xA0, 0x5B, 0xA1, 0x5B, 0xA2, 0x5B, 0xA3, 0x5B, 0xA4, 0x5B, 0xA5, 0x5B, 0xA6,\n\t0x5B, 0xA7, 0x5B, 0xA8, 0x5B, 0xA9, 0x5B, 0xAA, 0x5B, 0xAB, 0x5B, 0xAC, 0x5B, 0xAD, 0x5B, 0xAE, 0x5B, 0xAF, 0x5B, 0xB0, 0x5B, 0xB1, 0x5B, 0xB2, 0x5B, 0xB3, 0x5B, 0xB4, 0x5B, 0xB5, 0x5B, 0xB6,\n\t0x5B, 0xB7, 0x5B, 0xB8, 0x5B, 0xB9, 0x5B, 0xBA, 0x5B, 0xBB, 0x5B, 0xBC, 0x5B, 0xBD, 0x5B, 0xBE, 0x5B, 0xBF, 0x5B, 0xC0, 0x5B, 0xC1, 0x5B, 0xC2, 0x5B, 0xC3, 0x5B, 0xC4, 0x5B, 0xC5, 0x5B, 0xC6,\n\t0x5B, 0xC7, 0x5B, 0xC8, 0x5B, 0xC9, 0x5B, 0xCA, 0x5B, 0xCB, 0x5B, 0xCC, 0x5B, 0xCD, 0x5B, 0xCE, 0x5B, 0xCF, 0x5B, 0xD0, 0x5B, 0xD1, 0x5B, 0xD2, 0x5B, 0xD3, 0x5B, 0xD4, 0x5B, 0xD5, 0x5B, 0xD6,\n\t0x5B, 0xD7, 0x5B, 0xD8, 0x5B, 0xD9, 0x5B, 0xDA, 0x5B, 0xDB, 0x5B, 0xDC, 0x5B, 0xDD, 0x5B, 0xDE, 0x5B, 0xDF, 0x5B, 0xE0, 0x5B, 0xE1, 0x5B, 0xE2, 0x5B, 0xE3, 0x5B, 0xE4, 0x5B, 0xE5, 0x5B, 0xE6,\n\t0x5B, 0xE7, 0x5B, 0xE8, 0x5B, 0xE9, 0x5B, 0xEA, 0x5B, 0xEB, 0x5B, 0xEC, 0x5B, 0xED, 0x5B, 0xEE, 0x5B, 0xEF, 0x5B, 0xF0, 0x5B, 0xF1, 0x5B, 0xF2, 0x5B, 0xF3, 0x5B, 0xF4, 0x5B, 0xF5, 0x5B, 0xF6,\n\t0x5B, 0xF7, 0x5B, 0xF8, 0x5B, 0xF9, 0x5B, 0xFA, 0x5B, 0xFB, 0x5B, 0xFC, 0x5B, 0xFD, 0x5B, 0xFE, 0x5B, 0xFF, 0x5B, 0x00, 0x5C, 0x01, 0x5C, 0x02, 0x5C, 0x03, 0x5C, 0x04, 0x5C, 0x05, 0x5C, 0x06,\n\t0x5C, 0x07, 0x5C, 0x08, 0x5C, 0x09, 0x5C, 0x0A, 0x5C, 0x0B, 0x5C, 0x0C, 0x5C, 0x0D, 0x5C, 0x0E, 0x5C, 0x0F, 0x5C, 0x10, 0x5C, 0x11, 0x5C, 0x12, 0x5C, 0x13, 0x5C, 0x14, 0x5C, 0x15, 0x5C, 0x16,\n\t0x5C, 0x17, 0x5C, 0x18, 0x5C, 0x19, 0x5C, 0x1A, 0x5C, 0x1B, 0x5C, 0x1C, 0x5C, 0x1D, 0x5C, 0x1E, 0x5C, 0x1F, 0x5C, 0x20, 0x5C, 0x21, 0x5C, 0x22, 0x5C, 0x23, 0x5C, 0x24, 0x5C, 0x25, 0x5C, 0x26,\n\t0x5C, 0x27, 0x5C, 0x28, 0x5C, 0x29, 0x5C, 0x2A, 0x5C, 0x2B, 0x5C, 0x2C, 0x5C, 0x2D, 0x5C, 0x2E, 0x5C, 0x2F, 0x5C, 0x30, 0x5C, 0x31, 0x5C, 0x32, 0x5C, 0x33, 0x5C, 0x34, 0x5C, 0x35, 0x5C, 0x36,\n\t0x5C, 0x37, 0x5C, 0x38, 0x5C, 0x39, 0x5C, 0x3A, 0x5C, 0x3B, 0x5C, 0x3C, 0x5C, 0x3D, 0x5C, 0x3E, 0x5C, 0x3F, 0x5C, 0x40, 0x5C, 0x41, 0x5C, 0x42, 0x5C, 0x43, 0x5C, 0x44, 0x5C, 0x45, 0x5C, 0x46,\n\t0x5C, 0x47, 0x5C, 0x48, 0x5C, 0x49, 0x5C, 0x4A, 0x5C, 0x4B, 0x5C, 0x4C, 0x5C, 0x4D, 0x5C, 0x4E, 0x5C, 0x4F, 0x5C, 0x50, 0x5C, 0x51, 0x5C, 0x52, 0x5C, 0x53, 0x5C, 0x54, 0x5C, 0x55, 0x5C, 0x56,\n\t0x5C, 0x57, 0x5C, 0x58, 0x5C, 0x59, 0x5C, 0x5A, 0x5C, 0x5B, 0x5C, 0x5C, 0x5C, 0x5D, 0x5C, 0x5E, 0x5C, 0x5F, 0x5C, 0x60, 0x5C, 0x61, 0x5C, 0x62, 0x5C, 0x63, 0x5C, 0x64, 0x5C, 0x65, 0x5C, 0x66,\n\t0x5C, 0x67, 0x5C, 0x68, 0x5C, 0x69, 0x5C, 0x6A, 0x5C, 0x6B, 0x5C, 0x6C, 0x5C, 0x6D, 0x5C, 0x6E, 0x5C, 0x6F, 0x5C, 0x70, 0x5C, 0x71, 0x5C, 0x72, 0x5C, 0x73, 0x5C, 0x74, 0x5C, 0x75, 0x5C, 0x76,\n\t0x5C, 0x77, 0x5C, 0x78, 0x5C, 0x79, 0x5C, 0x7A, 0x5C, 0x7B, 0x5C, 0x7C, 0x5C, 0x7D, 0x5C, 0x7E, 0x5C, 0x7F, 0x5C, 0x80, 0x5C, 0x81, 0x5C, 0x82, 0x5C, 0x83, 0x5C, 0x84, 0x5C, 0x85, 0x5C, 0x86,\n\t0x5C, 0x87, 0x5C, 0x88, 0x5C, 0x89, 0x5C, 0x8A, 0x5C, 0x8B, 0x5C, 0x8C, 0x5C, 0x8D, 0x5C, 0x8E, 0x5C, 0x8F, 0x5C, 0x90, 0x5C, 0x91, 0x5C, 0x92, 0x5C, 0x93, 0x5C, 0x94, 0x5C, 0x95, 0x5C, 0x96,\n\t0x5C, 0x97, 0x5C, 0x98, 0x5C, 0x99, 0x5C, 0x9A, 0x5C, 0x9B, 0x5C, 0x9C, 0x5C, 0x9D, 0x5C, 0x9E, 0x5C, 0x9F, 0x5C, 0xA0, 0x5C, 0xA1, 0x5C, 0xA2, 0x5C, 0xA3, 0x5C, 0xA4, 0x5C, 0xA5, 0x5C, 0xA6,\n\t0x5C, 0xA7, 0x5C, 0xA8, 0x5C, 0xA9, 0x5C, 0xAA, 0x5C, 0xAB, 0x5C, 0xAC, 0x5C, 0xAD, 0x5C, 0xAE, 0x5C, 0xAF, 0x5C, 0xB0, 0x5C, 0xB1, 0x5C, 0xB2, 0x5C, 0xB3, 0x5C, 0xB4, 0x5C, 0xB5, 0x5C, 0xB6,\n\t0x5C, 0xB7, 0x5C, 0xB8, 0x5C, 0xB9, 0x5C, 0xBA, 0x5C, 0xBB, 0x5C, 0xBC, 0x5C, 0xBD, 0x5C, 0xBE, 0x5C, 0xBF, 0x5C, 0xC0, 0x5C, 0xC1, 0x5C, 0xC2, 0x5C, 0xC3, 0x5C, 0xC4, 0x5C, 0xC5, 0x5C, 0xC6,\n\t0x5C, 0xC7, 0x5C, 0xC8, 0x5C, 0xC9, 0x5C, 0xCA, 0x5C, 0xCB, 0x5C, 0xCC, 0x5C, 0xCD, 0x5C, 0xCE, 0x5C, 0xCF, 0x5C, 0xD0, 0x5C, 0xD1, 0x5C, 0xD2, 0x5C, 0xD3, 0x5C, 0xD4, 0x5C, 0xD5, 0x5C, 0xD6,\n\t0x5C, 0xD7, 0x5C, 0xD8, 0x5C, 0xD9, 0x5C, 0xDA, 0x5C, 0xDB, 0x5C, 0xDC, 0x5C, 0xDD, 0x5C, 0xDE, 0x5C, 0xDF, 0x5C, 0xE0, 0x5C, 0xE1, 0x5C, 0xE2, 0x5C, 0xE3, 0x5C, 0xE4, 0x5C, 0xE5, 0x5C, 0xE6,\n\t0x5C, 0xE7, 0x5C, 0xE8, 0x5C, 0xE9, 0x5C, 0xEA, 0x5C, 0xEB, 0x5C, 0xEC, 0x5C, 0xED, 0x5C, 0xEE, 0x5C, 0xEF, 0x5C, 0xF0, 0x5C, 0xF1, 0x5C, 0xF2, 0x5C, 0xF3, 0x5C, 0xF4, 0x5C, 0xF5, 0x5C, 0xF6,\n\t0x5C, 0xF7, 0x5C, 0xF8, 0x5C, 0xF9, 0x5C, 0xFA, 0x5C, 0xFB, 0x5C, 0xFC, 0x5C, 0xFD, 0x5C, 0xFE, 0x5C, 0xFF, 0x5C, 0x00, 0x5D, 0x01, 0x5D, 0x02, 0x5D, 0x03, 0x5D, 0x04, 0x5D, 0x05, 0x5D, 0x06,\n\t0x5D, 0x07, 0x5D, 0x08, 0x5D, 0x09, 0x5D, 0x0A, 0x5D, 0x0B, 0x5D, 0x0C, 0x5D, 0x0D, 0x5D, 0x0E, 0x5D, 0x0F, 0x5D, 0x10, 0x5D, 0x11, 0x5D, 0x12, 0x5D, 0x13, 0x5D, 0x14, 0x5D, 0x15, 0x5D, 0x16,\n\t0x5D, 0x17, 0x5D, 0x18, 0x5D, 0x19, 0x5D, 0x1A, 0x5D, 0x1B, 0x5D, 0x1C, 0x5D, 0x1D, 0x5D, 0x1E, 0x5D, 0x1F, 0x5D, 0x20, 0x5D, 0x21, 0x5D, 0x22, 0x5D, 0x23, 0x5D, 0x24, 0x5D, 0x25, 0x5D, 0x26,\n\t0x5D, 0x27, 0x5D, 0x28, 0x5D, 0x29, 0x5D, 0x2A, 0x5D, 0x2B, 0x5D, 0x2C, 0x5D, 0x2D, 0x5D, 0x2E, 0x5D, 0x2F, 0x5D, 0x30, 0x5D, 0x31, 0x5D, 0x32, 0x5D, 0x33, 0x5D, 0x34, 0x5D, 0x35, 0x5D, 0x36,\n\t0x5D, 0x37, 0x5D, 0x38, 0x5D, 0x39, 0x5D, 0x3A, 0x5D, 0x3B, 0x5D, 0x3C, 0x5D, 0x3D, 0x5D, 0x3E, 0x5D, 0x3F, 0x5D, 0x40, 0x5D, 0x41, 0x5D, 0x42, 0x5D, 0x43, 0x5D, 0x44, 0x5D, 0x45, 0x5D, 0x46,\n\t0x5D, 0x47, 0x5D, 0x48, 0x5D, 0x49, 0x5D, 0x4A, 0x5D, 0x4B, 0x5D, 0x4C, 0x5D, 0x4D, 0x5D, 0x4E, 0x5D, 0x4F, 0x5D, 0x50, 0x5D, 0x51, 0x5D, 0x52, 0x5D, 0x53, 0x5D, 0x54, 0x5D, 0x55, 0x5D, 0x56,\n\t0x5D, 0x57, 0x5D, 0x58, 0x5D, 0x59, 0x5D, 0x5A, 0x5D, 0x5B, 0x5D, 0x5C, 0x5D, 0x5D, 0x5D, 0x5E, 0x5D, 0x5F, 0x5D, 0x60, 0x5D, 0x61, 0x5D, 0x62, 0x5D, 0x63, 0x5D, 0x64, 0x5D, 0x65, 0x5D, 0x66,\n\t0x5D, 0x67, 0x5D, 0x68, 0x5D, 0x69, 0x5D, 0x6A, 0x5D, 0x6B, 0x5D, 0x6C, 0x5D, 0x6D, 0x5D, 0x6E, 0x5D, 0x6F, 0x5D, 0x70, 0x5D, 0x71, 0x5D, 0x72, 0x5D, 0x73, 0x5D, 0x74, 0x5D, 0x75, 0x5D, 0x76,\n\t0x5D, 0x77, 0x5D, 0x78, 0x5D, 0x79, 0x5D, 0x7A, 0x5D, 0x7B, 0x5D, 0x7C, 0x5D, 0x7D, 0x5D, 0x7E, 0x5D, 0x7F, 0x5D, 0x80, 0x5D, 0x81, 0x5D, 0x82, 0x5D, 0x83, 0x5D, 0x84, 0x5D, 0x85, 0x5D, 0x86,\n\t0x5D, 0x87, 0x5D, 0x88, 0x5D, 0x89, 0x5D, 0x8A, 0x5D, 0x8B, 0x5D, 0x8C, 0x5D, 0x8D, 0x5D, 0x8E, 0x5D, 0x8F, 0x5D, 0x90, 0x5D, 0x91, 0x5D, 0x92, 0x5D, 0x93, 0x5D, 0x94, 0x5D, 0x95, 0x5D, 0x96,\n\t0x5D, 0x97, 0x5D, 0x98, 0x5D, 0x99, 0x5D, 0x9A, 0x5D, 0x9B, 0x5D, 0x9C, 0x5D, 0x9D, 0x5D, 0x9E, 0x5D, 0x9F, 0x5D, 0xA0, 0x5D, 0xA1, 0x5D, 0xA2, 0x5D, 0xA3, 0x5D, 0xA4, 0x5D, 0xA5, 0x5D, 0xA6,\n\t0x5D, 0xA7, 0x5D, 0xA8, 0x5D, 0xA9, 0x5D, 0xAA, 0x5D, 0xAB, 0x5D, 0xAC, 0x5D, 0xAD, 0x5D, 0xAE, 0x5D, 0xAF, 0x5D, 0xB0, 0x5D, 0xB1, 0x5D, 0xB2, 0x5D, 0xB3, 0x5D, 0xB4, 0x5D, 0xB5, 0x5D, 0xB6,\n\t0x5D, 0xB7, 0x5D, 0xB8, 0x5D, 0xB9, 0x5D, 0xBA, 0x5D, 0xBB, 0x5D, 0xBC, 0x5D, 0xBD, 0x5D, 0xBE, 0x5D, 0xBF, 0x5D, 0xC0, 0x5D, 0xC1, 0x5D, 0xC2, 0x5D, 0xC3, 0x5D, 0xC4, 0x5D, 0xC5, 0x5D, 0xC6,\n\t0x5D, 0xC7, 0x5D, 0xC8, 0x5D, 0xC9, 0x5D, 0xCA, 0x5D, 0xCB, 0x5D, 0xCC, 0x5D, 0xCD, 0x5D, 0xCE, 0x5D, 0xCF, 0x5D, 0xD0, 0x5D, 0xD1, 0x5D, 0xD2, 0x5D, 0xD3, 0x5D, 0xD4, 0x5D, 0xD5, 0x5D, 0xD6,\n\t0x5D, 0xD7, 0x5D, 0xD8, 0x5D, 0xD9, 0x5D, 0xDA, 0x5D, 0xDB, 0x5D, 0xDC, 0x5D, 0xDD, 0x5D, 0xDE, 0x5D, 0xDF, 0x5D, 0xE0, 0x5D, 0xE1, 0x5D, 0xE2, 0x5D, 0xE3, 0x5D, 0xE4, 0x5D, 0xE5, 0x5D, 0xE6,\n\t0x5D, 0xE7, 0x5D, 0xE8, 0x5D, 0xE9, 0x5D, 0xEA, 0x5D, 0xEB, 0x5D, 0xEC, 0x5D, 0xED, 0x5D, 0xEE, 0x5D, 0xEF, 0x5D, 0xF0, 0x5D, 0xF1, 0x5D, 0xF2, 0x5D, 0xF3, 0x5D, 0xF4, 0x5D, 0xF5, 0x5D, 0xF6,\n\t0x5D, 0xF7, 0x5D, 0xF8, 0x5D, 0xF9, 0x5D, 0xFA, 0x5D, 0xFB, 0x5D, 0xFC, 0x5D, 0xFD, 0x5D, 0xFE, 0x5D, 0xFF, 0x5D, 0x00, 0x5E, 0x01, 0x5E, 0x02, 0x5E, 0x03, 0x5E, 0x04, 0x5E, 0x05, 0x5E, 0x06,\n\t0x5E, 0x07, 0x5E, 0x08, 0x5E, 0x09, 0x5E, 0x0A, 0x5E, 0x0B, 0x5E, 0x0C, 0x5E, 0x0D, 0x5E, 0x0E, 0x5E, 0x0F, 0x5E, 0x10, 0x5E, 0x11, 0x5E, 0x12, 0x5E, 0x13, 0x5E, 0x14, 0x5E, 0x15, 0x5E, 0x16,\n\t0x5E, 0x17, 0x5E, 0x18, 0x5E, 0x19, 0x5E, 0x1A, 0x5E, 0x1B, 0x5E, 0x1C, 0x5E, 0x1D, 0x5E, 0x1E, 0x5E, 0x1F, 0x5E, 0x20, 0x5E, 0x21, 0x5E, 0x22, 0x5E, 0x23, 0x5E, 0x24, 0x5E, 0x25, 0x5E, 0x26,\n\t0x5E, 0x27, 0x5E, 0x28, 0x5E, 0x29, 0x5E, 0x2A, 0x5E, 0x2B, 0x5E, 0x2C, 0x5E, 0x2D, 0x5E, 0x2E, 0x5E, 0x2F, 0x5E, 0x30, 0x5E, 0x31, 0x5E, 0x32, 0x5E, 0x33, 0x5E, 0x34, 0x5E, 0x35, 0x5E, 0x36,\n\t0x5E, 0x37, 0x5E, 0x38, 0x5E, 0x39, 0x5E, 0x3A, 0x5E, 0x3B, 0x5E, 0x3C, 0x5E, 0x3D, 0x5E, 0x3E, 0x5E, 0x3F, 0x5E, 0x40, 0x5E, 0x41, 0x5E, 0x42, 0x5E, 0x43, 0x5E, 0x44, 0x5E, 0x45, 0x5E, 0x46,\n\t0x5E, 0x47, 0x5E, 0x48, 0x5E, 0x49, 0x5E, 0x4A, 0x5E, 0x4B, 0x5E, 0x4C, 0x5E, 0x4D, 0x5E, 0x4E, 0x5E, 0x4F, 0x5E, 0x50, 0x5E, 0x51, 0x5E, 0x52, 0x5E, 0x53, 0x5E, 0x54, 0x5E, 0x55, 0x5E, 0x56,\n\t0x5E, 0x57, 0x5E, 0x58, 0x5E, 0x59, 0x5E, 0x5A, 0x5E, 0x5B, 0x5E, 0x5C, 0x5E, 0x5D, 0x5E, 0x5E, 0x5E, 0x5F, 0x5E, 0x60, 0x5E, 0x61, 0x5E, 0x62, 0x5E, 0x63, 0x5E, 0x64, 0x5E, 0x65, 0x5E, 0x66,\n\t0x5E, 0x67, 0x5E, 0x68, 0x5E, 0x69, 0x5E, 0x6A, 0x5E, 0x6B, 0x5E, 0x6C, 0x5E, 0x6D, 0x5E, 0x6E, 0x5E, 0x6F, 0x5E, 0x70, 0x5E, 0x71, 0x5E, 0x72, 0x5E, 0x73, 0x5E, 0x74, 0x5E, 0x75, 0x5E, 0x76,\n\t0x5E, 0x77, 0x5E, 0x78, 0x5E, 0x79, 0x5E, 0x7A, 0x5E, 0x7B, 0x5E, 0x7C, 0x5E, 0x7D, 0x5E, 0x7E, 0x5E, 0x7F, 0x5E, 0x80, 0x5E, 0x81, 0x5E, 0x82, 0x5E, 0x83, 0x5E, 0x84, 0x5E, 0x85, 0x5E, 0x86,\n\t0x5E, 0x87, 0x5E, 0x88, 0x5E, 0x89, 0x5E, 0x8A, 0x5E, 0x8B, 0x5E, 0x8C, 0x5E, 0x8D, 0x5E, 0x8E, 0x5E, 0x8F, 0x5E, 0x90, 0x5E, 0x91, 0x5E, 0x92, 0x5E, 0x93, 0x5E, 0x94, 0x5E, 0x95, 0x5E, 0x96,\n\t0x5E, 0x97, 0x5E, 0x98, 0x5E, 0x99, 0x5E, 0x9A, 0x5E, 0x9B, 0x5E, 0x9C, 0x5E, 0x9D, 0x5E, 0x9E, 0x5E, 0x9F, 0x5E, 0xA0, 0x5E, 0xA1, 0x5E, 0xA2, 0x5E, 0xA3, 0x5E, 0xA4, 0x5E, 0xA5, 0x5E, 0xA6,\n\t0x5E, 0xA7, 0x5E, 0xA8, 0x5E, 0xA9, 0x5E, 0xAA, 0x5E, 0xAB, 0x5E, 0xAC, 0x5E, 0xAD, 0x5E, 0xAE, 0x5E, 0xAF, 0x5E, 0xB0, 0x5E, 0xB1, 0x5E, 0xB2, 0x5E, 0xB3, 0x5E, 0xB4, 0x5E, 0xB5, 0x5E, 0xB6,\n\t0x5E, 0xB7, 0x5E, 0xB8, 0x5E, 0xB9, 0x5E, 0xBA, 0x5E, 0xBB, 0x5E, 0xBC, 0x5E, 0xBD, 0x5E, 0xBE, 0x5E, 0xBF, 0x5E, 0xC0, 0x5E, 0xC1, 0x5E, 0xC2, 0x5E, 0xC3, 0x5E, 0xC4, 0x5E, 0xC5, 0x5E, 0xC6,\n\t0x5E, 0xC7, 0x5E, 0xC8, 0x5E, 0xC9, 0x5E, 0xCA, 0x5E, 0xCB, 0x5E, 0xCC, 0x5E, 0xCD, 0x5E, 0xCE, 0x5E, 0xCF, 0x5E, 0xD0, 0x5E, 0xD1, 0x5E, 0xD2, 0x5E, 0xD3, 0x5E, 0xD4, 0x5E, 0xD5, 0x5E, 0xD6,\n\t0x5E, 0xD7, 0x5E, 0xD8, 0x5E, 0xD9, 0x5E, 0xDA, 0x5E, 0xDB, 0x5E, 0xDC, 0x5E, 0xDD, 0x5E, 0xDE, 0x5E, 0xDF, 0x5E, 0xE0, 0x5E, 0xE1, 0x5E, 0xE2, 0x5E, 0xE3, 0x5E, 0xE4, 0x5E, 0xE5, 0x5E, 0xE6,\n\t0x5E, 0xE7, 0x5E, 0xE8, 0x5E, 0xE9, 0x5E, 0xEA, 0x5E, 0xEB, 0x5E, 0xEC, 0x5E, 0xED, 0x5E, 0xEE, 0x5E, 0xEF, 0x5E, 0xF0, 0x5E, 0xF1, 0x5E, 0xF2, 0x5E, 0xF3, 0x5E, 0xF4, 0x5E, 0xF5, 0x5E, 0xF6,\n\t0x5E, 0xF7, 0x5E, 0xF8, 0x5E, 0xF9, 0x5E, 0xFA, 0x5E, 0xFB, 0x5E, 0xFC, 0x5E, 0xFD, 0x5E, 0xFE, 0x5E, 0xFF, 0x5E, 0x00, 0x5F, 0x01, 0x5F, 0x02, 0x5F, 0x03, 0x5F, 0x04, 0x5F, 0x05, 0x5F, 0x06,\n\t0x5F, 0x07, 0x5F, 0x08, 0x5F, 0x09, 0x5F, 0x0A, 0x5F, 0x0B, 0x5F, 0x0C, 0x5F, 0x0D, 0x5F, 0x0E, 0x5F, 0x0F, 0x5F, 0x10, 0x5F, 0x11, 0x5F, 0x12, 0x5F, 0x13, 0x5F, 0x14, 0x5F, 0x15, 0x5F, 0x16,\n\t0x5F, 0x17, 0x5F, 0x18, 0x5F, 0x19, 0x5F, 0x1A, 0x5F, 0x1B, 0x5F, 0x1C, 0x5F, 0x1D, 0x5F, 0x1E, 0x5F, 0x1F, 0x5F, 0x20, 0x5F, 0x21, 0x5F, 0x22, 0x5F, 0x23, 0x5F, 0x24, 0x5F, 0x25, 0x5F, 0x26,\n\t0x5F, 0x27, 0x5F, 0x28, 0x5F, 0x29, 0x5F, 0x2A, 0x5F, 0x2B, 0x5F, 0x2C, 0x5F, 0x2D, 0x5F, 0x2E, 0x5F, 0x2F, 0x5F, 0x30, 0x5F, 0x31, 0x5F, 0x32, 0x5F, 0x33, 0x5F, 0x34, 0x5F, 0x35, 0x5F, 0x36,\n\t0x5F, 0x37, 0x5F, 0x38, 0x5F, 0x39, 0x5F, 0x3A, 0x5F, 0x3B, 0x5F, 0x3C, 0x5F, 0x3D, 0x5F, 0x3E, 0x5F, 0x3F, 0x5F, 0x40, 0x5F, 0x41, 0x5F, 0x42, 0x5F, 0x43, 0x5F, 0x44, 0x5F, 0x45, 0x5F, 0x46,\n\t0x5F, 0x47, 0x5F, 0x48, 0x5F, 0x49, 0x5F, 0x4A, 0x5F, 0x4B, 0x5F, 0x4C, 0x5F, 0x4D, 0x5F, 0x4E, 0x5F, 0x4F, 0x5F, 0x50, 0x5F, 0x51, 0x5F, 0x52, 0x5F, 0x53, 0x5F, 0x54, 0x5F, 0x55, 0x5F, 0x56,\n\t0x5F, 0x57, 0x5F, 0x58, 0x5F, 0x59, 0x5F, 0x5A, 0x5F, 0x5B, 0x5F, 0x5C, 0x5F, 0x5D, 0x5F, 0x5E, 0x5F, 0x5F, 0x5F, 0x60, 0x5F, 0x61, 0x5F, 0x62, 0x5F, 0x63, 0x5F, 0x64, 0x5F, 0x65, 0x5F, 0x66,\n\t0x5F, 0x67, 0x5F, 0x68, 0x5F, 0x69, 0x5F, 0x6A, 0x5F, 0x6B, 0x5F, 0x6C, 0x5F, 0x6D, 0x5F, 0x6E, 0x5F, 0x6F, 0x5F, 0x70, 0x5F, 0x71, 0x5F, 0x72, 0x5F, 0x73, 0x5F, 0x74, 0x5F, 0x75, 0x5F, 0x76,\n\t0x5F, 0x77, 0x5F, 0x78, 0x5F, 0x79, 0x5F, 0x7A, 0x5F, 0x7B, 0x5F, 0x7C, 0x5F, 0x7D, 0x5F, 0x7E, 0x5F, 0x7F, 0x5F, 0x80, 0x5F, 0x81, 0x5F, 0x82, 0x5F, 0x83, 0x5F, 0x84, 0x5F, 0x85, 0x5F, 0x86,\n\t0x5F, 0x87, 0x5F, 0x88, 0x5F, 0x89, 0x5F, 0x8A, 0x5F, 0x8B, 0x5F, 0x8C, 0x5F, 0x8D, 0x5F, 0x8E, 0x5F, 0x8F, 0x5F, 0x90, 0x5F, 0x91, 0x5F, 0x92, 0x5F, 0x93, 0x5F, 0x94, 0x5F, 0x95, 0x5F, 0x96,\n\t0x5F, 0x97, 0x5F, 0x98, 0x5F, 0x99, 0x5F, 0x9A, 0x5F, 0x9B, 0x5F, 0x9C, 0x5F, 0x9D, 0x5F, 0x9E, 0x5F, 0x9F, 0x5F, 0xA0, 0x5F, 0xA1, 0x5F, 0xA2, 0x5F, 0xA3, 0x5F, 0xA4, 0x5F, 0xA5, 0x5F, 0xA6,\n\t0x5F, 0xA7, 0x5F, 0xA8, 0x5F, 0xA9, 0x5F, 0xAA, 0x5F, 0xAB, 0x5F, 0xAC, 0x5F, 0xAD, 0x5F, 0xAE, 0x5F, 0xAF, 0x5F, 0xB0, 0x5F, 0xB1, 0x5F, 0xB2, 0x5F, 0xB3, 0x5F, 0xB4, 0x5F, 0xB5, 0x5F, 0xB6,\n\t0x5F, 0xB7, 0x5F, 0xB8, 0x5F, 0xB9, 0x5F, 0xBA, 0x5F, 0xBB, 0x5F, 0xBC, 0x5F, 0xBD, 0x5F, 0xBE, 0x5F, 0xBF, 0x5F, 0xC0, 0x5F, 0xC1, 0x5F, 0xC2, 0x5F, 0xC3, 0x5F, 0xC4, 0x5F, 0xC5, 0x5F, 0xC6,\n\t0x5F, 0xC7, 0x5F, 0xC8, 0x5F, 0xC9, 0x5F, 0xCA, 0x5F, 0xCB, 0x5F, 0xCC, 0x5F, 0xCD, 0x5F, 0xCE, 0x5F, 0xCF, 0x5F, 0xD0, 0x5F, 0xD1, 0x5F, 0xD2, 0x5F, 0xD3, 0x5F, 0xD4, 0x5F, 0xD5, 0x5F, 0xD6,\n\t0x5F, 0xD7, 0x5F, 0xD8, 0x5F, 0xD9, 0x5F, 0xDA, 0x5F, 0xDB, 0x5F, 0xDC, 0x5F, 0xDD, 0x5F, 0xDE, 0x5F, 0xDF, 0x5F, 0xE0, 0x5F, 0xE1, 0x5F, 0xE2, 0x5F, 0xE3, 0x5F, 0xE4, 0x5F, 0xE5, 0x5F, 0xE6,\n\t0x5F, 0xE7, 0x5F, 0xE8, 0x5F, 0xE9, 0x5F, 0xEA, 0x5F, 0xEB, 0x5F, 0xEC, 0x5F, 0xED, 0x5F, 0xEE, 0x5F, 0xEF, 0x5F, 0xF0, 0x5F, 0xF1, 0x5F, 0xF2, 0x5F, 0xF3, 0x5F, 0xF4, 0x5F, 0xF5, 0x5F, 0xF6,\n\t0x5F, 0xF7, 0x5F, 0xF8, 0x5F, 0xF9, 0x5F, 0xFA, 0x5F, 0xFB, 0x5F, 0xFC, 0x5F, 0xFD, 0x5F, 0xFE, 0x5F, 0xFF, 0x5F, 0x00, 0x60, 0x01, 0x60, 0x02, 0x60, 0x03, 0x60, 0x04, 0x60, 0x05, 0x60, 0x06,\n\t0x60, 0x07, 0x60, 0x08, 0x60, 0x09, 0x60, 0x0A, 0x60, 0x0B, 0x60, 0x0C, 0x60, 0x0D, 0x60, 0x0E, 0x60, 0x0F, 0x60, 0x10, 0x60, 0x11, 0x60, 0x12, 0x60, 0x13, 0x60, 0x14, 0x60, 0x15, 0x60, 0x16,\n\t0x60, 0x17, 0x60, 0x18, 0x60, 0x19, 0x60, 0x1A, 0x60, 0x1B, 0x60, 0x1C, 0x60, 0x1D, 0x60, 0x1E, 0x60, 0x1F, 0x60, 0x20, 0x60, 0x21, 0x60, 0x22, 0x60, 0x23, 0x60, 0x24, 0x60, 0x25, 0x60, 0x26,\n\t0x60, 0x27, 0x60, 0x28, 0x60, 0x29, 0x60, 0x2A, 0x60, 0x2B, 0x60, 0x2C, 0x60, 0x2D, 0x60, 0x2E, 0x60, 0x2F, 0x60, 0x30, 0x60, 0x31, 0x60, 0x32, 0x60, 0x33, 0x60, 0x34, 0x60, 0x35, 0x60, 0x36,\n\t0x60, 0x37, 0x60, 0x38, 0x60, 0x39, 0x60, 0x3A, 0x60, 0x3B, 0x60, 0x3C, 0x60, 0x3D, 0x60, 0x3E, 0x60, 0x3F, 0x60, 0x40, 0x60, 0x41, 0x60, 0x42, 0x60, 0x43, 0x60, 0x44, 0x60, 0x45, 0x60, 0x46,\n\t0x60, 0x47, 0x60, 0x48, 0x60, 0x49, 0x60, 0x4A, 0x60, 0x4B, 0x60, 0x4C, 0x60, 0x4D, 0x60, 0x4E, 0x60, 0x4F, 0x60, 0x50, 0x60, 0x51, 0x60, 0x52, 0x60, 0x53, 0x60, 0x54, 0x60, 0x55, 0x60, 0x56,\n\t0x60, 0x57, 0x60, 0x58, 0x60, 0x59, 0x60, 0x5A, 0x60, 0x5B, 0x60, 0x5C, 0x60, 0x5D, 0x60, 0x5E, 0x60, 0x5F, 0x60, 0x60, 0x60, 0x61, 0x60, 0x62, 0x60, 0x63, 0x60, 0x64, 0x60, 0x65, 0x60, 0x66,\n\t0x60, 0x67, 0x60, 0x68, 0x60, 0x69, 0x60, 0x6A, 0x60, 0x6B, 0x60, 0x6C, 0x60, 0x6D, 0x60, 0x6E, 0x60, 0x6F, 0x60, 0x70, 0x60, 0x71, 0x60, 0x72, 0x60, 0x73, 0x60, 0x74, 0x60, 0x75, 0x60, 0x76,\n\t0x60, 0x77, 0x60, 0x78, 0x60, 0x79, 0x60, 0x7A, 0x60, 0x7B, 0x60, 0x7C, 0x60, 0x7D, 0x60, 0x7E, 0x60, 0x7F, 0x60, 0x80, 0x60, 0x81, 0x60, 0x82, 0x60, 0x83, 0x60, 0x84, 0x60, 0x85, 0x60, 0x86,\n\t0x60, 0x87, 0x60, 0x88, 0x60, 0x89, 0x60, 0x8A, 0x60, 0x8B, 0x60, 0x8C, 0x60, 0x8D, 0x60, 0x8E, 0x60, 0x8F, 0x60, 0x90, 0x60, 0x91, 0x60, 0x92, 0x60, 0x93, 0x60, 0x94, 0x60, 0x95, 0x60, 0x96,\n\t0x60, 0x97, 0x60, 0x98, 0x60, 0x99, 0x60, 0x9A, 0x60, 0x9B, 0x60, 0x9C, 0x60, 0x9D, 0x60, 0x9E, 0x60, 0x9F, 0x60, 0xA0, 0x60, 0xA1, 0x60, 0xA2, 0x60, 0xA3, 0x60, 0xA4, 0x60, 0xA5, 0x60, 0xA6,\n\t0x60, 0xA7, 0x60, 0xA8, 0x60, 0xA9, 0x60, 0xAA, 0x60, 0xAB, 0x60, 0xAC, 0x60, 0xAD, 0x60, 0xAE, 0x60, 0xAF, 0x60, 0xB0, 0x60, 0xB1, 0x60, 0xB2, 0x60, 0xB3, 0x60, 0xB4, 0x60, 0xB5, 0x60, 0xB6,\n\t0x60, 0xB7, 0x60, 0xB8, 0x60, 0xB9, 0x60, 0xBA, 0x60, 0xBB, 0x60, 0xBC, 0x60, 0xBD, 0x60, 0xBE, 0x60, 0xBF, 0x60, 0xC0, 0x60, 0xC1, 0x60, 0xC2, 0x60, 0xC3, 0x60, 0xC4, 0x60, 0xC5, 0x60, 0xC6,\n\t0x60, 0xC7, 0x60, 0xC8, 0x60, 0xC9, 0x60, 0xCA, 0x60, 0xCB, 0x60, 0xCC, 0x60, 0xCD, 0x60, 0xCE, 0x60, 0xCF, 0x60, 0xD0, 0x60, 0xD1, 0x60, 0xD2, 0x60, 0xD3, 0x60, 0xD4, 0x60, 0xD5, 0x60, 0xD6,\n\t0x60, 0xD7, 0x60, 0xD8, 0x60, 0xD9, 0x60, 0xDA, 0x60, 0xDB, 0x60, 0xDC, 0x60, 0xDD, 0x60, 0xDE, 0x60, 0xDF, 0x60, 0xE0, 0x60, 0xE1, 0x60, 0xE2, 0x60, 0xE3, 0x60, 0xE4, 0x60, 0xE5, 0x60, 0xE6,\n\t0x60, 0xE7, 0x60, 0xE8, 0x60, 0xE9, 0x60, 0xEA, 0x60, 0xEB, 0x60, 0xEC, 0x60, 0xED, 0x60, 0xEE, 0x60, 0xEF, 0x60, 0xF0, 0x60, 0xF1, 0x60, 0xF2, 0x60, 0xF3, 0x60, 0xF4, 0x60, 0xF5, 0x60, 0xF6,\n\t0x60, 0xF7, 0x60, 0xF8, 0x60, 0xF9, 0x60, 0xFA, 0x60, 0xFB, 0x60, 0xFC, 0x60, 0xFD, 0x60, 0xFE, 0x60, 0xFF, 0x60, 0x00, 0x61, 0x01, 0x61, 0x02, 0x61, 0x03, 0x61, 0x04, 0x61, 0x05, 0x61, 0x06,\n\t0x61, 0x07, 0x61, 0x08, 0x61, 0x09, 0x61, 0x0A, 0x61, 0x0B, 0x61, 0x0C, 0x61, 0x0D, 0x61, 0x0E, 0x61, 0x0F, 0x61, 0x10, 0x61, 0x11, 0x61, 0x12, 0x61, 0x13, 0x61, 0x14, 0x61, 0x15, 0x61, 0x16,\n\t0x61, 0x17, 0x61, 0x18, 0x61, 0x19, 0x61, 0x1A, 0x61, 0x1B, 0x61, 0x1C, 0x61, 0x1D, 0x61, 0x1E, 0x61, 0x1F, 0x61, 0x20, 0x61, 0x21, 0x61, 0x22, 0x61, 0x23, 0x61, 0x24, 0x61, 0x25, 0x61, 0x26,\n\t0x61, 0x27, 0x61, 0x28, 0x61, 0x29, 0x61, 0x2A, 0x61, 0x2B, 0x61, 0x2C, 0x61, 0x2D, 0x61, 0x2E, 0x61, 0x2F, 0x61, 0x30, 0x61, 0x31, 0x61, 0x32, 0x61, 0x33, 0x61, 0x34, 0x61, 0x35, 0x61, 0x36,\n\t0x61, 0x37, 0x61, 0x38, 0x61, 0x39, 0x61, 0x3A, 0x61, 0x3B, 0x61, 0x3C, 0x61, 0x3D, 0x61, 0x3E, 0x61, 0x3F, 0x61, 0x40, 0x61, 0x41, 0x61, 0x42, 0x61, 0x43, 0x61, 0x44, 0x61, 0x45, 0x61, 0x46,\n\t0x61, 0x47, 0x61, 0x48, 0x61, 0x49, 0x61, 0x4A, 0x61, 0x4B, 0x61, 0x4C, 0x61, 0x4D, 0x61, 0x4E, 0x61, 0x4F, 0x61, 0x50, 0x61, 0x51, 0x61, 0x52, 0x61, 0x53, 0x61, 0x54, 0x61, 0x55, 0x61, 0x56,\n\t0x61, 0x57, 0x61, 0x58, 0x61, 0x59, 0x61, 0x5A, 0x61, 0x5B, 0x61, 0x5C, 0x61, 0x5D, 0x61, 0x5E, 0x61, 0x5F, 0x61, 0x60, 0x61, 0x61, 0x61, 0x62, 0x61, 0x63, 0x61, 0x64, 0x61, 0x65, 0x61, 0x66,\n\t0x61, 0x67, 0x61, 0x68, 0x61, 0x69, 0x61, 0x6A, 0x61, 0x6B, 0x61, 0x6C, 0x61, 0x6D, 0x61, 0x6E, 0x61, 0x6F, 0x61, 0x70, 0x61, 0x71, 0x61, 0x72, 0x61, 0x73, 0x61, 0x74, 0x61, 0x75, 0x61, 0x76,\n\t0x61, 0x77, 0x61, 0x78, 0x61, 0x79, 0x61, 0x7A, 0x61, 0x7B, 0x61, 0x7C, 0x61, 0x7D, 0x61, 0x7E, 0x61, 0x7F, 0x61, 0x80, 0x61, 0x81, 0x61, 0x82, 0x61, 0x83, 0x61, 0x84, 0x61, 0x85, 0x61, 0x86,\n\t0x61, 0x87, 0x61, 0x88, 0x61, 0x89, 0x61, 0x8A, 0x61, 0x8B, 0x61, 0x8C, 0x61, 0x8D, 0x61, 0x8E, 0x61, 0x8F, 0x61, 0x90, 0x61, 0x91, 0x61, 0x92, 0x61, 0x93, 0x61, 0x94, 0x61, 0x95, 0x61, 0x96,\n\t0x61, 0x97, 0x61, 0x98, 0x61, 0x99, 0x61, 0x9A, 0x61, 0x9B, 0x61, 0x9C, 0x61, 0x9D, 0x61, 0x9E, 0x61, 0x9F, 0x61, 0xA0, 0x61, 0xA1, 0x61, 0xA2, 0x61, 0xA3, 0x61, 0xA4, 0x61, 0xA5, 0x61, 0xA6,\n\t0x61, 0xA7, 0x61, 0xA8, 0x61, 0xA9, 0x61, 0xAA, 0x61, 0xAB, 0x61, 0xAC, 0x61, 0xAD, 0x61, 0xAE, 0x61, 0xAF, 0x61, 0xB0, 0x61, 0xB1, 0x61, 0xB2, 0x61, 0xB3, 0x61, 0xB4, 0x61, 0xB5, 0x61, 0xB6,\n\t0x61, 0xB7, 0x61, 0xB8, 0x61, 0xB9, 0x61, 0xBA, 0x61, 0xBB, 0x61, 0xBC, 0x61, 0xBD, 0x61, 0xBE, 0x61, 0xBF, 0x61, 0xC0, 0x61, 0xC1, 0x61, 0xC2, 0x61, 0xC3, 0x61, 0xC4, 0x61, 0xC5, 0x61, 0xC6,\n\t0x61, 0xC7, 0x61, 0xC8, 0x61, 0xC9, 0x61, 0xCA, 0x61, 0xCB, 0x61, 0xCC, 0x61, 0xCD, 0x61, 0xCE, 0x61, 0xCF, 0x61, 0xD0, 0x61, 0xD1, 0x61, 0xD2, 0x61, 0xD3, 0x61, 0xD4, 0x61, 0xD5, 0x61, 0xD6,\n\t0x61, 0xD7, 0x61, 0xD8, 0x61, 0xD9, 0x61, 0xDA, 0x61, 0xDB, 0x61, 0xDC, 0x61, 0xDD, 0x61, 0xDE, 0x61, 0xDF, 0x61, 0xE0, 0x61, 0xE1, 0x61, 0xE2, 0x61, 0xE3, 0x61, 0xE4, 0x61, 0xE5, 0x61, 0xE6,\n\t0x61, 0xE7, 0x61, 0xE8, 0x61, 0xE9, 0x61, 0xEA, 0x61, 0xEB, 0x61, 0xEC, 0x61, 0xED, 0x61, 0xEE, 0x61, 0xEF, 0x61, 0xF0, 0x61, 0xF1, 0x61, 0xF2, 0x61, 0xF3, 0x61, 0xF4, 0x61, 0xF5, 0x61, 0xF6,\n\t0x61, 0xF7, 0x61, 0xF8, 0x61, 0xF9, 0x61, 0xFA, 0x61, 0xFB, 0x61, 0xFC, 0x61, 0xFD, 0x61, 0xFE, 0x61, 0xFF, 0x61, 0x00, 0x62, 0x01, 0x62, 0x02, 0x62, 0x03, 0x62, 0x04, 0x62, 0x05, 0x62, 0x06,\n\t0x62, 0x07, 0x62, 0x08, 0x62, 0x09, 0x62, 0x0A, 0x62, 0x0B, 0x62, 0x0C, 0x62, 0x0D, 0x62, 0x0E, 0x62, 0x0F, 0x62, 0x10, 0x62, 0x11, 0x62, 0x12, 0x62, 0x13, 0x62, 0x14, 0x62, 0x15, 0x62, 0x16,\n\t0x62, 0x17, 0x62, 0x18, 0x62, 0x19, 0x62, 0x1A, 0x62, 0x1B, 0x62, 0x1C, 0x62, 0x1D, 0x62, 0x1E, 0x62, 0x1F, 0x62, 0x20, 0x62, 0x21, 0x62, 0x22, 0x62, 0x23, 0x62, 0x24, 0x62, 0x25, 0x62, 0x26,\n\t0x62, 0x27, 0x62, 0x28, 0x62, 0x29, 0x62, 0x2A, 0x62, 0x2B, 0x62, 0x2C, 0x62, 0x2D, 0x62, 0x2E, 0x62, 0x2F, 0x62, 0x30, 0x62, 0x31, 0x62, 0x32, 0x62, 0x33, 0x62, 0x34, 0x62, 0x35, 0x62, 0x36,\n\t0x62, 0x37, 0x62, 0x38, 0x62, 0x39, 0x62, 0x3A, 0x62, 0x3B, 0x62, 0x3C, 0x62, 0x3D, 0x62, 0x3E, 0x62, 0x3F, 0x62, 0x40, 0x62, 0x41, 0x62, 0x42, 0x62, 0x43, 0x62, 0x44, 0x62, 0x45, 0x62, 0x46,\n\t0x62, 0x47, 0x62, 0x48, 0x62, 0x49, 0x62, 0x4A, 0x62, 0x4B, 0x62, 0x4C, 0x62, 0x4D, 0x62, 0x4E, 0x62, 0x4F, 0x62, 0x50, 0x62, 0x51, 0x62, 0x52, 0x62, 0x53, 0x62, 0x54, 0x62, 0x55, 0x62, 0x56,\n\t0x62, 0x57, 0x62, 0x58, 0x62, 0x59, 0x62, 0x5A, 0x62, 0x5B, 0x62, 0x5C, 0x62, 0x5D, 0x62, 0x5E, 0x62, 0x5F, 0x62, 0x60, 0x62, 0x61, 0x62, 0x62, 0x62, 0x63, 0x62, 0x64, 0x62, 0x65, 0x62, 0x66,\n\t0x62, 0x67, 0x62, 0x68, 0x62, 0x69, 0x62, 0x6A, 0x62, 0x6B, 0x62, 0x6C, 0x62, 0x6D, 0x62, 0x6E, 0x62, 0x6F, 0x62, 0x70, 0x62, 0x71, 0x62, 0x72, 0x62, 0x73, 0x62, 0x74, 0x62, 0x75, 0x62, 0x76,\n\t0x62, 0x77, 0x62, 0x78, 0x62, 0x79, 0x62, 0x7A, 0x62, 0x7B, 0x62, 0x7C, 0x62, 0x7D, 0x62, 0x7E, 0x62, 0x7F, 0x62, 0x80, 0x62, 0x81, 0x62, 0x82, 0x62, 0x83, 0x62, 0x84, 0x62, 0x85, 0x62, 0x86,\n\t0x62, 0x87, 0x62, 0x88, 0x62, 0x89, 0x62, 0x8A, 0x62, 0x8B, 0x62, 0x8C, 0x62, 0x8D, 0x62, 0x8E, 0x62, 0x8F, 0x62, 0x90, 0x62, 0x91, 0x62, 0x92, 0x62, 0x93, 0x62, 0x94, 0x62, 0x95, 0x62, 0x96,\n\t0x62, 0x97, 0x62, 0x98, 0x62, 0x99, 0x62, 0x9A, 0x62, 0x9B, 0x62, 0x9C, 0x62, 0x9D, 0x62, 0x9E, 0x62, 0x9F, 0x62, 0xA0, 0x62, 0xA1, 0x62, 0xA2, 0x62, 0xA3, 0x62, 0xA4, 0x62, 0xA5, 0x62, 0xA6,\n\t0x62, 0xA7, 0x62, 0xA8, 0x62, 0xA9, 0x62, 0xAA, 0x62, 0xAB, 0x62, 0xAC, 0x62, 0xAD, 0x62, 0xAE, 0x62, 0xAF, 0x62, 0xB0, 0x62, 0xB1, 0x62, 0xB2, 0x62, 0xB3, 0x62, 0xB4, 0x62, 0xB5, 0x62, 0xB6,\n\t0x62, 0xB7, 0x62, 0xB8, 0x62, 0xB9, 0x62, 0xBA, 0x62, 0xBB, 0x62, 0xBC, 0x62, 0xBD, 0x62, 0xBE, 0x62, 0xBF, 0x62, 0xC0, 0x62, 0xC1, 0x62, 0xC2, 0x62, 0xC3, 0x62, 0xC4, 0x62, 0xC5, 0x62, 0xC6,\n\t0x62, 0xC7, 0x62, 0xC8, 0x62, 0xC9, 0x62, 0xCA, 0x62, 0xCB, 0x62, 0xCC, 0x62, 0xCD, 0x62, 0xCE, 0x62, 0xCF, 0x62, 0xD0, 0x62, 0xD1, 0x62, 0xD2, 0x62, 0xD3, 0x62, 0xD4, 0x62, 0xD5, 0x62, 0xD6,\n\t0x62, 0xD7, 0x62, 0xD8, 0x62, 0xD9, 0x62, 0xDA, 0x62, 0xDB, 0x62, 0xDC, 0x62, 0xDD, 0x62, 0xDE, 0x62, 0xDF, 0x62, 0xE0, 0x62, 0xE1, 0x62, 0xE2, 0x62, 0xE3, 0x62, 0xE4, 0x62, 0xE5, 0x62, 0xE6,\n\t0x62, 0xE7, 0x62, 0xE8, 0x62, 0xE9, 0x62, 0xEA, 0x62, 0xEB, 0x62, 0xEC, 0x62, 0xED, 0x62, 0xEE, 0x62, 0xEF, 0x62, 0xF0, 0x62, 0xF1, 0x62, 0xF2, 0x62, 0xF3, 0x62, 0xF4, 0x62, 0xF5, 0x62, 0xF6,\n\t0x62, 0xF7, 0x62, 0xF8, 0x62, 0xF9, 0x62, 0xFA, 0x62, 0xFB, 0x62, 0xFC, 0x62, 0xFD, 0x62, 0xFE, 0x62, 0xFF, 0x62, 0x00, 0x63, 0x01, 0x63, 0x02, 0x63, 0x03, 0x63, 0x04, 0x63, 0x05, 0x63, 0x06,\n\t0x63, 0x07, 0x63, 0x08, 0x63, 0x09, 0x63, 0x0A, 0x63, 0x0B, 0x63, 0x0C, 0x63, 0x0D, 0x63, 0x0E, 0x63, 0x0F, 0x63, 0x10, 0x63, 0x11, 0x63, 0x12, 0x63, 0x13, 0x63, 0x14, 0x63, 0x15, 0x63, 0x16,\n\t0x63, 0x17, 0x63, 0x18, 0x63, 0x19, 0x63, 0x1A, 0x63, 0x1B, 0x63, 0x1C, 0x63, 0x1D, 0x63, 0x1E, 0x63, 0x1F, 0x63, 0x20, 0x63, 0x21, 0x63, 0x22, 0x63, 0x23, 0x63, 0x24, 0x63, 0x25, 0x63, 0x26,\n\t0x63, 0x27, 0x63, 0x28, 0x63, 0x29, 0x63, 0x2A, 0x63, 0x2B, 0x63, 0x2C, 0x63, 0x2D, 0x63, 0x2E, 0x63, 0x2F, 0x63, 0x30, 0x63, 0x31, 0x63, 0x32, 0x63, 0x33, 0x63, 0x34, 0x63, 0x35, 0x63, 0x36,\n\t0x63, 0x37, 0x63, 0x38, 0x63, 0x39, 0x63, 0x3A, 0x63, 0x3B, 0x63, 0x3C, 0x63, 0x3D, 0x63, 0x3E, 0x63, 0x3F, 0x63, 0x40, 0x63, 0x41, 0x63, 0x42, 0x63, 0x43, 0x63, 0x44, 0x63, 0x45, 0x63, 0x46,\n\t0x63, 0x47, 0x63, 0x48, 0x63, 0x49, 0x63, 0x4A, 0x63, 0x4B, 0x63, 0x4C, 0x63, 0x4D, 0x63, 0x4E, 0x63, 0x4F, 0x63, 0x50, 0x63, 0x51, 0x63, 0x52, 0x63, 0x53, 0x63, 0x54, 0x63, 0x55, 0x63, 0x56,\n\t0x63, 0x57, 0x63, 0x58, 0x63, 0x59, 0x63, 0x5A, 0x63, 0x5B, 0x63, 0x5C, 0x63, 0x5D, 0x63, 0x5E, 0x63, 0x5F, 0x63, 0x60, 0x63, 0x61, 0x63, 0x62, 0x63, 0x63, 0x63, 0x64, 0x63, 0x65, 0x63, 0x66,\n\t0x63, 0x67, 0x63, 0x68, 0x63, 0x69, 0x63, 0x6A, 0x63, 0x6B, 0x63, 0x6C, 0x63, 0x6D, 0x63, 0x6E, 0x63, 0x6F, 0x63, 0x70, 0x63, 0x71, 0x63, 0x72, 0x63, 0x73, 0x63, 0x74, 0x63, 0x75, 0x63, 0x76,\n\t0x63, 0x77, 0x63, 0x78, 0x63, 0x79, 0x63, 0x7A, 0x63, 0x7B, 0x63, 0x7C, 0x63, 0x7D, 0x63, 0x7E, 0x63, 0x7F, 0x63, 0x80, 0x63, 0x81, 0x63, 0x82, 0x63, 0x83, 0x63, 0x84, 0x63, 0x85, 0x63, 0x86,\n\t0x63, 0x87, 0x63, 0x88, 0x63, 0x89, 0x63, 0x8A, 0x63, 0x8B, 0x63, 0x8C, 0x63, 0x8D, 0x63, 0x8E, 0x63, 0x8F, 0x63, 0x90, 0x63, 0x91, 0x63, 0x92, 0x63, 0x93, 0x63, 0x94, 0x63, 0x95, 0x63, 0x96,\n\t0x63, 0x97, 0x63, 0x98, 0x63, 0x99, 0x63, 0x9A, 0x63, 0x9B, 0x63, 0x9C, 0x63, 0x9D, 0x63, 0x9E, 0x63, 0x9F, 0x63, 0xA0, 0x63, 0xA1, 0x63, 0xA2, 0x63, 0xA3, 0x63, 0xA4, 0x63, 0xA5, 0x63, 0xA6,\n\t0x63, 0xA7, 0x63, 0xA8, 0x63, 0xA9, 0x63, 0xAA, 0x63, 0xAB, 0x63, 0xAC, 0x63, 0xAD, 0x63, 0xAE, 0x63, 0xAF, 0x63, 0xB0, 0x63, 0xB1, 0x63, 0xB2, 0x63, 0xB3, 0x63, 0xB4, 0x63, 0xB5, 0x63, 0xB6,\n\t0x63, 0xB7, 0x63, 0xB8, 0x63, 0xB9, 0x63, 0xBA, 0x63, 0xBB, 0x63, 0xBC, 0x63, 0xBD, 0x63, 0xBE, 0x63, 0xBF, 0x63, 0xC0, 0x63, 0xC1, 0x63, 0xC2, 0x63, 0xC3, 0x63, 0xC4, 0x63, 0xC5, 0x63, 0xC6,\n\t0x63, 0xC7, 0x63, 0xC8, 0x63, 0xC9, 0x63, 0xCA, 0x63, 0xCB, 0x63, 0xCC, 0x63, 0xCD, 0x63, 0xCE, 0x63, 0xCF, 0x63, 0xD0, 0x63, 0xD1, 0x63, 0xD2, 0x63, 0xD3, 0x63, 0xD4, 0x63, 0xD5, 0x63, 0xD6,\n\t0x63, 0xD7, 0x63, 0xD8, 0x63, 0xD9, 0x63, 0xDA, 0x63, 0xDB, 0x63, 0xDC, 0x63, 0xDD, 0x63, 0xDE, 0x63, 0xDF, 0x63, 0xE0, 0x63, 0xE1, 0x63, 0xE2, 0x63, 0xE3, 0x63, 0xE4, 0x63, 0xE5, 0x63, 0xE6,\n\t0x63, 0xE7, 0x63, 0xE8, 0x63, 0xE9, 0x63, 0xEA, 0x63, 0xEB, 0x63, 0xEC, 0x63, 0xED, 0x63, 0xEE, 0x63, 0xEF, 0x63, 0xF0, 0x63, 0xF1, 0x63, 0xF2, 0x63, 0xF3, 0x63, 0xF4, 0x63, 0xF5, 0x63, 0xF6,\n\t0x63, 0xF7, 0x63, 0xF8, 0x63, 0xF9, 0x63, 0xFA, 0x63, 0xFB, 0x63, 0xFC, 0x63, 0xFD, 0x63, 0xFE, 0x63, 0xFF, 0x63, 0x00, 0x64, 0x01, 0x64, 0x02, 0x64, 0x03, 0x64, 0x04, 0x64, 0x05, 0x64, 0x06,\n\t0x64, 0x07, 0x64, 0x08, 0x64, 0x09, 0x64, 0x0A, 0x64, 0x0B, 0x64, 0x0C, 0x64, 0x0D, 0x64, 0x0E, 0x64, 0x0F, 0x64, 0x10, 0x64, 0x11, 0x64, 0x12, 0x64, 0x13, 0x64, 0x14, 0x64, 0x15, 0x64, 0x16,\n\t0x64, 0x17, 0x64, 0x18, 0x64, 0x19, 0x64, 0x1A, 0x64, 0x1B, 0x64, 0x1C, 0x64, 0x1D, 0x64, 0x1E, 0x64, 0x1F, 0x64, 0x20, 0x64, 0x21, 0x64, 0x22, 0x64, 0x23, 0x64, 0x24, 0x64, 0x25, 0x64, 0x26,\n\t0x64, 0x27, 0x64, 0x28, 0x64, 0x29, 0x64, 0x2A, 0x64, 0x2B, 0x64, 0x2C, 0x64, 0x2D, 0x64, 0x2E, 0x64, 0x2F, 0x64, 0x30, 0x64, 0x31, 0x64, 0x32, 0x64, 0x33, 0x64, 0x34, 0x64, 0x35, 0x64, 0x36,\n\t0x64, 0x37, 0x64, 0x38, 0x64, 0x39, 0x64, 0x3A, 0x64, 0x3B, 0x64, 0x3C, 0x64, 0x3D, 0x64, 0x3E, 0x64, 0x3F, 0x64, 0x40, 0x64, 0x41, 0x64, 0x42, 0x64, 0x43, 0x64, 0x44, 0x64, 0x45, 0x64, 0x46,\n\t0x64, 0x47, 0x64, 0x48, 0x64, 0x49, 0x64, 0x4A, 0x64, 0x4B, 0x64, 0x4C, 0x64, 0x4D, 0x64, 0x4E, 0x64, 0x4F, 0x64, 0x50, 0x64, 0x51, 0x64, 0x52, 0x64, 0x53, 0x64, 0x54, 0x64, 0x55, 0x64, 0x56,\n\t0x64, 0x57, 0x64, 0x58, 0x64, 0x59, 0x64, 0x5A, 0x64, 0x5B, 0x64, 0x5C, 0x64, 0x5D, 0x64, 0x5E, 0x64, 0x5F, 0x64, 0x60, 0x64, 0x61, 0x64, 0x62, 0x64, 0x63, 0x64, 0x64, 0x64, 0x65, 0x64, 0x66,\n\t0x64, 0x67, 0x64, 0x68, 0x64, 0x69, 0x64, 0x6A, 0x64, 0x6B, 0x64, 0x6C, 0x64, 0x6D, 0x64, 0x6E, 0x64, 0x6F, 0x64, 0x70, 0x64, 0x71, 0x64, 0x72, 0x64, 0x73, 0x64, 0x74, 0x64, 0x75, 0x64, 0x76,\n\t0x64, 0x77, 0x64, 0x78, 0x64, 0x79, 0x64, 0x7A, 0x64, 0x7B, 0x64, 0x7C, 0x64, 0x7D, 0x64, 0x7E, 0x64, 0x7F, 0x64, 0x80, 0x64, 0x81, 0x64, 0x82, 0x64, 0x83, 0x64, 0x84, 0x64, 0x85, 0x64, 0x86,\n\t0x64, 0x87, 0x64, 0x88, 0x64, 0x89, 0x64, 0x8A, 0x64, 0x8B, 0x64, 0x8C, 0x64, 0x8D, 0x64, 0x8E, 0x64, 0x8F, 0x64, 0x90, 0x64, 0x91, 0x64, 0x92, 0x64, 0x93, 0x64, 0x94, 0x64, 0x95, 0x64, 0x96,\n\t0x64, 0x97, 0x64, 0x98, 0x64, 0x99, 0x64, 0x9A, 0x64, 0x9B, 0x64, 0x9C, 0x64, 0x9D, 0x64, 0x9E, 0x64, 0x9F, 0x64, 0xA0, 0x64, 0xA1, 0x64, 0xA2, 0x64, 0xA3, 0x64, 0xA4, 0x64, 0xA5, 0x64, 0xA6,\n\t0x64, 0xA7, 0x64, 0xA8, 0x64, 0xA9, 0x64, 0xAA, 0x64, 0xAB, 0x64, 0xAC, 0x64, 0xAD, 0x64, 0xAE, 0x64, 0xAF, 0x64, 0xB0, 0x64, 0xB1, 0x64, 0xB2, 0x64, 0xB3, 0x64, 0xB4, 0x64, 0xB5, 0x64, 0xB6,\n\t0x64, 0xB7, 0x64, 0xB8, 0x64, 0xB9, 0x64, 0xBA, 0x64, 0xBB, 0x64, 0xBC, 0x64, 0xBD, 0x64, 0xBE, 0x64, 0xBF, 0x64, 0xC0, 0x64, 0xC1, 0x64, 0xC2, 0x64, 0xC3, 0x64, 0xC4, 0x64, 0xC5, 0x64, 0xC6,\n\t0x64, 0xC7, 0x64, 0xC8, 0x64, 0xC9, 0x64, 0xCA, 0x64, 0xCB, 0x64, 0xCC, 0x64, 0xCD, 0x64, 0xCE, 0x64, 0xCF, 0x64, 0xD0, 0x64, 0xD1, 0x64, 0xD2, 0x64, 0xD3, 0x64, 0xD4, 0x64, 0xD5, 0x64, 0xD6,\n\t0x64, 0xD7, 0x64, 0xD8, 0x64, 0xD9, 0x64, 0xDA, 0x64, 0xDB, 0x64, 0xDC, 0x64, 0xDD, 0x64, 0xDE, 0x64, 0xDF, 0x64, 0xE0, 0x64, 0xE1, 0x64, 0xE2, 0x64, 0xE3, 0x64, 0xE4, 0x64, 0xE5, 0x64, 0xE6,\n\t0x64, 0xE7, 0x64, 0xE8, 0x64, 0xE9, 0x64, 0xEA, 0x64, 0xEB, 0x64, 0xEC, 0x64, 0xED, 0x64, 0xEE, 0x64, 0xEF, 0x64, 0xF0, 0x64, 0xF1, 0x64, 0xF2, 0x64, 0xF3, 0x64, 0xF4, 0x64, 0xF5, 0x64, 0xF6,\n\t0x64, 0xF7, 0x64, 0xF8, 0x64, 0xF9, 0x64, 0xFA, 0x64, 0xFB, 0x64, 0xFC, 0x64, 0xFD, 0x64, 0xFE, 0x64, 0xFF, 0x64, 0x00, 0x65, 0x01, 0x65, 0x02, 0x65, 0x03, 0x65, 0x04, 0x65, 0x05, 0x65, 0x06,\n\t0x65, 0x07, 0x65, 0x08, 0x65, 0x09, 0x65, 0x0A, 0x65, 0x0B, 0x65, 0x0C, 0x65, 0x0D, 0x65, 0x0E, 0x65, 0x0F, 0x65, 0x10, 0x65, 0x11, 0x65, 0x12, 0x65, 0x13, 0x65, 0x14, 0x65, 0x15, 0x65, 0x16,\n\t0x65, 0x17, 0x65, 0x18, 0x65, 0x19, 0x65, 0x1A, 0x65, 0x1B, 0x65, 0x1C, 0x65, 0x1D, 0x65, 0x1E, 0x65, 0x1F, 0x65, 0x20, 0x65, 0x21, 0x65, 0x22, 0x65, 0x23, 0x65, 0x24, 0x65, 0x25, 0x65, 0x26,\n\t0x65, 0x27, 0x65, 0x28, 0x65, 0x29, 0x65, 0x2A, 0x65, 0x2B, 0x65, 0x2C, 0x65, 0x2D, 0x65, 0x2E, 0x65, 0x2F, 0x65, 0x30, 0x65, 0x31, 0x65, 0x32, 0x65, 0x33, 0x65, 0x34, 0x65, 0x35, 0x65, 0x36,\n\t0x65, 0x37, 0x65, 0x38, 0x65, 0x39, 0x65, 0x3A, 0x65, 0x3B, 0x65, 0x3C, 0x65, 0x3D, 0x65, 0x3E, 0x65, 0x3F, 0x65, 0x40, 0x65, 0x41, 0x65, 0x42, 0x65, 0x43, 0x65, 0x44, 0x65, 0x45, 0x65, 0x46,\n\t0x65, 0x47, 0x65, 0x48, 0x65, 0x49, 0x65, 0x4A, 0x65, 0x4B, 0x65, 0x4C, 0x65, 0x4D, 0x65, 0x4E, 0x65, 0x4F, 0x65, 0x50, 0x65, 0x51, 0x65, 0x52, 0x65, 0x53, 0x65, 0x54, 0x65, 0x55, 0x65, 0x56,\n\t0x65, 0x57, 0x65, 0x58, 0x65, 0x59, 0x65, 0x5A, 0x65, 0x5B, 0x65, 0x5C, 0x65, 0x5D, 0x65, 0x5E, 0x65, 0x5F, 0x65, 0x60, 0x65, 0x61, 0x65, 0x62, 0x65, 0x63, 0x65, 0x64, 0x65, 0x65, 0x65, 0x66,\n\t0x65, 0x67, 0x65, 0x68, 0x65, 0x69, 0x65, 0x6A, 0x65, 0x6B, 0x65, 0x6C, 0x65, 0x6D, 0x65, 0x6E, 0x65, 0x6F, 0x65, 0x70, 0x65, 0x71, 0x65, 0x72, 0x65, 0x73, 0x65, 0x74, 0x65, 0x75, 0x65, 0x76,\n\t0x65, 0x77, 0x65, 0x78, 0x65, 0x79, 0x65, 0x7A, 0x65, 0x7B, 0x65, 0x7C, 0x65, 0x7D, 0x65, 0x7E, 0x65, 0x7F, 0x65, 0x80, 0x65, 0x81, 0x65, 0x82, 0x65, 0x83, 0x65, 0x84, 0x65, 0x85, 0x65, 0x86,\n\t0x65, 0x87, 0x65, 0x88, 0x65, 0x89, 0x65, 0x8A, 0x65, 0x8B, 0x65, 0x8C, 0x65, 0x8D, 0x65, 0x8E, 0x65, 0x8F, 0x65, 0x90, 0x65, 0x91, 0x65, 0x92, 0x65, 0x93, 0x65, 0x94, 0x65, 0x95, 0x65, 0x96,\n\t0x65, 0x97, 0x65, 0x98, 0x65, 0x99, 0x65, 0x9A, 0x65, 0x9B, 0x65, 0x9C, 0x65, 0x9D, 0x65, 0x9E, 0x65, 0x9F, 0x65, 0xA0, 0x65, 0xA1, 0x65, 0xA2, 0x65, 0xA3, 0x65, 0xA4, 0x65, 0xA5, 0x65, 0xA6,\n\t0x65, 0xA7, 0x65, 0xA8, 0x65, 0xA9, 0x65, 0xAA, 0x65, 0xAB, 0x65, 0xAC, 0x65, 0xAD, 0x65, 0xAE, 0x65, 0xAF, 0x65, 0xB0, 0x65, 0xB1, 0x65, 0xB2, 0x65, 0xB3, 0x65, 0xB4, 0x65, 0xB5, 0x65, 0xB6,\n\t0x65, 0xB7, 0x65, 0xB8, 0x65, 0xB9, 0x65, 0xBA, 0x65, 0xBB, 0x65, 0xBC, 0x65, 0xBD, 0x65, 0xBE, 0x65, 0xBF, 0x65, 0xC0, 0x65, 0xC1, 0x65, 0xC2, 0x65, 0xC3, 0x65, 0xC4, 0x65, 0xC5, 0x65, 0xC6,\n\t0x65, 0xC7, 0x65, 0xC8, 0x65, 0xC9, 0x65, 0xCA, 0x65, 0xCB, 0x65, 0xCC, 0x65, 0xCD, 0x65, 0xCE, 0x65, 0xCF, 0x65, 0xD0, 0x65, 0xD1, 0x65, 0xD2, 0x65, 0xD3, 0x65, 0xD4, 0x65, 0xD5, 0x65, 0xD6,\n\t0x65, 0xD7, 0x65, 0xD8, 0x65, 0xD9, 0x65, 0xDA, 0x65, 0xDB, 0x65, 0xDC, 0x65, 0xDD, 0x65, 0xDE, 0x65, 0xDF, 0x65, 0xE0, 0x65, 0xE1, 0x65, 0xE2, 0x65, 0xE3, 0x65, 0xE4, 0x65, 0xE5, 0x65, 0xE6,\n\t0x65, 0xE7, 0x65, 0xE8, 0x65, 0xE9, 0x65, 0xEA, 0x65, 0xEB, 0x65, 0xEC, 0x65, 0xED, 0x65, 0xEE, 0x65, 0xEF, 0x65, 0xF0, 0x65, 0xF1, 0x65, 0xF2, 0x65, 0xF3, 0x65, 0xF4, 0x65, 0xF5, 0x65, 0xF6,\n\t0x65, 0xF7, 0x65, 0xF8, 0x65, 0xF9, 0x65, 0xFA, 0x65, 0xFB, 0x65, 0xFC, 0x65, 0xFD, 0x65, 0xFE, 0x65, 0xFF, 0x65, 0x00, 0x66, 0x01, 0x66, 0x02, 0x66, 0x03, 0x66, 0x04, 0x66, 0x05, 0x66, 0x06,\n\t0x66, 0x07, 0x66, 0x08, 0x66, 0x09, 0x66, 0x0A, 0x66, 0x0B, 0x66, 0x0C, 0x66, 0x0D, 0x66, 0x0E, 0x66, 0x0F, 0x66, 0x10, 0x66, 0x11, 0x66, 0x12, 0x66, 0x13, 0x66, 0x14, 0x66, 0x15, 0x66, 0x16,\n\t0x66, 0x17, 0x66, 0x18, 0x66, 0x19, 0x66, 0x1A, 0x66, 0x1B, 0x66, 0x1C, 0x66, 0x1D, 0x66, 0x1E, 0x66, 0x1F, 0x66, 0x20, 0x66, 0x21, 0x66, 0x22, 0x66, 0x23, 0x66, 0x24, 0x66, 0x25, 0x66, 0x26,\n\t0x66, 0x27, 0x66, 0x28, 0x66, 0x29, 0x66, 0x2A, 0x66, 0x2B, 0x66, 0x2C, 0x66, 0x2D, 0x66, 0x2E, 0x66, 0x2F, 0x66, 0x30, 0x66, 0x31, 0x66, 0x32, 0x66, 0x33, 0x66, 0x34, 0x66, 0x35, 0x66, 0x36,\n\t0x66, 0x37, 0x66, 0x38, 0x66, 0x39, 0x66, 0x3A, 0x66, 0x3B, 0x66, 0x3C, 0x66, 0x3D, 0x66, 0x3E, 0x66, 0x3F, 0x66, 0x40, 0x66, 0x41, 0x66, 0x42, 0x66, 0x43, 0x66, 0x44, 0x66, 0x45, 0x66, 0x46,\n\t0x66, 0x47, 0x66, 0x48, 0x66, 0x49, 0x66, 0x4A, 0x66, 0x4B, 0x66, 0x4C, 0x66, 0x4D, 0x66, 0x4E, 0x66, 0x4F, 0x66, 0x50, 0x66, 0x51, 0x66, 0x52, 0x66, 0x53, 0x66, 0x54, 0x66, 0x55, 0x66, 0x56,\n\t0x66, 0x57, 0x66, 0x58, 0x66, 0x59, 0x66, 0x5A, 0x66, 0x5B, 0x66, 0x5C, 0x66, 0x5D, 0x66, 0x5E, 0x66, 0x5F, 0x66, 0x60, 0x66, 0x61, 0x66, 0x62, 0x66, 0x63, 0x66, 0x64, 0x66, 0x65, 0x66, 0x66,\n\t0x66, 0x67, 0x66, 0x68, 0x66, 0x69, 0x66, 0x6A, 0x66, 0x6B, 0x66, 0x6C, 0x66, 0x6D, 0x66, 0x6E, 0x66, 0x6F, 0x66, 0x70, 0x66, 0x71, 0x66, 0x72, 0x66, 0x73, 0x66, 0x74, 0x66, 0x75, 0x66, 0x76,\n\t0x66, 0x77, 0x66, 0x78, 0x66, 0x79, 0x66, 0x7A, 0x66, 0x7B, 0x66, 0x7C, 0x66, 0x7D, 0x66, 0x7E, 0x66, 0x7F, 0x66, 0x80, 0x66, 0x81, 0x66, 0x82, 0x66, 0x83, 0x66, 0x84, 0x66, 0x85, 0x66, 0x86,\n\t0x66, 0x87, 0x66, 0x88, 0x66, 0x89, 0x66, 0x8A, 0x66, 0x8B, 0x66, 0x8C, 0x66, 0x8D, 0x66, 0x8E, 0x66, 0x8F, 0x66, 0x90, 0x66, 0x91, 0x66, 0x92, 0x66, 0x93, 0x66, 0x94, 0x66, 0x95, 0x66, 0x96,\n\t0x66, 0x97, 0x66, 0x98, 0x66, 0x99, 0x66, 0x9A, 0x66, 0x9B, 0x66, 0x9C, 0x66, 0x9D, 0x66, 0x9E, 0x66, 0x9F, 0x66, 0xA0, 0x66, 0xA1, 0x66, 0xA2, 0x66, 0xA3, 0x66, 0xA4, 0x66, 0xA5, 0x66, 0xA6,\n\t0x66, 0xA7, 0x66, 0xA8, 0x66, 0xA9, 0x66, 0xAA, 0x66, 0xAB, 0x66, 0xAC, 0x66, 0xAD, 0x66, 0xAE, 0x66, 0xAF, 0x66, 0xB0, 0x66, 0xB1, 0x66, 0xB2, 0x66, 0xB3, 0x66, 0xB4, 0x66, 0xB5, 0x66, 0xB6,\n\t0x66, 0xB7, 0x66, 0xB8, 0x66, 0xB9, 0x66, 0xBA, 0x66, 0xBB, 0x66, 0xBC, 0x66, 0xBD, 0x66, 0xBE, 0x66, 0xBF, 0x66, 0xC0, 0x66, 0xC1, 0x66, 0xC2, 0x66, 0xC3, 0x66, 0xC4, 0x66, 0xC5, 0x66, 0xC6,\n\t0x66, 0xC7, 0x66, 0xC8, 0x66, 0xC9, 0x66, 0xCA, 0x66, 0xCB, 0x66, 0xCC, 0x66, 0xCD, 0x66, 0xCE, 0x66, 0xCF, 0x66, 0xD0, 0x66, 0xD1, 0x66, 0xD2, 0x66, 0xD3, 0x66, 0xD4, 0x66, 0xD5, 0x66, 0xD6,\n\t0x66, 0xD7, 0x66, 0xD8, 0x66, 0xD9, 0x66, 0xDA, 0x66, 0xDB, 0x66, 0xDC, 0x66, 0xDD, 0x66, 0xDE, 0x66, 0xDF, 0x66, 0xE0, 0x66, 0xE1, 0x66, 0xE2, 0x66, 0xE3, 0x66, 0xE4, 0x66, 0xE5, 0x66, 0xE6,\n\t0x66, 0xE7, 0x66, 0xE8, 0x66, 0xE9, 0x66, 0xEA, 0x66, 0xEB, 0x66, 0xEC, 0x66, 0xED, 0x66, 0xEE, 0x66, 0xEF, 0x66, 0xF0, 0x66, 0xF1, 0x66, 0xF2, 0x66, 0xF3, 0x66, 0xF4, 0x66, 0xF5, 0x66, 0xF6,\n\t0x66, 0xF7, 0x66, 0xF8, 0x66, 0xF9, 0x66, 0xFA, 0x66, 0xFB, 0x66, 0xFC, 0x66, 0xFD, 0x66, 0xFE, 0x66, 0xFF, 0x66, 0x00, 0x67, 0x01, 0x67, 0x02, 0x67, 0x03, 0x67, 0x04, 0x67, 0x05, 0x67, 0x06,\n\t0x67, 0x07, 0x67, 0x08, 0x67, 0x09, 0x67, 0x0A, 0x67, 0x0B, 0x67, 0x0C, 0x67, 0x0D, 0x67, 0x0E, 0x67, 0x0F, 0x67, 0x10, 0x67, 0x11, 0x67, 0x12, 0x67, 0x13, 0x67, 0x14, 0x67, 0x15, 0x67, 0x16,\n\t0x67, 0x17, 0x67, 0x18, 0x67, 0x19, 0x67, 0x1A, 0x67, 0x1B, 0x67, 0x1C, 0x67, 0x1D, 0x67, 0x1E, 0x67, 0x1F, 0x67, 0x20, 0x67, 0x21, 0x67, 0x22, 0x67, 0x23, 0x67, 0x24, 0x67, 0x25, 0x67, 0x26,\n\t0x67, 0x27, 0x67, 0x28, 0x67, 0x29, 0x67, 0x2A, 0x67, 0x2B, 0x67, 0x2C, 0x67, 0x2D, 0x67, 0x2E, 0x67, 0x2F, 0x67, 0x30, 0x67, 0x31, 0x67, 0x32, 0x67, 0x33, 0x67, 0x34, 0x67, 0x35, 0x67, 0x36,\n\t0x67, 0x37, 0x67, 0x38, 0x67, 0x39, 0x67, 0x3A, 0x67, 0x3B, 0x67, 0x3C, 0x67, 0x3D, 0x67, 0x3E, 0x67, 0x3F, 0x67, 0x40, 0x67, 0x41, 0x67, 0x42, 0x67, 0x43, 0x67, 0x44, 0x67, 0x45, 0x67, 0x46,\n\t0x67, 0x47, 0x67, 0x48, 0x67, 0x49, 0x67, 0x4A, 0x67, 0x4B, 0x67, 0x4C, 0x67, 0x4D, 0x67, 0x4E, 0x67, 0x4F, 0x67, 0x50, 0x67, 0x51, 0x67, 0x52, 0x67, 0x53, 0x67, 0x54, 0x67, 0x55, 0x67, 0x56,\n\t0x67, 0x57, 0x67, 0x58, 0x67, 0x59, 0x67, 0x5A, 0x67, 0x5B, 0x67, 0x5C, 0x67, 0x5D, 0x67, 0x5E, 0x67, 0x5F, 0x67, 0x60, 0x67, 0x61, 0x67, 0x62, 0x67, 0x63, 0x67, 0x64, 0x67, 0x65, 0x67, 0x66,\n\t0x67, 0x67, 0x67, 0x68, 0x67, 0x69, 0x67, 0x6A, 0x67, 0x6B, 0x67, 0x6C, 0x67, 0x6D, 0x67, 0x6E, 0x67, 0x6F, 0x67, 0x70, 0x67, 0x71, 0x67, 0x72, 0x67, 0x73, 0x67, 0x74, 0x67, 0x75, 0x67, 0x76,\n\t0x67, 0x77, 0x67, 0x78, 0x67, 0x79, 0x67, 0x7A, 0x67, 0x7B, 0x67, 0x7C, 0x67, 0x7D, 0x67, 0x7E, 0x67, 0x7F, 0x67, 0x80, 0x67, 0x81, 0x67, 0x82, 0x67, 0x83, 0x67, 0x84, 0x67, 0x85, 0x67, 0x86,\n\t0x67, 0x87, 0x67, 0x88, 0x67, 0x89, 0x67, 0x8A, 0x67, 0x8B, 0x67, 0x8C, 0x67, 0x8D, 0x67, 0x8E, 0x67, 0x8F, 0x67, 0x90, 0x67, 0x91, 0x67, 0x92, 0x67, 0x93, 0x67, 0x94, 0x67, 0x95, 0x67, 0x96,\n\t0x67, 0x97, 0x67, 0x98, 0x67, 0x99, 0x67, 0x9A, 0x67, 0x9B, 0x67, 0x9C, 0x67, 0x9D, 0x67, 0x9E, 0x67, 0x9F, 0x67, 0xA0, 0x67, 0xA1, 0x67, 0xA2, 0x67, 0xA3, 0x67, 0xA4, 0x67, 0xA5, 0x67, 0xA6,\n\t0x67, 0xA7, 0x67, 0xA8, 0x67, 0xA9, 0x67, 0xAA, 0x67, 0xAB, 0x67, 0xAC, 0x67, 0xAD, 0x67, 0xAE, 0x67, 0xAF, 0x67, 0xB0, 0x67, 0xB1, 0x67, 0xB2, 0x67, 0xB3, 0x67, 0xB4, 0x67, 0xB5, 0x67, 0xB6,\n\t0x67, 0xB7, 0x67, 0xB8, 0x67, 0xB9, 0x67, 0xBA, 0x67, 0xBB, 0x67, 0xBC, 0x67, 0xBD, 0x67, 0xBE, 0x67, 0xBF, 0x67, 0xC0, 0x67, 0xC1, 0x67, 0xC2, 0x67, 0xC3, 0x67, 0xC4, 0x67, 0xC5, 0x67, 0xC6,\n\t0x67, 0xC7, 0x67, 0xC8, 0x67, 0xC9, 0x67, 0xCA, 0x67, 0xCB, 0x67, 0xCC, 0x67, 0xCD, 0x67, 0xCE, 0x67, 0xCF, 0x67, 0xD0, 0x67, 0xD1, 0x67, 0xD2, 0x67, 0xD3, 0x67, 0xD4, 0x67, 0xD5, 0x67, 0xD6,\n\t0x67, 0xD7, 0x67, 0xD8, 0x67, 0xD9, 0x67, 0xDA, 0x67, 0xDB, 0x67, 0xDC, 0x67, 0xDD, 0x67, 0xDE, 0x67, 0xDF, 0x67, 0xE0, 0x67, 0xE1, 0x67, 0xE2, 0x67, 0xE3, 0x67, 0xE4, 0x67, 0xE5, 0x67, 0xE6,\n\t0x67, 0xE7, 0x67, 0xE8, 0x67, 0xE9, 0x67, 0xEA, 0x67, 0xEB, 0x67, 0xEC, 0x67, 0xED, 0x67, 0xEE, 0x67, 0xEF, 0x67, 0xF0, 0x67, 0xF1, 0x67, 0xF2, 0x67, 0xF3, 0x67, 0xF4, 0x67, 0xF5, 0x67, 0xF6,\n\t0x67, 0xF7, 0x67, 0xF8, 0x67, 0xF9, 0x67, 0xFA, 0x67, 0xFB, 0x67, 0xFC, 0x67, 0xFD, 0x67, 0xFE, 0x67, 0xFF, 0x67, 0x00, 0x68, 0x01, 0x68, 0x02, 0x68, 0x03, 0x68, 0x04, 0x68, 0x05, 0x68, 0x06,\n\t0x68, 0x07, 0x68, 0x08, 0x68, 0x09, 0x68, 0x0A, 0x68, 0x0B, 0x68, 0x0C, 0x68, 0x0D, 0x68, 0x0E, 0x68, 0x0F, 0x68, 0x10, 0x68, 0x11, 0x68, 0x12, 0x68, 0x13, 0x68, 0x14, 0x68, 0x15, 0x68, 0x16,\n\t0x68, 0x17, 0x68, 0x18, 0x68, 0x19, 0x68, 0x1A, 0x68, 0x1B, 0x68, 0x1C, 0x68, 0x1D, 0x68, 0x1E, 0x68, 0x1F, 0x68, 0x20, 0x68, 0x21, 0x68, 0x22, 0x68, 0x23, 0x68, 0x24, 0x68, 0x25, 0x68, 0x26,\n\t0x68, 0x27, 0x68, 0x28, 0x68, 0x29, 0x68, 0x2A, 0x68, 0x2B, 0x68, 0x2C, 0x68, 0x2D, 0x68, 0x2E, 0x68, 0x2F, 0x68, 0x30, 0x68, 0x31, 0x68, 0x32, 0x68, 0x33, 0x68, 0x34, 0x68, 0x35, 0x68, 0x36,\n\t0x68, 0x37, 0x68, 0x38, 0x68, 0x39, 0x68, 0x3A, 0x68, 0x3B, 0x68, 0x3C, 0x68, 0x3D, 0x68, 0x3E, 0x68, 0x3F, 0x68, 0x40, 0x68, 0x41, 0x68, 0x42, 0x68, 0x43, 0x68, 0x44, 0x68, 0x45, 0x68, 0x46,\n\t0x68, 0x47, 0x68, 0x48, 0x68, 0x49, 0x68, 0x4A, 0x68, 0x4B, 0x68, 0x4C, 0x68, 0x4D, 0x68, 0x4E, 0x68, 0x4F, 0x68, 0x50, 0x68, 0x51, 0x68, 0x52, 0x68, 0x53, 0x68, 0x54, 0x68, 0x55, 0x68, 0x56,\n\t0x68, 0x57, 0x68, 0x58, 0x68, 0x59, 0x68, 0x5A, 0x68, 0x5B, 0x68, 0x5C, 0x68, 0x5D, 0x68, 0x5E, 0x68, 0x5F, 0x68, 0x60, 0x68, 0x61, 0x68, 0x62, 0x68, 0x63, 0x68, 0x64, 0x68, 0x65, 0x68, 0x66,\n\t0x68, 0x67, 0x68, 0x68, 0x68, 0x69, 0x68, 0x6A, 0x68, 0x6B, 0x68, 0x6C, 0x68, 0x6D, 0x68, 0x6E, 0x68, 0x6F, 0x68, 0x70, 0x68, 0x71, 0x68, 0x72, 0x68, 0x73, 0x68, 0x74, 0x68, 0x75, 0x68, 0x76,\n\t0x68, 0x77, 0x68, 0x78, 0x68, 0x79, 0x68, 0x7A, 0x68, 0x7B, 0x68, 0x7C, 0x68, 0x7D, 0x68, 0x7E, 0x68, 0x7F, 0x68, 0x80, 0x68, 0x81, 0x68, 0x82, 0x68, 0x83, 0x68, 0x84, 0x68, 0x85, 0x68, 0x86,\n\t0x68, 0x87, 0x68, 0x88, 0x68, 0x89, 0x68, 0x8A, 0x68, 0x8B, 0x68, 0x8C, 0x68, 0x8D, 0x68, 0x8E, 0x68, 0x8F, 0x68, 0x90, 0x68, 0x91, 0x68, 0x92, 0x68, 0x93, 0x68, 0x94, 0x68, 0x95, 0x68, 0x96,\n\t0x68, 0x97, 0x68, 0x98, 0x68, 0x99, 0x68, 0x9A, 0x68, 0x9B, 0x68, 0x9C, 0x68, 0x9D, 0x68, 0x9E, 0x68, 0x9F, 0x68, 0xA0, 0x68, 0xA1, 0x68, 0xA2, 0x68, 0xA3, 0x68, 0xA4, 0x68, 0xA5, 0x68, 0xA6,\n\t0x68, 0xA7, 0x68, 0xA8, 0x68, 0xA9, 0x68, 0xAA, 0x68, 0xAB, 0x68, 0xAC, 0x68, 0xAD, 0x68, 0xAE, 0x68, 0xAF, 0x68, 0xB0, 0x68, 0xB1, 0x68, 0xB2, 0x68, 0xB3, 0x68, 0xB4, 0x68, 0xB5, 0x68, 0xB6,\n\t0x68, 0xB7, 0x68, 0xB8, 0x68, 0xB9, 0x68, 0xBA, 0x68, 0xBB, 0x68, 0xBC, 0x68, 0xBD, 0x68, 0xBE, 0x68, 0xBF, 0x68, 0xC0, 0x68, 0xC1, 0x68, 0xC2, 0x68, 0xC3, 0x68, 0xC4, 0x68, 0xC5, 0x68, 0xC6,\n\t0x68, 0xC7, 0x68, 0xC8, 0x68, 0xC9, 0x68, 0xCA, 0x68, 0xCB, 0x68, 0xCC, 0x68, 0xCD, 0x68, 0xCE, 0x68, 0xCF, 0x68, 0xD0, 0x68, 0xD1, 0x68, 0xD2, 0x68, 0xD3, 0x68, 0xD4, 0x68, 0xD5, 0x68, 0xD6,\n\t0x68, 0xD7, 0x68, 0xD8, 0x68, 0xD9, 0x68, 0xDA, 0x68, 0xDB, 0x68, 0xDC, 0x68, 0xDD, 0x68, 0xDE, 0x68, 0xDF, 0x68, 0xE0, 0x68, 0xE1, 0x68, 0xE2, 0x68, 0xE3, 0x68, 0xE4, 0x68, 0xE5, 0x68, 0xE6,\n\t0x68, 0xE7, 0x68, 0xE8, 0x68, 0xE9, 0x68, 0xEA, 0x68, 0xEB, 0x68, 0xEC, 0x68, 0xED, 0x68, 0xEE, 0x68, 0xEF, 0x68, 0xF0, 0x68, 0xF1, 0x68, 0xF2, 0x68, 0xF3, 0x68, 0xF4, 0x68, 0xF5, 0x68, 0xF6,\n\t0x68, 0xF7, 0x68, 0xF8, 0x68, 0xF9, 0x68, 0xFA, 0x68, 0xFB, 0x68, 0xFC, 0x68, 0xFD, 0x68, 0xFE, 0x68, 0xFF, 0x68, 0x00, 0x69, 0x01, 0x69, 0x02, 0x69, 0x03, 0x69, 0x04, 0x69, 0x05, 0x69, 0x06,\n\t0x69, 0x07, 0x69, 0x08, 0x69, 0x09, 0x69, 0x0A, 0x69, 0x0B, 0x69, 0x0C, 0x69, 0x0D, 0x69, 0x0E, 0x69, 0x0F, 0x69, 0x10, 0x69, 0x11, 0x69, 0x12, 0x69, 0x13, 0x69, 0x14, 0x69, 0x15, 0x69, 0x16,\n\t0x69, 0x17, 0x69, 0x18, 0x69, 0x19, 0x69, 0x1A, 0x69, 0x1B, 0x69, 0x1C, 0x69, 0x1D, 0x69, 0x1E, 0x69, 0x1F, 0x69, 0x20, 0x69, 0x21, 0x69, 0x22, 0x69, 0x23, 0x69, 0x24, 0x69, 0x25, 0x69, 0x26,\n\t0x69, 0x27, 0x69, 0x28, 0x69, 0x29, 0x69, 0x2A, 0x69, 0x2B, 0x69, 0x2C, 0x69, 0x2D, 0x69, 0x2E, 0x69, 0x2F, 0x69, 0x30, 0x69, 0x31, 0x69, 0x32, 0x69, 0x33, 0x69, 0x34, 0x69, 0x35, 0x69, 0x36,\n\t0x69, 0x37, 0x69, 0x38, 0x69, 0x39, 0x69, 0x3A, 0x69, 0x3B, 0x69, 0x3C, 0x69, 0x3D, 0x69, 0x3E, 0x69, 0x3F, 0x69, 0x40, 0x69, 0x41, 0x69, 0x42, 0x69, 0x43, 0x69, 0x44, 0x69, 0x45, 0x69, 0x46,\n\t0x69, 0x47, 0x69, 0x48, 0x69, 0x49, 0x69, 0x4A, 0x69, 0x4B, 0x69, 0x4C, 0x69, 0x4D, 0x69, 0x4E, 0x69, 0x4F, 0x69, 0x50, 0x69, 0x51, 0x69, 0x52, 0x69, 0x53, 0x69, 0x54, 0x69, 0x55, 0x69, 0x56,\n\t0x69, 0x57, 0x69, 0x58, 0x69, 0x59, 0x69, 0x5A, 0x69, 0x5B, 0x69, 0x5C, 0x69, 0x5D, 0x69, 0x5E, 0x69, 0x5F, 0x69, 0x60, 0x69, 0x61, 0x69, 0x62, 0x69, 0x63, 0x69, 0x64, 0x69, 0x65, 0x69, 0x66,\n\t0x69, 0x67, 0x69, 0x68, 0x69, 0x69, 0x69, 0x6A, 0x69, 0x6B, 0x69, 0x6C, 0x69, 0x6D, 0x69, 0x6E, 0x69, 0x6F, 0x69, 0x70, 0x69, 0x71, 0x69, 0x72, 0x69, 0x73, 0x69, 0x74, 0x69, 0x75, 0x69, 0x76,\n\t0x69, 0x77, 0x69, 0x78, 0x69, 0x79, 0x69, 0x7A, 0x69, 0x7B, 0x69, 0x7C, 0x69, 0x7D, 0x69, 0x7E, 0x69, 0x7F, 0x69, 0x80, 0x69, 0x81, 0x69, 0x82, 0x69, 0x83, 0x69, 0x84, 0x69, 0x85, 0x69, 0x86,\n\t0x69, 0x87, 0x69, 0x88, 0x69, 0x89, 0x69, 0x8A, 0x69, 0x8B, 0x69, 0x8C, 0x69, 0x8D, 0x69, 0x8E, 0x69, 0x8F, 0x69, 0x90, 0x69, 0x91, 0x69, 0x92, 0x69, 0x93, 0x69, 0x94, 0x69, 0x95, 0x69, 0x96,\n\t0x69, 0x97, 0x69, 0x98, 0x69, 0x99, 0x69, 0x9A, 0x69, 0x9B, 0x69, 0x9C, 0x69, 0x9D, 0x69, 0x9E, 0x69, 0x9F, 0x69, 0xA0, 0x69, 0xA1, 0x69, 0xA2, 0x69, 0xA3, 0x69, 0xA4, 0x69, 0xA5, 0x69, 0xA6,\n\t0x69, 0xA7, 0x69, 0xA8, 0x69, 0xA9, 0x69, 0xAA, 0x69, 0xAB, 0x69, 0xAC, 0x69, 0xAD, 0x69, 0xAE, 0x69, 0xAF, 0x69, 0xB0, 0x69, 0xB1, 0x69, 0xB2, 0x69, 0xB3, 0x69, 0xB4, 0x69, 0xB5, 0x69, 0xB6,\n\t0x69, 0xB7, 0x69, 0xB8, 0x69, 0xB9, 0x69, 0xBA, 0x69, 0xBB, 0x69, 0xBC, 0x69, 0xBD, 0x69, 0xBE, 0x69, 0xBF, 0x69, 0xC0, 0x69, 0xC1, 0x69, 0xC2, 0x69, 0xC3, 0x69, 0xC4, 0x69, 0xC5, 0x69, 0xC6,\n\t0x69, 0xC7, 0x69, 0xC8, 0x69, 0xC9, 0x69, 0xCA, 0x69, 0xCB, 0x69, 0xCC, 0x69, 0xCD, 0x69, 0xCE, 0x69, 0xCF, 0x69, 0xD0, 0x69, 0xD1, 0x69, 0xD2, 0x69, 0xD3, 0x69, 0xD4, 0x69, 0xD5, 0x69, 0xD6,\n\t0x69, 0xD7, 0x69, 0xD8, 0x69, 0xD9, 0x69, 0xDA, 0x69, 0xDB, 0x69, 0xDC, 0x69, 0xDD, 0x69, 0xDE, 0x69, 0xDF, 0x69, 0xE0, 0x69, 0xE1, 0x69, 0xE2, 0x69, 0xE3, 0x69, 0xE4, 0x69, 0xE5, 0x69, 0xE6,\n\t0x69, 0xE7, 0x69, 0xE8, 0x69, 0xE9, 0x69, 0xEA, 0x69, 0xEB, 0x69, 0xEC, 0x69, 0xED, 0x69, 0xEE, 0x69, 0xEF, 0x69, 0xF0, 0x69, 0xF1, 0x69, 0xF2, 0x69, 0xF3, 0x69, 0xF4, 0x69, 0xF5, 0x69, 0xF6,\n\t0x69, 0xF7, 0x69, 0xF8, 0x69, 0xF9, 0x69, 0xFA, 0x69, 0xFB, 0x69, 0xFC, 0x69, 0xFD, 0x69, 0xFE, 0x69, 0xFF, 0x69, 0x00, 0x6A, 0x01, 0x6A, 0x02, 0x6A, 0x03, 0x6A, 0x04, 0x6A, 0x05, 0x6A, 0x06,\n\t0x6A, 0x07, 0x6A, 0x08, 0x6A, 0x09, 0x6A, 0x0A, 0x6A, 0x0B, 0x6A, 0x0C, 0x6A, 0x0D, 0x6A, 0x0E, 0x6A, 0x0F, 0x6A, 0x10, 0x6A, 0x11, 0x6A, 0x12, 0x6A, 0x13, 0x6A, 0x14, 0x6A, 0x15, 0x6A, 0x16,\n\t0x6A, 0x17, 0x6A, 0x18, 0x6A, 0x19, 0x6A, 0x1A, 0x6A, 0x1B, 0x6A, 0x1C, 0x6A, 0x1D, 0x6A, 0x1E, 0x6A, 0x1F, 0x6A, 0x20, 0x6A, 0x21, 0x6A, 0x22, 0x6A, 0x23, 0x6A, 0x24, 0x6A, 0x25, 0x6A, 0x26,\n\t0x6A, 0x27, 0x6A, 0x28, 0x6A, 0x29, 0x6A, 0x2A, 0x6A, 0x2B, 0x6A, 0x2C, 0x6A, 0x2D, 0x6A, 0x2E, 0x6A, 0x2F, 0x6A, 0x30, 0x6A, 0x31, 0x6A, 0x32, 0x6A, 0x33, 0x6A, 0x34, 0x6A, 0x35, 0x6A, 0x36,\n\t0x6A, 0x37, 0x6A, 0x38, 0x6A, 0x39, 0x6A, 0x3A, 0x6A, 0x3B, 0x6A, 0x3C, 0x6A, 0x3D, 0x6A, 0x3E, 0x6A, 0x3F, 0x6A, 0x40, 0x6A, 0x41, 0x6A, 0x42, 0x6A, 0x43, 0x6A, 0x44, 0x6A, 0x45, 0x6A, 0x46,\n\t0x6A, 0x47, 0x6A, 0x48, 0x6A, 0x49, 0x6A, 0x4A, 0x6A, 0x4B, 0x6A, 0x4C, 0x6A, 0x4D, 0x6A, 0x4E, 0x6A, 0x4F, 0x6A, 0x50, 0x6A, 0x51, 0x6A, 0x52, 0x6A, 0x53, 0x6A, 0x54, 0x6A, 0x55, 0x6A, 0x56,\n\t0x6A, 0x57, 0x6A, 0x58, 0x6A, 0x59, 0x6A, 0x5A, 0x6A, 0x5B, 0x6A, 0x5C, 0x6A, 0x5D, 0x6A, 0x5E, 0x6A, 0x5F, 0x6A, 0x60, 0x6A, 0x61, 0x6A, 0x62, 0x6A, 0x63, 0x6A, 0x64, 0x6A, 0x65, 0x6A, 0x66,\n\t0x6A, 0x67, 0x6A, 0x68, 0x6A, 0x69, 0x6A, 0x6A, 0x6A, 0x6B, 0x6A, 0x6C, 0x6A, 0x6D, 0x6A, 0x6E, 0x6A, 0x6F, 0x6A, 0x70, 0x6A, 0x71, 0x6A, 0x72, 0x6A, 0x73, 0x6A, 0x74, 0x6A, 0x75, 0x6A, 0x76,\n\t0x6A, 0x77, 0x6A, 0x78, 0x6A, 0x79, 0x6A, 0x7A, 0x6A, 0x7B, 0x6A, 0x7C, 0x6A, 0x7D, 0x6A, 0x7E, 0x6A, 0x7F, 0x6A, 0x80, 0x6A, 0x81, 0x6A, 0x82, 0x6A, 0x83, 0x6A, 0x84, 0x6A, 0x85, 0x6A, 0x86,\n\t0x6A, 0x87, 0x6A, 0x88, 0x6A, 0x89, 0x6A, 0x8A, 0x6A, 0x8B, 0x6A, 0x8C, 0x6A, 0x8D, 0x6A, 0x8E, 0x6A, 0x8F, 0x6A, 0x90, 0x6A, 0x91, 0x6A, 0x92, 0x6A, 0x93, 0x6A, 0x94, 0x6A, 0x95, 0x6A, 0x96,\n\t0x6A, 0x97, 0x6A, 0x98, 0x6A, 0x99, 0x6A, 0x9A, 0x6A, 0x9B, 0x6A, 0x9C, 0x6A, 0x9D, 0x6A, 0x9E, 0x6A, 0x9F, 0x6A, 0xA0, 0x6A, 0xA1, 0x6A, 0xA2, 0x6A, 0xA3, 0x6A, 0xA4, 0x6A, 0xA5, 0x6A, 0xA6,\n\t0x6A, 0xA7, 0x6A, 0xA8, 0x6A, 0xA9, 0x6A, 0xAA, 0x6A, 0xAB, 0x6A, 0xAC, 0x6A, 0xAD, 0x6A, 0xAE, 0x6A, 0xAF, 0x6A, 0xB0, 0x6A, 0xB1, 0x6A, 0xB2, 0x6A, 0xB3, 0x6A, 0xB4, 0x6A, 0xB5, 0x6A, 0xB6,\n\t0x6A, 0xB7, 0x6A, 0xB8, 0x6A, 0xB9, 0x6A, 0xBA, 0x6A, 0xBB, 0x6A, 0xBC, 0x6A, 0xBD, 0x6A, 0xBE, 0x6A, 0xBF, 0x6A, 0xC0, 0x6A, 0xC1, 0x6A, 0xC2, 0x6A, 0xC3, 0x6A, 0xC4, 0x6A, 0xC5, 0x6A, 0xC6,\n\t0x6A, 0xC7, 0x6A, 0xC8, 0x6A, 0xC9, 0x6A, 0xCA, 0x6A, 0xCB, 0x6A, 0xCC, 0x6A, 0xCD, 0x6A, 0xCE, 0x6A, 0xCF, 0x6A, 0xD0, 0x6A, 0xD1, 0x6A, 0xD2, 0x6A, 0xD3, 0x6A, 0xD4, 0x6A, 0xD5, 0x6A, 0xD6,\n\t0x6A, 0xD7, 0x6A, 0xD8, 0x6A, 0xD9, 0x6A, 0xDA, 0x6A, 0xDB, 0x6A, 0xDC, 0x6A, 0xDD, 0x6A, 0xDE, 0x6A, 0xDF, 0x6A, 0xE0, 0x6A, 0xE1, 0x6A, 0xE2, 0x6A, 0xE3, 0x6A, 0xE4, 0x6A, 0xE5, 0x6A, 0xE6,\n\t0x6A, 0xE7, 0x6A, 0xE8, 0x6A, 0xE9, 0x6A, 0xEA, 0x6A, 0xEB, 0x6A, 0xEC, 0x6A, 0xED, 0x6A, 0xEE, 0x6A, 0xEF, 0x6A, 0xF0, 0x6A, 0xF1, 0x6A, 0xF2, 0x6A, 0xF3, 0x6A, 0xF4, 0x6A, 0xF5, 0x6A, 0xF6,\n\t0x6A, 0xF7, 0x6A, 0xF8, 0x6A, 0xF9, 0x6A, 0xFA, 0x6A, 0xFB, 0x6A, 0xFC, 0x6A, 0xFD, 0x6A, 0xFE, 0x6A, 0xFF, 0x6A, 0x00, 0x6B, 0x01, 0x6B, 0x02, 0x6B, 0x03, 0x6B, 0x04, 0x6B, 0x05, 0x6B, 0x06,\n\t0x6B, 0x07, 0x6B, 0x08, 0x6B, 0x09, 0x6B, 0x0A, 0x6B, 0x0B, 0x6B, 0x0C, 0x6B, 0x0D, 0x6B, 0x0E, 0x6B, 0x0F, 0x6B, 0x10, 0x6B, 0x11, 0x6B, 0x12, 0x6B, 0x13, 0x6B, 0x14, 0x6B, 0x15, 0x6B, 0x16,\n\t0x6B, 0x17, 0x6B, 0x18, 0x6B, 0x19, 0x6B, 0x1A, 0x6B, 0x1B, 0x6B, 0x1C, 0x6B, 0x1D, 0x6B, 0x1E, 0x6B, 0x1F, 0x6B, 0x20, 0x6B, 0x21, 0x6B, 0x22, 0x6B, 0x23, 0x6B, 0x24, 0x6B, 0x25, 0x6B, 0x26,\n\t0x6B, 0x27, 0x6B, 0x28, 0x6B, 0x29, 0x6B, 0x2A, 0x6B, 0x2B, 0x6B, 0x2C, 0x6B, 0x2D, 0x6B, 0x2E, 0x6B, 0x2F, 0x6B, 0x30, 0x6B, 0x31, 0x6B, 0x32, 0x6B, 0x33, 0x6B, 0x34, 0x6B, 0x35, 0x6B, 0x36,\n\t0x6B, 0x37, 0x6B, 0x38, 0x6B, 0x39, 0x6B, 0x3A, 0x6B, 0x3B, 0x6B, 0x3C, 0x6B, 0x3D, 0x6B, 0x3E, 0x6B, 0x3F, 0x6B, 0x40, 0x6B, 0x41, 0x6B, 0x42, 0x6B, 0x43, 0x6B, 0x44, 0x6B, 0x45, 0x6B, 0x46,\n\t0x6B, 0x47, 0x6B, 0x48, 0x6B, 0x49, 0x6B, 0x4A, 0x6B, 0x4B, 0x6B, 0x4C, 0x6B, 0x4D, 0x6B, 0x4E, 0x6B, 0x4F, 0x6B, 0x50, 0x6B, 0x51, 0x6B, 0x52, 0x6B, 0x53, 0x6B, 0x54, 0x6B, 0x55, 0x6B, 0x56,\n\t0x6B, 0x57, 0x6B, 0x58, 0x6B, 0x59, 0x6B, 0x5A, 0x6B, 0x5B, 0x6B, 0x5C, 0x6B, 0x5D, 0x6B, 0x5E, 0x6B, 0x5F, 0x6B, 0x60, 0x6B, 0x61, 0x6B, 0x62, 0x6B, 0x63, 0x6B, 0x64, 0x6B, 0x65, 0x6B, 0x66,\n\t0x6B, 0x67, 0x6B, 0x68, 0x6B, 0x69, 0x6B, 0x6A, 0x6B, 0x6B, 0x6B, 0x6C, 0x6B, 0x6D, 0x6B, 0x6E, 0x6B, 0x6F, 0x6B, 0x70, 0x6B, 0x71, 0x6B, 0x72, 0x6B, 0x73, 0x6B, 0x74, 0x6B, 0x75, 0x6B, 0x76,\n\t0x6B, 0x77, 0x6B, 0x78, 0x6B, 0x79, 0x6B, 0x7A, 0x6B, 0x7B, 0x6B, 0x7C, 0x6B, 0x7D, 0x6B, 0x7E, 0x6B, 0x7F, 0x6B, 0x80, 0x6B, 0x81, 0x6B, 0x82, 0x6B, 0x83, 0x6B, 0x84, 0x6B, 0x85, 0x6B, 0x86,\n\t0x6B, 0x87, 0x6B, 0x88, 0x6B, 0x89, 0x6B, 0x8A, 0x6B, 0x8B, 0x6B, 0x8C, 0x6B, 0x8D, 0x6B, 0x8E, 0x6B, 0x8F, 0x6B, 0x90, 0x6B, 0x91, 0x6B, 0x92, 0x6B, 0x93, 0x6B, 0x94, 0x6B, 0x95, 0x6B, 0x96,\n\t0x6B, 0x97, 0x6B, 0x98, 0x6B, 0x99, 0x6B, 0x9A, 0x6B, 0x9B, 0x6B, 0x9C, 0x6B, 0x9D, 0x6B, 0x9E, 0x6B, 0x9F, 0x6B, 0xA0, 0x6B, 0xA1, 0x6B, 0xA2, 0x6B, 0xA3, 0x6B, 0xA4, 0x6B, 0xA5, 0x6B, 0xA6,\n\t0x6B, 0xA7, 0x6B, 0xA8, 0x6B, 0xA9, 0x6B, 0xAA, 0x6B, 0xAB, 0x6B, 0xAC, 0x6B, 0xAD, 0x6B, 0xAE, 0x6B, 0xAF, 0x6B, 0xB0, 0x6B, 0xB1, 0x6B, 0xB2, 0x6B, 0xB3, 0x6B, 0xB4, 0x6B, 0xB5, 0x6B, 0xB6,\n\t0x6B, 0xB7, 0x6B, 0xB8, 0x6B, 0xB9, 0x6B, 0xBA, 0x6B, 0xBB, 0x6B, 0xBC, 0x6B, 0xBD, 0x6B, 0xBE, 0x6B, 0xBF, 0x6B, 0xC0, 0x6B, 0xC1, 0x6B, 0xC2, 0x6B, 0xC3, 0x6B, 0xC4, 0x6B, 0xC5, 0x6B, 0xC6,\n\t0x6B, 0xC7, 0x6B, 0xC8, 0x6B, 0xC9, 0x6B, 0xCA, 0x6B, 0xCB, 0x6B, 0xCC, 0x6B, 0xCD, 0x6B, 0xCE, 0x6B, 0xCF, 0x6B, 0xD0, 0x6B, 0xD1, 0x6B, 0xD2, 0x6B, 0xD3, 0x6B, 0xD4, 0x6B, 0xD5, 0x6B, 0xD6,\n\t0x6B, 0xD7, 0x6B, 0xD8, 0x6B, 0xD9, 0x6B, 0xDA, 0x6B, 0xDB, 0x6B, 0xDC, 0x6B, 0xDD, 0x6B, 0xDE, 0x6B, 0xDF, 0x6B, 0xE0, 0x6B, 0xE1, 0x6B, 0xE2, 0x6B, 0xE3, 0x6B, 0xE4, 0x6B, 0xE5, 0x6B, 0xE6,\n\t0x6B, 0xE7, 0x6B, 0xE8, 0x6B, 0xE9, 0x6B, 0xEA, 0x6B, 0xEB, 0x6B, 0xEC, 0x6B, 0xED, 0x6B, 0xEE, 0x6B, 0xEF, 0x6B, 0xF0, 0x6B, 0xF1, 0x6B, 0xF2, 0x6B, 0xF3, 0x6B, 0xF4, 0x6B, 0xF5, 0x6B, 0xF6,\n\t0x6B, 0xF7, 0x6B, 0xF8, 0x6B, 0xF9, 0x6B, 0xFA, 0x6B, 0xFB, 0x6B, 0xFC, 0x6B, 0xFD, 0x6B, 0xFE, 0x6B, 0xFF, 0x6B, 0x00, 0x6C, 0x01, 0x6C, 0x02, 0x6C, 0x03, 0x6C, 0x04, 0x6C, 0x05, 0x6C, 0x06,\n\t0x6C, 0x07, 0x6C, 0x08, 0x6C, 0x09, 0x6C, 0x0A, 0x6C, 0x0B, 0x6C, 0x0C, 0x6C, 0x0D, 0x6C, 0x0E, 0x6C, 0x0F, 0x6C, 0x10, 0x6C, 0x11, 0x6C, 0x12, 0x6C, 0x13, 0x6C, 0x14, 0x6C, 0x15, 0x6C, 0x16,\n\t0x6C, 0x17, 0x6C, 0x18, 0x6C, 0x19, 0x6C, 0x1A, 0x6C, 0x1B, 0x6C, 0x1C, 0x6C, 0x1D, 0x6C, 0x1E, 0x6C, 0x1F, 0x6C, 0x20, 0x6C, 0x21, 0x6C, 0x22, 0x6C, 0x23, 0x6C, 0x24, 0x6C, 0x25, 0x6C, 0x26,\n\t0x6C, 0x27, 0x6C, 0x28, 0x6C, 0x29, 0x6C, 0x2A, 0x6C, 0x2B, 0x6C, 0x2C, 0x6C, 0x2D, 0x6C, 0x2E, 0x6C, 0x2F, 0x6C, 0x30, 0x6C, 0x31, 0x6C, 0x32, 0x6C, 0x33, 0x6C, 0x34, 0x6C, 0x35, 0x6C, 0x36,\n\t0x6C, 0x37, 0x6C, 0x38, 0x6C, 0x39, 0x6C, 0x3A, 0x6C, 0x3B, 0x6C, 0x3C, 0x6C, 0x3D, 0x6C, 0x3E, 0x6C, 0x3F, 0x6C, 0x40, 0x6C, 0x41, 0x6C, 0x42, 0x6C, 0x43, 0x6C, 0x44, 0x6C, 0x45, 0x6C, 0x46,\n\t0x6C, 0x47, 0x6C, 0x48, 0x6C, 0x49, 0x6C, 0x4A, 0x6C, 0x4B, 0x6C, 0x4C, 0x6C, 0x4D, 0x6C, 0x4E, 0x6C, 0x4F, 0x6C, 0x50, 0x6C, 0x51, 0x6C, 0x52, 0x6C, 0x53, 0x6C, 0x54, 0x6C, 0x55, 0x6C, 0x56,\n\t0x6C, 0x57, 0x6C, 0x58, 0x6C, 0x59, 0x6C, 0x5A, 0x6C, 0x5B, 0x6C, 0x5C, 0x6C, 0x5D, 0x6C, 0x5E, 0x6C, 0x5F, 0x6C, 0x60, 0x6C, 0x61, 0x6C, 0x62, 0x6C, 0x63, 0x6C, 0x64, 0x6C, 0x65, 0x6C, 0x66,\n\t0x6C, 0x67, 0x6C, 0x68, 0x6C, 0x69, 0x6C, 0x6A, 0x6C, 0x6B, 0x6C, 0x6C, 0x6C, 0x6D, 0x6C, 0x6E, 0x6C, 0x6F, 0x6C, 0x70, 0x6C, 0x71, 0x6C, 0x72, 0x6C, 0x73, 0x6C, 0x74, 0x6C, 0x75, 0x6C, 0x76,\n\t0x6C, 0x77, 0x6C, 0x78, 0x6C, 0x79, 0x6C, 0x7A, 0x6C, 0x7B, 0x6C, 0x7C, 0x6C, 0x7D, 0x6C, 0x7E, 0x6C, 0x7F, 0x6C, 0x80, 0x6C, 0x81, 0x6C, 0x82, 0x6C, 0x83, 0x6C, 0x84, 0x6C, 0x85, 0x6C, 0x86,\n\t0x6C, 0x87, 0x6C, 0x88, 0x6C, 0x89, 0x6C, 0x8A, 0x6C, 0x8B, 0x6C, 0x8C, 0x6C, 0x8D, 0x6C, 0x8E, 0x6C, 0x8F, 0x6C, 0x90, 0x6C, 0x91, 0x6C, 0x92, 0x6C, 0x93, 0x6C, 0x94, 0x6C, 0x95, 0x6C, 0x96,\n\t0x6C, 0x97, 0x6C, 0x98, 0x6C, 0x99, 0x6C, 0x9A, 0x6C, 0x9B, 0x6C, 0x9C, 0x6C, 0x9D, 0x6C, 0x9E, 0x6C, 0x9F, 0x6C, 0xA0, 0x6C, 0xA1, 0x6C, 0xA2, 0x6C, 0xA3, 0x6C, 0xA4, 0x6C, 0xA5, 0x6C, 0xA6,\n\t0x6C, 0xA7, 0x6C, 0xA8, 0x6C, 0xA9, 0x6C, 0xAA, 0x6C, 0xAB, 0x6C, 0xAC, 0x6C, 0xAD, 0x6C, 0xAE, 0x6C, 0xAF, 0x6C, 0xB0, 0x6C, 0xB1, 0x6C, 0xB2, 0x6C, 0xB3, 0x6C, 0xB4, 0x6C, 0xB5, 0x6C, 0xB6,\n\t0x6C, 0xB7, 0x6C, 0xB8, 0x6C, 0xB9, 0x6C, 0xBA, 0x6C, 0xBB, 0x6C, 0xBC, 0x6C, 0xBD, 0x6C, 0xBE, 0x6C, 0xBF, 0x6C, 0xC0, 0x6C, 0xC1, 0x6C, 0xC2, 0x6C, 0xC3, 0x6C, 0xC4, 0x6C, 0xC5, 0x6C, 0xC6,\n\t0x6C, 0xC7, 0x6C, 0xC8, 0x6C, 0xC9, 0x6C, 0xCA, 0x6C, 0xCB, 0x6C, 0xCC, 0x6C, 0xCD, 0x6C, 0xCE, 0x6C, 0xCF, 0x6C, 0xD0, 0x6C, 0xD1, 0x6C, 0xD2, 0x6C, 0xD3, 0x6C, 0xD4, 0x6C, 0xD5, 0x6C, 0xD6,\n\t0x6C, 0xD7, 0x6C, 0xD8, 0x6C, 0xD9, 0x6C, 0xDA, 0x6C, 0xDB, 0x6C, 0xDC, 0x6C, 0xDD, 0x6C, 0xDE, 0x6C, 0xDF, 0x6C, 0xE0, 0x6C, 0xE1, 0x6C, 0xE2, 0x6C, 0xE3, 0x6C, 0xE4, 0x6C, 0xE5, 0x6C, 0xE6,\n\t0x6C, 0xE7, 0x6C, 0xE8, 0x6C, 0xE9, 0x6C, 0xEA, 0x6C, 0xEB, 0x6C, 0xEC, 0x6C, 0xED, 0x6C, 0xEE, 0x6C, 0xEF, 0x6C, 0xF0, 0x6C, 0xF1, 0x6C, 0xF2, 0x6C, 0xF3, 0x6C, 0xF4, 0x6C, 0xF5, 0x6C, 0xF6,\n\t0x6C, 0xF7, 0x6C, 0xF8, 0x6C, 0xF9, 0x6C, 0xFA, 0x6C, 0xFB, 0x6C, 0xFC, 0x6C, 0xFD, 0x6C, 0xFE, 0x6C, 0xFF, 0x6C, 0x00, 0x6D, 0x01, 0x6D, 0x02, 0x6D, 0x03, 0x6D, 0x04, 0x6D, 0x05, 0x6D, 0x06,\n\t0x6D, 0x07, 0x6D, 0x08, 0x6D, 0x09, 0x6D, 0x0A, 0x6D, 0x0B, 0x6D, 0x0C, 0x6D, 0x0D, 0x6D, 0x0E, 0x6D, 0x0F, 0x6D, 0x10, 0x6D, 0x11, 0x6D, 0x12, 0x6D, 0x13, 0x6D, 0x14, 0x6D, 0x15, 0x6D, 0x16,\n\t0x6D, 0x17, 0x6D, 0x18, 0x6D, 0x19, 0x6D, 0x1A, 0x6D, 0x1B, 0x6D, 0x1C, 0x6D, 0x1D, 0x6D, 0x1E, 0x6D, 0x1F, 0x6D, 0x20, 0x6D, 0x21, 0x6D, 0x22, 0x6D, 0x23, 0x6D, 0x24, 0x6D, 0x25, 0x6D, 0x26,\n\t0x6D, 0x27, 0x6D, 0x28, 0x6D, 0x29, 0x6D, 0x2A, 0x6D, 0x2B, 0x6D, 0x2C, 0x6D, 0x2D, 0x6D, 0x2E, 0x6D, 0x2F, 0x6D, 0x30, 0x6D, 0x31, 0x6D, 0x32, 0x6D, 0x33, 0x6D, 0x34, 0x6D, 0x35, 0x6D, 0x36,\n\t0x6D, 0x37, 0x6D, 0x38, 0x6D, 0x39, 0x6D, 0x3A, 0x6D, 0x3B, 0x6D, 0x3C, 0x6D, 0x3D, 0x6D, 0x3E, 0x6D, 0x3F, 0x6D, 0x40, 0x6D, 0x41, 0x6D, 0x42, 0x6D, 0x43, 0x6D, 0x44, 0x6D, 0x45, 0x6D, 0x46,\n\t0x6D, 0x47, 0x6D, 0x48, 0x6D, 0x49, 0x6D, 0x4A, 0x6D, 0x4B, 0x6D, 0x4C, 0x6D, 0x4D, 0x6D, 0x4E, 0x6D, 0x4F, 0x6D, 0x50, 0x6D, 0x51, 0x6D, 0x52, 0x6D, 0x53, 0x6D, 0x54, 0x6D, 0x55, 0x6D, 0x56,\n\t0x6D, 0x57, 0x6D, 0x58, 0x6D, 0x59, 0x6D, 0x5A, 0x6D, 0x5B, 0x6D, 0x5C, 0x6D, 0x5D, 0x6D, 0x5E, 0x6D, 0x5F, 0x6D, 0x60, 0x6D, 0x61, 0x6D, 0x62, 0x6D, 0x63, 0x6D, 0x64, 0x6D, 0x65, 0x6D, 0x66,\n\t0x6D, 0x67, 0x6D, 0x68, 0x6D, 0x69, 0x6D, 0x6A, 0x6D, 0x6B, 0x6D, 0x6C, 0x6D, 0x6D, 0x6D, 0x6E, 0x6D, 0x6F, 0x6D, 0x70, 0x6D, 0x71, 0x6D, 0x72, 0x6D, 0x73, 0x6D, 0x74, 0x6D, 0x75, 0x6D, 0x76,\n\t0x6D, 0x77, 0x6D, 0x78, 0x6D, 0x79, 0x6D, 0x7A, 0x6D, 0x7B, 0x6D, 0x7C, 0x6D, 0x7D, 0x6D, 0x7E, 0x6D, 0x7F, 0x6D, 0x80, 0x6D, 0x81, 0x6D, 0x82, 0x6D, 0x83, 0x6D, 0x84, 0x6D, 0x85, 0x6D, 0x86,\n\t0x6D, 0x87, 0x6D, 0x88, 0x6D, 0x89, 0x6D, 0x8A, 0x6D, 0x8B, 0x6D, 0x8C, 0x6D, 0x8D, 0x6D, 0x8E, 0x6D, 0x8F, 0x6D, 0x90, 0x6D, 0x91, 0x6D, 0x92, 0x6D, 0x93, 0x6D, 0x94, 0x6D, 0x95, 0x6D, 0x96,\n\t0x6D, 0x97, 0x6D, 0x98, 0x6D, 0x99, 0x6D, 0x9A, 0x6D, 0x9B, 0x6D, 0x9C, 0x6D, 0x9D, 0x6D, 0x9E, 0x6D, 0x9F, 0x6D, 0xA0, 0x6D, 0xA1, 0x6D, 0xA2, 0x6D, 0xA3, 0x6D, 0xA4, 0x6D, 0xA5, 0x6D, 0xA6,\n\t0x6D, 0xA7, 0x6D, 0xA8, 0x6D, 0xA9, 0x6D, 0xAA, 0x6D, 0xAB, 0x6D, 0xAC, 0x6D, 0xAD, 0x6D, 0xAE, 0x6D, 0xAF, 0x6D, 0xB0, 0x6D, 0xB1, 0x6D, 0xB2, 0x6D, 0xB3, 0x6D, 0xB4, 0x6D, 0xB5, 0x6D, 0xB6,\n\t0x6D, 0xB7, 0x6D, 0xB8, 0x6D, 0xB9, 0x6D, 0xBA, 0x6D, 0xBB, 0x6D, 0xBC, 0x6D, 0xBD, 0x6D, 0xBE, 0x6D, 0xBF, 0x6D, 0xC0, 0x6D, 0xC1, 0x6D, 0xC2, 0x6D, 0xC3, 0x6D, 0xC4, 0x6D, 0xC5, 0x6D, 0xC6,\n\t0x6D, 0xC7, 0x6D, 0xC8, 0x6D, 0xC9, 0x6D, 0xCA, 0x6D, 0xCB, 0x6D, 0xCC, 0x6D, 0xCD, 0x6D, 0xCE, 0x6D, 0xCF, 0x6D, 0xD0, 0x6D, 0xD1, 0x6D, 0xD2, 0x6D, 0xD3, 0x6D, 0xD4, 0x6D, 0xD5, 0x6D, 0xD6,\n\t0x6D, 0xD7, 0x6D, 0xD8, 0x6D, 0xD9, 0x6D, 0xDA, 0x6D, 0xDB, 0x6D, 0xDC, 0x6D, 0xDD, 0x6D, 0xDE, 0x6D, 0xDF, 0x6D, 0xE0, 0x6D, 0xE1, 0x6D, 0xE2, 0x6D, 0xE3, 0x6D, 0xE4, 0x6D, 0xE5, 0x6D, 0xE6,\n\t0x6D, 0xE7, 0x6D, 0xE8, 0x6D, 0xE9, 0x6D, 0xEA, 0x6D, 0xEB, 0x6D, 0xEC, 0x6D, 0xED, 0x6D, 0xEE, 0x6D, 0xEF, 0x6D, 0xF0, 0x6D, 0xF1, 0x6D, 0xF2, 0x6D, 0xF3, 0x6D, 0xF4, 0x6D, 0xF5, 0x6D, 0xF6,\n\t0x6D, 0xF7, 0x6D, 0xF8, 0x6D, 0xF9, 0x6D, 0xFA, 0x6D, 0xFB, 0x6D, 0xFC, 0x6D, 0xFD, 0x6D, 0xFE, 0x6D, 0xFF, 0x6D, 0x00, 0x6E, 0x01, 0x6E, 0x02, 0x6E, 0x03, 0x6E, 0x04, 0x6E, 0x05, 0x6E, 0x06,\n\t0x6E, 0x07, 0x6E, 0x08, 0x6E, 0x09, 0x6E, 0x0A, 0x6E, 0x0B, 0x6E, 0x0C, 0x6E, 0x0D, 0x6E, 0x0E, 0x6E, 0x0F, 0x6E, 0x10, 0x6E, 0x11, 0x6E, 0x12, 0x6E, 0x13, 0x6E, 0x14, 0x6E, 0x15, 0x6E, 0x16,\n\t0x6E, 0x17, 0x6E, 0x18, 0x6E, 0x19, 0x6E, 0x1A, 0x6E, 0x1B, 0x6E, 0x1C, 0x6E, 0x1D, 0x6E, 0x1E, 0x6E, 0x1F, 0x6E, 0x20, 0x6E, 0x21, 0x6E, 0x22, 0x6E, 0x23, 0x6E, 0x24, 0x6E, 0x25, 0x6E, 0x26,\n\t0x6E, 0x27, 0x6E, 0x28, 0x6E, 0x29, 0x6E, 0x2A, 0x6E, 0x2B, 0x6E, 0x2C, 0x6E, 0x2D, 0x6E, 0x2E, 0x6E, 0x2F, 0x6E, 0x30, 0x6E, 0x31, 0x6E, 0x32, 0x6E, 0x33, 0x6E, 0x34, 0x6E, 0x35, 0x6E, 0x36,\n\t0x6E, 0x37, 0x6E, 0x38, 0x6E, 0x39, 0x6E, 0x3A, 0x6E, 0x3B, 0x6E, 0x3C, 0x6E, 0x3D, 0x6E, 0x3E, 0x6E, 0x3F, 0x6E, 0x40, 0x6E, 0x41, 0x6E, 0x42, 0x6E, 0x43, 0x6E, 0x44, 0x6E, 0x45, 0x6E, 0x46,\n\t0x6E, 0x47, 0x6E, 0x48, 0x6E, 0x49, 0x6E, 0x4A, 0x6E, 0x4B, 0x6E, 0x4C, 0x6E, 0x4D, 0x6E, 0x4E, 0x6E, 0x4F, 0x6E, 0x50, 0x6E, 0x51, 0x6E, 0x52, 0x6E, 0x53, 0x6E, 0x54, 0x6E, 0x55, 0x6E, 0x56,\n\t0x6E, 0x57, 0x6E, 0x58, 0x6E, 0x59, 0x6E, 0x5A, 0x6E, 0x5B, 0x6E, 0x5C, 0x6E, 0x5D, 0x6E, 0x5E, 0x6E, 0x5F, 0x6E, 0x60, 0x6E, 0x61, 0x6E, 0x62, 0x6E, 0x63, 0x6E, 0x64, 0x6E, 0x65, 0x6E, 0x66,\n\t0x6E, 0x67, 0x6E, 0x68, 0x6E, 0x69, 0x6E, 0x6A, 0x6E, 0x6B, 0x6E, 0x6C, 0x6E, 0x6D, 0x6E, 0x6E, 0x6E, 0x6F, 0x6E, 0x70, 0x6E, 0x71, 0x6E, 0x72, 0x6E, 0x73, 0x6E, 0x74, 0x6E, 0x75, 0x6E, 0x76,\n\t0x6E, 0x77, 0x6E, 0x78, 0x6E, 0x79, 0x6E, 0x7A, 0x6E, 0x7B, 0x6E, 0x7C, 0x6E, 0x7D, 0x6E, 0x7E, 0x6E, 0x7F, 0x6E, 0x80, 0x6E, 0x81, 0x6E, 0x82, 0x6E, 0x83, 0x6E, 0x84, 0x6E, 0x85, 0x6E, 0x86,\n\t0x6E, 0x87, 0x6E, 0x88, 0x6E, 0x89, 0x6E, 0x8A, 0x6E, 0x8B, 0x6E, 0x8C, 0x6E, 0x8D, 0x6E, 0x8E, 0x6E, 0x8F, 0x6E, 0x90, 0x6E, 0x91, 0x6E, 0x92, 0x6E, 0x93, 0x6E, 0x94, 0x6E, 0x95, 0x6E, 0x96,\n\t0x6E, 0x97, 0x6E, 0x98, 0x6E, 0x99, 0x6E, 0x9A, 0x6E, 0x9B, 0x6E, 0x9C, 0x6E, 0x9D, 0x6E, 0x9E, 0x6E, 0x9F, 0x6E, 0xA0, 0x6E, 0xA1, 0x6E, 0xA2, 0x6E, 0xA3, 0x6E, 0xA4, 0x6E, 0xA5, 0x6E, 0xA6,\n\t0x6E, 0xA7, 0x6E, 0xA8, 0x6E, 0xA9, 0x6E, 0xAA, 0x6E, 0xAB, 0x6E, 0xAC, 0x6E, 0xAD, 0x6E, 0xAE, 0x6E, 0xAF, 0x6E, 0xB0, 0x6E, 0xB1, 0x6E, 0xB2, 0x6E, 0xB3, 0x6E, 0xB4, 0x6E, 0xB5, 0x6E, 0xB6,\n\t0x6E, 0xB7, 0x6E, 0xB8, 0x6E, 0xB9, 0x6E, 0xBA, 0x6E, 0xBB, 0x6E, 0xBC, 0x6E, 0xBD, 0x6E, 0xBE, 0x6E, 0xBF, 0x6E, 0xC0, 0x6E, 0xC1, 0x6E, 0xC2, 0x6E, 0xC3, 0x6E, 0xC4, 0x6E, 0xC5, 0x6E, 0xC6,\n\t0x6E, 0xC7, 0x6E, 0xC8, 0x6E, 0xC9, 0x6E, 0xCA, 0x6E, 0xCB, 0x6E, 0xCC, 0x6E, 0xCD, 0x6E, 0xCE, 0x6E, 0xCF, 0x6E, 0xD0, 0x6E, 0xD1, 0x6E, 0xD2, 0x6E, 0xD3, 0x6E, 0xD4, 0x6E, 0xD5, 0x6E, 0xD6,\n\t0x6E, 0xD7, 0x6E, 0xD8, 0x6E, 0xD9, 0x6E, 0xDA, 0x6E, 0xDB, 0x6E, 0xDC, 0x6E, 0xDD, 0x6E, 0xDE, 0x6E, 0xDF, 0x6E, 0xE0, 0x6E, 0xE1, 0x6E, 0xE2, 0x6E, 0xE3, 0x6E, 0xE4, 0x6E, 0xE5, 0x6E, 0xE6,\n\t0x6E, 0xE7, 0x6E, 0xE8, 0x6E, 0xE9, 0x6E, 0xEA, 0x6E, 0xEB, 0x6E, 0xEC, 0x6E, 0xED, 0x6E, 0xEE, 0x6E, 0xEF, 0x6E, 0xF0, 0x6E, 0xF1, 0x6E, 0xF2, 0x6E, 0xF3, 0x6E, 0xF4, 0x6E, 0xF5, 0x6E, 0xF6,\n\t0x6E, 0xF7, 0x6E, 0xF8, 0x6E, 0xF9, 0x6E, 0xFA, 0x6E, 0xFB, 0x6E, 0xFC, 0x6E, 0xFD, 0x6E, 0xFE, 0x6E, 0xFF, 0x6E, 0x00, 0x6F, 0x01, 0x6F, 0x02, 0x6F, 0x03, 0x6F, 0x04, 0x6F, 0x05, 0x6F, 0x06,\n\t0x6F, 0x07, 0x6F, 0x08, 0x6F, 0x09, 0x6F, 0x0A, 0x6F, 0x0B, 0x6F, 0x0C, 0x6F, 0x0D, 0x6F, 0x0E, 0x6F, 0x0F, 0x6F, 0x10, 0x6F, 0x11, 0x6F, 0x12, 0x6F, 0x13, 0x6F, 0x14, 0x6F, 0x15, 0x6F, 0x16,\n\t0x6F, 0x17, 0x6F, 0x18, 0x6F, 0x19, 0x6F, 0x1A, 0x6F, 0x1B, 0x6F, 0x1C, 0x6F, 0x1D, 0x6F, 0x1E, 0x6F, 0x1F, 0x6F, 0x20, 0x6F, 0x21, 0x6F, 0x22, 0x6F, 0x23, 0x6F, 0x24, 0x6F, 0x25, 0x6F, 0x26,\n\t0x6F, 0x27, 0x6F, 0x28, 0x6F, 0x29, 0x6F, 0x2A, 0x6F, 0x2B, 0x6F, 0x2C, 0x6F, 0x2D, 0x6F, 0x2E, 0x6F, 0x2F, 0x6F, 0x30, 0x6F, 0x31, 0x6F, 0x32, 0x6F, 0x33, 0x6F, 0x34, 0x6F, 0x35, 0x6F, 0x36,\n\t0x6F, 0x37, 0x6F, 0x38, 0x6F, 0x39, 0x6F, 0x3A, 0x6F, 0x3B, 0x6F, 0x3C, 0x6F, 0x3D, 0x6F, 0x3E, 0x6F, 0x3F, 0x6F, 0x40, 0x6F, 0x41, 0x6F, 0x42, 0x6F, 0x43, 0x6F, 0x44, 0x6F, 0x45, 0x6F, 0x46,\n\t0x6F, 0x47, 0x6F, 0x48, 0x6F, 0x49, 0x6F, 0x4A, 0x6F, 0x4B, 0x6F, 0x4C, 0x6F, 0x4D, 0x6F, 0x4E, 0x6F, 0x4F, 0x6F, 0x50, 0x6F, 0x51, 0x6F, 0x52, 0x6F, 0x53, 0x6F, 0x54, 0x6F, 0x55, 0x6F, 0x56,\n\t0x6F, 0x57, 0x6F, 0x58, 0x6F, 0x59, 0x6F, 0x5A, 0x6F, 0x5B, 0x6F, 0x5C, 0x6F, 0x5D, 0x6F, 0x5E, 0x6F, 0x5F, 0x6F, 0x60, 0x6F, 0x61, 0x6F, 0x62, 0x6F, 0x63, 0x6F, 0x64, 0x6F, 0x65, 0x6F, 0x66,\n\t0x6F, 0x67, 0x6F, 0x68, 0x6F, 0x69, 0x6F, 0x6A, 0x6F, 0x6B, 0x6F, 0x6C, 0x6F, 0x6D, 0x6F, 0x6E, 0x6F, 0x6F, 0x6F, 0x70, 0x6F, 0x71, 0x6F, 0x72, 0x6F, 0x73, 0x6F, 0x74, 0x6F, 0x75, 0x6F, 0x76,\n\t0x6F, 0x77, 0x6F, 0x78, 0x6F, 0x79, 0x6F, 0x7A, 0x6F, 0x7B, 0x6F, 0x7C, 0x6F, 0x7D, 0x6F, 0x7E, 0x6F, 0x7F, 0x6F, 0x80, 0x6F, 0x81, 0x6F, 0x82, 0x6F, 0x83, 0x6F, 0x84, 0x6F, 0x85, 0x6F, 0x86,\n\t0x6F, 0x87, 0x6F, 0x88, 0x6F, 0x89, 0x6F, 0x8A, 0x6F, 0x8B, 0x6F, 0x8C, 0x6F, 0x8D, 0x6F, 0x8E, 0x6F, 0x8F, 0x6F, 0x90, 0x6F, 0x91, 0x6F, 0x92, 0x6F, 0x93, 0x6F, 0x94, 0x6F, 0x95, 0x6F, 0x96,\n\t0x6F, 0x97, 0x6F, 0x98, 0x6F, 0x99, 0x6F, 0x9A, 0x6F, 0x9B, 0x6F, 0x9C, 0x6F, 0x9D, 0x6F, 0x9E, 0x6F, 0x9F, 0x6F, 0xA0, 0x6F, 0xA1, 0x6F, 0xA2, 0x6F, 0xA3, 0x6F, 0xA4, 0x6F, 0xA5, 0x6F, 0xA6,\n\t0x6F, 0xA7, 0x6F, 0xA8, 0x6F, 0xA9, 0x6F, 0xAA, 0x6F, 0xAB, 0x6F, 0xAC, 0x6F, 0xAD, 0x6F, 0xAE, 0x6F, 0xAF, 0x6F, 0xB0, 0x6F, 0xB1, 0x6F, 0xB2, 0x6F, 0xB3, 0x6F, 0xB4, 0x6F, 0xB5, 0x6F, 0xB6,\n\t0x6F, 0xB7, 0x6F, 0xB8, 0x6F, 0xB9, 0x6F, 0xBA, 0x6F, 0xBB, 0x6F, 0xBC, 0x6F, 0xBD, 0x6F, 0xBE, 0x6F, 0xBF, 0x6F, 0xC0, 0x6F, 0xC1, 0x6F, 0xC2, 0x6F, 0xC3, 0x6F, 0xC4, 0x6F, 0xC5, 0x6F, 0xC6,\n\t0x6F, 0xC7, 0x6F, 0xC8, 0x6F, 0xC9, 0x6F, 0xCA, 0x6F, 0xCB, 0x6F, 0xCC, 0x6F, 0xCD, 0x6F, 0xCE, 0x6F, 0xCF, 0x6F, 0xD0, 0x6F, 0xD1, 0x6F, 0xD2, 0x6F, 0xD3, 0x6F, 0xD4, 0x6F, 0xD5, 0x6F, 0xD6,\n\t0x6F, 0xD7, 0x6F, 0xD8, 0x6F, 0xD9, 0x6F, 0xDA, 0x6F, 0xDB, 0x6F, 0xDC, 0x6F, 0xDD, 0x6F, 0xDE, 0x6F, 0xDF, 0x6F, 0xE0, 0x6F, 0xE1, 0x6F, 0xE2, 0x6F, 0xE3, 0x6F, 0xE4, 0x6F, 0xE5, 0x6F, 0xE6,\n\t0x6F, 0xE7, 0x6F, 0xE8, 0x6F, 0xE9, 0x6F, 0xEA, 0x6F, 0xEB, 0x6F, 0xEC, 0x6F, 0xED, 0x6F, 0xEE, 0x6F, 0xEF, 0x6F, 0xF0, 0x6F, 0xF1, 0x6F, 0xF2, 0x6F, 0xF3, 0x6F, 0xF4, 0x6F, 0xF5, 0x6F, 0xF6,\n\t0x6F, 0xF7, 0x6F, 0xF8, 0x6F, 0xF9, 0x6F, 0xFA, 0x6F, 0xFB, 0x6F, 0xFC, 0x6F, 0xFD, 0x6F, 0xFE, 0x6F, 0xFF, 0x6F, 0x00, 0x70, 0x01, 0x70, 0x02, 0x70, 0x03, 0x70, 0x04, 0x70, 0x05, 0x70, 0x06,\n\t0x70, 0x07, 0x70, 0x08, 0x70, 0x09, 0x70, 0x0A, 0x70, 0x0B, 0x70, 0x0C, 0x70, 0x0D, 0x70, 0x0E, 0x70, 0x0F, 0x70, 0x10, 0x70, 0x11, 0x70, 0x12, 0x70, 0x13, 0x70, 0x14, 0x70, 0x15, 0x70, 0x16,\n\t0x70, 0x17, 0x70, 0x18, 0x70, 0x19, 0x70, 0x1A, 0x70, 0x1B, 0x70, 0x1C, 0x70, 0x1D, 0x70, 0x1E, 0x70, 0x1F, 0x70, 0x20, 0x70, 0x21, 0x70, 0x22, 0x70, 0x23, 0x70, 0x24, 0x70, 0x25, 0x70, 0x26,\n\t0x70, 0x27, 0x70, 0x28, 0x70, 0x29, 0x70, 0x2A, 0x70, 0x2B, 0x70, 0x2C, 0x70, 0x2D, 0x70, 0x2E, 0x70, 0x2F, 0x70, 0x30, 0x70, 0x31, 0x70, 0x32, 0x70, 0x33, 0x70, 0x34, 0x70, 0x35, 0x70, 0x36,\n\t0x70, 0x37, 0x70, 0x38, 0x70, 0x39, 0x70, 0x3A, 0x70, 0x3B, 0x70, 0x3C, 0x70, 0x3D, 0x70, 0x3E, 0x70, 0x3F, 0x70, 0x40, 0x70, 0x41, 0x70, 0x42, 0x70, 0x43, 0x70, 0x44, 0x70, 0x45, 0x70, 0x46,\n\t0x70, 0x47, 0x70, 0x48, 0x70, 0x49, 0x70, 0x4A, 0x70, 0x4B, 0x70, 0x4C, 0x70, 0x4D, 0x70, 0x4E, 0x70, 0x4F, 0x70, 0x50, 0x70, 0x51, 0x70, 0x52, 0x70, 0x53, 0x70, 0x54, 0x70, 0x55, 0x70, 0x56,\n\t0x70, 0x57, 0x70, 0x58, 0x70, 0x59, 0x70, 0x5A, 0x70, 0x5B, 0x70, 0x5C, 0x70, 0x5D, 0x70, 0x5E, 0x70, 0x5F, 0x70, 0x60, 0x70, 0x61, 0x70, 0x62, 0x70, 0x63, 0x70, 0x64, 0x70, 0x65, 0x70, 0x66,\n\t0x70, 0x67, 0x70, 0x68, 0x70, 0x69, 0x70, 0x6A, 0x70, 0x6B, 0x70, 0x6C, 0x70, 0x6D, 0x70, 0x6E, 0x70, 0x6F, 0x70, 0x70, 0x70, 0x71, 0x70, 0x72, 0x70, 0x73, 0x70, 0x74, 0x70, 0x75, 0x70, 0x76,\n\t0x70, 0x77, 0x70, 0x78, 0x70, 0x79, 0x70, 0x7A, 0x70, 0x7B, 0x70, 0x7C, 0x70, 0x7D, 0x70, 0x7E, 0x70, 0x7F, 0x70, 0x80, 0x70, 0x81, 0x70, 0x82, 0x70, 0x83, 0x70, 0x84, 0x70, 0x85, 0x70, 0x86,\n\t0x70, 0x87, 0x70, 0x88, 0x70, 0x89, 0x70, 0x8A, 0x70, 0x8B, 0x70, 0x8C, 0x70, 0x8D, 0x70, 0x8E, 0x70, 0x8F, 0x70, 0x90, 0x70, 0x91, 0x70, 0x92, 0x70, 0x93, 0x70, 0x94, 0x70, 0x95, 0x70, 0x96,\n\t0x70, 0x97, 0x70, 0x98, 0x70, 0x99, 0x70, 0x9A, 0x70, 0x9B, 0x70, 0x9C, 0x70, 0x9D, 0x70, 0x9E, 0x70, 0x9F, 0x70, 0xA0, 0x70, 0xA1, 0x70, 0xA2, 0x70, 0xA3, 0x70, 0xA4, 0x70, 0xA5, 0x70, 0xA6,\n\t0x70, 0xA7, 0x70, 0xA8, 0x70, 0xA9, 0x70, 0xAA, 0x70, 0xAB, 0x70, 0xAC, 0x70, 0xAD, 0x70, 0xAE, 0x70, 0xAF, 0x70, 0xB0, 0x70, 0xB1, 0x70, 0xB2, 0x70, 0xB3, 0x70, 0xB4, 0x70, 0xB5, 0x70, 0xB6,\n\t0x70, 0xB7, 0x70, 0xB8, 0x70, 0xB9, 0x70, 0xBA, 0x70, 0xBB, 0x70, 0xBC, 0x70, 0xBD, 0x70, 0xBE, 0x70, 0xBF, 0x70, 0xC0, 0x70, 0xC1, 0x70, 0xC2, 0x70, 0xC3, 0x70, 0xC4, 0x70, 0xC5, 0x70, 0xC6,\n\t0x70, 0xC7, 0x70, 0xC8, 0x70, 0xC9, 0x70, 0xCA, 0x70, 0xCB, 0x70, 0xCC, 0x70, 0xCD, 0x70, 0xCE, 0x70, 0xCF, 0x70, 0xD0, 0x70, 0xD1, 0x70, 0xD2, 0x70, 0xD3, 0x70, 0xD4, 0x70, 0xD5, 0x70, 0xD6,\n\t0x70, 0xD7, 0x70, 0xD8, 0x70, 0xD9, 0x70, 0xDA, 0x70, 0xDB, 0x70, 0xDC, 0x70, 0xDD, 0x70, 0xDE, 0x70, 0xDF, 0x70, 0xE0, 0x70, 0xE1, 0x70, 0xE2, 0x70, 0xE3, 0x70, 0xE4, 0x70, 0xE5, 0x70, 0xE6,\n\t0x70, 0xE7, 0x70, 0xE8, 0x70, 0xE9, 0x70, 0xEA, 0x70, 0xEB, 0x70, 0xEC, 0x70, 0xED, 0x70, 0xEE, 0x70, 0xEF, 0x70, 0xF0, 0x70, 0xF1, 0x70, 0xF2, 0x70, 0xF3, 0x70, 0xF4, 0x70, 0xF5, 0x70, 0xF6,\n\t0x70, 0xF7, 0x70, 0xF8, 0x70, 0xF9, 0x70, 0xFA, 0x70, 0xFB, 0x70, 0xFC, 0x70, 0xFD, 0x70, 0xFE, 0x70, 0xFF, 0x70, 0x00, 0x71, 0x01, 0x71, 0x02, 0x71, 0x03, 0x71, 0x04, 0x71, 0x05, 0x71, 0x06,\n\t0x71, 0x07, 0x71, 0x08, 0x71, 0x09, 0x71, 0x0A, 0x71, 0x0B, 0x71, 0x0C, 0x71, 0x0D, 0x71, 0x0E, 0x71, 0x0F, 0x71, 0x10, 0x71, 0x11, 0x71, 0x12, 0x71, 0x13, 0x71, 0x14, 0x71, 0x15, 0x71, 0x16,\n\t0x71, 0x17, 0x71, 0x18, 0x71, 0x19, 0x71, 0x1A, 0x71, 0x1B, 0x71, 0x1C, 0x71, 0x1D, 0x71, 0x1E, 0x71, 0x1F, 0x71, 0x20, 0x71, 0x21, 0x71, 0x22, 0x71, 0x23, 0x71, 0x24, 0x71, 0x25, 0x71, 0x26,\n\t0x71, 0x27, 0x71, 0x28, 0x71, 0x29, 0x71, 0x2A, 0x71, 0x2B, 0x71, 0x2C, 0x71, 0x2D, 0x71, 0x2E, 0x71, 0x2F, 0x71, 0x30, 0x71, 0x31, 0x71, 0x32, 0x71, 0x33, 0x71, 0x34, 0x71, 0x35, 0x71, 0x36,\n\t0x71, 0x37, 0x71, 0x38, 0x71, 0x39, 0x71, 0x3A, 0x71, 0x3B, 0x71, 0x3C, 0x71, 0x3D, 0x71, 0x3E, 0x71, 0x3F, 0x71, 0x40, 0x71, 0x41, 0x71, 0x42, 0x71, 0x43, 0x71, 0x44, 0x71, 0x45, 0x71, 0x46,\n\t0x71, 0x47, 0x71, 0x48, 0x71, 0x49, 0x71, 0x4A, 0x71, 0x4B, 0x71, 0x4C, 0x71, 0x4D, 0x71, 0x4E, 0x71, 0x4F, 0x71, 0x50, 0x71, 0x51, 0x71, 0x52, 0x71, 0x53, 0x71, 0x54, 0x71, 0x55, 0x71, 0x56,\n\t0x71, 0x57, 0x71, 0x58, 0x71, 0x59, 0x71, 0x5A, 0x71, 0x5B, 0x71, 0x5C, 0x71, 0x5D, 0x71, 0x5E, 0x71, 0x5F, 0x71, 0x60, 0x71, 0x61, 0x71, 0x62, 0x71, 0x63, 0x71, 0x64, 0x71, 0x65, 0x71, 0x66,\n\t0x71, 0x67, 0x71, 0x68, 0x71, 0x69, 0x71, 0x6A, 0x71, 0x6B, 0x71, 0x6C, 0x71, 0x6D, 0x71, 0x6E, 0x71, 0x6F, 0x71, 0x70, 0x71, 0x71, 0x71, 0x72, 0x71, 0x73, 0x71, 0x74, 0x71, 0x75, 0x71, 0x76,\n\t0x71, 0x77, 0x71, 0x78, 0x71, 0x79, 0x71, 0x7A, 0x71, 0x7B, 0x71, 0x7C, 0x71, 0x7D, 0x71, 0x7E, 0x71, 0x7F, 0x71, 0x80, 0x71, 0x81, 0x71, 0x82, 0x71, 0x83, 0x71, 0x84, 0x71, 0x85, 0x71, 0x86,\n\t0x71, 0x87, 0x71, 0x88, 0x71, 0x89, 0x71, 0x8A, 0x71, 0x8B, 0x71, 0x8C, 0x71, 0x8D, 0x71, 0x8E, 0x71, 0x8F, 0x71, 0x90, 0x71, 0x91, 0x71, 0x92, 0x71, 0x93, 0x71, 0x94, 0x71, 0x95, 0x71, 0x96,\n\t0x71, 0x97, 0x71, 0x98, 0x71, 0x99, 0x71, 0x9A, 0x71, 0x9B, 0x71, 0x9C, 0x71, 0x9D, 0x71, 0x9E, 0x71, 0x9F, 0x71, 0xA0, 0x71, 0xA1, 0x71, 0xA2, 0x71, 0xA3, 0x71, 0xA4, 0x71, 0xA5, 0x71, 0xA6,\n\t0x71, 0xA7, 0x71, 0xA8, 0x71, 0xA9, 0x71, 0xAA, 0x71, 0xAB, 0x71, 0xAC, 0x71, 0xAD, 0x71, 0xAE, 0x71, 0xAF, 0x71, 0xB0, 0x71, 0xB1, 0x71, 0xB2, 0x71, 0xB3, 0x71, 0xB4, 0x71, 0xB5, 0x71, 0xB6,\n\t0x71, 0xB7, 0x71, 0xB8, 0x71, 0xB9, 0x71, 0xBA, 0x71, 0xBB, 0x71, 0xBC, 0x71, 0xBD, 0x71, 0xBE, 0x71, 0xBF, 0x71, 0xC0, 0x71, 0xC1, 0x71, 0xC2, 0x71, 0xC3, 0x71, 0xC4, 0x71, 0xC5, 0x71, 0xC6,\n\t0x71, 0xC7, 0x71, 0xC8, 0x71, 0xC9, 0x71, 0xCA, 0x71, 0xCB, 0x71, 0xCC, 0x71, 0xCD, 0x71, 0xCE, 0x71, 0xCF, 0x71, 0xD0, 0x71, 0xD1, 0x71, 0xD2, 0x71, 0xD3, 0x71, 0xD4, 0x71, 0xD5, 0x71, 0xD6,\n\t0x71, 0xD7, 0x71, 0xD8, 0x71, 0xD9, 0x71, 0xDA, 0x71, 0xDB, 0x71, 0xDC, 0x71, 0xDD, 0x71, 0xDE, 0x71, 0xDF, 0x71, 0xE0, 0x71, 0xE1, 0x71, 0xE2, 0x71, 0xE3, 0x71, 0xE4, 0x71, 0xE5, 0x71, 0xE6,\n\t0x71, 0xE7, 0x71, 0xE8, 0x71, 0xE9, 0x71, 0xEA, 0x71, 0xEB, 0x71, 0xEC, 0x71, 0xED, 0x71, 0xEE, 0x71, 0xEF, 0x71, 0xF0, 0x71, 0xF1, 0x71, 0xF2, 0x71, 0xF3, 0x71, 0xF4, 0x71, 0xF5, 0x71, 0xF6,\n\t0x71, 0xF7, 0x71, 0xF8, 0x71, 0xF9, 0x71, 0xFA, 0x71, 0xFB, 0x71, 0xFC, 0x71, 0xFD, 0x71, 0xFE, 0x71, 0xFF, 0x71, 0x00, 0x72, 0x01, 0x72, 0x02, 0x72, 0x03, 0x72, 0x04, 0x72, 0x05, 0x72, 0x06,\n\t0x72, 0x07, 0x72, 0x08, 0x72, 0x09, 0x72, 0x0A, 0x72, 0x0B, 0x72, 0x0C, 0x72, 0x0D, 0x72, 0x0E, 0x72, 0x0F, 0x72, 0x10, 0x72, 0x11, 0x72, 0x12, 0x72, 0x13, 0x72, 0x14, 0x72, 0x15, 0x72, 0x16,\n\t0x72, 0x17, 0x72, 0x18, 0x72, 0x19, 0x72, 0x1A, 0x72, 0x1B, 0x72, 0x1C, 0x72, 0x1D, 0x72, 0x1E, 0x72, 0x1F, 0x72, 0x20, 0x72, 0x21, 0x72, 0x22, 0x72, 0x23, 0x72, 0x24, 0x72, 0x25, 0x72, 0x26,\n\t0x72, 0x27, 0x72, 0x28, 0x72, 0x29, 0x72, 0x2A, 0x72, 0x2B, 0x72, 0x2C, 0x72, 0x2D, 0x72, 0x2E, 0x72, 0x2F, 0x72, 0x30, 0x72, 0x31, 0x72, 0x32, 0x72, 0x33, 0x72, 0x34, 0x72, 0x35, 0x72, 0x36,\n\t0x72, 0x37, 0x72, 0x38, 0x72, 0x39, 0x72, 0x3A, 0x72, 0x3B, 0x72, 0x3C, 0x72, 0x3D, 0x72, 0x3E, 0x72, 0x3F, 0x72, 0x40, 0x72, 0x41, 0x72, 0x42, 0x72, 0x43, 0x72, 0x44, 0x72, 0x45, 0x72, 0x46,\n\t0x72, 0x47, 0x72, 0x48, 0x72, 0x49, 0x72, 0x4A, 0x72, 0x4B, 0x72, 0x4C, 0x72, 0x4D, 0x72, 0x4E, 0x72, 0x4F, 0x72, 0x50, 0x72, 0x51, 0x72, 0x52, 0x72, 0x53, 0x72, 0x54, 0x72, 0x55, 0x72, 0x56,\n\t0x72, 0x57, 0x72, 0x58, 0x72, 0x59, 0x72, 0x5A, 0x72, 0x5B, 0x72, 0x5C, 0x72, 0x5D, 0x72, 0x5E, 0x72, 0x5F, 0x72, 0x60, 0x72, 0x61, 0x72, 0x62, 0x72, 0x63, 0x72, 0x64, 0x72, 0x65, 0x72, 0x66,\n\t0x72, 0x67, 0x72, 0x68, 0x72, 0x69, 0x72, 0x6A, 0x72, 0x6B, 0x72, 0x6C, 0x72, 0x6D, 0x72, 0x6E, 0x72, 0x6F, 0x72, 0x70, 0x72, 0x71, 0x72, 0x72, 0x72, 0x73, 0x72, 0x74, 0x72, 0x75, 0x72, 0x76,\n\t0x72, 0x77, 0x72, 0x78, 0x72, 0x79, 0x72, 0x7A, 0x72, 0x7B, 0x72, 0x7C, 0x72, 0x7D, 0x72, 0x7E, 0x72, 0x7F, 0x72, 0x80, 0x72, 0x81, 0x72, 0x82, 0x72, 0x83, 0x72, 0x84, 0x72, 0x85, 0x72, 0x86,\n\t0x72, 0x87, 0x72, 0x88, 0x72, 0x89, 0x72, 0x8A, 0x72, 0x8B, 0x72, 0x8C, 0x72, 0x8D, 0x72, 0x8E, 0x72, 0x8F, 0x72, 0x90, 0x72, 0x91, 0x72, 0x92, 0x72, 0x93, 0x72, 0x94, 0x72, 0x95, 0x72, 0x96,\n\t0x72, 0x97, 0x72, 0x98, 0x72, 0x99, 0x72, 0x9A, 0x72, 0x9B, 0x72, 0x9C, 0x72, 0x9D, 0x72, 0x9E, 0x72, 0x9F, 0x72, 0xA0, 0x72, 0xA1, 0x72, 0xA2, 0x72, 0xA3, 0x72, 0xA4, 0x72, 0xA5, 0x72, 0xA6,\n\t0x72, 0xA7, 0x72, 0xA8, 0x72, 0xA9, 0x72, 0xAA, 0x72, 0xAB, 0x72, 0xAC, 0x72, 0xAD, 0x72, 0xAE, 0x72, 0xAF, 0x72, 0xB0, 0x72, 0xB1, 0x72, 0xB2, 0x72, 0xB3, 0x72, 0xB4, 0x72, 0xB5, 0x72, 0xB6,\n\t0x72, 0xB7, 0x72, 0xB8, 0x72, 0xB9, 0x72, 0xBA, 0x72, 0xBB, 0x72, 0xBC, 0x72, 0xBD, 0x72, 0xBE, 0x72, 0xBF, 0x72, 0xC0, 0x72, 0xC1, 0x72, 0xC2, 0x72, 0xC3, 0x72, 0xC4, 0x72, 0xC5, 0x72, 0xC6,\n\t0x72, 0xC7, 0x72, 0xC8, 0x72, 0xC9, 0x72, 0xCA, 0x72, 0xCB, 0x72, 0xCC, 0x72, 0xCD, 0x72, 0xCE, 0x72, 0xCF, 0x72, 0xD0, 0x72, 0xD1, 0x72, 0xD2, 0x72, 0xD3, 0x72, 0xD4, 0x72, 0xD5, 0x72, 0xD6,\n\t0x72, 0xD7, 0x72, 0xD8, 0x72, 0xD9, 0x72, 0xDA, 0x72, 0xDB, 0x72, 0xDC, 0x72, 0xDD, 0x72, 0xDE, 0x72, 0xDF, 0x72, 0xE0, 0x72, 0xE1, 0x72, 0xE2, 0x72, 0xE3, 0x72, 0xE4, 0x72, 0xE5, 0x72, 0xE6,\n\t0x72, 0xE7, 0x72, 0xE8, 0x72, 0xE9, 0x72, 0xEA, 0x72, 0xEB, 0x72, 0xEC, 0x72, 0xED, 0x72, 0xEE, 0x72, 0xEF, 0x72, 0xF0, 0x72, 0xF1, 0x72, 0xF2, 0x72, 0xF3, 0x72, 0xF4, 0x72, 0xF5, 0x72, 0xF6,\n\t0x72, 0xF7, 0x72, 0xF8, 0x72, 0xF9, 0x72, 0xFA, 0x72, 0xFB, 0x72, 0xFC, 0x72, 0xFD, 0x72, 0xFE, 0x72, 0xFF, 0x72, 0x00, 0x73, 0x01, 0x73, 0x02, 0x73, 0x03, 0x73, 0x04, 0x73, 0x05, 0x73, 0x06,\n\t0x73, 0x07, 0x73, 0x08, 0x73, 0x09, 0x73, 0x0A, 0x73, 0x0B, 0x73, 0x0C, 0x73, 0x0D, 0x73, 0x0E, 0x73, 0x0F, 0x73, 0x10, 0x73, 0x11, 0x73, 0x12, 0x73, 0x13, 0x73, 0x14, 0x73, 0x15, 0x73, 0x16,\n\t0x73, 0x17, 0x73, 0x18, 0x73, 0x19, 0x73, 0x1A, 0x73, 0x1B, 0x73, 0x1C, 0x73, 0x1D, 0x73, 0x1E, 0x73, 0x1F, 0x73, 0x20, 0x73, 0x21, 0x73, 0x22, 0x73, 0x23, 0x73, 0x24, 0x73, 0x25, 0x73, 0x26,\n\t0x73, 0x27, 0x73, 0x28, 0x73, 0x29, 0x73, 0x2A, 0x73, 0x2B, 0x73, 0x2C, 0x73, 0x2D, 0x73, 0x2E, 0x73, 0x2F, 0x73, 0x30, 0x73, 0x31, 0x73, 0x32, 0x73, 0x33, 0x73, 0x34, 0x73, 0x35, 0x73, 0x36,\n\t0x73, 0x37, 0x73, 0x38, 0x73, 0x39, 0x73, 0x3A, 0x73, 0x3B, 0x73, 0x3C, 0x73, 0x3D, 0x73, 0x3E, 0x73, 0x3F, 0x73, 0x40, 0x73, 0x41, 0x73, 0x42, 0x73, 0x43, 0x73, 0x44, 0x73, 0x45, 0x73, 0x46,\n\t0x73, 0x47, 0x73, 0x48, 0x73, 0x49, 0x73, 0x4A, 0x73, 0x4B, 0x73, 0x4C, 0x73, 0x4D, 0x73, 0x4E, 0x73, 0x4F, 0x73, 0x50, 0x73, 0x51, 0x73, 0x52, 0x73, 0x53, 0x73, 0x54, 0x73, 0x55, 0x73, 0x56,\n\t0x73, 0x57, 0x73, 0x58, 0x73, 0x59, 0x73, 0x5A, 0x73, 0x5B, 0x73, 0x5C, 0x73, 0x5D, 0x73, 0x5E, 0x73, 0x5F, 0x73, 0x60, 0x73, 0x61, 0x73, 0x62, 0x73, 0x63, 0x73, 0x64, 0x73, 0x65, 0x73, 0x66,\n\t0x73, 0x67, 0x73, 0x68, 0x73, 0x69, 0x73, 0x6A, 0x73, 0x6B, 0x73, 0x6C, 0x73, 0x6D, 0x73, 0x6E, 0x73, 0x6F, 0x73, 0x70, 0x73, 0x71, 0x73, 0x72, 0x73, 0x73, 0x73, 0x74, 0x73, 0x75, 0x73, 0x76,\n\t0x73, 0x77, 0x73, 0x78, 0x73, 0x79, 0x73, 0x7A, 0x73, 0x7B, 0x73, 0x7C, 0x73, 0x7D, 0x73, 0x7E, 0x73, 0x7F, 0x73, 0x80, 0x73, 0x81, 0x73, 0x82, 0x73, 0x83, 0x73, 0x84, 0x73, 0x85, 0x73, 0x86,\n\t0x73, 0x87, 0x73, 0x88, 0x73, 0x89, 0x73, 0x8A, 0x73, 0x8B, 0x73, 0x8C, 0x73, 0x8D, 0x73, 0x8E, 0x73, 0x8F, 0x73, 0x90, 0x73, 0x91, 0x73, 0x92, 0x73, 0x93, 0x73, 0x94, 0x73, 0x95, 0x73, 0x96,\n\t0x73, 0x97, 0x73, 0x98, 0x73, 0x99, 0x73, 0x9A, 0x73, 0x9B, 0x73, 0x9C, 0x73, 0x9D, 0x73, 0x9E, 0x73, 0x9F, 0x73, 0xA0, 0x73, 0xA1, 0x73, 0xA2, 0x73, 0xA3, 0x73, 0xA4, 0x73, 0xA5, 0x73, 0xA6,\n\t0x73, 0xA7, 0x73, 0xA8, 0x73, 0xA9, 0x73, 0xAA, 0x73, 0xAB, 0x73, 0xAC, 0x73, 0xAD, 0x73, 0xAE, 0x73, 0xAF, 0x73, 0xB0, 0x73, 0xB1, 0x73, 0xB2, 0x73, 0xB3, 0x73, 0xB4, 0x73, 0xB5, 0x73, 0xB6,\n\t0x73, 0xB7, 0x73, 0xB8, 0x73, 0xB9, 0x73, 0xBA, 0x73, 0xBB, 0x73, 0xBC, 0x73, 0xBD, 0x73, 0xBE, 0x73, 0xBF, 0x73, 0xC0, 0x73, 0xC1, 0x73, 0xC2, 0x73, 0xC3, 0x73, 0xC4, 0x73, 0xC5, 0x73, 0xC6,\n\t0x73, 0xC7, 0x73, 0xC8, 0x73, 0xC9, 0x73, 0xCA, 0x73, 0xCB, 0x73, 0xCC, 0x73, 0xCD, 0x73, 0xCE, 0x73, 0xCF, 0x73, 0xD0, 0x73, 0xD1, 0x73, 0xD2, 0x73, 0xD3, 0x73, 0xD4, 0x73, 0xD5, 0x73, 0xD6,\n\t0x73, 0xD7, 0x73, 0xD8, 0x73, 0xD9, 0x73, 0xDA, 0x73, 0xDB, 0x73, 0xDC, 0x73, 0xDD, 0x73, 0xDE, 0x73, 0xDF, 0x73, 0xE0, 0x73, 0xE1, 0x73, 0xE2, 0x73, 0xE3, 0x73, 0xE4, 0x73, 0xE5, 0x73, 0xE6,\n\t0x73, 0xE7, 0x73, 0xE8, 0x73, 0xE9, 0x73, 0xEA, 0x73, 0xEB, 0x73, 0xEC, 0x73, 0xED, 0x73, 0xEE, 0x73, 0xEF, 0x73, 0xF0, 0x73, 0xF1, 0x73, 0xF2, 0x73, 0xF3, 0x73, 0xF4, 0x73, 0xF5, 0x73, 0xF6,\n\t0x73, 0xF7, 0x73, 0xF8, 0x73, 0xF9, 0x73, 0xFA, 0x73, 0xFB, 0x73, 0xFC, 0x73, 0xFD, 0x73, 0xFE, 0x73, 0xFF, 0x73, 0x00, 0x74, 0x01, 0x74, 0x02, 0x74, 0x03, 0x74, 0x04, 0x74, 0x05, 0x74, 0x06,\n\t0x74, 0x07, 0x74, 0x08, 0x74, 0x09, 0x74, 0x0A, 0x74, 0x0B, 0x74, 0x0C, 0x74, 0x0D, 0x74, 0x0E, 0x74, 0x0F, 0x74, 0x10, 0x74, 0x11, 0x74, 0x12, 0x74, 0x13, 0x74, 0x14, 0x74, 0x15, 0x74, 0x16,\n\t0x74, 0x17, 0x74, 0x18, 0x74, 0x19, 0x74, 0x1A, 0x74, 0x1B, 0x74, 0x1C, 0x74, 0x1D, 0x74, 0x1E, 0x74, 0x1F, 0x74, 0x20, 0x74, 0x21, 0x74, 0x22, 0x74, 0x23, 0x74, 0x24, 0x74, 0x25, 0x74, 0x26,\n\t0x74, 0x27, 0x74, 0x28, 0x74, 0x29, 0x74, 0x2A, 0x74, 0x2B, 0x74, 0x2C, 0x74, 0x2D, 0x74, 0x2E, 0x74, 0x2F, 0x74, 0x30, 0x74, 0x31, 0x74, 0x32, 0x74, 0x33, 0x74, 0x34, 0x74, 0x35, 0x74, 0x36,\n\t0x74, 0x37, 0x74, 0x38, 0x74, 0x39, 0x74, 0x3A, 0x74, 0x3B, 0x74, 0x3C, 0x74, 0x3D, 0x74, 0x3E, 0x74, 0x3F, 0x74, 0x40, 0x74, 0x41, 0x74, 0x42, 0x74, 0x43, 0x74, 0x44, 0x74, 0x45, 0x74, 0x46,\n\t0x74, 0x47, 0x74, 0x48, 0x74, 0x49, 0x74, 0x4A, 0x74, 0x4B, 0x74, 0x4C, 0x74, 0x4D, 0x74, 0x4E, 0x74, 0x4F, 0x74, 0x50, 0x74, 0x51, 0x74, 0x52, 0x74, 0x53, 0x74, 0x54, 0x74, 0x55, 0x74, 0x56,\n\t0x74, 0x57, 0x74, 0x58, 0x74, 0x59, 0x74, 0x5A, 0x74, 0x5B, 0x74, 0x5C, 0x74, 0x5D, 0x74, 0x5E, 0x74, 0x5F, 0x74, 0x60, 0x74, 0x61, 0x74, 0x62, 0x74, 0x63, 0x74, 0x64, 0x74, 0x65, 0x74, 0x66,\n\t0x74, 0x67, 0x74, 0x68, 0x74, 0x69, 0x74, 0x6A, 0x74, 0x6B, 0x74, 0x6C, 0x74, 0x6D, 0x74, 0x6E, 0x74, 0x6F, 0x74, 0x70, 0x74, 0x71, 0x74, 0x72, 0x74, 0x73, 0x74, 0x74, 0x74, 0x75, 0x74, 0x76,\n\t0x74, 0x77, 0x74, 0x78, 0x74, 0x79, 0x74, 0x7A, 0x74, 0x7B, 0x74, 0x7C, 0x74, 0x7D, 0x74, 0x7E, 0x74, 0x7F, 0x74, 0x80, 0x74, 0x81, 0x74, 0x82, 0x74, 0x83, 0x74, 0x84, 0x74, 0x85, 0x74, 0x86,\n\t0x74, 0x87, 0x74, 0x88, 0x74, 0x89, 0x74, 0x8A, 0x74, 0x8B, 0x74, 0x8C, 0x74, 0x8D, 0x74, 0x8E, 0x74, 0x8F, 0x74, 0x90, 0x74, 0x91, 0x74, 0x92, 0x74, 0x93, 0x74, 0x94, 0x74, 0x95, 0x74, 0x96,\n\t0x74, 0x97, 0x74, 0x98, 0x74, 0x99, 0x74, 0x9A, 0x74, 0x9B, 0x74, 0x9C, 0x74, 0x9D, 0x74, 0x9E, 0x74, 0x9F, 0x74, 0xA0, 0x74, 0xA1, 0x74, 0xA2, 0x74, 0xA3, 0x74, 0xA4, 0x74, 0xA5, 0x74, 0xA6,\n\t0x74, 0xA7, 0x74, 0xA8, 0x74, 0xA9, 0x74, 0xAA, 0x74, 0xAB, 0x74, 0xAC, 0x74, 0xAD, 0x74, 0xAE, 0x74, 0xAF, 0x74, 0xB0, 0x74, 0xB1, 0x74, 0xB2, 0x74, 0xB3, 0x74, 0xB4, 0x74, 0xB5, 0x74, 0xB6,\n\t0x74, 0xB7, 0x74, 0xB8, 0x74, 0xB9, 0x74, 0xBA, 0x74, 0xBB, 0x74, 0xBC, 0x74, 0xBD, 0x74, 0xBE, 0x74, 0xBF, 0x74, 0xC0, 0x74, 0xC1, 0x74, 0xC2, 0x74, 0xC3, 0x74, 0xC4, 0x74, 0xC5, 0x74, 0xC6,\n\t0x74, 0xC7, 0x74, 0xC8, 0x74, 0xC9, 0x74, 0xCA, 0x74, 0xCB, 0x74, 0xCC, 0x74, 0xCD, 0x74, 0xCE, 0x74, 0xCF, 0x74, 0xD0, 0x74, 0xD1, 0x74, 0xD2, 0x74, 0xD3, 0x74, 0xD4, 0x74, 0xD5, 0x74, 0xD6,\n\t0x74, 0xD7, 0x74, 0xD8, 0x74, 0xD9, 0x74, 0xDA, 0x74, 0xDB, 0x74, 0xDC, 0x74, 0xDD, 0x74, 0xDE, 0x74, 0xDF, 0x74, 0xE0, 0x74, 0xE1, 0x74, 0xE2, 0x74, 0xE3, 0x74, 0xE4, 0x74, 0xE5, 0x74, 0xE6,\n\t0x74, 0xE7, 0x74, 0xE8, 0x74, 0xE9, 0x74, 0xEA, 0x74, 0xEB, 0x74, 0xEC, 0x74, 0xED, 0x74, 0xEE, 0x74, 0xEF, 0x74, 0xF0, 0x74, 0xF1, 0x74, 0xF2, 0x74, 0xF3, 0x74, 0xF4, 0x74, 0xF5, 0x74, 0xF6,\n\t0x74, 0xF7, 0x74, 0xF8, 0x74, 0xF9, 0x74, 0xFA, 0x74, 0xFB, 0x74, 0xFC, 0x74, 0xFD, 0x74, 0xFE, 0x74, 0xFF, 0x74, 0x00, 0x75, 0x01, 0x75, 0x02, 0x75, 0x03, 0x75, 0x04, 0x75, 0x05, 0x75, 0x06,\n\t0x75, 0x07, 0x75, 0x08, 0x75, 0x09, 0x75, 0x0A, 0x75, 0x0B, 0x75, 0x0C, 0x75, 0x0D, 0x75, 0x0E, 0x75, 0x0F, 0x75, 0x10, 0x75, 0x11, 0x75, 0x12, 0x75, 0x13, 0x75, 0x14, 0x75, 0x15, 0x75, 0x16,\n\t0x75, 0x17, 0x75, 0x18, 0x75, 0x19, 0x75, 0x1A, 0x75, 0x1B, 0x75, 0x1C, 0x75, 0x1D, 0x75, 0x1E, 0x75, 0x1F, 0x75, 0x20, 0x75, 0x21, 0x75, 0x22, 0x75, 0x23, 0x75, 0x24, 0x75, 0x25, 0x75, 0x26,\n\t0x75, 0x27, 0x75, 0x28, 0x75, 0x29, 0x75, 0x2A, 0x75, 0x2B, 0x75, 0x2C, 0x75, 0x2D, 0x75, 0x2E, 0x75, 0x2F, 0x75, 0x30, 0x75, 0x31, 0x75, 0x32, 0x75, 0x33, 0x75, 0x34, 0x75, 0x35, 0x75, 0x36,\n\t0x75, 0x37, 0x75, 0x38, 0x75, 0x39, 0x75, 0x3A, 0x75, 0x3B, 0x75, 0x3C, 0x75, 0x3D, 0x75, 0x3E, 0x75, 0x3F, 0x75, 0x40, 0x75, 0x41, 0x75, 0x42, 0x75, 0x43, 0x75, 0x44, 0x75, 0x45, 0x75, 0x46,\n\t0x75, 0x47, 0x75, 0x48, 0x75, 0x49, 0x75, 0x4A, 0x75, 0x4B, 0x75, 0x4C, 0x75, 0x4D, 0x75, 0x4E, 0x75, 0x4F, 0x75, 0x50, 0x75, 0x51, 0x75, 0x52, 0x75, 0x53, 0x75, 0x54, 0x75, 0x55, 0x75, 0x56,\n\t0x75, 0x57, 0x75, 0x58, 0x75, 0x59, 0x75, 0x5A, 0x75, 0x5B, 0x75, 0x5C, 0x75, 0x5D, 0x75, 0x5E, 0x75, 0x5F, 0x75, 0x60, 0x75, 0x61, 0x75, 0x62, 0x75, 0x63, 0x75, 0x64, 0x75, 0x65, 0x75, 0x66,\n\t0x75, 0x67, 0x75, 0x68, 0x75, 0x69, 0x75, 0x6A, 0x75, 0x6B, 0x75, 0x6C, 0x75, 0x6D, 0x75, 0x6E, 0x75, 0x6F, 0x75, 0x70, 0x75, 0x71, 0x75, 0x72, 0x75, 0x73, 0x75, 0x74, 0x75, 0x75, 0x75, 0x76,\n\t0x75, 0x77, 0x75, 0x78, 0x75, 0x79, 0x75, 0x7A, 0x75, 0x7B, 0x75, 0x7C, 0x75, 0x7D, 0x75, 0x7E, 0x75, 0x7F, 0x75, 0x80, 0x75, 0x81, 0x75, 0x82, 0x75, 0x83, 0x75, 0x84, 0x75, 0x85, 0x75, 0x86,\n\t0x75, 0x87, 0x75, 0x88, 0x75, 0x89, 0x75, 0x8A, 0x75, 0x8B, 0x75, 0x8C, 0x75, 0x8D, 0x75, 0x8E, 0x75, 0x8F, 0x75, 0x90, 0x75, 0x91, 0x75, 0x92, 0x75, 0x93, 0x75, 0x94, 0x75, 0x95, 0x75, 0x96,\n\t0x75, 0x97, 0x75, 0x98, 0x75, 0x99, 0x75, 0x9A, 0x75, 0x9B, 0x75, 0x9C, 0x75, 0x9D, 0x75, 0x9E, 0x75, 0x9F, 0x75, 0xA0, 0x75, 0xA1, 0x75, 0xA2, 0x75, 0xA3, 0x75, 0xA4, 0x75, 0xA5, 0x75, 0xA6,\n\t0x75, 0xA7, 0x75, 0xA8, 0x75, 0xA9, 0x75, 0xAA, 0x75, 0xAB, 0x75, 0xAC, 0x75, 0xAD, 0x75, 0xAE, 0x75, 0xAF, 0x75, 0xB0, 0x75, 0xB1, 0x75, 0xB2, 0x75, 0xB3, 0x75, 0xB4, 0x75, 0xB5, 0x75, 0xB6,\n\t0x75, 0xB7, 0x75, 0xB8, 0x75, 0xB9, 0x75, 0xBA, 0x75, 0xBB, 0x75, 0xBC, 0x75, 0xBD, 0x75, 0xBE, 0x75, 0xBF, 0x75, 0xC0, 0x75, 0xC1, 0x75, 0xC2, 0x75, 0xC3, 0x75, 0xC4, 0x75, 0xC5, 0x75, 0xC6,\n\t0x75, 0xC7, 0x75, 0xC8, 0x75, 0xC9, 0x75, 0xCA, 0x75, 0xCB, 0x75, 0xCC, 0x75, 0xCD, 0x75, 0xCE, 0x75, 0xCF, 0x75, 0xD0, 0x75, 0xD1, 0x75, 0xD2, 0x75, 0xD3, 0x75, 0xD4, 0x75, 0xD5, 0x75, 0xD6,\n\t0x75, 0xD7, 0x75, 0xD8, 0x75, 0xD9, 0x75, 0xDA, 0x75, 0xDB, 0x75, 0xDC, 0x75, 0xDD, 0x75, 0xDE, 0x75, 0xDF, 0x75, 0xE0, 0x75, 0xE1, 0x75, 0xE2, 0x75, 0xE3, 0x75, 0xE4, 0x75, 0xE5, 0x75, 0xE6,\n\t0x75, 0xE7, 0x75, 0xE8, 0x75, 0xE9, 0x75, 0xEA, 0x75, 0xEB, 0x75, 0xEC, 0x75, 0xED, 0x75, 0xEE, 0x75, 0xEF, 0x75, 0xF0, 0x75, 0xF1, 0x75, 0xF2, 0x75, 0xF3, 0x75, 0xF4, 0x75, 0xF5, 0x75, 0xF6,\n\t0x75, 0xF7, 0x75, 0xF8, 0x75, 0xF9, 0x75, 0xFA, 0x75, 0xFB, 0x75, 0xFC, 0x75, 0xFD, 0x75, 0xFE, 0x75, 0xFF, 0x75, 0x00, 0x76, 0x01, 0x76, 0x02, 0x76, 0x03, 0x76, 0x04, 0x76, 0x05, 0x76, 0x06,\n\t0x76, 0x07, 0x76, 0x08, 0x76, 0x09, 0x76, 0x0A, 0x76, 0x0B, 0x76, 0x0C, 0x76, 0x0D, 0x76, 0x0E, 0x76, 0x0F, 0x76, 0x10, 0x76, 0x11, 0x76, 0x12, 0x76, 0x13, 0x76, 0x14, 0x76, 0x15, 0x76, 0x16,\n\t0x76, 0x17, 0x76, 0x18, 0x76, 0x19, 0x76, 0x1A, 0x76, 0x1B, 0x76, 0x1C, 0x76, 0x1D, 0x76, 0x1E, 0x76, 0x1F, 0x76, 0x20, 0x76, 0x21, 0x76, 0x22, 0x76, 0x23, 0x76, 0x24, 0x76, 0x25, 0x76, 0x26,\n\t0x76, 0x27, 0x76, 0x28, 0x76, 0x29, 0x76, 0x2A, 0x76, 0x2B, 0x76, 0x2C, 0x76, 0x2D, 0x76, 0x2E, 0x76, 0x2F, 0x76, 0x30, 0x76, 0x31, 0x76, 0x32, 0x76, 0x33, 0x76, 0x34, 0x76, 0x35, 0x76, 0x36,\n\t0x76, 0x37, 0x76, 0x38, 0x76, 0x39, 0x76, 0x3A, 0x76, 0x3B, 0x76, 0x3C, 0x76, 0x3D, 0x76, 0x3E, 0x76, 0x3F, 0x76, 0x40, 0x76, 0x41, 0x76, 0x42, 0x76, 0x43, 0x76, 0x44, 0x76, 0x45, 0x76, 0x46,\n\t0x76, 0x47, 0x76, 0x48, 0x76, 0x49, 0x76, 0x4A, 0x76, 0x4B, 0x76, 0x4C, 0x76, 0x4D, 0x76, 0x4E, 0x76, 0x4F, 0x76, 0x50, 0x76, 0x51, 0x76, 0x52, 0x76, 0x53, 0x76, 0x54, 0x76, 0x55, 0x76, 0x56,\n\t0x76, 0x57, 0x76, 0x58, 0x76, 0x59, 0x76, 0x5A, 0x76, 0x5B, 0x76, 0x5C, 0x76, 0x5D, 0x76, 0x5E, 0x76, 0x5F, 0x76, 0x60, 0x76, 0x61, 0x76, 0x62, 0x76, 0x63, 0x76, 0x64, 0x76, 0x65, 0x76, 0x66,\n\t0x76, 0x67, 0x76, 0x68, 0x76, 0x69, 0x76, 0x6A, 0x76, 0x6B, 0x76, 0x6C, 0x76, 0x6D, 0x76, 0x6E, 0x76, 0x6F, 0x76, 0x70, 0x76, 0x71, 0x76, 0x72, 0x76, 0x73, 0x76, 0x74, 0x76, 0x75, 0x76, 0x76,\n\t0x76, 0x77, 0x76, 0x78, 0x76, 0x79, 0x76, 0x7A, 0x76, 0x7B, 0x76, 0x7C, 0x76, 0x7D, 0x76, 0x7E, 0x76, 0x7F, 0x76, 0x80, 0x76, 0x81, 0x76, 0x82, 0x76, 0x83, 0x76, 0x84, 0x76, 0x85, 0x76, 0x86,\n\t0x76, 0x87, 0x76, 0x88, 0x76, 0x89, 0x76, 0x8A, 0x76, 0x8B, 0x76, 0x8C, 0x76, 0x8D, 0x76, 0x8E, 0x76, 0x8F, 0x76, 0x90, 0x76, 0x91, 0x76, 0x92, 0x76, 0x93, 0x76, 0x94, 0x76, 0x95, 0x76, 0x96,\n\t0x76, 0x97, 0x76, 0x98, 0x76, 0x99, 0x76, 0x9A, 0x76, 0x9B, 0x76, 0x9C, 0x76, 0x9D, 0x76, 0x9E, 0x76, 0x9F, 0x76, 0xA0, 0x76, 0xA1, 0x76, 0xA2, 0x76, 0xA3, 0x76, 0xA4, 0x76, 0xA5, 0x76, 0xA6,\n\t0x76, 0xA7, 0x76, 0xA8, 0x76, 0xA9, 0x76, 0xAA, 0x76, 0xAB, 0x76, 0xAC, 0x76, 0xAD, 0x76, 0xAE, 0x76, 0xAF, 0x76, 0xB0, 0x76, 0xB1, 0x76, 0xB2, 0x76, 0xB3, 0x76, 0xB4, 0x76, 0xB5, 0x76, 0xB6,\n\t0x76, 0xB7, 0x76, 0xB8, 0x76, 0xB9, 0x76, 0xBA, 0x76, 0xBB, 0x76, 0xBC, 0x76, 0xBD, 0x76, 0xBE, 0x76, 0xBF, 0x76, 0xC0, 0x76, 0xC1, 0x76, 0xC2, 0x76, 0xC3, 0x76, 0xC4, 0x76, 0xC5, 0x76, 0xC6,\n\t0x76, 0xC7, 0x76, 0xC8, 0x76, 0xC9, 0x76, 0xCA, 0x76, 0xCB, 0x76, 0xCC, 0x76, 0xCD, 0x76, 0xCE, 0x76, 0xCF, 0x76, 0xD0, 0x76, 0xD1, 0x76, 0xD2, 0x76, 0xD3, 0x76, 0xD4, 0x76, 0xD5, 0x76, 0xD6,\n\t0x76, 0xD7, 0x76, 0xD8, 0x76, 0xD9, 0x76, 0xDA, 0x76, 0xDB, 0x76, 0xDC, 0x76, 0xDD, 0x76, 0xDE, 0x76, 0xDF, 0x76, 0xE0, 0x76, 0xE1, 0x76, 0xE2, 0x76, 0xE3, 0x76, 0xE4, 0x76, 0xE5, 0x76, 0xE6,\n\t0x76, 0xE7, 0x76, 0xE8, 0x76, 0xE9, 0x76, 0xEA, 0x76, 0xEB, 0x76, 0xEC, 0x76, 0xED, 0x76, 0xEE, 0x76, 0xEF, 0x76, 0xF0, 0x76, 0xF1, 0x76, 0xF2, 0x76, 0xF3, 0x76, 0xF4, 0x76, 0xF5, 0x76, 0xF6,\n\t0x76, 0xF7, 0x76, 0xF8, 0x76, 0xF9, 0x76, 0xFA, 0x76, 0xFB, 0x76, 0xFC, 0x76, 0xFD, 0x76, 0xFE, 0x76, 0xFF, 0x76, 0x00, 0x77, 0x01, 0x77, 0x02, 0x77, 0x03, 0x77, 0x04, 0x77, 0x05, 0x77, 0x06,\n\t0x77, 0x07, 0x77, 0x08, 0x77, 0x09, 0x77, 0x0A, 0x77, 0x0B, 0x77, 0x0C, 0x77, 0x0D, 0x77, 0x0E, 0x77, 0x0F, 0x77, 0x10, 0x77, 0x11, 0x77, 0x12, 0x77, 0x13, 0x77, 0x14, 0x77, 0x15, 0x77, 0x16,\n\t0x77, 0x17, 0x77, 0x18, 0x77, 0x19, 0x77, 0x1A, 0x77, 0x1B, 0x77, 0x1C, 0x77, 0x1D, 0x77, 0x1E, 0x77, 0x1F, 0x77, 0x20, 0x77, 0x21, 0x77, 0x22, 0x77, 0x23, 0x77, 0x24, 0x77, 0x25, 0x77, 0x26,\n\t0x77, 0x27, 0x77, 0x28, 0x77, 0x29, 0x77, 0x2A, 0x77, 0x2B, 0x77, 0x2C, 0x77, 0x2D, 0x77, 0x2E, 0x77, 0x2F, 0x77, 0x30, 0x77, 0x31, 0x77, 0x32, 0x77, 0x33, 0x77, 0x34, 0x77, 0x35, 0x77, 0x36,\n\t0x77, 0x37, 0x77, 0x38, 0x77, 0x39, 0x77, 0x3A, 0x77, 0x3B, 0x77, 0x3C, 0x77, 0x3D, 0x77, 0x3E, 0x77, 0x3F, 0x77, 0x40, 0x77, 0x41, 0x77, 0x42, 0x77, 0x43, 0x77, 0x44, 0x77, 0x45, 0x77, 0x46,\n\t0x77, 0x47, 0x77, 0x48, 0x77, 0x49, 0x77, 0x4A, 0x77, 0x4B, 0x77, 0x4C, 0x77, 0x4D, 0x77, 0x4E, 0x77, 0x4F, 0x77, 0x50, 0x77, 0x51, 0x77, 0x52, 0x77, 0x53, 0x77, 0x54, 0x77, 0x55, 0x77, 0x56,\n\t0x77, 0x57, 0x77, 0x58, 0x77, 0x59, 0x77, 0x5A, 0x77, 0x5B, 0x77, 0x5C, 0x77, 0x5D, 0x77, 0x5E, 0x77, 0x5F, 0x77, 0x60, 0x77, 0x61, 0x77, 0x62, 0x77, 0x63, 0x77, 0x64, 0x77, 0x65, 0x77, 0x66,\n\t0x77, 0x67, 0x77, 0x68, 0x77, 0x69, 0x77, 0x6A, 0x77, 0x6B, 0x77, 0x6C, 0x77, 0x6D, 0x77, 0x6E, 0x77, 0x6F, 0x77, 0x70, 0x77, 0x71, 0x77, 0x72, 0x77, 0x73, 0x77, 0x74, 0x77, 0x75, 0x77, 0x76,\n\t0x77, 0x77, 0x77, 0x78, 0x77, 0x79, 0x77, 0x7A, 0x77, 0x7B, 0x77, 0x7C, 0x77, 0x7D, 0x77, 0x7E, 0x77, 0x7F, 0x77, 0x80, 0x77, 0x81, 0x77, 0x82, 0x77, 0x83, 0x77, 0x84, 0x77, 0x85, 0x77, 0x86,\n\t0x77, 0x87, 0x77, 0x88, 0x77, 0x89, 0x77, 0x8A, 0x77, 0x8B, 0x77, 0x8C, 0x77, 0x8D, 0x77, 0x8E, 0x77, 0x8F, 0x77, 0x90, 0x77, 0x91, 0x77, 0x92, 0x77, 0x93, 0x77, 0x94, 0x77, 0x95, 0x77, 0x96,\n\t0x77, 0x97, 0x77, 0x98, 0x77, 0x99, 0x77, 0x9A, 0x77, 0x9B, 0x77, 0x9C, 0x77, 0x9D, 0x77, 0x9E, 0x77, 0x9F, 0x77, 0xA0, 0x77, 0xA1, 0x77, 0xA2, 0x77, 0xA3, 0x77, 0xA4, 0x77, 0xA5, 0x77, 0xA6,\n\t0x77, 0xA7, 0x77, 0xA8, 0x77, 0xA9, 0x77, 0xAA, 0x77, 0xAB, 0x77, 0xAC, 0x77, 0xAD, 0x77, 0xAE, 0x77, 0xAF, 0x77, 0xB0, 0x77, 0xB1, 0x77, 0xB2, 0x77, 0xB3, 0x77, 0xB4, 0x77, 0xB5, 0x77, 0xB6,\n\t0x77, 0xB7, 0x77, 0xB8, 0x77, 0xB9, 0x77, 0xBA, 0x77, 0xBB, 0x77, 0xBC, 0x77, 0xBD, 0x77, 0xBE, 0x77, 0xBF, 0x77, 0xC0, 0x77, 0xC1, 0x77, 0xC2, 0x77, 0xC3, 0x77, 0xC4, 0x77, 0xC5, 0x77, 0xC6,\n\t0x77, 0xC7, 0x77, 0xC8, 0x77, 0xC9, 0x77, 0xCA, 0x77, 0xCB, 0x77, 0xCC, 0x77, 0xCD, 0x77, 0xCE, 0x77, 0xCF, 0x77, 0xD0, 0x77, 0xD1, 0x77, 0xD2, 0x77, 0xD3, 0x77, 0xD4, 0x77, 0xD5, 0x77, 0xD6,\n\t0x77, 0xD7, 0x77, 0xD8, 0x77, 0xD9, 0x77, 0xDA, 0x77, 0xDB, 0x77, 0xDC, 0x77, 0xDD, 0x77, 0xDE, 0x77, 0xDF, 0x77, 0xE0, 0x77, 0xE1, 0x77, 0xE2, 0x77, 0xE3, 0x77, 0xE4, 0x77, 0xE5, 0x77, 0xE6,\n\t0x77, 0xE7, 0x77, 0xE8, 0x77, 0xE9, 0x77, 0xEA, 0x77, 0xEB, 0x77, 0xEC, 0x77, 0xED, 0x77, 0xEE, 0x77, 0xEF, 0x77, 0xF0, 0x77, 0xF1, 0x77, 0xF2, 0x77, 0xF3, 0x77, 0xF4, 0x77, 0xF5, 0x77, 0xF6,\n\t0x77, 0xF7, 0x77, 0xF8, 0x77, 0xF9, 0x77, 0xFA, 0x77, 0xFB, 0x77, 0xFC, 0x77, 0xFD, 0x77, 0xFE, 0x77, 0xFF, 0x77, 0x00, 0x78, 0x01, 0x78, 0x02, 0x78, 0x03, 0x78, 0x04, 0x78, 0x05, 0x78, 0x06,\n\t0x78, 0x07, 0x78, 0x08, 0x78, 0x09, 0x78, 0x0A, 0x78, 0x0B, 0x78, 0x0C, 0x78, 0x0D, 0x78, 0x0E, 0x78, 0x0F, 0x78, 0x10, 0x78, 0x11, 0x78, 0x12, 0x78, 0x13, 0x78, 0x14, 0x78, 0x15, 0x78, 0x16,\n\t0x78, 0x17, 0x78, 0x18, 0x78, 0x19, 0x78, 0x1A, 0x78, 0x1B, 0x78, 0x1C, 0x78, 0x1D, 0x78, 0x1E, 0x78, 0x1F, 0x78, 0x20, 0x78, 0x21, 0x78, 0x22, 0x78, 0x23, 0x78, 0x24, 0x78, 0x25, 0x78, 0x26,\n\t0x78, 0x27, 0x78, 0x28, 0x78, 0x29, 0x78, 0x2A, 0x78, 0x2B, 0x78, 0x2C, 0x78, 0x2D, 0x78, 0x2E, 0x78, 0x2F, 0x78, 0x30, 0x78, 0x31, 0x78, 0x32, 0x78, 0x33, 0x78, 0x34, 0x78, 0x35, 0x78, 0x36,\n\t0x78, 0x37, 0x78, 0x38, 0x78, 0x39, 0x78, 0x3A, 0x78, 0x3B, 0x78, 0x3C, 0x78, 0x3D, 0x78, 0x3E, 0x78, 0x3F, 0x78, 0x40, 0x78, 0x41, 0x78, 0x42, 0x78, 0x43, 0x78, 0x44, 0x78, 0x45, 0x78, 0x46,\n\t0x78, 0x47, 0x78, 0x48, 0x78, 0x49, 0x78, 0x4A, 0x78, 0x4B, 0x78, 0x4C, 0x78, 0x4D, 0x78, 0x4E, 0x78, 0x4F, 0x78, 0x50, 0x78, 0x51, 0x78, 0x52, 0x78, 0x53, 0x78, 0x54, 0x78, 0x55, 0x78, 0x56,\n\t0x78, 0x57, 0x78, 0x58, 0x78, 0x59, 0x78, 0x5A, 0x78, 0x5B, 0x78, 0x5C, 0x78, 0x5D, 0x78, 0x5E, 0x78, 0x5F, 0x78, 0x60, 0x78, 0x61, 0x78, 0x62, 0x78, 0x63, 0x78, 0x64, 0x78, 0x65, 0x78, 0x66,\n\t0x78, 0x67, 0x78, 0x68, 0x78, 0x69, 0x78, 0x6A, 0x78, 0x6B, 0x78, 0x6C, 0x78, 0x6D, 0x78, 0x6E, 0x78, 0x6F, 0x78, 0x70, 0x78, 0x71, 0x78, 0x72, 0x78, 0x73, 0x78, 0x74, 0x78, 0x75, 0x78, 0x76,\n\t0x78, 0x77, 0x78, 0x78, 0x78, 0x79, 0x78, 0x7A, 0x78, 0x7B, 0x78, 0x7C, 0x78, 0x7D, 0x78, 0x7E, 0x78, 0x7F, 0x78, 0x80, 0x78, 0x81, 0x78, 0x82, 0x78, 0x83, 0x78, 0x84, 0x78, 0x85, 0x78, 0x86,\n\t0x78, 0x87, 0x78, 0x88, 0x78, 0x89, 0x78, 0x8A, 0x78, 0x8B, 0x78, 0x8C, 0x78, 0x8D, 0x78, 0x8E, 0x78, 0x8F, 0x78, 0x90, 0x78, 0x91, 0x78, 0x92, 0x78, 0x93, 0x78, 0x94, 0x78, 0x95, 0x78, 0x96,\n\t0x78, 0x97, 0x78, 0x98, 0x78, 0x99, 0x78, 0x9A, 0x78, 0x9B, 0x78, 0x9C, 0x78, 0x9D, 0x78, 0x9E, 0x78, 0x9F, 0x78, 0xA0, 0x78, 0xA1, 0x78, 0xA2, 0x78, 0xA3, 0x78, 0xA4, 0x78, 0xA5, 0x78, 0xA6,\n\t0x78, 0xA7, 0x78, 0xA8, 0x78, 0xA9, 0x78, 0xAA, 0x78, 0xAB, 0x78, 0xAC, 0x78, 0xAD, 0x78, 0xAE, 0x78, 0xAF, 0x78, 0xB0, 0x78, 0xB1, 0x78, 0xB2, 0x78, 0xB3, 0x78, 0xB4, 0x78, 0xB5, 0x78, 0xB6,\n\t0x78, 0xB7, 0x78, 0xB8, 0x78, 0xB9, 0x78, 0xBA, 0x78, 0xBB, 0x78, 0xBC, 0x78, 0xBD, 0x78, 0xBE, 0x78, 0xBF, 0x78, 0xC0, 0x78, 0xC1, 0x78, 0xC2, 0x78, 0xC3, 0x78, 0xC4, 0x78, 0xC5, 0x78, 0xC6,\n\t0x78, 0xC7, 0x78, 0xC8, 0x78, 0xC9, 0x78, 0xCA, 0x78, 0xCB, 0x78, 0xCC, 0x78, 0xCD, 0x78, 0xCE, 0x78, 0xCF, 0x78, 0xD0, 0x78, 0xD1, 0x78, 0xD2, 0x78, 0xD3, 0x78, 0xD4, 0x78, 0xD5, 0x78, 0xD6,\n\t0x78, 0xD7, 0x78, 0xD8, 0x78, 0xD9, 0x78, 0xDA, 0x78, 0xDB, 0x78, 0xDC, 0x78, 0xDD, 0x78, 0xDE, 0x78, 0xDF, 0x78, 0xE0, 0x78, 0xE1, 0x78, 0xE2, 0x78, 0xE3, 0x78, 0xE4, 0x78, 0xE5, 0x78, 0xE6,\n\t0x78, 0xE7, 0x78, 0xE8, 0x78, 0xE9, 0x78, 0xEA, 0x78, 0xEB, 0x78, 0xEC, 0x78, 0xED, 0x78, 0xEE, 0x78, 0xEF, 0x78, 0xF0, 0x78, 0xF1, 0x78, 0xF2, 0x78, 0xF3, 0x78, 0xF4, 0x78, 0xF5, 0x78, 0xF6,\n\t0x78, 0xF7, 0x78, 0xF8, 0x78, 0xF9, 0x78, 0xFA, 0x78, 0xFB, 0x78, 0xFC, 0x78, 0xFD, 0x78, 0xFE, 0x78, 0xFF, 0x78, 0x00, 0x79, 0x01, 0x79, 0x02, 0x79, 0x03, 0x79, 0x04, 0x79, 0x05, 0x79, 0x06,\n\t0x79, 0x07, 0x79, 0x08, 0x79, 0x09, 0x79, 0x0A, 0x79, 0x0B, 0x79, 0x0C, 0x79, 0x0D, 0x79, 0x0E, 0x79, 0x0F, 0x79, 0x10, 0x79, 0x11, 0x79, 0x12, 0x79, 0x13, 0x79, 0x14, 0x79, 0x15, 0x79, 0x16,\n\t0x79, 0x17, 0x79, 0x18, 0x79, 0x19, 0x79, 0x1A, 0x79, 0x1B, 0x79, 0x1C, 0x79, 0x1D, 0x79, 0x1E, 0x79, 0x1F, 0x79, 0x20, 0x79, 0x21, 0x79, 0x22, 0x79, 0x23, 0x79, 0x24, 0x79, 0x25, 0x79, 0x26,\n\t0x79, 0x27, 0x79, 0x28, 0x79, 0x29, 0x79, 0x2A, 0x79, 0x2B, 0x79, 0x2C, 0x79, 0x2D, 0x79, 0x2E, 0x79, 0x2F, 0x79, 0x30, 0x79, 0x31, 0x79, 0x32, 0x79, 0x33, 0x79, 0x34, 0x79, 0x35, 0x79, 0x36,\n\t0x79, 0x37, 0x79, 0x38, 0x79, 0x39, 0x79, 0x3A, 0x79, 0x3B, 0x79, 0x3C, 0x79, 0x3D, 0x79, 0x3E, 0x79, 0x3F, 0x79, 0x40, 0x79, 0x41, 0x79, 0x42, 0x79, 0x43, 0x79, 0x44, 0x79, 0x45, 0x79, 0x46,\n\t0x79, 0x47, 0x79, 0x48, 0x79, 0x49, 0x79, 0x4A, 0x79, 0x4B, 0x79, 0x4C, 0x79, 0x4D, 0x79, 0x4E, 0x79, 0x4F, 0x79, 0x50, 0x79, 0x51, 0x79, 0x52, 0x79, 0x53, 0x79, 0x54, 0x79, 0x55, 0x79, 0x56,\n\t0x79, 0x57, 0x79, 0x58, 0x79, 0x59, 0x79, 0x5A, 0x79, 0x5B, 0x79, 0x5C, 0x79, 0x5D, 0x79, 0x5E, 0x79, 0x5F, 0x79, 0x60, 0x79, 0x61, 0x79, 0x62, 0x79, 0x63, 0x79, 0x64, 0x79, 0x65, 0x79, 0x66,\n\t0x79, 0x67, 0x79, 0x68, 0x79, 0x69, 0x79, 0x6A, 0x79, 0x6B, 0x79, 0x6C, 0x79, 0x6D, 0x79, 0x6E, 0x79, 0x6F, 0x79, 0x70, 0x79, 0x71, 0x79, 0x72, 0x79, 0x73, 0x79, 0x74, 0x79, 0x75, 0x79, 0x76,\n\t0x79, 0x77, 0x79, 0x78, 0x79, 0x79, 0x79, 0x7A, 0x79, 0x7B, 0x79, 0x7C, 0x79, 0x7D, 0x79, 0x7E, 0x79, 0x7F, 0x79, 0x80, 0x79, 0x81, 0x79, 0x82, 0x79, 0x83, 0x79, 0x84, 0x79, 0x85, 0x79, 0x86,\n\t0x79, 0x87, 0x79, 0x88, 0x79, 0x89, 0x79, 0x8A, 0x79, 0x8B, 0x79, 0x8C, 0x79, 0x8D, 0x79, 0x8E, 0x79, 0x8F, 0x79, 0x90, 0x79, 0x91, 0x79, 0x92, 0x79, 0x93, 0x79, 0x94, 0x79, 0x95, 0x79, 0x96,\n\t0x79, 0x97, 0x79, 0x98, 0x79, 0x99, 0x79, 0x9A, 0x79, 0x9B, 0x79, 0x9C, 0x79, 0x9D, 0x79, 0x9E, 0x79, 0x9F, 0x79, 0xA0, 0x79, 0xA1, 0x79, 0xA2, 0x79, 0xA3, 0x79, 0xA4, 0x79, 0xA5, 0x79, 0xA6,\n\t0x79, 0xA7, 0x79, 0xA8, 0x79, 0xA9, 0x79, 0xAA, 0x79, 0xAB, 0x79, 0xAC, 0x79, 0xAD, 0x79, 0xAE, 0x79, 0xAF, 0x79, 0xB0, 0x79, 0xB1, 0x79, 0xB2, 0x79, 0xB3, 0x79, 0xB4, 0x79, 0xB5, 0x79, 0xB6,\n\t0x79, 0xB7, 0x79, 0xB8, 0x79, 0xB9, 0x79, 0xBA, 0x79, 0xBB, 0x79, 0xBC, 0x79, 0xBD, 0x79, 0xBE, 0x79, 0xBF, 0x79, 0xC0, 0x79, 0xC1, 0x79, 0xC2, 0x79, 0xC3, 0x79, 0xC4, 0x79, 0xC5, 0x79, 0xC6,\n\t0x79, 0xC7, 0x79, 0xC8, 0x79, 0xC9, 0x79, 0xCA, 0x79, 0xCB, 0x79, 0xCC, 0x79, 0xCD, 0x79, 0xCE, 0x79, 0xCF, 0x79, 0xD0, 0x79, 0xD1, 0x79, 0xD2, 0x79, 0xD3, 0x79, 0xD4, 0x79, 0xD5, 0x79, 0xD6,\n\t0x79, 0xD7, 0x79, 0xD8, 0x79, 0xD9, 0x79, 0xDA, 0x79, 0xDB, 0x79, 0xDC, 0x79, 0xDD, 0x79, 0xDE, 0x79, 0xDF, 0x79, 0xE0, 0x79, 0xE1, 0x79, 0xE2, 0x79, 0xE3, 0x79, 0xE4, 0x79, 0xE5, 0x79, 0xE6,\n\t0x79, 0xE7, 0x79, 0xE8, 0x79, 0xE9, 0x79, 0xEA, 0x79, 0xEB, 0x79, 0xEC, 0x79, 0xED, 0x79, 0xEE, 0x79, 0xEF, 0x79, 0xF0, 0x79, 0xF1, 0x79, 0xF2, 0x79, 0xF3, 0x79, 0xF4, 0x79, 0xF5, 0x79, 0xF6,\n\t0x79, 0xF7, 0x79, 0xF8, 0x79, 0xF9, 0x79, 0xFA, 0x79, 0xFB, 0x79, 0xFC, 0x79, 0xFD, 0x79, 0xFE, 0x79, 0xFF, 0x79, 0x00, 0x7A, 0x01, 0x7A, 0x02, 0x7A, 0x03, 0x7A, 0x04, 0x7A, 0x05, 0x7A, 0x06,\n\t0x7A, 0x07, 0x7A, 0x08, 0x7A, 0x09, 0x7A, 0x0A, 0x7A, 0x0B, 0x7A, 0x0C, 0x7A, 0x0D, 0x7A, 0x0E, 0x7A, 0x0F, 0x7A, 0x10, 0x7A, 0x11, 0x7A, 0x12, 0x7A, 0x13, 0x7A, 0x14, 0x7A, 0x15, 0x7A, 0x16,\n\t0x7A, 0x17, 0x7A, 0x18, 0x7A, 0x19, 0x7A, 0x1A, 0x7A, 0x1B, 0x7A, 0x1C, 0x7A, 0x1D, 0x7A, 0x1E, 0x7A, 0x1F, 0x7A, 0x20, 0x7A, 0x21, 0x7A, 0x22, 0x7A, 0x23, 0x7A, 0x24, 0x7A, 0x25, 0x7A, 0x26,\n\t0x7A, 0x27, 0x7A, 0x28, 0x7A, 0x29, 0x7A, 0x2A, 0x7A, 0x2B, 0x7A, 0x2C, 0x7A, 0x2D, 0x7A, 0x2E, 0x7A, 0x2F, 0x7A, 0x30, 0x7A, 0x31, 0x7A, 0x32, 0x7A, 0x33, 0x7A, 0x34, 0x7A, 0x35, 0x7A, 0x36,\n\t0x7A, 0x37, 0x7A, 0x38, 0x7A, 0x39, 0x7A, 0x3A, 0x7A, 0x3B, 0x7A, 0x3C, 0x7A, 0x3D, 0x7A, 0x3E, 0x7A, 0x3F, 0x7A, 0x40, 0x7A, 0x41, 0x7A, 0x42, 0x7A, 0x43, 0x7A, 0x44, 0x7A, 0x45, 0x7A, 0x46,\n\t0x7A, 0x47, 0x7A, 0x48, 0x7A, 0x49, 0x7A, 0x4A, 0x7A, 0x4B, 0x7A, 0x4C, 0x7A, 0x4D, 0x7A, 0x4E, 0x7A, 0x4F, 0x7A, 0x50, 0x7A, 0x51, 0x7A, 0x52, 0x7A, 0x53, 0x7A, 0x54, 0x7A, 0x55, 0x7A, 0x56,\n\t0x7A, 0x57, 0x7A, 0x58, 0x7A, 0x59, 0x7A, 0x5A, 0x7A, 0x5B, 0x7A, 0x5C, 0x7A, 0x5D, 0x7A, 0x5E, 0x7A, 0x5F, 0x7A, 0x60, 0x7A, 0x61, 0x7A, 0x62, 0x7A, 0x63, 0x7A, 0x64, 0x7A, 0x65, 0x7A, 0x66,\n\t0x7A, 0x67, 0x7A, 0x68, 0x7A, 0x69, 0x7A, 0x6A, 0x7A, 0x6B, 0x7A, 0x6C, 0x7A, 0x6D, 0x7A, 0x6E, 0x7A, 0x6F, 0x7A, 0x70, 0x7A, 0x71, 0x7A, 0x72, 0x7A, 0x73, 0x7A, 0x74, 0x7A, 0x75, 0x7A, 0x76,\n\t0x7A, 0x77, 0x7A, 0x78, 0x7A, 0x79, 0x7A, 0x7A, 0x7A, 0x7B, 0x7A, 0x7C, 0x7A, 0x7D, 0x7A, 0x7E, 0x7A, 0x7F, 0x7A, 0x80, 0x7A, 0x81, 0x7A, 0x82, 0x7A, 0x83, 0x7A, 0x84, 0x7A, 0x85, 0x7A, 0x86,\n\t0x7A, 0x87, 0x7A, 0x88, 0x7A, 0x89, 0x7A, 0x8A, 0x7A, 0x8B, 0x7A, 0x8C, 0x7A, 0x8D, 0x7A, 0x8E, 0x7A, 0x8F, 0x7A, 0x90, 0x7A, 0x91, 0x7A, 0x92, 0x7A, 0x93, 0x7A, 0x94, 0x7A, 0x95, 0x7A, 0x96,\n\t0x7A, 0x97, 0x7A, 0x98, 0x7A, 0x99, 0x7A, 0x9A, 0x7A, 0x9B, 0x7A, 0x9C, 0x7A, 0x9D, 0x7A, 0x9E, 0x7A, 0x9F, 0x7A, 0xA0, 0x7A, 0xA1, 0x7A, 0xA2, 0x7A, 0xA3, 0x7A, 0xA4, 0x7A, 0xA5, 0x7A, 0xA6,\n\t0x7A, 0xA7, 0x7A, 0xA8, 0x7A, 0xA9, 0x7A, 0xAA, 0x7A, 0xAB, 0x7A, 0xAC, 0x7A, 0xAD, 0x7A, 0xAE, 0x7A, 0xAF, 0x7A, 0xB0, 0x7A, 0xB1, 0x7A, 0xB2, 0x7A, 0xB3, 0x7A, 0xB4, 0x7A, 0xB5, 0x7A, 0xB6,\n\t0x7A, 0xB7, 0x7A, 0xB8, 0x7A, 0xB9, 0x7A, 0xBA, 0x7A, 0xBB, 0x7A, 0xBC, 0x7A, 0xBD, 0x7A, 0xBE, 0x7A, 0xBF, 0x7A, 0xC0, 0x7A, 0xC1, 0x7A, 0xC2, 0x7A, 0xC3, 0x7A, 0xC4, 0x7A, 0xC5, 0x7A, 0xC6,\n\t0x7A, 0xC7, 0x7A, 0xC8, 0x7A, 0xC9, 0x7A, 0xCA, 0x7A, 0xCB, 0x7A, 0xCC, 0x7A, 0xCD, 0x7A, 0xCE, 0x7A, 0xCF, 0x7A, 0xD0, 0x7A, 0xD1, 0x7A, 0xD2, 0x7A, 0xD3, 0x7A, 0xD4, 0x7A, 0xD5, 0x7A, 0xD6,\n\t0x7A, 0xD7, 0x7A, 0xD8, 0x7A, 0xD9, 0x7A, 0xDA, 0x7A, 0xDB, 0x7A, 0xDC, 0x7A, 0xDD, 0x7A, 0xDE, 0x7A, 0xDF, 0x7A, 0xE0, 0x7A, 0xE1, 0x7A, 0xE2, 0x7A, 0xE3, 0x7A, 0xE4, 0x7A, 0xE5, 0x7A, 0xE6,\n\t0x7A, 0xE7, 0x7A, 0xE8, 0x7A, 0xE9, 0x7A, 0xEA, 0x7A, 0xEB, 0x7A, 0xEC, 0x7A, 0xED, 0x7A, 0xEE, 0x7A, 0xEF, 0x7A, 0xF0, 0x7A, 0xF1, 0x7A, 0xF2, 0x7A, 0xF3, 0x7A, 0xF4, 0x7A, 0xF5, 0x7A, 0xF6,\n\t0x7A, 0xF7, 0x7A, 0xF8, 0x7A, 0xF9, 0x7A, 0xFA, 0x7A, 0xFB, 0x7A, 0xFC, 0x7A, 0xFD, 0x7A, 0xFE, 0x7A, 0xFF, 0x7A, 0x00, 0x7B, 0x01, 0x7B, 0x02, 0x7B, 0x03, 0x7B, 0x04, 0x7B, 0x05, 0x7B, 0x06,\n\t0x7B, 0x07, 0x7B, 0x08, 0x7B, 0x09, 0x7B, 0x0A, 0x7B, 0x0B, 0x7B, 0x0C, 0x7B, 0x0D, 0x7B, 0x0E, 0x7B, 0x0F, 0x7B, 0x10, 0x7B, 0x11, 0x7B, 0x12, 0x7B, 0x13, 0x7B, 0x14, 0x7B, 0x15, 0x7B, 0x16,\n\t0x7B, 0x17, 0x7B, 0x18, 0x7B, 0x19, 0x7B, 0x1A, 0x7B, 0x1B, 0x7B, 0x1C, 0x7B, 0x1D, 0x7B, 0x1E, 0x7B, 0x1F, 0x7B, 0x20, 0x7B, 0x21, 0x7B, 0x22, 0x7B, 0x23, 0x7B, 0x24, 0x7B, 0x25, 0x7B, 0x26,\n\t0x7B, 0x27, 0x7B, 0x28, 0x7B, 0x29, 0x7B, 0x2A, 0x7B, 0x2B, 0x7B, 0x2C, 0x7B, 0x2D, 0x7B, 0x2E, 0x7B, 0x2F, 0x7B, 0x30, 0x7B, 0x31, 0x7B, 0x32, 0x7B, 0x33, 0x7B, 0x34, 0x7B, 0x35, 0x7B, 0x36,\n\t0x7B, 0x37, 0x7B, 0x38, 0x7B, 0x39, 0x7B, 0x3A, 0x7B, 0x3B, 0x7B, 0x3C, 0x7B, 0x3D, 0x7B, 0x3E, 0x7B, 0x3F, 0x7B, 0x40, 0x7B, 0x41, 0x7B, 0x42, 0x7B, 0x43, 0x7B, 0x44, 0x7B, 0x45, 0x7B, 0x46,\n\t0x7B, 0x47, 0x7B, 0x48, 0x7B, 0x49, 0x7B, 0x4A, 0x7B, 0x4B, 0x7B, 0x4C, 0x7B, 0x4D, 0x7B, 0x4E, 0x7B, 0x4F, 0x7B, 0x50, 0x7B, 0x51, 0x7B, 0x52, 0x7B, 0x53, 0x7B, 0x54, 0x7B, 0x55, 0x7B, 0x56,\n\t0x7B, 0x57, 0x7B, 0x58, 0x7B, 0x59, 0x7B, 0x5A, 0x7B, 0x5B, 0x7B, 0x5C, 0x7B, 0x5D, 0x7B, 0x5E, 0x7B, 0x5F, 0x7B, 0x60, 0x7B, 0x61, 0x7B, 0x62, 0x7B, 0x63, 0x7B, 0x64, 0x7B, 0x65, 0x7B, 0x66,\n\t0x7B, 0x67, 0x7B, 0x68, 0x7B, 0x69, 0x7B, 0x6A, 0x7B, 0x6B, 0x7B, 0x6C, 0x7B, 0x6D, 0x7B, 0x6E, 0x7B, 0x6F, 0x7B, 0x70, 0x7B, 0x71, 0x7B, 0x72, 0x7B, 0x73, 0x7B, 0x74, 0x7B, 0x75, 0x7B, 0x76,\n\t0x7B, 0x77, 0x7B, 0x78, 0x7B, 0x79, 0x7B, 0x7A, 0x7B, 0x7B, 0x7B, 0x7C, 0x7B, 0x7D, 0x7B, 0x7E, 0x7B, 0x7F, 0x7B, 0x80, 0x7B, 0x81, 0x7B, 0x82, 0x7B, 0x83, 0x7B, 0x84, 0x7B, 0x85, 0x7B, 0x86,\n\t0x7B, 0x87, 0x7B, 0x88, 0x7B, 0x89, 0x7B, 0x8A, 0x7B, 0x8B, 0x7B, 0x8C, 0x7B, 0x8D, 0x7B, 0x8E, 0x7B, 0x8F, 0x7B, 0x90, 0x7B, 0x91, 0x7B, 0x92, 0x7B, 0x93, 0x7B, 0x94, 0x7B, 0x95, 0x7B, 0x96,\n\t0x7B, 0x97, 0x7B, 0x98, 0x7B, 0x99, 0x7B, 0x9A, 0x7B, 0x9B, 0x7B, 0x9C, 0x7B, 0x9D, 0x7B, 0x9E, 0x7B, 0x9F, 0x7B, 0xA0, 0x7B, 0xA1, 0x7B, 0xA2, 0x7B, 0xA3, 0x7B, 0xA4, 0x7B, 0xA5, 0x7B, 0xA6,\n\t0x7B, 0xA7, 0x7B, 0xA8, 0x7B, 0xA9, 0x7B, 0xAA, 0x7B, 0xAB, 0x7B, 0xAC, 0x7B, 0xAD, 0x7B, 0xAE, 0x7B, 0xAF, 0x7B, 0xB0, 0x7B, 0xB1, 0x7B, 0xB2, 0x7B, 0xB3, 0x7B, 0xB4, 0x7B, 0xB5, 0x7B, 0xB6,\n\t0x7B, 0xB7, 0x7B, 0xB8, 0x7B, 0xB9, 0x7B, 0xBA, 0x7B, 0xBB, 0x7B, 0xBC, 0x7B, 0xBD, 0x7B, 0xBE, 0x7B, 0xBF, 0x7B, 0xC0, 0x7B, 0xC1, 0x7B, 0xC2, 0x7B, 0xC3, 0x7B, 0xC4, 0x7B, 0xC5, 0x7B, 0xC6,\n\t0x7B, 0xC7, 0x7B, 0xC8, 0x7B, 0xC9, 0x7B, 0xCA, 0x7B, 0xCB, 0x7B, 0xCC, 0x7B, 0xCD, 0x7B, 0xCE, 0x7B, 0xCF, 0x7B, 0xD0, 0x7B, 0xD1, 0x7B, 0xD2, 0x7B, 0xD3, 0x7B, 0xD4, 0x7B, 0xD5, 0x7B, 0xD6,\n\t0x7B, 0xD7, 0x7B, 0xD8, 0x7B, 0xD9, 0x7B, 0xDA, 0x7B, 0xDB, 0x7B, 0xDC, 0x7B, 0xDD, 0x7B, 0xDE, 0x7B, 0xDF, 0x7B, 0xE0, 0x7B, 0xE1, 0x7B, 0xE2, 0x7B, 0xE3, 0x7B, 0xE4, 0x7B, 0xE5, 0x7B, 0xE6,\n\t0x7B, 0xE7, 0x7B, 0xE8, 0x7B, 0xE9, 0x7B, 0xEA, 0x7B, 0xEB, 0x7B, 0xEC, 0x7B, 0xED, 0x7B, 0xEE, 0x7B, 0xEF, 0x7B, 0xF0, 0x7B, 0xF1, 0x7B, 0xF2, 0x7B, 0xF3, 0x7B, 0xF4, 0x7B, 0xF5, 0x7B, 0xF6,\n\t0x7B, 0xF7, 0x7B, 0xF8, 0x7B, 0xF9, 0x7B, 0xFA, 0x7B, 0xFB, 0x7B, 0xFC, 0x7B, 0xFD, 0x7B, 0xFE, 0x7B, 0xFF, 0x7B, 0x00, 0x7C, 0x01, 0x7E, 0x02, 0x7E, 0x03, 0x7E, 0x04, 0x7E, 0x05, 0x7E, 0x06,\n\t0x7E, 0x07, 0x7E, 0x08, 0x7E, 0x09, 0x7E, 0x0A, 0x7E, 0x0B, 0x7E, 0x0C, 0x7E, 0x0D, 0x7E, 0x0E, 0x7E, 0x0F, 0x7E, 0x10, 0x7E, 0x11, 0x7E, 0x12, 0x7E, 0x13, 0x7E, 0x14, 0x7E, 0x15, 0x7E, 0x16,\n\t0x7E, 0x17, 0x7E, 0x18, 0x7E, 0x19, 0x7E, 0x1A, 0x7E, 0x1B, 0x7E, 0x1C, 0x7E, 0x1D, 0x7E, 0x1E, 0x7E, 0x1F, 0x7E, 0x20, 0x7E, 0x21, 0x7E, 0x22, 0x7E, 0x23, 0x7E, 0x24, 0x7E, 0x25, 0x7E, 0x26,\n\t0x7E, 0x27, 0x7E, 0x28, 0x7E, 0x29, 0x7E, 0x2A, 0x7E, 0x2B, 0x7E, 0x2C, 0x7E, 0x2D, 0x7E, 0x2E, 0x7E, 0x2F, 0x7E, 0x30, 0x7E, 0x31, 0x7E, 0x32, 0x7E, 0x33, 0x7E, 0x34, 0x7E, 0x35, 0x7E, 0x36,\n\t0x7E, 0x37, 0x7E, 0x38, 0x7E, 0x39, 0x7E, 0x3A, 0x7E, 0x3B, 0x7E, 0x3C, 0x7E, 0x3D, 0x7E, 0x3E, 0x7E, 0x3F, 0x7E, 0x40, 0x7E, 0x41, 0x7E, 0x42, 0x7E, 0x43, 0x7E, 0x44, 0x7E, 0x45, 0x7E, 0x46,\n\t0x7E, 0x47, 0x7E, 0x48, 0x7E, 0x49, 0x7E, 0x4A, 0x7E, 0x4B, 0x7E, 0x4C, 0x7E, 0x4D, 0x7E, 0x4E, 0x7E, 0x4F, 0x7E, 0x50, 0x7E, 0x51, 0x7E, 0x52, 0x7E, 0x53, 0x7E, 0x54, 0x7E, 0x55, 0x7E, 0x56,\n\t0x7E, 0x57, 0x7E, 0x58, 0x7E, 0x59, 0x7E, 0x5A, 0x7E, 0x5B, 0x7E, 0x5C, 0x7E, 0x5D, 0x7E, 0x5E, 0x7E, 0x5F, 0x7E, 0x60, 0x7E, 0x61, 0x7E, 0x62, 0x7E, 0x63, 0x7E, 0x64, 0x7E, 0x65, 0x7E, 0x66,\n\t0x7E, 0x67, 0x7E, 0x68, 0x7E, 0x69, 0x7E, 0x6A, 0x7E, 0x6B, 0x7E, 0x6C, 0x7E, 0x6D, 0x7E, 0x6E, 0x7E, 0x6F, 0x7E, 0x70, 0x7E, 0x71, 0x7E, 0x72, 0x7E, 0x73, 0x7E, 0x74, 0x7E, 0x75, 0x7E, 0x76,\n\t0x7E, 0x77, 0x7E, 0x78, 0x7E, 0x79, 0x7E, 0x7A, 0x7E, 0x7B, 0x7E, 0x7C, 0x7E, 0x7D, 0x7E, 0x7E, 0x7E, 0x7F, 0x7E, 0x80, 0x7E, 0x81, 0x7E, 0x82, 0x7E, 0x83, 0x7E, 0x84, 0x7E, 0x85, 0x7E, 0x86,\n\t0x7E, 0x87, 0x7E, 0x88, 0x7E, 0x89, 0x7E, 0x8A, 0x7E, 0x8B, 0x7E, 0x8C, 0x7E, 0x8D, 0x7E, 0x8E, 0x7E, 0x8F, 0x7E, 0x90, 0x7E, 0x91, 0x7E, 0x92, 0x7E, 0x93, 0x7E, 0x94, 0x7E, 0x95, 0x7E, 0x96,\n\t0x7E, 0x97, 0x7E, 0x98, 0x7E, 0x99, 0x7E, 0x9A, 0x7E, 0x9B, 0x7E, 0x9C, 0x7E, 0x9D, 0x7E, 0x9E, 0x7E, 0x9F, 0x7E, 0xA0, 0x7E, 0xA1, 0x7E, 0xA2, 0x7E, 0xA3, 0x7E, 0xA4, 0x7E, 0xA5, 0x7E, 0xA6,\n\t0x7E, 0xA7, 0x7E, 0xA8, 0x7E, 0xA9, 0x7E, 0xAA, 0x7E, 0xAB, 0x7E, 0xAC, 0x7E, 0xAD, 0x7E, 0xAE, 0x7E, 0xAF, 0x7E, 0xB0, 0x7E, 0xB1, 0x7E, 0xB2, 0x7E, 0xB3, 0x7E, 0xB4, 0x7E, 0xB5, 0x7E, 0xB6,\n\t0x7E, 0xB7, 0x7E, 0xB8, 0x7E, 0xB9, 0x7E, 0xBA, 0x7E, 0xBB, 0x7E, 0xBC, 0x7E, 0xBD, 0x7E, 0xBE, 0x7E, 0xBF, 0x7E, 0xC0, 0x7E, 0xC1, 0x7E, 0xC2, 0x7E, 0xC3, 0x7E, 0xC4, 0x7E, 0xC5, 0x7E, 0xC6,\n\t0x7E, 0xC7, 0x7E, 0xC8, 0x7E, 0xC9, 0x7E, 0xCA, 0x7E, 0xCB, 0x7E, 0xCC, 0x7E, 0xCD, 0x7E, 0xCE, 0x7E, 0xCF, 0x7E, 0xD0, 0x7E, 0xD1, 0x7E, 0xD2, 0x7E, 0xD3, 0x7E, 0xD4, 0x7E, 0xD5, 0x7E, 0xD6,\n\t0x7E, 0xD7, 0x7E, 0xD8, 0x7E, 0xD9, 0x7E, 0xDA, 0x7E, 0xDB, 0x7E, 0xDC, 0x7E, 0xDD, 0x7E, 0xDE, 0x7E, 0xDF, 0x7E, 0xE0, 0x7E, 0xE1, 0x7E, 0xE2, 0x7E, 0xE3, 0x7E, 0xE4, 0x7E, 0xE5, 0x7E, 0xE6,\n\t0x7E, 0xE7, 0x7E, 0xE8, 0x7E, 0xE9, 0x7E, 0xEA, 0x7E, 0xEB, 0x7E, 0xEC, 0x7E, 0xED, 0x7E, 0xEE, 0x7E, 0xEF, 0x7E, 0xF0, 0x7E, 0xF1, 0x7E, 0xF2, 0x7E, 0xF3, 0x7E, 0xF4, 0x7E, 0xF5, 0x7E, 0xF6,\n\t0x7E, 0xF7, 0x7E, 0xF8, 0x7E, 0xF9, 0x7E, 0xFA, 0x7E, 0xFB, 0x7E, 0xFC, 0x7E, 0xFD, 0x7E, 0xFE, 0x7E, 0xFF, 0x7E, 0x00, 0x7F, 0x01, 0x7F, 0x02, 0x7F, 0x03, 0x7F, 0x04, 0x7F, 0x05, 0x7F, 0x06,\n\t0x7F, 0x07, 0x7F, 0x08, 0x7F, 0x09, 0x7F, 0x0A, 0x7F, 0x0B, 0x7F, 0x0C, 0x7F, 0x0D, 0x7F, 0x0E, 0x7F, 0x0F, 0x7F, 0x10, 0x7F, 0x11, 0x7F, 0x12, 0x7F, 0x13, 0x7F, 0x14, 0x7F, 0x15, 0x7F, 0x16,\n\t0x7F, 0x17, 0x7F, 0x18, 0x7F, 0x19, 0x7F, 0x1A, 0x7F, 0x1B, 0x7F, 0x1C, 0x7F, 0x1D, 0x7F, 0x1E, 0x7F, 0x1F, 0x7F, 0x20, 0x7F, 0x21, 0x7F, 0x22, 0x7F, 0x23, 0x7F, 0x24, 0x7F, 0x25, 0x7F, 0x26,\n\t0x7F, 0x27, 0x7F, 0x28, 0x7F, 0x29, 0x7F, 0x2A, 0x7F, 0x2B, 0x7F, 0x2C, 0x7F, 0x2D, 0x7F, 0x2E, 0x7F, 0x2F, 0x7F, 0x30, 0x7F, 0x31, 0x7F, 0x32, 0x7F, 0x33, 0x7F, 0x34, 0x7F, 0x35, 0x7F, 0x36,\n\t0x7F, 0x37, 0x7F, 0x38, 0x7F, 0x39, 0x7F, 0x3A, 0x7F, 0x3B, 0x7F, 0x3C, 0x7F, 0x3D, 0x7F, 0x3E, 0x7F, 0x3F, 0x7F, 0x40, 0x7F, 0x41, 0x7F, 0x42, 0x7F, 0x43, 0x7F, 0x44, 0x7F, 0x45, 0x7F, 0x46,\n\t0x7F, 0x47, 0x7F, 0x48, 0x7F, 0x49, 0x7F, 0x4A, 0x7F, 0x4B, 0x7F, 0x4C, 0x7F, 0x4D, 0x7F, 0x4E, 0x7F, 0x4F, 0x7F, 0x50, 0x7F, 0x51, 0x7F, 0x52, 0x7F, 0x53, 0x7F, 0x54, 0x7F, 0x55, 0x7F, 0x56,\n\t0x7F, 0x57, 0x7F, 0x58, 0x7F, 0x59, 0x7F, 0x5A, 0x7F, 0x5B, 0x7F, 0x5C, 0x7F, 0x5D, 0x7F, 0x5E, 0x7F, 0x5F, 0x7F, 0x60, 0x7F, 0x61, 0x7F, 0x62, 0x7F, 0x63, 0x7F, 0x64, 0x7F, 0x65, 0x7F, 0x66,\n\t0x7F, 0x67, 0x7F, 0x68, 0x7F, 0x69, 0x7F, 0x6A, 0x7F, 0x6B, 0x7F, 0x6C, 0x7F, 0x6D, 0x7F, 0x6E, 0x7F, 0x6F, 0x7F, 0x70, 0x7F, 0x71, 0x7F, 0x72, 0x7F, 0x73, 0x7F, 0x74, 0x7F, 0x75, 0x7F, 0x76,\n\t0x7F, 0x77, 0x7F, 0x78, 0x7F, 0x79, 0x7F, 0x7A, 0x7F, 0x7B, 0x7F, 0x7C, 0x7F, 0x7D, 0x7F, 0x7E, 0x7F, 0x7F, 0x7F, 0x80, 0x7F, 0x81, 0x7F, 0x82, 0x7F, 0x83, 0x7F, 0x84, 0x7F, 0x85, 0x7F, 0x86,\n\t0x7F, 0x87, 0x7F, 0x88, 0x7F, 0x89, 0x7F, 0x8A, 0x7F, 0x8B, 0x7F, 0x8C, 0x7F, 0x8D, 0x7F, 0x8E, 0x7F, 0x8F, 0x7F, 0x90, 0x7F, 0x91, 0x7F, 0x92, 0x7F, 0x93, 0x7F, 0x94, 0x7F, 0x95, 0x7F, 0x96,\n\t0x7F, 0x97, 0x7F, 0x98, 0x7F, 0x99, 0x7F, 0x9A, 0x7F, 0x9B, 0x7F, 0x9C, 0x7F, 0x9D, 0x7F, 0x9E, 0x7F, 0x9F, 0x7F, 0xA0, 0x7F, 0xA1, 0x7F, 0xA2, 0x7F, 0xA3, 0x7F, 0xA4, 0x7F, 0xA5, 0x7F, 0xA6,\n\t0x7F, 0xA7, 0x7F, 0xA8, 0x7F, 0xA9, 0x7F, 0xAA, 0x7F, 0xAB, 0x7F, 0xAC, 0x7F, 0xAD, 0x7F, 0xAE, 0x7F, 0xAF, 0x7F, 0xB0, 0x7F, 0xB1, 0x7F, 0xB2, 0x7F, 0xB3, 0x7F, 0xB4, 0x7F, 0xB5, 0x7F, 0xB6,\n\t0x7F, 0xB7, 0x7F, 0xB8, 0x7F, 0xB9, 0x7F, 0xBA, 0x7F, 0xBB, 0x7F, 0xBC, 0x7F, 0xBD, 0x7F, 0xBE, 0x7F, 0xBF, 0x7F, 0xC0, 0x7F, 0xC1, 0x7F, 0xC2, 0x7F, 0xC3, 0x7F, 0xC4, 0x7F, 0xC5, 0x7F, 0xC6,\n\t0x7F, 0xC7, 0x7F, 0xC8, 0x7F, 0xC9, 0x7F, 0xCA, 0x7F, 0xCB, 0x7F, 0xCC, 0x7F, 0xCD, 0x7F, 0xCE, 0x7F, 0xCF, 0x7F, 0xD0, 0x7F, 0xD1, 0x7F, 0xD2, 0x7F, 0xD3, 0x7F, 0xD4, 0x7F, 0xD5, 0x7F, 0xD6,\n\t0x7F, 0xD7, 0x7F, 0xD8, 0x7F, 0xD9, 0x7F, 0xDA, 0x7F, 0xDB, 0x7F, 0xDC, 0x7F, 0xDD, 0x7F, 0xDE, 0x7F, 0xDF, 0x7F, 0xE0, 0x7F, 0xE1, 0x7F, 0xE2, 0x7F, 0xE3, 0x7F, 0xE4, 0x7F, 0xE5, 0x7F, 0xE6,\n\t0x7F, 0xE7, 0x7F, 0xE8, 0x7F, 0xE9, 0x7F, 0xEA, 0x7F, 0xEB, 0x7F, 0xEC, 0x7F, 0xED, 0x7F, 0xEE, 0x7F, 0xEF, 0x7F, 0xF0, 0x7F, 0xF1, 0x7F, 0xF2, 0x7F, 0xF3, 0x7F, 0xF4, 0x7F, 0xF5, 0x7F, 0xF6,\n\t0x7F, 0xF7, 0x7F, 0xF8, 0x7F, 0xF9, 0x7F, 0xFA, 0x7F, 0xFB, 0x7F, 0xFC, 0x7F, 0xFD, 0x7F, 0xFE, 0x7F, 0xFF, 0x7F, 0x00, 0x7E, 0x00, 0x04, 0xFF, 0xFF, 0xFF, 0xEE, 0x00, 0x03, 0xFE, 0x00, 0x07,\n\t0xFA, 0x00, 0x0B, 0xF6, 0x00, 0x0F, 0xF2, 0xF0, 0xA1, 0x04, 0x80, 0x04, 0x80, 0x05, 0x80, 0x05, 0x80, 0x06, 0x80, 0x06, 0x80, 0x07, 0x80, 0x07, 0x80, 0x08, 0x80, 0x08, 0x80, 0x09, 0x80, 0x09,\n\t0x80, 0x0A, 0x80, 0x0A, 0x80, 0x0B, 0x80, 0x0B, 0x80, 0x0C, 0x80, 0x0C, 0x80, 0x0D, 0x80, 0x0D, 0x80, 0x0E, 0x80, 0x0E, 0x80, 0x0F, 0x80, 0x0F, 0x80, 0x10, 0x80, 0x10, 0x80, 0x11, 0x80, 0x11,\n\t0x80, 0x12, 0x80, 0x12, 0x80, 0x13, 0x80, 0x13, 0x80, 0x14, 0x80, 0x14, 0x80, 0x15, 0x80, 0x15, 0x80, 0x16, 0x80, 0x16, 0x80, 0x17, 0x80, 0x17, 0x80, 0x18, 0x80, 0x18, 0x80, 0x19, 0x80, 0x19,\n\t0x80, 0x1A, 0x80, 0x1A, 0x80, 0x1B, 0x80, 0x1B, 0x80, 0x1C, 0x80, 0x1C, 0x80, 0x1D, 0x80, 0x1D, 0x80, 0x1E, 0x80, 0x1E, 0x80, 0x1F, 0x80, 0x1F, 0x80, 0x20, 0x80, 0x20, 0x80, 0x21, 0x80, 0x21,\n\t0x80, 0x22, 0x80, 0x22, 0x80, 0x23, 0x80, 0x23, 0x80, 0x24, 0x80, 0x24, 0x80, 0x25, 0x80, 0x25, 0x80, 0x26, 0x80, 0x26, 0x80, 0x27, 0x80, 0x27, 0x80, 0x28, 0x80, 0x28, 0x80, 0x29, 0x80, 0x29,\n\t0x80, 0x2A, 0x80, 0x2A, 0x80, 0x2B, 0x80, 0x2B, 0x80, 0x2C, 0x80, 0x2C, 0x80, 0x2D, 0x80, 0x2D, 0x80, 0x2E, 0x80, 0x2E, 0x80, 0x2F, 0x80, 0x2F, 0x80, 0x99, 0x99, 0xF0, 0xFF, 0x2E, 0x31, 0x80,\n\t0x31, 0x80, 0x32, 0x80, 0x32, 0x80, 0x33, 0x80, 0x33, 0x80, 0x34, 0x80, 0x34, 0x80, 0x35, 0x80, 0x35, 0x80, 0x36, 0x80, 0x36, 0x80, 0x37, 0x80, 0x37, 0x80, 0x38, 0x80, 0x38, 0x80, 0x39, 0x80,\n\t0x39, 0x80, 0x3A, 0x80, 0x3A, 0x80, 0x3B, 0x80, 0x3B, 0x80, 0x3C, 0x80, 0x3C, 0x80, 0x3D, 0x80, 0x3D, 0x80, 0x3E, 0x80, 0x3E, 0x80, 0x3F, 0x80, 0x3F, 0x80, 0x40, 0x80, 0x40, 0x80, 0x41, 0x80,\n\t0x41, 0x80, 0x42, 0x80, 0x42, 0x80, 0x43, 0x80, 0x43, 0x80, 0x44, 0x80, 0x44, 0x80, 0x45, 0x80, 0x45, 0x80, 0x46, 0x80, 0x46, 0x80, 0x47, 0x80, 0x47, 0x80, 0x48, 0x80, 0x48, 0x80, 0x49, 0x80,\n\t0x49, 0x80, 0x4A, 0x80, 0x4A, 0x80, 0x4B, 0x80, 0x4B, 0x80, 0x4C, 0x80, 0x4C, 0x80, 0x4D, 0x80, 0x4D, 0x80, 0x4E, 0x80, 0x4E, 0x80, 0x4F, 0x80, 0x4F, 0x80, 0x50, 0x80, 0x50, 0x80, 0x51, 0x80,\n\t0x51, 0x80, 0x52, 0x80, 0x52, 0x80, 0x53, 0x80, 0x53, 0x80, 0x54, 0x80, 0x54, 0x80, 0x55, 0x80, 0x55, 0x80, 0x56, 0x80, 0x56, 0x80, 0x57, 0x80, 0x57, 0x80, 0x58, 0x80, 0x58, 0x80, 0x59, 0x80,\n\t0x59, 0x80, 0x5A, 0x80, 0x5A, 0x80, 0x5B, 0x80, 0x5B, 0x80, 0x5C, 0x80, 0x5C, 0x80, 0x5D, 0x80, 0x5D, 0x80, 0x5E, 0x80, 0x5E, 0x80, 0x5F, 0x80, 0x5F, 0x80, 0x60, 0x80, 0x60, 0x80, 0x61, 0x80,\n\t0x61, 0x80, 0x62, 0x80, 0x62, 0x80, 0x63, 0x80, 0x63, 0x80, 0x64, 0x80, 0x64, 0x80, 0x65, 0x80, 0x65, 0x80, 0x66, 0x80, 0x66, 0x80, 0x67, 0x80, 0x67, 0x80, 0x68, 0x80, 0x68, 0x80, 0x69, 0x80,\n\t0x69, 0x80, 0x6A, 0x80, 0x6A, 0x80, 0x6B, 0x80, 0x6B, 0x80, 0x6C, 0x80, 0x6C, 0x80, 0x6D, 0x80, 0x6D, 0x80, 0x6E, 0x80, 0x6E, 0x80, 0x6F, 0x80, 0x6F, 0x80, 0x70, 0x80, 0x70, 0x80, 0x71, 0x80,\n\t0x71, 0x80, 0x72, 0x80, 0x72, 0x80, 0x73, 0x80, 0x73, 0x80, 0x74, 0x80, 0x74, 0x80, 0x75, 0x80, 0x75, 0x80, 0x76, 0x80, 0x76, 0x80, 0x77, 0x80, 0x77, 0x80, 0x78, 0x80, 0x78, 0x80, 0x79, 0x80,\n\t0x79, 0x80, 0x7A, 0x80, 0x7A, 0x80, 0x7B, 0x80, 0x7B, 0x80, 0x7C, 0x80, 0x7C, 0x80, 0x7D, 0x80, 0x7D, 0x80, 0x7E, 0x80, 0x7E, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x01, 0x00, 0xF0, 0xFF, 0xF2, 0x81,\n\t0x80, 0x81, 0x80, 0x82, 0x80, 0x82, 0x80, 0x83, 0x80, 0x83, 0x80, 0x84, 0x80, 0x84, 0x80, 0x85, 0x80, 0x85, 0x80, 0x86, 0x80, 0x86, 0x80, 0x87, 0x80, 0x87, 0x80, 0x88, 0x80, 0x88, 0x80, 0x89,\n\t0x80, 0x89, 0x80, 0x8A, 0x80, 0x8A, 0x80, 0x8B, 0x80, 0x8B, 0x80, 0x8C, 0x80, 0x8C, 0x80, 0x8D, 0x80, 0x8D, 0x80, 0x8E, 0x80, 0x8E, 0x80, 0x8F, 0x80, 0x8F, 0x80, 0x90, 0x80, 0x90, 0x80, 0x91,\n\t0x80, 0x91, 0x80, 0x92, 0x80, 0x92, 0x80, 0x93, 0x80, 0x93, 0x80, 0x94, 0x80, 0x94, 0x80, 0x95, 0x80, 0x95, 0x80, 0x96, 0x80, 0x96, 0x80, 0x97, 0x80, 0x97, 0x80, 0x98, 0x80, 0x98, 0x80, 0x99,\n\t0x80, 0x99, 0x80, 0x9A, 0x80, 0x9A, 0x80, 0x9B, 0x80, 0x9B, 0x80, 0x9C, 0x80, 0x9C, 0x80, 0x9D, 0x80, 0x9D, 0x80, 0x9E, 0x80, 0x9E, 0x80, 0x9F, 0x80, 0x9F, 0x80, 0xA0, 0x80, 0xA0, 0x80, 0xA1,\n\t0x80, 0xA1, 0x80, 0xA2, 0x80, 0xA2, 0x80, 0xA3, 0x80, 0xA3, 0x80, 0xA4, 0x80, 0xA4, 0x80, 0xA5, 0x80, 0xA5, 0x80, 0xA6, 0x80, 0xA6, 0x80, 0xA7, 0x80, 0xA7, 0x80, 0xA8, 0x80, 0xA8, 0x80, 0xA9,\n\t0x80, 0xA9, 0x80, 0xAA, 0x80, 0xAA, 0x80, 0xAB, 0x80, 0xAB, 0x80, 0xAC, 0x80, 0xAC, 0x80, 0xAD, 0x80, 0xAD, 0x80, 0xAE, 0x80, 0xAE, 0x80, 0xAF, 0x80, 0xAF, 0x80, 0xB0, 0x80, 0xB0, 0x80, 0xB1,\n\t0x80, 0xB1, 0x80, 0xB2, 0x80, 0xB2, 0x80, 0xB3, 0x80, 0xB3, 0x80, 0xB4, 0x80, 0xB4, 0x80, 0xB5, 0x80, 0xB5, 0x80, 0xB6, 0x80, 0xB6, 0x80, 0xB7, 0x80, 0xB7, 0x80, 0xB8, 0x80, 0xB8, 0x80, 0xB9,\n\t0x80, 0xB9, 0x80, 0xBA, 0x80, 0xBA, 0x80, 0xBB, 0x80, 0xBB, 0x80, 0xBC, 0x80, 0xBC, 0x80, 0xBD, 0x80, 0xBD, 0x80, 0xBE, 0x80, 0xBE, 0x80, 0xBF, 0x80, 0xBF, 0x80, 0xC0, 0x80, 0xC0, 0x80, 0xC1,\n\t0x80, 0xC1, 0x80, 0xC2, 0x80, 0xC2, 0x80, 0xC3, 0x80, 0xC3, 0x80, 0xC4, 0x80, 0xC4, 0x80, 0xC5, 0x80, 0xC5, 0x80, 0xC6, 0x80, 0xC6, 0x80, 0xC7, 0x80, 0xC7, 0x80, 0xC8, 0x80, 0xC8, 0x80, 0xC9,\n\t0x80, 0xC9, 0x80, 0xCA, 0x80, 0xCA, 0x80, 0xCB, 0x80, 0xCB, 0x80, 0xCC, 0x80, 0xCC, 0x80, 0xCD, 0x80, 0xCD, 0x80, 0xCE, 0x80, 0xCE, 0x80, 0xCF, 0x80, 0xCF, 0x80, 0xD0, 0x80, 0xD0, 0x80, 0xD1,\n\t0x80, 0xD1, 0x80, 0xD2, 0x80, 0xD2, 0x80, 0xD3, 0x80, 0xD3, 0x80, 0xD4, 0x80, 0xD4, 0x80, 0xD5, 0x80, 0xD5, 0x80, 0xD6, 0x80, 0xD6, 0x80, 0xD7, 0x80, 0xD7, 0x80, 0xD8, 0x80, 0xD8, 0x80, 0xD9,\n\t0x80, 0xD9, 0x80, 0xDA, 0x80, 0xDA, 0x80, 0xDB, 0x80, 0xDB, 0x80, 0xDC, 0x80, 0xDC, 0x80, 0xDD, 0x80, 0xDD, 0x80, 0xDE, 0x80, 0xDE, 0x80, 0xDF, 0x80, 0xDF, 0x80, 0xE0, 0x80, 0xE0, 0x80, 0xE1,\n\t0x80, 0xE1, 0x80, 0xE2, 0x80, 0xE2, 0x80, 0xE3, 0x80, 0xE3, 0x80, 0xE4, 0x80, 0xE4, 0x80, 0xE5, 0x80, 0xE5, 0x80, 0xE6, 0x80, 0xE6, 0x80, 0xE7, 0x80, 0xE7, 0x80, 0xE8, 0x80, 0xE8, 0x80, 0xE9,\n\t0x80, 0xE9, 0x80, 0xEA, 0x80, 0xEA, 0x80, 0xEB, 0x80, 0xEB, 0x80, 0xEC, 0x80, 0xEC, 0x80, 0xED, 0x80, 0xED, 0x80, 0xEE, 0x80, 0xEE, 0x80, 0xEF, 0x80, 0xEF, 0x80, 0xF0, 0x80, 0xF0, 0x80, 0xF1,\n\t0x80, 0xF1, 0x80, 0xF2, 0x80, 0xF2, 0x80, 0xF3, 0x80, 0xF3, 0x80, 0xF4, 0x80, 0xF4, 0x80, 0xF5, 0x80, 0xF5, 0x80, 0xF6, 0x80, 0xF6, 0x80, 0xF7, 0x80, 0xF7, 0x80, 0xF8, 0x80, 0xF8, 0x80, 0xF9,\n\t0x80, 0xF9, 0x80, 0xFA, 0x80, 0xFA, 0x80, 0xFB, 0x80, 0xFB, 0x80, 0xFC, 0x80, 0xFC, 0x80, 0xFD, 0x80, 0xFD, 0x80, 0xFE, 0x80, 0xFE, 0x80, 0xFF, 0x80, 0xFF, 0x80, 0x00, 0x81, 0x00, 0x81, 0x03,\n\t0xFE, 0x00, 0x07, 0xFA, 0x00, 0x0B, 0xF6, 0xF0, 0xFF, 0xE2, 0x04, 0x81, 0x04, 0x81, 0x05, 0x81, 0x05, 0x81, 0x06, 0x81, 0x06, 0x81, 0x07, 0x81, 0x07, 0x81, 0x08, 0x81, 0x08, 0x81, 0x09, 0x81,\n\t0x09, 0x81, 0x0A, 0x81, 0x0A, 0x81, 0x0B, 0x81, 0x0B, 0x81, 0x0C, 0x81, 0x0C, 0x81, 0x0D, 0x81, 0x0D, 0x81, 0x0E, 0x81, 0x0E, 0x81, 0x0F, 0x81, 0x0F, 0x81, 0x10, 0x81, 0x10, 0x81, 0x11, 0x81,\n\t0x11, 0x81, 0x12, 0x81, 0x12, 0x81, 0x13, 0x81, 0x13, 0x81, 0x14, 0x81, 0x14, 0x81, 0x15, 0x81, 0x15, 0x81, 0x16, 0x81, 0x16, 0x81, 0x17, 0x81, 0x17, 0x81, 0x18, 0x81, 0x18, 0x81, 0x19, 0x81,\n\t0x19, 0x81, 0x1A, 0x81, 0x1A, 0x81, 0x1B, 0x81, 0x1B, 0x81, 0x1C, 0x81, 0x1C, 0x81, 0x1D, 0x81, 0x1D, 0x81, 0x1E, 0x81, 0x1E, 0x81, 0x1F, 0x81, 0x1F, 0x81, 0x20, 0x81, 0x20, 0x81, 0x21, 0x81,\n\t0x21, 0x81, 0x22, 0x81, 0x22, 0x81, 0x23, 0x81, 0x23, 0x81, 0x24, 0x81, 0x24, 0x81, 0x25, 0x81, 0x25, 0x81, 0x26, 0x81, 0x26, 0x81, 0x27, 0x81, 0x27, 0x81, 0x28, 0x81, 0x28, 0x81, 0x29, 0x81,\n\t0x29, 0x81, 0x2A, 0x81, 0x2A, 0x81, 0x2B, 0x81, 0x2B, 0x81, 0x2C, 0x81, 0x2C, 0x81, 0x2D, 0x81, 0x2D, 0x81, 0x2E, 0x81, 0x2E, 0x81, 0x2F, 0x81, 0x2F, 0x81, 0x30, 0x81, 0x30, 0x81, 0x31, 0x81,\n\t0x31, 0x81, 0x32, 0x81, 0x32, 0x81, 0x33, 0x81, 0x33, 0x81, 0x34, 0x81, 0x34, 0x81, 0x35, 0x81, 0x35, 0x81, 0x36, 0x81, 0x36, 0x81, 0x37, 0x81, 0x37, 0x81, 0x38, 0x81, 0x38, 0x81, 0x39, 0x81,\n\t0x39, 0x81, 0x3A, 0x81, 0x3A, 0x81, 0x3B, 0x81, 0x3B, 0x81, 0x3C, 0x81, 0x3C, 0x81, 0x3D, 0x81, 0x3D, 0x81, 0x3E, 0x81, 0x3E, 0x81, 0x3F, 0x81, 0x3F, 0x81, 0x40, 0x81, 0x40, 0x81, 0x41, 0x81,\n\t0x41, 0x81, 0x42, 0x81, 0x42, 0x81, 0x43, 0x81, 0x43, 0x81, 0x44, 0x81, 0x44, 0x81, 0x45, 0x81, 0x45, 0x81, 0x46, 0x81, 0x46, 0x81, 0x47, 0x81, 0x47, 0x81, 0x48, 0x81, 0x48, 0x81, 0x49, 0x81,\n\t0x49, 0x81, 0x4A, 0x81, 0x4A, 0x81, 0x4B, 0x81, 0x4B, 0x81, 0x4C, 0x81, 0x4C, 0x81, 0x4D, 0x81, 0x4D, 0x81, 0x4E, 0x81, 0x4E, 0x81, 0x4F, 0x81, 0x4F, 0x81, 0x50, 0x81, 0x50, 0x81, 0x51, 0x81,\n\t0x51, 0x81, 0x52, 0x81, 0x52, 0x81, 0x53, 0x81, 0x53, 0x81, 0x54, 0x81, 0x54, 0x81, 0x55, 0x81, 0x55, 0x81, 0x56, 0x81, 0x56, 0x81, 0x57, 0x81, 0x57, 0x81, 0x58, 0x81, 0x58, 0x81, 0x59, 0x81,\n\t0x59, 0x81, 0x5A, 0x81, 0x5A, 0x81, 0x5B, 0x81, 0x5B, 0x81, 0x5C, 0x81, 0x5C, 0x81, 0x5D, 0x81, 0x5D, 0x81, 0x5E, 0x81, 0x5E, 0x81, 0x5F, 0x81, 0x5F, 0x81, 0x60, 0x81, 0x60, 0x81, 0x61, 0x81,\n\t0x61, 0x81, 0x62, 0x81, 0x62, 0x81, 0x63, 0x81, 0x63, 0x81, 0x64, 0x81, 0x64, 0x81, 0x65, 0x81, 0x65, 0x81, 0x66, 0x81, 0x66, 0x81, 0x67, 0x81, 0x67, 0x81, 0x68, 0x81, 0x68, 0x81, 0x69, 0x81,\n\t0x69, 0x81, 0x6A, 0x81, 0x6A, 0x81, 0x6B, 0x81, 0x6B, 0x81, 0x6C, 0x81, 0x6C, 0x81, 0x6D, 0x81, 0x6D, 0x81, 0x6E, 0x81, 0x6E, 0x81, 0x6F, 0x81, 0x6F, 0x81, 0x70, 0x81, 0x70, 0x81, 0x71, 0x81,\n\t0x71, 0x81, 0x72, 0x81, 0x72, 0x81, 0x73, 0x81, 0x73, 0x81, 0x74, 0x81, 0x74, 0x81, 0x75, 0x81, 0x75, 0x81, 0x76, 0x81, 0x76, 0x81, 0x77, 0x81, 0x77, 0x81, 0x78, 0x81, 0x78, 0x81, 0x79, 0x81,\n\t0x79, 0x81, 0x7A, 0x81, 0x7A, 0x81, 0x7B, 0x81, 0x7B, 0x81, 0x7C, 0x81, 0x7C, 0x81, 0x7D, 0x81, 0x7D, 0x81, 0x7E, 0x81, 0x7E, 0x81, 0x7F, 0x81, 0x7F, 0x81, 0xFD, 0x03, 0x00, 0x01, 0x00, 0xF0,\n\t0xFF, 0xF2, 0x82, 0x81, 0x82, 0x81, 0x83, 0x81, 0x83, 0x81, 0x84, 0x81, 0x84, 0x81, 0x85, 0x81, 0x85, 0x81, 0x86, 0x81, 0x86, 0x81, 0x87, 0x81, 0x87, 0x81, 0x88, 0x81, 0x88, 0x81, 0x89, 0x81,\n\t0x89, 0x81, 0x8A, 0x81, 0x8A, 0x81, 0x8B, 0x81, 0x8B, 0x81, 0x8C, 0x81, 0x8C, 0x81, 0x8D, 0x81, 0x8D, 0x81, 0x8E, 0x81, 0x8E, 0x81, 0x8F, 0x81, 0x8F, 0x81, 0x90, 0x81, 0x90, 0x81, 0x91, 0x81,\n\t0x91, 0x81, 0x92, 0x81, 0x92, 0x81, 0x93, 0x81, 0x93, 0x81, 0x94, 0x81, 0x94, 0x81, 0x95, 0x81, 0x95, 0x81, 0x96, 0x81, 0x96, 0x81, 0x97, 0x81, 0x97, 0x81, 0x98, 0x81, 0x98, 0x81, 0x99, 0x81,\n\t0x99, 0x81, 0x9A, 0x81, 0x9A, 0x81, 0x9B, 0x81, 0x9B, 0x81, 0x9C, 0x81, 0x9C, 0x81, 0x9D, 0x81, 0x9D, 0x81, 0x9E, 0x81, 0x9E, 0x81, 0x9F, 0x81, 0x9F, 0x81, 0xA0, 0x81, 0xA0, 0x81, 0xA1, 0x81,\n\t0xA1, 0x81, 0xA2, 0x81, 0xA2, 0x81, 0xA3, 0x81, 0xA3, 0x81, 0xA4, 0x81, 0xA4, 0x81, 0xA5, 0x81, 0xA5, 0x81, 0xA6, 0x81, 0xA6, 0x81, 0xA7, 0x81, 0xA7, 0x81, 0xA8, 0x81, 0xA8, 0x81, 0xA9, 0x81,\n\t0xA9, 0x81, 0xAA, 0x81, 0xAA, 0x81, 0xAB, 0x81, 0xAB, 0x81, 0xAC, 0x81, 0xAC, 0x81, 0xAD, 0x81, 0xAD, 0x81, 0xAE, 0x81, 0xAE, 0x81, 0xAF, 0x81, 0xAF, 0x81, 0xB0, 0x81, 0xB0, 0x81, 0xB1, 0x81,\n\t0xB1, 0x81, 0xB2, 0x81, 0xB2, 0x81, 0xB3, 0x81, 0xB3, 0x81, 0xB4, 0x81, 0xB4, 0x81, 0xB5, 0x81, 0xB5, 0x81, 0xB6, 0x81, 0xB6, 0x81, 0xB7, 0x81, 0xB7, 0x81, 0xB8, 0x81, 0xB8, 0x81, 0xB9, 0x81,\n\t0xB9, 0x81, 0xBA, 0x81, 0xBA, 0x81, 0xBB, 0x81, 0xBB, 0x81, 0xBC, 0x81, 0xBC, 0x81, 0xBD, 0x81, 0xBD, 0x81, 0xBE, 0x81, 0xBE, 0x81, 0xBF, 0x81, 0xBF, 0x81, 0xC0, 0x81, 0xC0, 0x81, 0xC1, 0x81,\n\t0xC1, 0x81, 0xC2, 0x81, 0xC2, 0x81, 0xC3, 0x81, 0xC3, 0x81, 0xC4, 0x81, 0xC4, 0x81, 0xC5, 0x81, 0xC5, 0x81, 0xC6, 0x81, 0xC6, 0x81, 0xC7, 0x81, 0xC7, 0x81, 0xC8, 0x81, 0xC8, 0x81, 0xC9, 0x81,\n\t0xC9, 0x81, 0xCA, 0x81, 0xCA, 0x81, 0xCB, 0x81, 0xCB, 0x81, 0xCC, 0x81, 0xCC, 0x81, 0xCD, 0x81, 0xCD, 0x81, 0xCE, 0x81, 0xCE, 0x81, 0xCF, 0x81, 0xCF, 0x81, 0xD0, 0x81, 0xD0, 0x81, 0xD1, 0x81,\n\t0xD1, 0x81, 0xD2, 0x81, 0xD2, 0x81, 0xD3, 0x81, 0xD3, 0x81, 0xD4, 0x81, 0xD4, 0x81, 0xD5, 0x81, 0xD5, 0x81, 0xD6, 0x81, 0xD6, 0x81, 0xD7, 0x81, 0xD7, 0x81, 0xD8, 0x81, 0xD8, 0x81, 0xD9, 0x81,\n\t0xD9, 0x81, 0xDA, 0x81, 0xDA, 0x81, 0xDB, 0x81, 0xDB, 0x81, 0xDC, 0x81, 0xDC, 0x81, 0xDD, 0x81, 0xDD, 0x81, 0xDE, 0x81, 0xDE, 0x81, 0xDF, 0x81, 0xDF, 0x81, 0xE0, 0x81, 0xE0, 0x81, 0xE1, 0x81,\n\t0xE1, 0x81, 0xE2, 0x81, 0xE2, 0x81, 0xE3, 0x81, 0xE3, 0x81, 0xE4, 0x81, 0xE4, 0x81, 0xE5, 0x81, 0xE5, 0x81, 0xE6, 0x81, 0xE6, 0x81, 0xE7, 0x81, 0xE7, 0x81, 0xE8, 0x81, 0xE8, 0x81, 0xE9, 0x81,\n\t0xE9, 0x81, 0xEA, 0x81, 0xEA, 0x81, 0xEB, 0x81, 0xEB, 0x81, 0xEC, 0x81, 0xEC, 0x81, 0xED, 0x81, 0xED, 0x81, 0xEE, 0x81, 0xEE, 0x81, 0xEF, 0x81, 0xEF, 0x81, 0xF0, 0x81, 0xF0, 0x81, 0xF1, 0x81,\n\t0xF1, 0x81, 0xF2, 0x81, 0xF2, 0x81, 0xF3, 0x81, 0xF3, 0x81, 0xF4, 0x81, 0xF4, 0x81, 0xF5, 0x81, 0xF5, 0x81, 0xF6, 0x81, 0xF6, 0x81, 0xF7, 0x81, 0xF7, 0x81, 0xF8, 0x81, 0xF8, 0x81, 0xF9, 0x81,\n\t0xF9, 0x81, 0xFA, 0x81, 0xFA, 0x81, 0xFB, 0x81, 0xFB, 0x81, 0xFC, 0x81, 0xFC, 0x81, 0xFD, 0x81, 0xFD, 0x81, 0xFE, 0x81, 0xFE, 0x81, 0xFF, 0x81, 0xFF, 0x81, 0x00, 0x82, 0x00, 0x82, 0x01, 0x82,\n\t0x01, 0x82, 0x03, 0xFE, 0x00, 0x07, 0xFA, 0xF0, 0xA1, 0x04, 0x82, 0x04, 0x82, 0x05, 0x82, 0x05, 0x82, 0x06, 0x82, 0x06, 0x82, 0x07, 0x82, 0x07, 0x82, 0x08, 0x82, 0x08, 0x82, 0x09, 0x82, 0x09,\n\t0x82, 0x0A, 0x82, 0x0A, 0x82, 0x0B, 0x82, 0x0B, 0x82, 0x0C, 0x82, 0x0C, 0x82, 0x0D, 0x82, 0x0D, 0x82, 0x0E, 0x82, 0x0E, 0x82, 0x0F, 0x82, 0x0F, 0x82, 0x10, 0x82, 0x10, 0x82, 0x11, 0x82, 0x11,\n\t0x82, 0x12, 0x82, 0x12, 0x82, 0x13, 0x82, 0x13, 0x82, 0x14, 0x82, 0x14, 0x82, 0x15, 0x82, 0x15, 0x82, 0x16, 0x82, 0x16, 0x82, 0x17, 0x82, 0x17, 0x82, 0x18, 0x82, 0x18, 0x82, 0x19, 0x82, 0x19,\n\t0x82, 0x1A, 0x82, 0x1A, 0x82, 0x1B, 0x82, 0x1B, 0x82, 0x1C, 0x82, 0x1C, 0x82, 0x1D, 0x82, 0x1D, 0x82, 0x1E, 0x82, 0x1E, 0x82, 0x1F, 0x82, 0x1F, 0x82, 0x20, 0x82, 0x20, 0x82, 0x21, 0x82, 0x21,\n\t0x82, 0x22, 0x82, 0x22, 0x82, 0x23, 0x82, 0x23, 0x82, 0x24, 0x82, 0x24, 0x82, 0x25, 0x82, 0x25, 0x82, 0x26, 0x82, 0x26, 0x82, 0x27, 0x82, 0x27, 0x82, 0x28, 0x82, 0x28, 0x82, 0x29, 0x82, 0x29,\n\t0x82, 0x2A, 0x82, 0x2A, 0x82, 0x2B, 0x82, 0x2B, 0x82, 0x2C, 0x82, 0x2C, 0x82, 0x2D, 0x82, 0x2D, 0x82, 0x2E, 0x82, 0x2E, 0x82, 0x2F, 0x82, 0x2F, 0x82, 0x93, 0xA1, 0xF0, 0xFF, 0x2E, 0x31, 0x82,\n\t0x31, 0x82, 0x32, 0x82, 0x32, 0x82, 0x33, 0x82, 0x33, 0x82, 0x34, 0x82, 0x34, 0x82, 0x35, 0x82, 0x35, 0x82, 0x36, 0x82, 0x36, 0x82, 0x37, 0x82, 0x37, 0x82, 0x38, 0x82, 0x38, 0x82, 0x39, 0x82,\n\t0x39, 0x82, 0x3A, 0x82, 0x3A, 0x82, 0x3B, 0x82, 0x3B, 0x82, 0x3C, 0x82, 0x3C, 0x82, 0x3D, 0x82, 0x3D, 0x82, 0x3E, 0x82, 0x3E, 0x82, 0x3F, 0x82, 0x3F, 0x82, 0x40, 0x82, 0x40, 0x82, 0x41, 0x82,\n\t0x41, 0x82, 0x42, 0x82, 0x42, 0x82, 0x43, 0x82, 0x43, 0x82, 0x44, 0x82, 0x44, 0x82, 0x45, 0x82, 0x45, 0x82, 0x46, 0x82, 0x46, 0x82, 0x47, 0x82, 0x47, 0x82, 0x48, 0x82, 0x48, 0x82, 0x49, 0x82,\n\t0x49, 0x82, 0x4A, 0x82, 0x4A, 0x82, 0x4B, 0x82, 0x4B, 0x82, 0x4C, 0x82, 0x4C, 0x82, 0x4D, 0x82, 0x4D, 0x82, 0x4E, 0x82, 0x4E, 0x82, 0x4F, 0x82, 0x4F, 0x82, 0x50, 0x82, 0x50, 0x82, 0x51, 0x82,\n\t0x51, 0x82, 0x52, 0x82, 0x52, 0x82, 0x53, 0x82, 0x53, 0x82, 0x54, 0x82, 0x54, 0x82, 0x55, 0x82, 0x55, 0x82, 0x56, 0x82, 0x56, 0x82, 0x57, 0x82, 0x57, 0x82, 0x58, 0x82, 0x58, 0x82, 0x59, 0x82,\n\t0x59, 0x82, 0x5A, 0x82, 0x5A, 0x82, 0x5B, 0x82, 0x5B, 0x82, 0x5C, 0x82, 0x5C, 0x82, 0x5D, 0x82, 0x5D, 0x82, 0x5E, 0x82, 0x5E, 0x82, 0x5F, 0x82, 0x5F, 0x82, 0x60, 0x82, 0x60, 0x82, 0x61, 0x82,\n\t0x61, 0x82, 0x62, 0x82, 0x62, 0x82, 0x63, 0x82, 0x63, 0x82, 0x64, 0x82, 0x64, 0x82, 0x65, 0x82, 0x65, 0x82, 0x66, 0x82, 0x66, 0x82, 0x67, 0x82, 0x67, 0x82, 0x68, 0x82, 0x68, 0x82, 0x69, 0x82,\n\t0x69, 0x82, 0x6A, 0x82, 0x6A, 0x82, 0x6B, 0x82, 0x6B, 0x82, 0x6C, 0x82, 0x6C, 0x82, 0x6D, 0x82, 0x6D, 0x82, 0x6E, 0x82, 0x6E, 0x82, 0x6F, 0x82, 0x6F, 0x82, 0x70, 0x82, 0x70, 0x82, 0x71, 0x82,\n\t0x71, 0x82, 0x72, 0x82, 0x72, 0x82, 0x73, 0x82, 0x73, 0x82, 0x74, 0x82, 0x74, 0x82, 0x75, 0x82, 0x75, 0x82, 0x76, 0x82, 0x76, 0x82, 0x77, 0x82, 0x77, 0x82, 0x78, 0x82, 0x78, 0x82, 0x79, 0x82,\n\t0x79, 0x82, 0x7A, 0x82, 0x7A, 0x82, 0x7B, 0x82, 0x7B, 0x82, 0x7C, 0x82, 0x7C, 0x82, 0x7D, 0x82, 0x7D, 0x82, 0x7E, 0x82, 0x7E, 0x82, 0x7F, 0x82, 0x7F, 0x82, 0xF9, 0x07, 0x00, 0xFD, 0x03, 0x00,\n\t0x01, 0x00, 0xF0, 0xFF, 0xF2, 0x83, 0x82, 0x83, 0x82, 0x84, 0x82, 0x84, 0x82, 0x85, 0x82, 0x85, 0x82, 0x86, 0x82, 0x86, 0x82, 0x87, 0x82, 0x87, 0x82, 0x88, 0x82, 0x88, 0x82, 0x89, 0x82, 0x89,\n\t0x82, 0x8A, 0x82, 0x8A, 0x82, 0x8B, 0x82, 0x8B, 0x82, 0x8C, 0x82, 0x8C, 0x82, 0x8D, 0x82, 0x8D, 0x82, 0x8E, 0x82, 0x8E, 0x82, 0x8F, 0x82, 0x8F, 0x82, 0x90, 0x82, 0x90, 0x82, 0x91, 0x82, 0x91,\n\t0x82, 0x92, 0x82, 0x92, 0x82, 0x93, 0x82, 0x93, 0x82, 0x94, 0x82, 0x94, 0x82, 0x95, 0x82, 0x95, 0x82, 0x96, 0x82, 0x96, 0x82, 0x97, 0x82, 0x97, 0x82, 0x98, 0x82, 0x98, 0x82, 0x99, 0x82, 0x99,\n\t0x82, 0x9A, 0x82, 0x9A, 0x82, 0x9B, 0x82, 0x9B, 0x82, 0x9C, 0x82, 0x9C, 0x82, 0x9D, 0x82, 0x9D, 0x82, 0x9E, 0x82, 0x9E, 0x82, 0x9F, 0x82, 0x9F, 0x82, 0xA0, 0x82, 0xA0, 0x82, 0xA1, 0x82, 0xA1,\n\t0x82, 0xA2, 0x82, 0xA2, 0x82, 0xA3, 0x82, 0xA3, 0x82, 0xA4, 0x82, 0xA4, 0x82, 0xA5, 0x82, 0xA5, 0x82, 0xA6, 0x82, 0xA6, 0x82, 0xA7, 0x82, 0xA7, 0x82, 0xA8, 0x82, 0xA8, 0x82, 0xA9, 0x82, 0xA9,\n\t0x82, 0xAA, 0x82, 0xAA, 0x82, 0xAB, 0x82, 0xAB, 0x82, 0xAC, 0x82, 0xAC, 0x82, 0xAD, 0x82, 0xAD, 0x82, 0xAE, 0x82, 0xAE, 0x82, 0xAF, 0x82, 0xAF, 0x82, 0xB0, 0x82, 0xB0, 0x82, 0xB1, 0x82, 0xB1,\n\t0x82, 0xB2, 0x82, 0xB2, 0x82, 0xB3, 0x82, 0xB3, 0x82, 0xB4, 0x82, 0xB4, 0x82, 0xB5, 0x82, 0xB5, 0x82, 0xB6, 0x82, 0xB6, 0x82, 0xB7, 0x82, 0xB7, 0x82, 0xB8, 0x82, 0xB8, 0x82, 0xB9, 0x82, 0xB9,\n\t0x82, 0xBA, 0x82, 0xBA, 0x82, 0xBB, 0x82, 0xBB, 0x82, 0xBC, 0x82, 0xBC, 0x82, 0xBD, 0x82, 0xBD, 0x82, 0xBE, 0x82, 0xBE, 0x82, 0xBF, 0x82, 0xBF, 0x82, 0xC0, 0x82, 0xC0, 0x82, 0xC1, 0x82, 0xC1,\n\t0x82, 0xC2, 0x82, 0xC2, 0x82, 0xC3, 0x82, 0xC3, 0x82, 0xC4, 0x82, 0xC4, 0x82, 0xC5, 0x82, 0xC5, 0x82, 0xC6, 0x82, 0xC6, 0x82, 0xC7, 0x82, 0xC7, 0x82, 0xC8, 0x82, 0xC8, 0x82, 0xC9, 0x82, 0xC9,\n\t0x82, 0xCA, 0x82, 0xCA, 0x82, 0xCB, 0x82, 0xCB, 0x82, 0xCC, 0x82, 0xCC, 0x82, 0xCD, 0x82, 0xCD, 0x82, 0xCE, 0x82, 0xCE, 0x82, 0xCF, 0x82, 0xCF, 0x82, 0xD0, 0x82, 0xD0, 0x82, 0xD1, 0x82, 0xD1,\n\t0x82, 0xD2, 0x82, 0xD2, 0x82, 0xD3, 0x82, 0xD3, 0x82, 0xD4, 0x82, 0xD4, 0x82, 0xD5, 0x82, 0xD5, 0x82, 0xD6, 0x82, 0xD6, 0x82, 0xD7, 0x82, 0xD7, 0x82, 0xD8, 0x82, 0xD8, 0x82, 0xD9, 0x82, 0xD9,\n\t0x82, 0xDA, 0x82, 0xDA, 0x82, 0xDB, 0x82, 0xDB, 0x82, 0xDC, 0x82, 0xDC, 0x82, 0xDD, 0x82, 0xDD, 0x82, 0xDE, 0x82, 0xDE, 0x82, 0xDF, 0x82, 0xDF, 0x82, 0xE0, 0x82, 0xE0, 0x82, 0xE1, 0x82, 0xE1,\n\t0x82, 0xE2, 0x82, 0xE2, 0x82, 0xE3, 0x82, 0xE3, 0x82, 0xE4, 0x82, 0xE4, 0x82, 0xE5, 0x82, 0xE5, 0x82, 0xE6, 0x82, 0xE6, 0x82, 0xE7, 0x82, 0xE7, 0x82, 0xE8, 0x82, 0xE8, 0x82, 0xE9, 0x82, 0xE9,\n\t0x82, 0xEA, 0x82, 0xEA, 0x82, 0xEB, 0x82, 0xEB, 0x82, 0xEC, 0x82, 0xEC, 0x82, 0xED, 0x82, 0xED, 0x82, 0xEE, 0x82, 0xEE, 0x82, 0xEF, 0x82, 0xEF, 0x82, 0xF0, 0x82, 0xF0, 0x82, 0xF1, 0x82, 0xF1,\n\t0x82, 0xF2, 0x82, 0xF2, 0x82, 0xF3, 0x82, 0xF3, 0x82, 0xF4, 0x82, 0xF4, 0x82, 0xF5, 0x82, 0xF5, 0x82, 0xF6, 0x82, 0xF6, 0x82, 0xF7, 0x82, 0xF7, 0x82, 0xF8, 0x82, 0xF8, 0x82, 0xF9, 0x82, 0xF9,\n\t0x82, 0xFA, 0x82, 0xFA, 0x82, 0xFB, 0x82, 0xFB, 0x82, 0xFC, 0x82, 0xFC, 0x82, 0xFD, 0x82, 0xFD, 0x82, 0xFE, 0x82, 0xFE, 0x82, 0xFF, 0x82, 0xFF, 0x82, 0x00, 0x83, 0x00, 0x83, 0x01, 0x83, 0x01,\n\t0x83, 0x02, 0x83, 0x02, 0x83, 0x03, 0xFE, 0xF0, 0xB1, 0x04, 0x83, 0x04, 0x83, 0x05, 0x83, 0x05, 0x83, 0x06, 0x83, 0x06, 0x83, 0x07, 0x83, 0x07, 0x83, 0x08, 0x83, 0x08, 0x83, 0x09, 0x83, 0x09,\n\t0x83, 0x0A, 0x83, 0x0A, 0x83, 0x0B, 0x83, 0x0B, 0x83, 0x0C, 0x83, 0x0C, 0x83, 0x0D, 0x83, 0x0D, 0x83, 0x0E, 0x83, 0x0E, 0x83, 0x0F, 0x83, 0x0F, 0x83, 0x10, 0x83, 0x10, 0x83, 0x11, 0x83, 0x11,\n\t0x83, 0x12, 0x83, 0x12, 0x83, 0x13, 0x83, 0x13, 0x83, 0x14, 0x83, 0x14, 0x83, 0x15, 0x83, 0x15, 0x83, 0x16, 0x83, 0x16, 0x83, 0x17, 0x83, 0x17, 0x83, 0x18, 0x83, 0x18, 0x83, 0x19, 0x83, 0x19,\n\t0x83, 0x1A, 0x83, 0x1A, 0x83, 0x1B, 0x83, 0x1B, 0x83, 0x1C, 0x83, 0x1C, 0x83, 0x1D, 0x83, 0x1D, 0x83, 0x1E, 0x83, 0x1E, 0x83, 0x1F, 0x83, 0x1F, 0x83, 0x20, 0x83, 0x20, 0x83, 0x21, 0x83, 0x21,\n\t0x83, 0x22, 0x83, 0x22, 0x83, 0x23, 0x83, 0x23, 0x83, 0x24, 0x83, 0x24, 0x83, 0x25, 0x83, 0x25, 0x83, 0x26, 0x83, 0x26, 0x83, 0x27, 0x83, 0x27, 0x83, 0x28, 0x83, 0x28, 0x83, 0x29, 0x83, 0x29,\n\t0x83, 0x2A, 0x83, 0x2A, 0x83, 0x2B, 0x83, 0x2B, 0x83, 0x2C, 0x83, 0x2C, 0x83, 0x2D, 0x83, 0x2D, 0x83, 0x2E, 0x83, 0x2E, 0x83, 0x2F, 0x83, 0x2F, 0x83, 0x30, 0x83, 0x30, 0x83, 0x31, 0x83, 0x31,\n\t0x83, 0x32, 0x83, 0x32, 0x83, 0x33, 0x83, 0x33, 0x83, 0x3D, 0x9F, 0xF0, 0xFF, 0x1E, 0x35, 0x83, 0x35, 0x83, 0x36, 0x83, 0x36, 0x83, 0x37, 0x83, 0x37, 0x83, 0x38, 0x83, 0x38, 0x83, 0x39, 0x83,\n\t0x39, 0x83, 0x3A, 0x83, 0x3A, 0x83, 0x3B, 0x83, 0x3B, 0x83, 0x3C, 0x83, 0x3C, 0x83, 0x3D, 0x83, 0x3D, 0x83, 0x3E, 0x83, 0x3E, 0x83, 0x3F, 0x83, 0x3F, 0x83, 0x40, 0x83, 0x40, 0x83, 0x41, 0x83,\n\t0x41, 0x83, 0x42, 0x83, 0x42, 0x83, 0x43, 0x83, 0x43, 0x83, 0x44, 0x83, 0x44, 0x83, 0x45, 0x83, 0x45, 0x83, 0x46, 0x83, 0x46, 0x83, 0x47, 0x83, 0x47, 0x83, 0x48, 0x83, 0x48, 0x83, 0x49, 0x83,\n\t0x49, 0x83, 0x4A, 0x83, 0x4A, 0x83, 0x4B, 0x83, 0x4B, 0x83, 0x4C, 0x83, 0x4C, 0x83, 0x4D, 0x83, 0x4D, 0x83, 0x4E, 0x83, 0x4E, 0x83, 0x4F, 0x83, 0x4F, 0x83, 0x50, 0x83, 0x50, 0x83, 0x51, 0x83,\n\t0x51, 0x83, 0x52, 0x83, 0x52, 0x83, 0x53, 0x83, 0x53, 0x83, 0x54, 0x83, 0x54, 0x83, 0x55, 0x83, 0x55, 0x83, 0x56, 0x83, 0x56, 0x83, 0x57, 0x83, 0x57, 0x83, 0x58, 0x83, 0x58, 0x83, 0x59, 0x83,\n\t0x59, 0x83, 0x5A, 0x83, 0x5A, 0x83, 0x5B, 0x83, 0x5B, 0x83, 0x5C, 0x83, 0x5C, 0x83, 0x5D, 0x83, 0x5D, 0x83, 0x5E, 0x83, 0x5E, 0x83, 0x5F, 0x83, 0x5F, 0x83, 0x60, 0x83, 0x60, 0x83, 0x61, 0x83,\n\t0x61, 0x83, 0x62, 0x83, 0x62, 0x83, 0x63, 0x83, 0x63, 0x83, 0x64, 0x83, 0x64, 0x83, 0x65, 0x83, 0x65, 0x83, 0x66, 0x83, 0x66, 0x83, 0x67, 0x83, 0x67, 0x83, 0x68, 0x83, 0x68, 0x83, 0x69, 0x83,\n\t0x69, 0x83, 0x6A, 0x83, 0x6A, 0x83, 0x6B, 0x83, 0x6B, 0x83, 0x6C, 0x83, 0x6C, 0x83, 0x6D, 0x83, 0x6D, 0x83, 0x6E, 0x83, 0x6E, 0x83, 0x6F, 0x83, 0x6F, 0x83, 0x70, 0x83, 0x70, 0x83, 0x71, 0x83,\n\t0x71, 0x83, 0x72, 0x83, 0x72, 0x83, 0x73, 0x83, 0x73, 0x83, 0x74, 0x83, 0x74, 0x83, 0x75, 0x83, 0x75, 0x83, 0x76, 0x83, 0x76, 0x83, 0x77, 0x83, 0x77, 0x83, 0x78, 0x83, 0x78, 0x83, 0x79, 0x83,\n\t0x79, 0x83, 0x7A, 0x83, 0x7A, 0x83, 0x7B, 0x83, 0x7B, 0x83, 0x7C, 0x83, 0x7C, 0x83, 0x7D, 0x83, 0x7D, 0x83, 0x7E, 0x83, 0x7E, 0x83, 0x7F, 0x83, 0x7F, 0x83, 0xF5, 0x0B, 0x00, 0xF9, 0x07, 0x00,\n\t0xFD, 0x03, 0x00, 0x01, 0x00, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,\n\t0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,\n\t0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xB4, 0x84, 0x83, 0x84, 0x83, 0x85, 0x83, 0x85, 0x83, 0x86, 0x83, 0x86, 0x83, 0x87, 0x83, 0x87, 0x83, 0x88, 0x83, 0x88, 0x83, 0x89, 0x83,\n\t0x89, 0x83, 0x8A, 0x83, 0x8A, 0x83, 0x8B, 0x83, 0x8B, 0x83, 0x8C, 0x83, 0x8C, 0x83, 0x8D, 0x83, 0x8D, 0x83, 0x8E, 0x83, 0x8E, 0x83, 0x8F, 0x83, 0x8F, 0x83, 0x90, 0x83, 0x90, 0x83, 0x91, 0x83,\n\t0x91, 0x83, 0x92, 0x83, 0x92, 0x83, 0x93, 0x83, 0x93, 0x83, 0x94, 0x83, 0x94, 0x83, 0x95, 0x83, 0x95, 0x83, 0x96, 0x83, 0x96, 0x83, 0x97, 0x83, 0x97, 0x83, 0x98, 0x83, 0x98, 0x83, 0x99, 0x83,\n\t0x99, 0x83, 0x9A, 0x83, 0x9A, 0x83, 0x9B, 0x83, 0x9B, 0x83, 0x9C, 0x83, 0x9C, 0x83, 0x9D, 0x83, 0x9D, 0x83, 0x9E, 0x83, 0x9E, 0x83, 0x9F, 0x83, 0x9F, 0x83, 0xA0, 0x83, 0xA0, 0x83, 0xA1, 0x83,\n\t0xA1, 0x83, 0xA2, 0x83, 0xA2, 0x83, 0xA3, 0x83, 0xA3, 0x83, 0xA4, 0x83, 0xA4, 0x83, 0xA5, 0x83, 0xA5, 0x83, 0xA6, 0x83, 0xA6, 0x83, 0xA7, 0x83, 0xA7, 0x83, 0xA8, 0x83, 0xA8, 0x83, 0xA9, 0x83,\n\t0xA9, 0x83, 0xAA, 0x83, 0xAA, 0x83, 0xAB, 0x83, 0xAB, 0x83, 0xAC, 0x83, 0xAC, 0x83, 0xAD, 0x83, 0xAD, 0x83, 0xAE, 0x83, 0xAE, 0x83, 0xAF, 0x83, 0xAF, 0x83, 0xB0, 0x83, 0xB0, 0x83, 0xB1, 0x83,\n\t0xB1, 0x83, 0xB2, 0x83, 0xB2, 0x83, 0xB3, 0x83, 0xB3, 0x83, 0xB4, 0x83, 0xB4, 0x83, 0xB5, 0x83, 0xB5, 0x83, 0xB6, 0x83, 0xB6, 0x83, 0xB7, 0x83, 0xB7, 0x83, 0xB8, 0x83, 0xB8, 0x83, 0xB9, 0x83,\n\t0xB9, 0x83, 0xBA, 0x83, 0xBA, 0x83, 0xBB, 0x83, 0xBB, 0x83, 0xBC, 0x83, 0xBC, 0x83, 0xBD, 0x83, 0xBD, 0x83, 0xBE, 0x83, 0xBE, 0x83, 0xBF, 0x83, 0xBF, 0x83, 0xC0, 0x83, 0xC0, 0x83, 0xC1, 0x83,\n\t0xC1, 0x83, 0xC2, 0x83, 0xC2, 0x83, 0xC3, 0x83, 0xC3, 0x83, 0xC4, 0x83, 0xC4, 0x83, 0xC5, 0x83, 0xC5, 0x83, 0xC6, 0x83, 0xC6, 0x83, 0xC7, 0x83, 0xC7, 0x83, 0xC8, 0x83, 0xC8, 0x83, 0xC9, 0x83,\n\t0xC9, 0x83, 0xCA, 0x83, 0xCA, 0x83, 0xCB, 0x83, 0xCB, 0x83, 0xCC, 0x83, 0xCC, 0x83, 0xCD, 0x83, 0xCD, 0x83, 0xCE, 0x83, 0xCE, 0x83, 0xCF, 0x83, 0xCF, 0x83, 0xD0, 0x83, 0xD0, 0x83, 0xD1, 0x83,\n\t0xD1, 0x83, 0xD2, 0x83, 0xD2, 0x83, 0xD3, 0x83, 0xD3, 0x83, 0xD4, 0x83, 0xD4, 0x83, 0xD5, 0x83, 0xD5, 0x83, 0xD6, 0x83, 0xD6, 0x83, 0xD7, 0x83, 0xD7, 0x83, 0xD8, 0x83, 0xD8, 0x83, 0xD9, 0x83,\n\t0xD9, 0x83, 0xDA, 0x83, 0xDA, 0x83, 0xDB, 0x83, 0xDB, 0x83, 0xDC, 0x83, 0xDC, 0x83, 0xDD, 0x83, 0xDD, 0x83, 0xDE, 0x83, 0xDE, 0x83, 0xDF, 0x83, 0xDF, 0x83, 0xE0, 0x83, 0xE0, 0x83, 0xE1, 0x83,\n\t0xE1, 0x83, 0xE2, 0x83, 0xE2, 0x83, 0xE3, 0x83, 0xE3, 0x83, 0xE4, 0x83, 0xE4, 0x83, 0xE5, 0x83, 0xE5, 0x83, 0xE6, 0x83, 0xE6, 0x83, 0xE7, 0x83, 0xE7, 0x83, 0xE8, 0x83, 0xE8, 0x83, 0xE9, 0x83,\n\t0xE9, 0x83, 0xEA, 0x83, 0xEA, 0x83, 0xEB, 0x83, 0xEB, 0x83, 0xEC, 0x83, 0xEC, 0x83, 0xED, 0x83, 0xED, 0x83, 0xEE, 0x83, 0xEE, 0x83, 0xEF, 0x83, 0xEF, 0x83, 0xF0, 0x83, 0xF0, 0x83, 0xF1, 0x83,\n\t0xF1, 0x83, 0xF2, 0x83, 0xF2, 0x83, 0xF3, 0x83, 0xF3, 0x83, 0xF4, 0x83, 0xF4, 0x83, 0xF5, 0x83, 0xF5, 0x83, 0xF6, 0x83, 0xF6, 0x83, 0xF7, 0x83, 0xF7, 0x83, 0xF8, 0x83, 0xF8, 0x83, 0xF9, 0x83,\n\t0xF9, 0x83, 0xFA, 0x83, 0xFA, 0x83, 0xFB, 0x83, 0xFB, 0x83, 0xFC, 0x83, 0xFC, 0x83, 0xFD, 0x83, 0xFD, 0x83, 0xFE, 0x83, 0xFE, 0x83, 0xFF, 0x83, 0xFF, 0x83, 0x00, 0x84, 0x01, 0x84, 0x02, 0x84,\n\t0x03, 0x84, 0x04, 0x84, 0x05, 0x84, 0x06, 0x84, 0x07, 0x84, 0x08, 0x84, 0x09, 0x84, 0x0A, 0x84, 0x0B, 0x84, 0x0C, 0x84, 0x0D, 0x84, 0x0E, 0x84, 0x0F, 0x84, 0x10, 0x84, 0x11, 0x84, 0x12, 0x84,\n\t0x13, 0x84, 0x14, 0x84, 0x15, 0x84, 0x16, 0x84, 0x17, 0x84, 0x18, 0x84, 0x19, 0x84, 0x1A, 0x84, 0x1B, 0x84, 0x1C, 0x84, 0x1D, 0x84, 0x1E, 0x84, 0x1F, 0x84, 0x20, 0x84, 0x21, 0x84, 0x22, 0x84,\n\t0x23, 0x84, 0x24, 0x84, 0x25, 0x84, 0x26, 0x84, 0x27, 0x84, 0x28, 0x84, 0x29, 0x84, 0x2A, 0x84, 0x2B, 0x84, 0x2C, 0x84, 0x2D, 0x84, 0x2E, 0x84, 0x2F, 0x84, 0x30, 0x84, 0x31, 0x84, 0x32, 0x84,\n\t0x33, 0x84, 0x34, 0x84, 0x35, 0x84, 0x36, 0x84, 0x37, 0x84, 0x38, 0x84, 0x39, 0x84, 0x3A, 0x84, 0x3B, 0x84, 0x3C, 0x84, 0x3D, 0x84, 0x3E, 0x84, 0x3F, 0x84, 0x40, 0x84, 0x41, 0x84, 0x42, 0x84,\n\t0x43, 0x84, 0x44, 0x84, 0x45, 0x84, 0x46, 0x84, 0x47, 0x84, 0x48, 0x84, 0x49, 0x84, 0x4A, 0x84, 0x4B, 0x84, 0x4C, 0x84, 0x4D, 0x84, 0x4E, 0x84, 0x4F, 0x84, 0x50, 0x84, 0x51, 0x84, 0x52, 0x84,\n\t0x53, 0x84, 0x54, 0x84, 0x55, 0x84, 0x56, 0x84, 0x57, 0x84, 0x58, 0x84, 0x59, 0x84, 0x5A, 0x84, 0x5B, 0x84, 0x5C, 0x84, 0x5D, 0x84, 0x5E, 0x84, 0x5F, 0x84, 0x60, 0x84, 0x61, 0x84, 0x62, 0x84,\n\t0x63, 0x84, 0x64, 0x84, 0x65, 0x84, 0x66, 0x84, 0x67, 0x84, 0x68, 0x84, 0x69, 0x84, 0x6A, 0x84, 0x6B, 0x84, 0x6C, 0x84, 0x6D, 0x84, 0x6E, 0x84, 0x6F, 0x84, 0x70, 0x84, 0x71, 0x84, 0x72, 0x84,\n\t0x73, 0x84, 0x74, 0x84, 0x75, 0x84, 0x76, 0x84, 0x77, 0x84, 0x78, 0x84, 0x79, 0x84, 0x7A, 0x84, 0x7B, 0x84, 0x7C, 0x84, 0x7D, 0x84, 0x7E, 0x84, 0x7F, 0x84, 0x80, 0x84, 0x81, 0x84, 0x82, 0x84,\n\t0x83, 0x84, 0x84, 0x84, 0x85, 0x84, 0x86, 0x84, 0x87, 0x84, 0x88, 0x84, 0x89, 0x84, 0x8A, 0x84, 0x8B, 0x84, 0x8C, 0x84, 0x8D, 0x84, 0x8E, 0x84, 0x8F, 0x84, 0x90, 0x84, 0x91, 0x84, 0x92, 0x84,\n\t0x93, 0x84, 0x94, 0x84, 0x95, 0x84, 0x96, 0x84, 0x97, 0x84, 0x98, 0x84, 0x99, 0x84, 0x9A, 0x84, 0x9B, 0x84, 0x9C, 0x84, 0x9D, 0x84, 0x9E, 0x84, 0x9F, 0x84, 0xA0, 0x84, 0xA1, 0x84, 0xA2, 0x84,\n\t0xA3, 0x84, 0xA4, 0x84, 0xA5, 0x84, 0xA6, 0x84, 0xA7, 0x84, 0xA8, 0x84, 0xA9, 0x84, 0xAA, 0x84, 0xAB, 0x84, 0xAC, 0x84, 0xAD, 0x84, 0xAE, 0x84, 0xAF, 0x84, 0xB0, 0x84, 0xB1, 0x84, 0xB2, 0x84,\n\t0xB3, 0x84, 0xB4, 0x84, 0xB5, 0x84, 0xB6, 0x84, 0xB7, 0x84, 0xB8, 0x84, 0xB9, 0x84, 0xBA, 0x84, 0xBB, 0x84, 0xBC, 0x84, 0xBD, 0x84, 0xBE, 0x84, 0xBF, 0x84, 0xC0, 0x84, 0xC1, 0x84, 0xC2, 0x84,\n\t0xC3, 0x84, 0xC4, 0x84, 0xC5, 0x84, 0xC6, 0x84, 0xC7, 0x84, 0xC8, 0x84, 0xC9, 0x84, 0xCA, 0x84, 0xCB, 0x84, 0xCC, 0x84, 0xCD, 0x84, 0xCE, 0x84, 0xCF, 0x84, 0xD0, 0x84, 0xD1, 0x84, 0xD2, 0x84,\n\t0xD3, 0x84, 0xD4, 0x84, 0xD5, 0x84, 0xD6, 0x84, 0xD7, 0x84, 0xD8, 0x84, 0xD9, 0x84, 0xDA, 0x84, 0xDB, 0x84, 0xDC, 0x84, 0xDD, 0x84, 0xDE, 0x84, 0xDF, 0x84, 0xE0, 0x84, 0xE1, 0x84, 0xE2, 0x84,\n\t0xE3, 0x84, 0xE4, 0x84, 0xE5, 0x84, 0xE6, 0x84, 0xE7, 0x84, 0xE8, 0x84, 0xE9, 0x84, 0xEA, 0x84, 0xEB, 0x84, 0xEC, 0x84, 0xED, 0x84, 0xEE, 0x84, 0xEF, 0x84, 0xF0, 0x84, 0xF1, 0x84, 0xF2, 0x84,\n\t0xF3, 0x84, 0xF4, 0x84, 0xF5, 0x84, 0xF6, 0x84, 0xF7, 0x84, 0xF8, 0x84, 0xF9, 0x84, 0xFA, 0x84, 0xFB, 0x84, 0xFC, 0x84, 0xFD, 0x84, 0xFE, 0x84, 0xFF, 0x84, 0x00, 0x85, 0x01, 0x85, 0x02, 0x85,\n\t0x03, 0x85, 0x04, 0x85, 0x05, 0x85, 0x06, 0x85, 0x07, 0x85, 0x08, 0x85, 0x09, 0x85, 0x0A, 0x85, 0x0B, 0x85, 0x0C, 0x85, 0x0D, 0x85, 0x0E, 0x85, 0x0F, 0x85, 0x10, 0x85, 0x11, 0x85, 0x12, 0x85,\n\t0x13, 0x85, 0x14, 0x85, 0x15, 0x85, 0x16, 0x85, 0x17, 0x85, 0x18, 0x85, 0x19, 0x85, 0x1A, 0x85, 0x1B, 0x85, 0x1C, 0x85, 0x1D, 0x85, 0x1E, 0x85, 0x1F, 0x85, 0x20, 0x85, 0x21, 0x85, 0x22, 0x85,\n\t0x23, 0x85, 0x24, 0x85, 0x25, 0x85, 0x26, 0x85, 0x27, 0x85, 0x28, 0x85, 0x29, 0x85, 0x2A, 0x85, 0x2B, 0x85, 0x2C, 0x85, 0x2D, 0x85, 0x2E, 0x85, 0x2F, 0x85, 0x30, 0x85, 0x31, 0x85, 0x32, 0x85,\n\t0x33, 0x85, 0x34, 0x85, 0x35, 0x85, 0x36, 0x85, 0x37, 0x85, 0x38, 0x85, 0x39, 0x85, 0x3A, 0x85, 0x3B, 0x85, 0x3C, 0x85, 0x3D, 0x85, 0x3E, 0x85, 0x3F, 0x85, 0x40, 0x85, 0x41, 0x85, 0x42, 0x85,\n\t0x43, 0x85, 0x44, 0x85, 0x45, 0x85, 0x46, 0x85, 0x47, 0x85, 0x48, 0x85, 0x49, 0x85, 0x4A, 0x85, 0x4B, 0x85, 0x4C, 0x85, 0x4D, 0x85, 0x4E, 0x85, 0x4F, 0x85, 0x50, 0x85, 0x51, 0x85, 0x52, 0x85,\n\t0x53, 0x85, 0x54, 0x85, 0x55, 0x85, 0x56, 0x85, 0x57, 0x85, 0x58, 0x85, 0x59, 0x85, 0x5A, 0x85, 0x5B, 0x85, 0x5C, 0x85, 0x5D, 0x85, 0x5E, 0x85, 0x5F, 0x85, 0x60, 0x85, 0x61, 0x85, 0x62, 0x85,\n\t0x63, 0x85, 0x64, 0x85, 0x65, 0x85, 0x66, 0x85, 0x67, 0x85, 0x68, 0x85, 0x69, 0x85, 0x6A, 0x85, 0x6B, 0x85, 0x6C, 0x85, 0x6D, 0x85, 0x6E, 0x85, 0x6F, 0x85, 0x70, 0x85, 0x71, 0x85, 0x72, 0x85,\n\t0x73, 0x85, 0x74, 0x85, 0x75, 0x85, 0x76, 0x85, 0x77, 0x85, 0x78, 0x85, 0x79, 0x85, 0x7A, 0x85, 0x7B, 0x85, 0x7C, 0x85, 0x7D, 0x85, 0x7E, 0x85, 0x7F, 0x85, 0x80, 0x85, 0x81, 0x85, 0x82, 0x85,\n\t0x83, 0x85, 0x84, 0x85, 0x85, 0x85, 0x86, 0x85, 0x87, 0x85, 0x88, 0x85, 0x89, 0x85, 0x8A, 0x85, 0x8B, 0x85, 0x8C, 0x85, 0x8D, 0x85, 0x8E, 0x85, 0x8F, 0x85, 0x90, 0x85, 0x91, 0x85, 0x92, 0x85,\n\t0x93, 0x85, 0x94, 0x85, 0x95, 0x85, 0x96, 0x85, 0x97, 0x85, 0x98, 0x85, 0x99, 0x85, 0x9A, 0x85, 0x9B, 0x85, 0x9C, 0x85, 0x9D, 0x85, 0x9E, 0x85, 0x9F, 0x85, 0xA0, 0x85, 0xA1, 0x85, 0xA2, 0x85,\n\t0xA3, 0x85, 0xA4, 0x85, 0xA5, 0x85, 0xA6, 0x85, 0xA7, 0x85, 0xA8, 0x85, 0xA9, 0x85, 0xAA, 0x85, 0xAB, 0x85, 0xAC, 0x85, 0xAD, 0x85, 0xAE, 0x85, 0xAF, 0x85, 0xB0, 0x85, 0xB1, 0x85, 0xB2, 0x85,\n\t0xB3, 0x85, 0xB4, 0x85, 0xB5, 0x85, 0xB6, 0x85, 0xB7, 0x85, 0xB8, 0x85, 0xB9, 0x85, 0xBA, 0x85, 0xBB, 0x85, 0xBC, 0x85, 0xBD, 0x85, 0xBE, 0x85, 0xBF, 0x85, 0xC0, 0x85, 0xC1, 0x85, 0xC2, 0x85,\n\t0xC3, 0x85, 0xC4, 0x85, 0xC5, 0x85, 0xC6, 0x85, 0xC7, 0x85, 0xC8, 0x85, 0xC9, 0x85, 0xCA, 0x85, 0xCB, 0x85, 0xCC, 0x85, 0xCD, 0x85, 0xCE, 0x85, 0xCF, 0x85, 0xD0, 0x85, 0xD1, 0x85, 0xD2, 0x85,\n\t0xD3, 0x85, 0xD4, 0x85, 0xD5, 0x85, 0xD6, 0x85, 0xD7, 0x85, 0xD8, 0x85, 0xD9, 0x85, 0xDA, 0x85, 0xDB, 0x85, 0xDC, 0x85, 0xDD, 0x85, 0xDE, 0x85, 0xDF, 0x85, 0xE0, 0x85, 0xE1, 0x85, 0xE2, 0x85,\n\t0xE3, 0x85, 0xE4, 0x85, 0xE5, 0x85, 0xE6, 0x85, 0xE7, 0x85, 0xE8, 0x85, 0xE9, 0x85, 0xEA, 0x85, 0xEB, 0x85, 0xEC, 0x85, 0xED, 0x85, 0xEE, 0x85, 0xEF, 0x85, 0xF0, 0x85, 0xF1, 0x85, 0xF2, 0x85,\n\t0xF3, 0x85, 0xF4, 0x85, 0xF5, 0x85, 0xF6, 0x85, 0xF7, 0x85, 0xF8, 0x85, 0xF9, 0x85, 0xFA, 0x85, 0xFB, 0x85, 0xFC, 0x85, 0xFD, 0x85, 0xFE, 0x85, 0xFF, 0x85, 0x00, 0x86, 0x01, 0x86, 0x02, 0x86,\n\t0x03, 0x86, 0x04, 0x86, 0x05, 0x86, 0x06, 0x86, 0x07, 0x86, 0x08, 0x86, 0x09, 0x86, 0x0A, 0x86, 0x0B, 0x86, 0x0C, 0x86, 0x0D, 0x86, 0x0E, 0x86, 0x0F, 0x86, 0x10, 0x86, 0x11, 0x86, 0x12, 0x86,\n\t0x13, 0x86, 0x14, 0x86, 0x15, 0x86, 0x16, 0x86, 0x17, 0x86, 0x18, 0x86, 0x19, 0x86, 0x1A, 0x86, 0x1B, 0x86, 0x1C, 0x86, 0x1D, 0x86, 0x1E, 0x86, 0x1F, 0x86, 0x20, 0x86, 0x21, 0x86, 0x22, 0x86,\n\t0x23, 0x86, 0x24, 0x86, 0x25, 0x86, 0x26, 0x86, 0x27, 0x86, 0x28, 0x86, 0x29, 0x86, 0x2A, 0x86, 0x2B, 0x86, 0x2C, 0x86, 0x2D, 0x86, 0x2E, 0x86, 0x2F, 0x86, 0x30, 0x86, 0x31, 0x86, 0x32, 0x86,\n\t0x33, 0x86, 0x34, 0x86, 0x35, 0x86, 0x36, 0x86, 0x37, 0x86, 0x38, 0x86, 0x39, 0x86, 0x3A, 0x86, 0x3B, 0x86, 0x3C, 0x86, 0x3D, 0x86, 0x3E, 0x86, 0x3F, 0x86, 0x40, 0x86, 0x41, 0x86, 0x42, 0x86,\n\t0x43, 0x86, 0x44, 0x86, 0x45, 0x86, 0x46, 0x86, 0x47, 0x86, 0x48, 0x86, 0x49, 0x86, 0x4A, 0x86, 0x4B, 0x86, 0x4C, 0x86, 0x4D, 0x86, 0x4E, 0x86, 0x4F, 0x86, 0x50, 0x86, 0x51, 0x86, 0x52, 0x86,\n\t0x53, 0x86, 0x54, 0x86, 0x55, 0x86, 0x56, 0x86, 0x57, 0x86, 0x58, 0x86, 0x59, 0x86, 0x5A, 0x86, 0x5B, 0x86, 0x5C, 0x86, 0x5D, 0x86, 0x5E, 0x86, 0x5F, 0x86, 0x60, 0x86, 0x61, 0x86, 0x62, 0x86,\n\t0x63, 0x86, 0x64, 0x86, 0x65, 0x86, 0x66, 0x86, 0x67, 0x86, 0x68, 0x86, 0x69, 0x86, 0x6A, 0x86, 0x6B, 0x86, 0x6C, 0x86, 0x6D, 0x86, 0x6E, 0x86, 0x6F, 0x86, 0x70, 0x86, 0x71, 0x86, 0x72, 0x86,\n\t0x73, 0x86, 0x74, 0x86, 0x75, 0x86, 0x76, 0x86, 0x77, 0x86, 0x78, 0x86, 0x79, 0x86, 0x7A, 0x86, 0x7B, 0x86, 0x7C, 0x86, 0x7D, 0x86, 0x7E, 0x86, 0x7F, 0x86, 0x80, 0x86, 0x81, 0x86, 0x82, 0x86,\n\t0x83, 0x86, 0x84, 0x86, 0x85, 0x86, 0x86, 0x86, 0x87, 0x86, 0x88, 0x86, 0x89, 0x86, 0x8A, 0x86, 0x8B, 0x86, 0x8C, 0x86, 0x8D, 0x86, 0x8E, 0x86, 0x8F, 0x86, 0x90, 0x86, 0x91, 0x86, 0x92, 0x86,\n\t0x93, 0x86, 0x94, 0x86, 0x95, 0x86, 0x96, 0x86, 0x97, 0x86, 0x98, 0x86, 0x99, 0x86, 0x9A, 0x86, 0x9B, 0x86, 0x9C, 0x86, 0x9D, 0x86, 0x9E, 0x86, 0x9F, 0x86, 0xA0, 0x86, 0xA1, 0x86, 0xA2, 0x86,\n\t0xA3, 0x86, 0xA4, 0x86, 0xA5, 0x86, 0xA6, 0x86, 0xA7, 0x86, 0xA8, 0x86, 0xA9, 0x86, 0xAA, 0x86, 0xAB, 0x86, 0xAC, 0x86, 0xAD, 0x86, 0xAE, 0x86, 0xAF, 0x86, 0xB0, 0x86, 0xB1, 0x86, 0xB2, 0x86,\n\t0xB3, 0x86, 0xB4, 0x86, 0xB5, 0x86, 0xB6, 0x86, 0xB7, 0x86, 0xB8, 0x86, 0xB9, 0x86, 0xBA, 0x86, 0xBB, 0x86, 0xBC, 0x86, 0xBD, 0x86, 0xBE, 0x86, 0xBF, 0x86, 0xC0, 0x86, 0xC1, 0x86, 0xC2, 0x86,\n\t0xC3, 0x86, 0xC4, 0x86, 0xC5, 0x86, 0xC6, 0x86, 0xC7, 0x86, 0xC8, 0x86, 0xC9, 0x86, 0xCA, 0x86, 0xCB, 0x86, 0xCC, 0x86, 0xCD, 0x86, 0xCE, 0x86, 0xCF, 0x86, 0xD0, 0x86, 0xD1, 0x86, 0xD2, 0x86,\n\t0xD3, 0x86, 0xD4, 0x86, 0xD5, 0x86, 0xD6, 0x86, 0xD7, 0x86, 0xD8, 0x86, 0xD9, 0x86, 0xDA, 0x86, 0xDB, 0x86, 0xDC, 0x86, 0xDD, 0x86, 0xDE, 0x86, 0xDF, 0x86, 0xE0, 0x86, 0xE1, 0x86, 0xE2, 0x86,\n\t0xE3, 0x86, 0xE4, 0x86, 0xE5, 0x86, 0xE6, 0x86, 0xE7, 0x86, 0xE8, 0x86, 0xE9, 0x86, 0xEA, 0x86, 0xEB, 0x86, 0xEC, 0x86, 0xED, 0x86, 0xEE, 0x86, 0xEF, 0x86, 0xF0, 0x86, 0xF1, 0x86, 0xF2, 0x86,\n\t0xF3, 0x86, 0xF4, 0x86, 0xF5, 0x86, 0xF6, 0x86, 0xF7, 0x86, 0xF8, 0x86, 0xF9, 0x86, 0xFA, 0x86, 0xFB, 0x86, 0xFC, 0x86, 0xFD, 0x86, 0xFE, 0x86, 0xFF, 0x86, 0x00, 0x87, 0x01, 0x87, 0x02, 0x87,\n\t0x03, 0x87, 0x04, 0x87, 0x05, 0x87, 0x06, 0x87, 0x07, 0x87, 0x08, 0x87, 0x09, 0x87, 0x0A, 0x87, 0x0B, 0x87, 0x0C, 0x87, 0x0D, 0x87, 0x0E, 0x87, 0x0F, 0x87, 0x10, 0x87, 0x11, 0x87, 0x12, 0x87,\n\t0x13, 0x87, 0x14, 0x87, 0x15, 0x87, 0x16, 0x87, 0x17, 0x87, 0x18, 0x87, 0x19, 0x87, 0x1A, 0x87, 0x1B, 0x87, 0x1C, 0x87, 0x1D, 0x87, 0x1E, 0x87, 0x1F, 0x87, 0x20, 0x87, 0x21, 0x87, 0x22, 0x87,\n\t0x23, 0x87, 0x24, 0x87, 0x25, 0x87, 0x26, 0x87, 0x27, 0x87, 0x28, 0x87, 0x29, 0x87, 0x2A, 0x87, 0x2B, 0x87, 0x2C, 0x87, 0x2D, 0x87, 0x2E, 0x87, 0x2F, 0x87, 0x30, 0x87, 0x31, 0x87, 0x32, 0x87,\n\t0x33, 0x87, 0x34, 0x87, 0x35, 0x87, 0x36, 0x87, 0x37, 0x87, 0x38, 0x87, 0x39, 0x87, 0x3A, 0x87, 0x3B, 0x87, 0x3C, 0x87, 0x3D, 0x87, 0x3E, 0x87, 0x3F, 0x87, 0x40, 0x87, 0x41, 0x87, 0x42, 0x87,\n\t0x43, 0x87, 0x44, 0x87, 0x45, 0x87, 0x46, 0x87, 0x47, 0x87, 0x48, 0x87, 0x49, 0x87, 0x4A, 0x87, 0x4B, 0x87, 0x4C, 0x87, 0x4D, 0x87, 0x4E, 0x87, 0x4F, 0x87, 0x50, 0x87, 0x51, 0x87, 0x52, 0x87,\n\t0x53, 0x87, 0x54, 0x87, 0x55, 0x87, 0x56, 0x87, 0x57, 0x87, 0x58, 0x87, 0x59, 0x87, 0x5A, 0x87, 0x5B, 0x87, 0x5C, 0x87, 0x5D, 0x87, 0x5E, 0x87, 0x5F, 0x87, 0x60, 0x87, 0x61, 0x87, 0x62, 0x87,\n\t0x63, 0x87, 0x64, 0x87, 0x65, 0x87, 0x66, 0x87, 0x67, 0x87, 0x68, 0x87, 0x69, 0x87, 0x6A, 0x87, 0x6B, 0x87, 0x6C, 0x87, 0x6D, 0x87, 0x6E, 0x87, 0x6F, 0x87, 0x70, 0x87, 0x71, 0x87, 0x72, 0x87,\n\t0x73, 0x87, 0x74, 0x87, 0x75, 0x87, 0x76, 0x87, 0x77, 0x87, 0x78, 0x87, 0x79, 0x87, 0x7A, 0x87, 0x7B, 0x87, 0x7C, 0x87, 0x7D, 0x87, 0x7E, 0x87, 0x7F, 0x87, 0x80, 0x87, 0x81, 0x87, 0x82, 0x87,\n\t0x83, 0x87, 0x84, 0x87, 0x85, 0x87, 0x86, 0x87, 0x87, 0x87, 0x88, 0x87, 0x89, 0x87, 0x8A, 0x87, 0x8B, 0x87, 0x8C, 0x87, 0x8D, 0x87, 0x8E, 0x87, 0x8F, 0x87, 0x90, 0x87, 0x91, 0x87, 0x92, 0x87,\n\t0x93, 0x87, 0x94, 0x87, 0x95, 0x87, 0x96, 0x87, 0x97, 0x87, 0x98, 0x87, 0x99, 0x87, 0x9A, 0x87, 0x9B, 0x87, 0x9C, 0x87, 0x9D, 0x87, 0x9E, 0x87, 0x9F, 0x87, 0xA0, 0x87, 0xA1, 0x87, 0xA2, 0x87,\n\t0xA3, 0x87, 0xA4, 0x87, 0xA5, 0x87, 0xA6, 0x87, 0xA7, 0x87, 0xA8, 0x87, 0xA9, 0x87, 0xAA, 0x87, 0xAB, 0x87, 0xAC, 0x87, 0xAD, 0x87, 0xAE, 0x87, 0xAF, 0x87, 0xB0, 0x87, 0xB1, 0x87, 0xB2, 0x87,\n\t0xB3, 0x87, 0xB4, 0x87, 0xB5, 0x87, 0xB6, 0x87, 0xB7, 0x87, 0xB8, 0x87, 0xB9, 0x87, 0xBA, 0x87, 0xBB, 0x87, 0xBC, 0x87, 0xBD, 0x87, 0xBE, 0x87, 0xBF, 0x87, 0xC0, 0x87, 0xC1, 0x87, 0xC2, 0x87,\n\t0xC3, 0x87, 0xC4, 0x87, 0xC5, 0x87, 0xC6, 0x87, 0xC7, 0x87, 0xC8, 0x87, 0xC9, 0x87, 0xCA, 0x87, 0xCB, 0x87, 0xCC, 0x87, 0xCD, 0x87, 0xCE, 0x87, 0xCF, 0x87, 0xD0, 0x87, 0xD1, 0x87, 0xD2, 0x87,\n\t0xD3, 0x87, 0xD4, 0x87, 0xD5, 0x87, 0xD6, 0x87, 0xD7, 0x87, 0xD8, 0x87, 0xD9, 0x87, 0xDA, 0x87, 0xDB, 0x87, 0xDC, 0x87, 0xDD, 0x87, 0xDE, 0x87, 0xDF, 0x87, 0xE0, 0x87, 0xE1, 0x87, 0xE2, 0x87,\n\t0xE3, 0x87, 0xE4, 0x87, 0xE5, 0x87, 0xE6, 0x87, 0xE7, 0x87, 0xE8, 0x87, 0xE9, 0x87, 0xEA, 0x87, 0xEB, 0x87, 0xEC, 0x87, 0xED, 0x87, 0xEE, 0x87, 0xEF, 0x87, 0xF0, 0x87, 0xF1, 0x87, 0xF2, 0x87,\n\t0xF3, 0x87, 0xF4, 0x87, 0xF5, 0x87, 0xF6, 0x87, 0xF7, 0x87, 0xF8, 0x87, 0xF9, 0x87, 0xFA, 0x87, 0xFB, 0x87, 0xFC, 0x87, 0xFD, 0x87, 0xFE, 0x87, 0xFF, 0x87, 0x00, 0x88, 0x01, 0x88, 0x02, 0x88,\n\t0x03, 0x88, 0x04, 0x88, 0x05, 0x88, 0x06, 0x88, 0x07, 0x88, 0x08, 0x88, 0x09, 0x88, 0x0A, 0x88, 0x0B, 0x88, 0x0C, 0x88, 0x0D, 0x88, 0x0E, 0x88, 0x0F, 0x88, 0x10, 0x88, 0x11, 0x88, 0x12, 0x88,\n\t0x13, 0x88, 0x14, 0x88, 0x15, 0x88, 0x16, 0x88, 0x17, 0x88, 0x18, 0x88, 0x19, 0x88, 0x1A, 0x88, 0x1B, 0x88, 0x1C, 0x88, 0x1D, 0x88, 0x1E, 0x88, 0x1F, 0x88, 0x20, 0x88, 0x21, 0x88, 0x22, 0x88,\n\t0x23, 0x88, 0x24, 0x88, 0x25, 0x88, 0x26, 0x88, 0x27, 0x88, 0x28, 0x88, 0x29, 0x88, 0x2A, 0x88, 0x2B, 0x88, 0x2C, 0x88, 0x2D, 0x88, 0x2E, 0x88, 0x2F, 0x88, 0x30, 0x88, 0x31, 0x88, 0x32, 0x88,\n\t0x33, 0x88, 0x34, 0x88, 0x35, 0x88, 0x36, 0x88, 0x37, 0x88, 0x38, 0x88, 0x39, 0x88, 0x3A, 0x88, 0x3B, 0x88, 0x3C, 0x88, 0x3D, 0x88, 0x3E, 0x88, 0x3F, 0x88, 0x40, 0x88, 0x41, 0x88, 0x42, 0x88,\n\t0x43, 0x88, 0x44, 0x88, 0x45, 0x88, 0x46, 0x88, 0x47, 0x88, 0x48, 0x88, 0x49, 0x88, 0x4A, 0x88, 0x4B, 0x88, 0x4C, 0x88, 0x4D, 0x88, 0x4E, 0x88, 0x4F, 0x88, 0x50, 0x88, 0x51, 0x88, 0x52, 0x88,\n\t0x53, 0x88, 0x54, 0x88, 0x55, 0x88, 0x56, 0x88, 0x57, 0x88, 0x58, 0x88, 0x59, 0x88, 0x5A, 0x88, 0x5B, 0x88, 0x5C, 0x88, 0x5D, 0x88, 0x5E, 0x88, 0x5F, 0x88, 0x60, 0x88, 0x61, 0x88, 0x62, 0x88,\n\t0x63, 0x88, 0x64, 0x88, 0x65, 0x88, 0x66, 0x88, 0x67, 0x88, 0x68, 0x88, 0x69, 0x88, 0x6A, 0x88, 0x6B, 0x88, 0x6C, 0x88, 0x6D, 0x88, 0x6E, 0x88, 0x6F, 0x88, 0x70, 0x88, 0x71, 0x88, 0x72, 0x88,\n\t0x73, 0x88, 0x74, 0x88, 0x75, 0x88, 0x76, 0x88, 0x77, 0x88, 0x78, 0x88, 0x79, 0x88, 0x7A, 0x88, 0x7B, 0x88, 0x7C, 0x88, 0x7D, 0x88, 0x7E, 0x88, 0x7F, 0x88, 0x80, 0x88, 0x81, 0x88, 0x82, 0x88,\n\t0x83, 0x88, 0x84, 0x88, 0x85, 0x88, 0x86, 0x88, 0x87, 0x88, 0x88, 0x88, 0x89, 0x88, 0x8A, 0x88, 0x8B, 0x88, 0x8C, 0x88, 0x8D, 0x88, 0x8E, 0x88, 0x8F, 0x88, 0x90, 0x88, 0x91, 0x88, 0x92, 0x88,\n\t0x93, 0x88, 0x94, 0x88, 0x95, 0x88, 0x96, 0x88, 0x97, 0x88, 0x98, 0x88, 0x99, 0x88, 0x9A, 0x88, 0x9B, 0x88, 0x9C, 0x88, 0x9D, 0x88, 0x9E, 0x88, 0x9F, 0x88, 0xA0, 0x88, 0xA1, 0x88, 0xA2, 0x88,\n\t0xA3, 0x88, 0xA4, 0x88, 0xA5, 0x88, 0xA6, 0x88, 0xA7, 0x88, 0xA8, 0x88, 0xA9, 0x88, 0xAA, 0x88, 0xAB, 0x88, 0xAC, 0x88, 0xAD, 0x88, 0xAE, 0x88, 0xAF, 0x88, 0xB0, 0x88, 0xB1, 0x88, 0xB2, 0x88,\n\t0xB3, 0x88, 0xB4, 0x88, 0xB5, 0x88, 0xB6, 0x88, 0xB7, 0x88, 0xB8, 0x88, 0xB9, 0x88, 0xBA, 0x88, 0xBB, 0x88, 0xBC, 0x88, 0xBD, 0x88, 0xBE, 0x88, 0xBF, 0x88, 0xC0, 0x88, 0xC1, 0x88, 0xC2, 0x88,\n\t0xC3, 0x88, 0xC4, 0x88, 0xC5, 0x88, 0xC6, 0x88, 0xC7, 0x88, 0xC8, 0x88, 0xC9, 0x88, 0xCA, 0x88, 0xCB, 0x88, 0xCC, 0x88, 0xCD, 0x88, 0xCE, 0x88, 0xCF, 0x88, 0xD0, 0x88, 0xD1, 0x88, 0xD2, 0x88,\n\t0xD3, 0x88, 0xD4, 0x88, 0xD5, 0x88, 0xD6, 0x88, 0xD7, 0x88, 0xD8, 0x88, 0xD9, 0x88, 0xDA, 0x88, 0xDB, 0x88, 0xDC, 0x88, 0xDD, 0x88, 0xDE, 0x88, 0xDF, 0x88, 0xE0, 0x88, 0xE1, 0x88, 0xE2, 0x88,\n\t0xE3, 0x88, 0xE4, 0x88, 0xE5, 0x88, 0xE6, 0x88, 0xE7, 0x88, 0xE8, 0x88, 0xE9, 0x88, 0xEA, 0x88, 0xEB, 0x88, 0xEC, 0x88, 0xED, 0x88, 0xEE, 0x88, 0xEF, 0x88, 0xF0, 0x88, 0xF1, 0x88, 0xF2, 0x88,\n\t0xF3, 0x88, 0xF4, 0x88, 0xF5, 0x88, 0xF6, 0x88, 0xF7, 0x88, 0xF8, 0x88, 0xF9, 0x88, 0xFA, 0x88, 0xFB, 0x88, 0xFC, 0x88, 0xFD, 0x88, 0xFE, 0x88, 0xFF, 0x88, 0x00, 0x89, 0x01, 0x89, 0x02, 0x89,\n\t0x03, 0x89, 0x04, 0x89, 0x05, 0x89, 0x06, 0x89, 0x07, 0x89, 0x08, 0x89, 0x09, 0x89, 0x0A, 0x89, 0x0B, 0x89, 0x0C, 0x89, 0x0D, 0x89, 0x0E, 0x89, 0x0F, 0x89, 0x10, 0x89, 0x11, 0x89, 0x12, 0x89,\n\t0x13, 0x89, 0x14, 0x89, 0x15, 0x89, 0x16, 0x89, 0x17, 0x89, 0x18, 0x89, 0x19, 0x89, 0x1A, 0x89, 0x1B, 0x89, 0x1C, 0x89, 0x1D, 0x89, 0x1E, 0x89, 0x1F, 0x89, 0x20, 0x89, 0x21, 0x89, 0x22, 0x89,\n\t0x23, 0x89, 0x24, 0x89, 0x25, 0x89, 0x26, 0x89, 0x27, 0x89, 0x28, 0x89, 0x29, 0x89, 0x2A, 0x89, 0x2B, 0x89, 0x2C, 0x89, 0x2D, 0x89, 0x2E, 0x89, 0x2F, 0x89, 0x30, 0x89, 0x31, 0x89, 0x32, 0x89,\n\t0x33, 0x89, 0x34, 0x89, 0x35, 0x89, 0x36, 0x89, 0x37, 0x89, 0x38, 0x89, 0x39, 0x89, 0x3A, 0x89, 0x3B, 0x89, 0x3C, 0x89, 0x3D, 0x89, 0x3E, 0x89, 0x3F, 0x89, 0x40, 0x89, 0x41, 0x89, 0x42, 0x89,\n\t0x43, 0x89, 0x44, 0x89, 0x45, 0x89, 0x46, 0x89, 0x47, 0x89, 0x48, 0x89, 0x49, 0x89, 0x4A, 0x89, 0x4B, 0x89, 0x4C, 0x89, 0x4D, 0x89, 0x4E, 0x89, 0x4F, 0x89, 0x50, 0x89, 0x51, 0x89, 0x52, 0x89,\n\t0x53, 0x89, 0x54, 0x89, 0x55, 0x89, 0x56, 0x89, 0x57, 0x89, 0x58, 0x89, 0x59, 0x89, 0x5A, 0x89, 0x5B, 0x89, 0x5C, 0x89, 0x5D, 0x89, 0x5E, 0x89, 0x5F, 0x89, 0x60, 0x89, 0x61, 0x89, 0x62, 0x89,\n\t0x63, 0x89, 0x64, 0x89, 0x65, 0x89, 0x66, 0x89, 0x67, 0x89, 0x68, 0x89, 0x69, 0x89, 0x6A, 0x89, 0x6B, 0x89, 0x6C, 0x89, 0x6D, 0x89, 0x6E, 0x89, 0x6F, 0x89, 0x70, 0x89, 0x71, 0x89, 0x72, 0x89,\n\t0x73, 0x89, 0x74, 0x89, 0x75, 0x89, 0x76, 0x89, 0x77, 0x89, 0x78, 0x89, 0x79, 0x89, 0x7A, 0x89, 0x7B, 0x89, 0x7C, 0x89, 0x7D, 0x89, 0x7E, 0x89, 0x7F, 0x89, 0x80, 0x89, 0x81, 0x89, 0x82, 0x89,\n\t0x83, 0x89, 0x84, 0x89, 0x85, 0x89, 0x86, 0x89, 0x87, 0x89, 0x88, 0x89, 0x89, 0x89, 0x8A, 0x89, 0x8B, 0x89, 0x8C, 0x89, 0x8D, 0x89, 0x8E, 0x89, 0x8F, 0x89, 0x90, 0x89, 0x91, 0x89, 0x92, 0x89,\n\t0x93, 0x89, 0x94, 0x89, 0x95, 0x89, 0x96, 0x89, 0x97, 0x89, 0x98, 0x89, 0x99, 0x89, 0x9A, 0x89, 0x9B, 0x89, 0x9C, 0x89, 0x9D, 0x89, 0x9E, 0x89, 0x9F, 0x89, 0xA0, 0x89, 0xA1, 0x89, 0xA2, 0x89,\n\t0xA3, 0x89, 0xA4, 0x89, 0xA5, 0x89, 0xA6, 0x89, 0xA7, 0x89, 0xA8, 0x89, 0xA9, 0x89, 0xAA, 0x89, 0xAB, 0x89, 0xAC, 0x89, 0xAD, 0x89, 0xAE, 0x89, 0xAF, 0x89, 0xB0, 0x89, 0xB1, 0x89, 0xB2, 0x89,\n\t0xB3, 0x89, 0xB4, 0x89, 0xB5, 0x89, 0xB6, 0x89, 0xB7, 0x89, 0xB8, 0x89, 0xB9, 0x89, 0xBA, 0x89, 0xBB, 0x89, 0xBC, 0x89, 0xBD, 0x89, 0xBE, 0x89, 0xBF, 0x89, 0xC0, 0x89, 0xC1, 0x89, 0xC2, 0x89,\n\t0xC3, 0x89, 0xC4, 0x89, 0xC5, 0x89, 0xC6, 0x89, 0xC7, 0x89, 0xC8, 0x89, 0xC9, 0x89, 0xCA, 0x89, 0xCB, 0x89, 0xCC, 0x89, 0xCD, 0x89, 0xCE, 0x89, 0xCF, 0x89, 0xD0, 0x89, 0xD1, 0x89, 0xD2, 0x89,\n\t0xD3, 0x89, 0xD4, 0x89, 0xD5, 0x89, 0xD6, 0x89, 0xD7, 0x89, 0xD8, 0x89, 0xD9, 0x89, 0xDA, 0x89, 0xDB, 0x89, 0xDC, 0x89, 0xDD, 0x89, 0xDE, 0x89, 0xDF, 0x89, 0xE0, 0x89, 0xE1, 0x89, 0xE2, 0x89,\n\t0xE3, 0x89, 0xE4, 0x89, 0xE5, 0x89, 0xE6, 0x89, 0xE7, 0x89, 0xE8, 0x89, 0xE9, 0x89, 0xEA, 0x89, 0xEB, 0x89, 0xEC, 0x89, 0xED, 0x89, 0xEE, 0x89, 0xEF, 0x89, 0xF0, 0x89, 0xF1, 0x89, 0xF2, 0x89,\n\t0xF3, 0x89, 0xF4, 0x89, 0xF5, 0x89, 0xF6, 0x89, 0xF7, 0x89, 0xF8, 0x89, 0xF9, 0x89, 0xFA, 0x89, 0xFB, 0x89, 0xFC, 0x89, 0xFD, 0x89, 0xFE, 0x89, 0xFF, 0x89, 0x00, 0x8A, 0x01, 0x8A, 0x02, 0x8A,\n\t0x03, 0x8A, 0x04, 0x8A, 0x05, 0x8A, 0x06, 0x8A, 0x07, 0x8A, 0x08, 0x8A, 0x09, 0x8A, 0x0A, 0x8A, 0x0B, 0x8A, 0x0C, 0x8A, 0x0D, 0x8A, 0x0E, 0x8A, 0x0F, 0x8A, 0x10, 0x8A, 0x11, 0x8A, 0x12, 0x8A,\n\t0x13, 0x8A, 0x14, 0x8A, 0x15, 0x8A, 0x16, 0x8A, 0x17, 0x8A, 0x18, 0x8A, 0x19, 0x8A, 0x1A, 0x8A, 0x1B, 0x8A, 0x1C, 0x8A, 0x1D, 0x8A, 0x1E, 0x8A, 0x1F, 0x8A, 0x20, 0x8A, 0x21, 0x8A, 0x22, 0x8A,\n\t0x23, 0x8A, 0x24, 0x8A, 0x25, 0x8A, 0x26, 0x8A, 0x27, 0x8A, 0x28, 0x8A, 0x29, 0x8A, 0x2A, 0x8A, 0x2B, 0x8A, 0x2C, 0x8A, 0x2D, 0x8A, 0x2E, 0x8A, 0x2F, 0x8A, 0x30, 0x8A, 0x31, 0x8A, 0x32, 0x8A,\n\t0x33, 0x8A, 0x34, 0x8A, 0x35, 0x8A, 0x36, 0x8A, 0x37, 0x8A, 0x38, 0x8A, 0x39, 0x8A, 0x3A, 0x8A, 0x3B, 0x8A, 0x3C, 0x8A, 0x3D, 0x8A, 0x3E, 0x8A, 0x3F, 0x8A, 0x40, 0x8A, 0x41, 0x8A, 0x42, 0x8A,\n\t0x43, 0x8A, 0x44, 0x8A, 0x45, 0x8A, 0x46, 0x8A, 0x47, 0x8A, 0x48, 0x8A, 0x49, 0x8A, 0x4A, 0x8A, 0x4B, 0x8A, 0x4C, 0x8A, 0x4D, 0x8A, 0x4E, 0x8A, 0x4F, 0x8A, 0x50, 0x8A, 0x51, 0x8A, 0x52, 0x8A,\n\t0x53, 0x8A, 0x54, 0x8A, 0x55, 0x8A, 0x55, 0x8A, 0x56, 0x8A, 0x57, 0x8A, 0x58, 0x8A, 0x59, 0x8A, 0x5A, 0x8A, 0x5B, 0x8A, 0x5C, 0x8A, 0x5D, 0x8A, 0x5E, 0x8A, 0x5F, 0x8A, 0x60, 0x8A, 0x61, 0x8A,\n\t0x62, 0x8A, 0x63, 0x8A, 0x64, 0x8A, 0x65, 0x8A, 0x66, 0x8A, 0x67, 0x8A, 0x68, 0x8A, 0x69, 0x8A, 0x6A, 0x8A, 0x6B, 0x8A, 0x6C, 0x8A, 0x6D, 0x8A, 0x6E, 0x8A, 0x6F, 0x8A, 0x70, 0x8A, 0x71, 0x8A,\n\t0x72, 0x8A, 0x73, 0x8A, 0x74, 0x8A, 0x75, 0x8A, 0x76, 0x8A, 0x77, 0x8A, 0x78, 0x8A, 0x79, 0x8A, 0x7A, 0x8A, 0x7B, 0x8A, 0x7C, 0x8A, 0x7D, 0x8A, 0x7E, 0x8A, 0x7F, 0x8A, 0x80, 0x8A, 0x81, 0x8A,\n\t0x82, 0x8A, 0x83, 0x8A, 0x84, 0x8A, 0x85, 0x8A, 0x86, 0x8A, 0x87, 0x8A, 0x88, 0x8A, 0x89, 0x8A, 0x8A, 0x8A, 0x8B, 0x8A, 0x8C, 0x8A, 0x8D, 0x8A, 0x8E, 0x8A, 0x8F, 0x8A, 0x90, 0x8A, 0x91, 0x8A,\n\t0x92, 0x8A, 0x93, 0x8A, 0x94, 0x8A, 0x95, 0x8A, 0x96, 0x8A, 0x97, 0x8A, 0x98, 0x8A, 0x99, 0x8A, 0x9A, 0x8A, 0x9B, 0x8A, 0x9C, 0x8A, 0x9D, 0x8A, 0x9E, 0x8A, 0x9F, 0x8A, 0xA0, 0x8A, 0xA1, 0x8A,\n\t0xA2, 0x8A, 0xA3, 0x8A, 0xA4, 0x8A, 0xA5, 0x8A, 0xA6, 0x8A, 0xA7, 0x8A, 0xA8, 0x8A, 0xA9, 0x8A, 0xAA, 0x8A, 0xAB, 0x8A, 0xAC, 0x8A, 0xAD, 0x8A, 0xAE, 0x8A, 0xAF, 0x8A, 0xB0, 0x8A, 0xB1, 0x8A,\n\t0xB2, 0x8A, 0xB3, 0x8A, 0xB4, 0x8A, 0xB5, 0x8A, 0xB6, 0x8A, 0xB7, 0x8A, 0xB8, 0x8A, 0xB9, 0x8A, 0xBA, 0x8A, 0xBB, 0x8A, 0xBC, 0x8A, 0xBD, 0x8A, 0xBE, 0x8A, 0xBF, 0x8A, 0xC0, 0x8A, 0xC1, 0x8A,\n\t0xC2, 0x8A, 0xC3, 0x8A, 0xC4, 0x8A, 0xC5, 0x8A, 0xC6, 0x8A, 0xC7, 0x8A, 0xC8, 0x8A, 0xC9, 0x8A, 0xCA, 0x8A, 0xCB, 0x8A, 0xCC, 0x8A, 0xCD, 0x8A, 0xCE, 0x8A, 0xCF, 0x8A, 0xD0, 0x8A, 0xD1, 0x8A,\n\t0xD2, 0x8A, 0xD3, 0x8A, 0xD4, 0x8A, 0xD5, 0x8A, 0xD6, 0x8A, 0xD7, 0x8A, 0xD8, 0x8A, 0xD9, 0x8A, 0xDA, 0x8A, 0xDB, 0x8A, 0xDC, 0x8A, 0xDD, 0x8A, 0xDE, 0x8A, 0xDF, 0x8A, 0xE0, 0x8A, 0xE1, 0x8A,\n\t0xE2, 0x8A, 0xE3, 0x8A, 0xE4, 0x8A, 0xE5, 0x8A, 0xE6, 0x8A, 0xE7, 0x8A, 0xE8, 0x8A, 0xE9, 0x8A, 0xEA, 0x8A, 0xEB, 0x8A, 0xEC, 0x8A, 0xED, 0x8A, 0xEE, 0x8A, 0xEF, 0x8A, 0xF0, 0x8A, 0xF1, 0x8A,\n\t0xF2, 0x8A, 0xF3, 0x8A, 0xF4, 0x8A, 0xF5, 0x8A, 0xF6, 0x8A, 0xF7, 0x8A, 0xF8, 0x8A, 0xF9, 0x8A, 0xFA, 0x8A, 0xFB, 0x8A, 0xFC, 0x8A, 0xFD, 0x8A, 0xFE, 0x8A, 0xFF, 0x8A, 0x00, 0x8B, 0x01, 0x8B,\n\t0x02, 0x8B, 0x03, 0x8B, 0x04, 0x8B, 0x05, 0x8B, 0x06, 0x8B, 0x07, 0x8B, 0x08, 0x8B, 0x09, 0x8B, 0x0A, 0x8B, 0x0B, 0x8B, 0x0C, 0x8B, 0x0D, 0x8B, 0x0E, 0x8B, 0x0F, 0x8B, 0x10, 0x8B, 0x11, 0x8B,\n\t0x12, 0x8B, 0x13, 0x8B, 0x14, 0x8B, 0x15, 0x8B, 0x16, 0x8B, 0x17, 0x8B, 0x18, 0x8B, 0x19, 0x8B, 0x1A, 0x8B, 0x1B, 0x8B, 0x1C, 0x8B, 0x1D, 0x8B, 0x1E, 0x8B, 0x1F, 0x8B, 0x20, 0x8B, 0x21, 0x8B,\n\t0x22, 0x8B, 0x23, 0x8B, 0x24, 0x8B, 0x25, 0x8B, 0x26, 0x8B, 0x27, 0x8B, 0x28, 0x8B, 0x29, 0x8B, 0x2A, 0x8B, 0x2B, 0x8B, 0x2C, 0x8B, 0x2D, 0x8B, 0x2E, 0x8B, 0x2F, 0x8B, 0x30, 0x8B, 0x31, 0x8B,\n\t0x32, 0x8B, 0x33, 0x8B, 0x34, 0x8B, 0x35, 0x8B, 0x36, 0x8B, 0x37, 0x8B, 0x38, 0x8B, 0x39, 0x8B, 0x3A, 0x8B, 0x3B, 0x8B, 0x3C, 0x8B, 0x3D, 0x8B, 0x3E, 0x8B, 0x3F, 0x8B, 0x40, 0x8B, 0x41, 0x8B,\n\t0x42, 0x8B, 0x43, 0x8B, 0x44, 0x8B, 0x45, 0x8B, 0x46, 0x8B, 0x47, 0x8B, 0x48, 0x8B, 0x49, 0x8B, 0x4A, 0x8B, 0x4B, 0x8B, 0x4C, 0x8B, 0x4D, 0x8B, 0x4E, 0x8B, 0x4F, 0x8B, 0x50, 0x8B, 0x51, 0x8B,\n\t0x52, 0x8B, 0x53, 0x8B, 0x54, 0x8B, 0x55, 0x8B, 0x56, 0x8B, 0x57, 0x8B, 0x58, 0x8B, 0x59, 0x8B, 0x5A, 0x8B, 0x5B, 0x8B, 0x5C, 0x8B, 0x5D, 0x8B, 0x5E, 0x8B, 0x5F, 0x8B, 0x60, 0x8B, 0x61, 0x8B,\n\t0x62, 0x8B, 0x63, 0x8B, 0x64, 0x8B, 0x65, 0x8B, 0x66, 0x8B, 0x67, 0x8B, 0x68, 0x8B, 0x69, 0x8B, 0x6A, 0x8B, 0x6B, 0x8B, 0x6C, 0x8B, 0x6D, 0x8B, 0x6E, 0x8B, 0x6F, 0x8B, 0x70, 0x8B, 0x71, 0x8B,\n\t0x72, 0x8B, 0x73, 0x8B, 0x74, 0x8B, 0x75, 0x8B, 0x76, 0x8B, 0x77, 0x8B, 0x78, 0x8B, 0x79, 0x8B, 0x7A, 0x8B, 0x7B, 0x8B, 0x7C, 0x8B, 0x7D, 0x8B, 0x7E, 0x8B, 0x7F, 0x8B, 0x80, 0x8B, 0x81, 0x8B,\n\t0x82, 0x8B, 0x83, 0x8B, 0x84, 0x8B, 0x85, 0x8B, 0x86, 0x8B, 0x87, 0x8B, 0x88, 0x8B, 0x89, 0x8B, 0x8A, 0x8B, 0x8B, 0x8B, 0x8C, 0x8B, 0x8D, 0x8B, 0x8E, 0x8B, 0x8F, 0x8B, 0x90, 0x8B, 0x91, 0x8B,\n\t0x92, 0x8B, 0x93, 0x8B, 0x94, 0x8B, 0x95, 0x8B, 0x96, 0x8B, 0x97, 0x8B, 0x98, 0x8B, 0x99, 0x8B, 0x9A, 0x8B, 0x9B, 0x8B, 0x9C, 0x8B, 0x9D, 0x8B, 0x9E, 0x8B, 0x9F, 0x8B, 0xA0, 0x8B, 0xA1, 0x8B,\n\t0xA2, 0x8B, 0xA3, 0x8B, 0xA4, 0x8B, 0xA5, 0x8B, 0xA6, 0x8B, 0xA7, 0x8B, 0xA8, 0x8B, 0xA9, 0x8B, 0xAA, 0x8B, 0xAB, 0x8B, 0xAC, 0x8B, 0xAD, 0x8B, 0xAE, 0x8B, 0xAF, 0x8B, 0xB0, 0x8B, 0xB1, 0x8B,\n\t0xB2, 0x8B, 0xB3, 0x8B, 0xB4, 0x8B, 0xB5, 0x8B, 0xB6, 0x8B, 0xB7, 0x8B, 0xB8, 0x8B, 0xB9, 0x8B, 0xBA, 0x8B, 0xBB, 0x8B, 0xBC, 0x8B, 0xBD, 0x8B, 0xBE, 0x8B, 0xBF, 0x8B, 0xC0, 0x8B, 0xC1, 0x8B,\n\t0xC2, 0x8B, 0xC3, 0x8B, 0xC4, 0x8B, 0xC5, 0x8B, 0xC6, 0x8B, 0xC7, 0x8B, 0xC8, 0x8B, 0xC9, 0x8B, 0xCA, 0x8B, 0xCB, 0x8B, 0xCC, 0x8B, 0xCD, 0x8B, 0xCE, 0x8B, 0xCF, 0x8B, 0xD0, 0x8B, 0xD1, 0x8B,\n\t0xD2, 0x8B, 0xD3, 0x8B, 0xD4, 0x8B, 0xD5, 0x8B, 0xD6, 0x8B, 0xD7, 0x8B, 0xD8, 0x8B, 0xD9, 0x8B, 0xDA, 0x8B, 0xDB, 0x8B, 0xDC, 0x8B, 0xDD, 0x8B, 0xDE, 0x8B, 0xDF, 0x8B, 0xE0, 0x8B, 0xE1, 0x8B,\n\t0xE2, 0x8B, 0xE3, 0x8B, 0xE4, 0x8B, 0xE5, 0x8B, 0xE6, 0x8B, 0xE7, 0x8B, 0xE8, 0x8B, 0xE9, 0x8B, 0xEA, 0x8B, 0xEB, 0x8B, 0xEC, 0x8B, 0xED, 0x8B, 0xEE, 0x8B, 0xEF, 0x8B, 0xF0, 0x8B, 0xF1, 0x8B,\n\t0xF2, 0x8B, 0xF3, 0x8B, 0xF4, 0x8B, 0xF5, 0x8B, 0xF6, 0x8B, 0xF7, 0x8B, 0xF8, 0x8B, 0xF9, 0x8B, 0xFA, 0x8B, 0xFB, 0x8B, 0xFC, 0x8B, 0xFD, 0x8B, 0xFE, 0x8B, 0xFF, 0x8B, 0x01, 0x8C, 0x02, 0x8C,\n\t0x03, 0x8C, 0x04, 0x8C, 0x05, 0x8C, 0x06, 0x8C, 0x07, 0x8C, 0x08, 0x8C, 0x09, 0x8C, 0x0A, 0x8C, 0x0B, 0x8C, 0x0C, 0x8C, 0x0D, 0x8C, 0x0E, 0x8C, 0x0F, 0x8C, 0x10, 0x8C, 0x11, 0x8C, 0x12, 0x8C,\n\t0x13, 0x8C, 0x14, 0x8C, 0x15, 0x8C, 0x16, 0x8C, 0x17, 0x8C, 0x18, 0x8C, 0x19, 0x8C, 0x1A, 0x8C, 0x1B, 0x8C, 0x1C, 0x8C, 0x1D, 0x8C, 0x1E, 0x8C, 0x1F, 0x8C, 0x20, 0x8C, 0x21, 0x8C, 0x22, 0x8C,\n\t0x23, 0x8C, 0x24, 0x8C, 0x25, 0x8C, 0x26, 0x8C, 0x27, 0x8C, 0x28, 0x8C, 0x29, 0x8C, 0x2A, 0x8C, 0x2B, 0x8C, 0x2C, 0x8C, 0x2D, 0x8C, 0x2E, 0x8C, 0x2F, 0x8C, 0x30, 0x8C, 0x31, 0x8C, 0x32, 0x8C,\n\t0x33, 0x8C, 0x34, 0x8C, 0x35, 0x8C, 0x36, 0x8C, 0x37, 0x8C, 0x38, 0x8C, 0x39, 0x8C, 0x3A, 0x8C, 0x3B, 0x8C, 0x3C, 0x8C, 0x3D, 0x8C, 0x3E, 0x8C, 0x3F, 0x8C, 0x40, 0x8C, 0x41, 0x8C, 0x42, 0x8C,\n\t0x43, 0x8C, 0x44, 0x8C, 0x45, 0x8C, 0x46, 0x8C, 0x47, 0x8C, 0x48, 0x8C, 0x49, 0x8C, 0x4A, 0x8C, 0x4B, 0x8C, 0x4C, 0x8C, 0x4D, 0x8C, 0x4E, 0x8C, 0x4F, 0x8C, 0x50, 0x8C, 0x51, 0x8C, 0x52, 0x8C,\n\t0x53, 0x8C, 0x54, 0x8C, 0x55, 0x8C, 0x56, 0x8C, 0x57, 0x8C, 0x58, 0x8C, 0x59, 0x8C, 0x5A, 0x8C, 0x5B, 0x8C, 0x5C, 0x8C, 0x5D, 0x8C, 0x5E, 0x8C, 0x5F, 0x8C, 0x60, 0x8C, 0x61, 0x8C, 0x62, 0x8C,\n\t0x63, 0x8C, 0x64, 0x8C, 0x65, 0x8C, 0x66, 0x8C, 0x67, 0x8C, 0x68, 0x8C, 0x69, 0x8C, 0x6A, 0x8C, 0x6B, 0x8C, 0x6C, 0x8C, 0x6D, 0x8C, 0x6E, 0x8C, 0x6F, 0x8C, 0x70, 0x8C, 0x71, 0x8C, 0x72, 0x8C,\n\t0x73, 0x8C, 0x74, 0x8C, 0x75, 0x8C, 0x76, 0x8C, 0x77, 0x8C, 0x78, 0x8C, 0x79, 0x8C, 0x7A, 0x8C, 0x7A, 0x8C, 0x7B, 0x8C, 0x7C, 0x8C, 0x7D, 0x8C, 0x7E, 0x8C, 0x7F, 0x8C, 0x80, 0x8C, 0x81, 0x8C,\n\t0x82, 0x8C, 0x83, 0x8C, 0x84, 0x8C, 0x85, 0x8C, 0x86, 0x8C, 0x87, 0x8C, 0x88, 0x8C, 0x89, 0x8C, 0x8A, 0x8C, 0x8B, 0x8C, 0x8C, 0x8C, 0x8D, 0x8C, 0x8E, 0x8C, 0x8F, 0x8C, 0x90, 0x8C, 0x91, 0x8C,\n\t0x92, 0x8C, 0x93, 0x8C, 0x94, 0x8C, 0x95, 0x8C, 0x96, 0x8C, 0x97, 0x8C, 0x98, 0x8C, 0x99, 0x8C, 0x9A, 0x8C, 0x9B, 0x8C, 0x9C, 0x8C, 0x9D, 0x8C, 0x9E, 0x8C, 0x9F, 0x8C, 0xA0, 0x8C, 0xA1, 0x8C,\n\t0xA2, 0x8C, 0xA3, 0x8C, 0xA4, 0x8C, 0xA5, 0x8C, 0xA6, 0x8C, 0xA7, 0x8C, 0xA8, 0x8C, 0xA9, 0x8C, 0xAA, 0x8C, 0xAB, 0x8C, 0xAC, 0x8C, 0xAD, 0x8C, 0xAE, 0x8C, 0xAF, 0x8C, 0xB0, 0x8C, 0xB1, 0x8C,\n\t0xB2, 0x8C, 0xB3, 0x8C, 0xB4, 0x8C, 0xB5, 0x8C, 0xB6, 0x8C, 0xB7, 0x8C, 0xB8, 0x8C, 0xB9, 0x8C, 0xBA, 0x8C, 0xBB, 0x8C, 0xBC, 0x8C, 0xBD, 0x8C, 0xBE, 0x8C, 0xBF, 0x8C, 0xC0, 0x8C, 0xC1, 0x8C,\n\t0xC2, 0x8C, 0xC3, 0x8C, 0xC4, 0x8C, 0xC5, 0x8C, 0xC6, 0x8C, 0xC7, 0x8C, 0xC8, 0x8C, 0xC9, 0x8C, 0xCA, 0x8C, 0xCB, 0x8C, 0xCC, 0x8C, 0xCD, 0x8C, 0xCE, 0x8C, 0xCF, 0x8C, 0xD0, 0x8C, 0xD1, 0x8C,\n\t0xD2, 0x8C, 0xD3, 0x8C, 0xD4, 0x8C, 0xD5, 0x8C, 0xD6, 0x8C, 0xD7, 0x8C, 0xD8, 0x8C, 0xD9, 0x8C, 0xDA, 0x8C, 0xDB, 0x8C, 0xDC, 0x8C, 0xDD, 0x8C, 0xDE, 0x8C, 0xDF, 0x8C, 0xE0, 0x8C, 0xE1, 0x8C,\n\t0xE2, 0x8C, 0xE3, 0x8C, 0xE4, 0x8C, 0xE5, 0x8C, 0xE6, 0x8C, 0xE7, 0x8C, 0xE8, 0x8C, 0xE9, 0x8C, 0xEA, 0x8C, 0xEB, 0x8C, 0xEC, 0x8C, 0xED, 0x8C, 0xEE, 0x8C, 0xEF, 0x8C, 0xF0, 0x8C, 0xF1, 0x8C,\n\t0xF2, 0x8C, 0xF3, 0x8C, 0xF4, 0x8C, 0xF5, 0x8C, 0xF6, 0x8C, 0xF7, 0x8C, 0xF8, 0x8C, 0xF9, 0x8C, 0xFA, 0x8C, 0xFB, 0x8C, 0xFC, 0x8C, 0xFD, 0x8C, 0xFE, 0x8C, 0xFF, 0x8C, 0x00, 0x8D, 0x01, 0x8D,\n\t0x02, 0x8D, 0x03, 0x8D, 0x04, 0x8D, 0x05, 0x8D, 0x06, 0x8D, 0x07, 0x8D, 0x08, 0x8D, 0x09, 0x8D, 0x0A, 0x8D, 0x0B, 0x8D, 0x0C, 0x8D, 0x0D, 0x8D, 0x0E, 0x8D, 0x0F, 0x8D, 0x10, 0x8D, 0x11, 0x8D,\n\t0x12, 0x8D, 0x13, 0x8D, 0x14, 0x8D, 0x15, 0x8D, 0x16, 0x8D, 0x17, 0x8D, 0x18, 0x8D, 0x19, 0x8D, 0x1A, 0x8D, 0x1B, 0x8D, 0x1C, 0x8D, 0x1D, 0x8D, 0x1E, 0x8D, 0x1F, 0x8D, 0x20, 0x8D, 0x21, 0x8D,\n\t0x22, 0x8D, 0x23, 0x8D, 0x24, 0x8D, 0x25, 0x8D, 0x26, 0x8D, 0x27, 0x8D, 0x28, 0x8D, 0x29, 0x8D, 0x2A, 0x8D, 0x2B, 0x8D, 0x2C, 0x8D, 0x2D, 0x8D, 0x2E, 0x8D, 0x2F, 0x8D, 0x30, 0x8D, 0x31, 0x8D,\n\t0x32, 0x8D, 0x33, 0x8D, 0x34, 0x8D, 0x35, 0x8D, 0x36, 0x8D, 0x37, 0x8D, 0x38, 0x8D, 0x39, 0x8D, 0x3A, 0x8D, 0x3B, 0x8D, 0x3C, 0x8D, 0x3D, 0x8D, 0x3E, 0x8D, 0x3F, 0x8D, 0x40, 0x8D, 0x41, 0x8D,\n\t0x42, 0x8D, 0x43, 0x8D, 0x44, 0x8D, 0x45, 0x8D, 0x46, 0x8D, 0x47, 0x8D, 0x48, 0x8D, 0x49, 0x8D, 0x4A, 0x8D, 0x4B, 0x8D, 0x4C, 0x8D, 0x4D, 0x8D, 0x4E, 0x8D, 0x4F, 0x8D, 0x50, 0x8D, 0x51, 0x8D,\n\t0x52, 0x8D, 0x53, 0x8D, 0x54, 0x8D, 0x55, 0x8D, 0x56, 0x8D, 0x57, 0x8D, 0x58, 0x8D, 0x59, 0x8D, 0x5A, 0x8D, 0x5B, 0x8D, 0x5C, 0x8D, 0x5D, 0x8D, 0x5E, 0x8D, 0x5F, 0x8D, 0x60, 0x8D, 0x61, 0x8D,\n\t0x62, 0x8D, 0x63, 0x8D, 0x64, 0x8D, 0x65, 0x8D, 0x66, 0x8D, 0x67, 0x8D, 0x68, 0x8D, 0x69, 0x8D, 0x6A, 0x8D, 0x6B, 0x8D, 0x6C, 0x8D, 0x6D, 0x8D, 0x6E, 0x8D, 0x6F, 0x8D, 0x70, 0x8D, 0x71, 0x8D,\n\t0x72, 0x8D, 0x73, 0x8D, 0x74, 0x8D, 0x75, 0x8D, 0x76, 0x8D, 0x77, 0x8D, 0x78, 0x8D, 0x79, 0x8D, 0x7A, 0x8D, 0x7B, 0x8D, 0x7C, 0x8D, 0x7D, 0x8D, 0x7E, 0x8D, 0x7F, 0x8D, 0x80, 0x8D, 0x81, 0x8D,\n\t0x82, 0x8D, 0x83, 0x8D, 0x84, 0x8D, 0x85, 0x8D, 0x86, 0x8D, 0x87, 0x8D, 0x88, 0x8D, 0x89, 0x8D, 0x8A, 0x8D, 0x8B, 0x8D, 0x8C, 0x8D, 0x8D, 0x8D, 0x8E, 0x8D, 0x8F, 0x8D, 0x90, 0x8D, 0x91, 0x8D,\n\t0x92, 0x8D, 0x93, 0x8D, 0x94, 0x8D, 0x95, 0x8D, 0x96, 0x8D, 0x97, 0x8D, 0x98, 0x8D, 0x99, 0x8D, 0x9A, 0x8D, 0x9B, 0x8D, 0x9C, 0x8D, 0x9D, 0x8D, 0x9E, 0x8D, 0x9F, 0x8D, 0xA0, 0x8D, 0xA1, 0x8D,\n\t0xA2, 0x8D, 0xA3, 0x8D, 0xA4, 0x8D, 0xA5, 0x8D, 0xA6, 0x8D, 0xA7, 0x8D, 0xA8, 0x8D, 0xA9, 0x8D, 0xAA, 0x8D, 0xAB, 0x8D, 0xAC, 0x8D, 0xAD, 0x8D, 0xAE, 0x8D, 0xAF, 0x8D, 0xB0, 0x8D, 0xB1, 0x8D,\n\t0xB2, 0x8D, 0xB3, 0x8D, 0xB4, 0x8D, 0xB5, 0x8D, 0xB6, 0x8D, 0xB7, 0x8D, 0xB8, 0x8D, 0xB9, 0x8D, 0xBA, 0x8D, 0xBB, 0x8D, 0xBC, 0x8D, 0xBD, 0x8D, 0xBE, 0x8D, 0xBF, 0x8D, 0xC0, 0x8D, 0xC1, 0x8D,\n\t0xC2, 0x8D, 0xC3, 0x8D, 0xC4, 0x8D, 0xC5, 0x8D, 0xC6, 0x8D, 0xC7, 0x8D, 0xC8, 0x8D, 0xC9, 0x8D, 0xCA, 0x8D, 0xCB, 0x8D, 0xCC, 0x8D, 0xCD, 0x8D, 0xCE, 0x8D, 0xCF, 0x8D, 0xD0, 0x8D, 0xD1, 0x8D,\n\t0xD2, 0x8D, 0xD3, 0x8D, 0xD4, 0x8D, 0xD5, 0x8D, 0xD6, 0x8D, 0xD7, 0x8D, 0xD8, 0x8D, 0xD9, 0x8D, 0xDA, 0x8D, 0xDB, 0x8D, 0xDC, 0x8D, 0xDD, 0x8D, 0xDE, 0x8D, 0xDF, 0x8D, 0xE0, 0x8D, 0xE1, 0x8D,\n\t0xE2, 0x8D, 0xE3, 0x8D, 0xE4, 0x8D, 0xE5, 0x8D, 0xE6, 0x8D, 0xE7, 0x8D, 0xE8, 0x8D, 0xE9, 0x8D, 0xEA, 0x8D, 0xEB, 0x8D, 0xEC, 0x8D, 0xED, 0x8D, 0xEE, 0x8D, 0xEF, 0x8D, 0xF0, 0x8D, 0xF1, 0x8D,\n\t0xF2, 0x8D, 0xF3, 0x8D, 0xF4, 0x8D, 0xF5, 0x8D, 0xF6, 0x8D, 0xF7, 0x8D, 0xF8, 0x8D, 0xF9, 0x8D, 0xFA, 0x8D, 0xFB, 0x8D, 0xFC, 0x8D, 0xFD, 0x8D, 0xFE, 0x8D, 0xFF, 0x8D, 0x00, 0x8E, 0x01, 0x8E,\n\t0x02, 0x8E, 0x03, 0x8E, 0x04, 0x8E, 0x05, 0x8E, 0x06, 0x8E, 0x07, 0x8E, 0x08, 0x8E, 0x09, 0x8E, 0x0A, 0x8E, 0x0B, 0x8E, 0x0C, 0x8E, 0x0D, 0x8E, 0x0E, 0x8E, 0x0F, 0x8E, 0x10, 0x8E, 0x11, 0x8E,\n\t0x12, 0x8E, 0x13, 0x8E, 0x14, 0x8E, 0x15, 0x8E, 0x16, 0x8E, 0x17, 0x8E, 0x18, 0x8E, 0x19, 0x8E, 0x1A, 0x8E, 0x1B, 0x8E, 0x1C, 0x8E, 0x1D, 0x8E, 0x1E, 0x8E, 0x1F, 0x8E, 0x20, 0x8E, 0x21, 0x8E,\n\t0x22, 0x8E, 0x23, 0x8E, 0x24, 0x8E, 0x25, 0x8E, 0x26, 0x8E, 0x27, 0x8E, 0x28, 0x8E, 0x29, 0x8E, 0x2A, 0x8E, 0x2B, 0x8E, 0x2C, 0x8E, 0x2D, 0x8E, 0x2E, 0x8E, 0x2F, 0x8E, 0x30, 0x8E, 0x31, 0x8E,\n\t0x32, 0x8E, 0x33, 0x8E, 0x34, 0x8E, 0x35, 0x8E, 0x36, 0x8E, 0x37, 0x8E, 0x38, 0x8E, 0x39, 0x8E, 0x3A, 0x8E, 0x3B, 0x8E, 0x3C, 0x8E, 0x3D, 0x8E, 0x3E, 0x8E, 0x3F, 0x8E, 0x40, 0x8E, 0x41, 0x8E,\n\t0x42, 0x8E, 0x43, 0x8E, 0x44, 0x8E, 0x45, 0x8E, 0x46, 0x8E, 0x47, 0x8E, 0x48, 0x8E, 0x49, 0x8E, 0x4A, 0x8E, 0x4B, 0x8E, 0x4C, 0x8E, 0x4D, 0x8E, 0x4E, 0x8E, 0x4F, 0x8E, 0x50, 0x8E, 0x51, 0x8E,\n\t0x52, 0x8E, 0x53, 0x8E, 0x54, 0x8E, 0x55, 0x8E, 0x56, 0x8E, 0x57, 0x8E, 0x58, 0x8E, 0x59, 0x8E, 0x5A, 0x8E, 0x5B, 0x8E, 0x5C, 0x8E, 0x5D, 0x8E, 0x5E, 0x8E, 0x5F, 0x8E, 0x60, 0x8E, 0x61, 0x8E,\n\t0x62, 0x8E, 0x63, 0x8E, 0x64, 0x8E, 0x65, 0x8E, 0x66, 0x8E, 0x67, 0x8E, 0x68, 0x8E, 0x69, 0x8E, 0x6A, 0x8E, 0x6B, 0x8E, 0x6C, 0x8E, 0x6D, 0x8E, 0x6E, 0x8E, 0x6F, 0x8E, 0x70, 0x8E, 0x71, 0x8E,\n\t0x72, 0x8E, 0x73, 0x8E, 0x74, 0x8E, 0x75, 0x8E, 0x76, 0x8E, 0x77, 0x8E, 0x78, 0x8E, 0x79, 0x8E, 0x7A, 0x8E, 0x7B, 0x8E, 0x7C, 0x8E, 0x7D, 0x8E, 0x7E, 0x8E, 0x7F, 0x8E, 0x80, 0x8E, 0x81, 0x8E,\n\t0x82, 0x8E, 0x83, 0x8E, 0x84, 0x8E, 0x85, 0x8E, 0x86, 0x8E, 0x87, 0x8E, 0x88, 0x8E, 0x89, 0x8E, 0x8A, 0x8E, 0x8B, 0x8E, 0x8C, 0x8E, 0x8D, 0x8E, 0x8E, 0x8E, 0x8F, 0x8E, 0x90, 0x8E, 0x91, 0x8E,\n\t0x92, 0x8E, 0x93, 0x8E, 0x94, 0x8E, 0x95, 0x8E, 0x96, 0x8E, 0x97, 0x8E, 0x98, 0x8E, 0x99, 0x8E, 0x9A, 0x8E, 0x9B, 0x8E, 0x9C, 0x8E, 0x9D, 0x8E, 0x9E, 0x8E, 0x9F, 0x8E, 0xA0, 0x8E, 0xA1, 0x8E,\n\t0xA2, 0x8E, 0xA3, 0x8E, 0xA4, 0x8E, 0xA5, 0x8E, 0xA6, 0x8E, 0xA7, 0x8E, 0xA8, 0x8E, 0xA9, 0x8E, 0xAA, 0x8E, 0xAB, 0x8E, 0xAC, 0x8E, 0xAD, 0x8E, 0xAE, 0x8E, 0xAF, 0x8E, 0xB0, 0x8E, 0xB1, 0x8E,\n\t0xB2, 0x8E, 0xB3, 0x8E, 0xB4, 0x8E, 0xB5, 0x8E, 0xB6, 0x8E, 0xB7, 0x8E, 0xB8, 0x8E, 0xB9, 0x8E, 0xBA, 0x8E, 0xBB, 0x8E, 0xBC, 0x8E, 0xBD, 0x8E, 0xBE, 0x8E, 0xBF, 0x8E, 0xC0, 0x8E, 0xC1, 0x8E,\n\t0xC2, 0x8E, 0xC3, 0x8E, 0xC4, 0x8E, 0xC5, 0x8E, 0xC6, 0x8E, 0xC7, 0x8E, 0xC8, 0x8E, 0xC9, 0x8E, 0xCA, 0x8E, 0xCB, 0x8E, 0xCC, 0x8E, 0xCD, 0x8E, 0xCE, 0x8E, 0xCF, 0x8E, 0xD0, 0x8E, 0xD1, 0x8E,\n\t0xD2, 0x8E, 0xD3, 0x8E, 0xD4, 0x8E, 0xD5, 0x8E, 0xD6, 0x8E, 0xD7, 0x8E, 0xD8, 0x8E, 0xD9, 0x8E, 0xDA, 0x8E, 0xDB, 0x8E, 0xDC, 0x8E, 0xDD, 0x8E, 0xDE, 0x8E, 0xDF, 0x8E, 0xE0, 0x8E, 0xE1, 0x8E,\n\t0xE2, 0x8E, 0xE3, 0x8E, 0xE4, 0x8E, 0xE5, 0x8E, 0xE6, 0x8E, 0xE7, 0x8E, 0xE8, 0x8E, 0xE9, 0x8E, 0xEA, 0x8E, 0xEB, 0x8E, 0xEC, 0x8E, 0xED, 0x8E, 0xEE, 0x8E, 0xEF, 0x8E, 0xF0, 0x8E, 0xF1, 0x8E,\n\t0xF2, 0x8E, 0xF3, 0x8E, 0xF4, 0x8E, 0xF5, 0x8E, 0xF6, 0x8E, 0xF7, 0x8E, 0xF8, 0x8E, 0xF9, 0x8E, 0xFA, 0x8E, 0xFB, 0x8E, 0xFC, 0x8E, 0xFD, 0x8E, 0xFE, 0x8E, 0xFF, 0x8E, 0x00, 0x8F, 0x01, 0x8F,\n\t0x02, 0x8F, 0x03, 0x8F, 0x04, 0x8F, 0x05, 0x8F, 0x06, 0x8F, 0x07, 0x8F, 0x08, 0x8F, 0x09, 0x8F, 0x0A, 0x8F, 0x0B, 0x8F, 0x0C, 0x8F, 0x0D, 0x8F, 0x0E, 0x8F, 0x0F, 0x8F, 0x10, 0x8F, 0x11, 0x8F,\n\t0x12, 0x8F, 0x13, 0x8F, 0x14, 0x8F, 0x15, 0x8F, 0x16, 0x8F, 0x17, 0x8F, 0x18, 0x8F, 0x19, 0x8F, 0x1A, 0x8F, 0x1B, 0x8F, 0x1C, 0x8F, 0x1D, 0x8F, 0x1E, 0x8F, 0x1F, 0x8F, 0x20, 0x8F, 0x21, 0x8F,\n\t0x22, 0x8F, 0x23, 0x8F, 0x24, 0x8F, 0x25, 0x8F, 0x26, 0x8F, 0x27, 0x8F, 0x28, 0x8F, 0x29, 0x8F, 0x2A, 0x8F, 0x2B, 0x8F, 0x2C, 0x8F, 0x2D, 0x8F, 0x2E, 0x8F, 0x2F, 0x8F, 0x30, 0x8F, 0x31, 0x8F,\n\t0x32, 0x8F, 0x33, 0x8F, 0x34, 0x8F, 0x35, 0x8F, 0x36, 0x8F, 0x37, 0x8F, 0x38, 0x8F, 0x39, 0x8F, 0x3A, 0x8F, 0x3B, 0x8F, 0x3C, 0x8F, 0x3D, 0x8F, 0x3E, 0x8F, 0x3F, 0x8F, 0x40, 0x8F, 0x41, 0x8F,\n\t0x42, 0x8F, 0x43, 0x8F, 0x44, 0x8F, 0x45, 0x8F, 0x46, 0x8F, 0x47, 0x8F, 0x48, 0x8F, 0x49, 0x8F, 0x4A, 0x8F, 0x4B, 0x8F, 0x4C, 0x8F, 0x4D, 0x8F, 0x4E, 0x8F, 0x4F, 0x8F, 0x50, 0x8F, 0x51, 0x8F,\n\t0x52, 0x8F, 0x53, 0x8F, 0x54, 0x8F, 0x55, 0x8F, 0x56, 0x8F, 0x57, 0x8F, 0x58, 0x8F, 0x59, 0x8F, 0x5A, 0x8F, 0x5B, 0x8F, 0x5C, 0x8F, 0x5D, 0x8F, 0x5E, 0x8F, 0x5F, 0x8F, 0x60, 0x8F, 0x61, 0x8F,\n\t0x62, 0x8F, 0x63, 0x8F, 0x64, 0x8F, 0x65, 0x8F, 0x66, 0x8F, 0x67, 0x8F, 0x68, 0x8F, 0x69, 0x8F, 0x6A, 0x8F, 0x6B, 0x8F, 0x6C, 0x8F, 0x6D, 0x8F, 0x6E, 0x8F, 0x6F, 0x8F, 0x70, 0x8F, 0x71, 0x8F,\n\t0x72, 0x8F, 0x73, 0x8F, 0x74, 0x8F, 0x75, 0x8F, 0x76, 0x8F, 0x77, 0x8F, 0x78, 0x8F, 0x79, 0x8F, 0x7A, 0x8F, 0x7B, 0x8F, 0x7C, 0x8F, 0x7D, 0x8F, 0x7E, 0x8F, 0x7F, 0x8F, 0x80, 0x8F, 0x81, 0x8F,\n\t0x82, 0x8F, 0x83, 0x8F, 0x84, 0x8F, 0x85, 0x8F, 0x86, 0x8F, 0x87, 0x8F, 0x88, 0x8F, 0x89, 0x8F, 0x8A, 0x8F, 0x8B, 0x8F, 0x8C, 0x8F, 0x8D, 0x8F, 0x8E, 0x8F, 0x8F, 0x8F, 0x90, 0x8F, 0x91, 0x8F,\n\t0x92, 0x8F, 0x93, 0x8F, 0x94, 0x8F, 0x95, 0x8F, 0x96, 0x8F, 0x97, 0x8F, 0x98, 0x8F, 0x99, 0x8F, 0x9A, 0x8F, 0x9B, 0x8F, 0x9C, 0x8F, 0x9D, 0x8F, 0x9E, 0x8F, 0x9F, 0x8F, 0xA0, 0x8F, 0xA1, 0x8F,\n\t0xA2, 0x8F, 0xA3, 0x8F, 0xA4, 0x8F, 0xA5, 0x8F, 0xA6, 0x8F, 0xA7, 0x8F, 0xA8, 0x8F, 0xA9, 0x8F, 0xAA, 0x8F, 0xAB, 0x8F, 0xAC, 0x8F, 0xAD, 0x8F, 0xAE, 0x8F, 0xAF, 0x8F, 0xB0, 0x8F, 0xB1, 0x8F,\n\t0xB2, 0x8F, 0xB3, 0x8F, 0xB4, 0x8F, 0xB5, 0x8F, 0xB6, 0x8F, 0xB7, 0x8F, 0xB8, 0x8F, 0xB9, 0x8F, 0xBA, 0x8F, 0xBB, 0x8F, 0xBC, 0x8F, 0xBD, 0x8F, 0xBE, 0x8F, 0xBF, 0x8F, 0xC0, 0x8F, 0xC0, 0x8F,\n\t0xC1, 0x8F, 0xC2, 0x8F, 0xC3, 0x8F, 0xC4, 0x8F, 0xC5, 0x8F, 0xC6, 0x8F, 0xC7, 0x8F, 0xC8, 0x8F, 0xC9, 0x8F, 0xCA, 0x8F, 0xCB, 0x8F, 0xCC, 0x8F, 0xCD, 0x8F, 0xCE, 0x8F, 0xCF, 0x8F, 0xD0, 0x8F,\n\t0xD1, 0x8F, 0xD2, 0x8F, 0xD3, 0x8F, 0xD4, 0x8F, 0xD5, 0x8F, 0xD6, 0x8F, 0xD7, 0x8F, 0xD8, 0x8F, 0xD9, 0x8F, 0xDA, 0x8F, 0xDB, 0x8F, 0xDC, 0x8F, 0xDD, 0x8F, 0xDE, 0x8F, 0xDF, 0x8F, 0xE0, 0x8F,\n\t0xE1, 0x8F, 0xE2, 0x8F, 0xE3, 0x8F, 0xE4, 0x8F, 0xE5, 0x8F, 0xE6, 0x8F, 0xE7, 0x8F, 0xE8, 0x8F, 0xE9, 0x8F, 0xEA, 0x8F, 0xEB, 0x8F, 0xEC, 0x8F, 0xED, 0x8F, 0xEE, 0x8F, 0xEF, 0x8F, 0xF0, 0x8F,\n\t0xF1, 0x8F, 0xF2, 0x8F, 0xF3, 0x8F, 0xF4, 0x8F, 0xF5, 0x8F, 0xF6, 0x8F, 0xF7, 0x8F, 0xF8, 0x8F, 0xF9, 0x8F, 0xFA, 0x8F, 0xFB, 0x8F, 0xFC, 0x8F, 0xFD, 0x8F, 0xFE, 0x8F, 0x00, 0x90, 0x01, 0x90,\n\t0x02, 0x90, 0x03, 0x90, 0x04, 0x90, 0x05, 0x90, 0x06, 0x90, 0x07, 0x90, 0x08, 0x90, 0x09, 0x90, 0x0A, 0x90, 0x0B, 0x90, 0x0C, 0x90, 0x0D, 0x90, 0x0E, 0x90, 0x0F, 0x90, 0x10, 0x90, 0x11, 0x90,\n\t0x12, 0x90, 0x13, 0x90, 0x14, 0x90, 0x15, 0x90, 0x16, 0x90, 0x17, 0x90, 0x18, 0x90, 0x19, 0x90, 0x1A, 0x90, 0x1B, 0x90, 0x1C, 0x90, 0x1D, 0x90, 0x1E, 0x90, 0x1F, 0x90, 0x20, 0x90, 0x21, 0x90,\n\t0x22, 0x90, 0x23, 0x90, 0x24, 0x90, 0x25, 0x90, 0x26, 0x90, 0x27, 0x90, 0x28, 0x90, 0x29, 0x90, 0x2A, 0x90, 0x2B, 0x90, 0x2C, 0x90, 0x2D, 0x90, 0x2E, 0x90, 0x2F, 0x90, 0x30, 0x90, 0x31, 0x90,\n\t0x32, 0x90, 0x33, 0x90, 0x34, 0x90, 0x35, 0x90, 0x36, 0x90, 0x37, 0x90, 0x38, 0x90, 0x39, 0x90, 0x3A, 0x90, 0x3B, 0x90, 0x3C, 0x90, 0x3D, 0x90, 0x3E, 0x90, 0x3F, 0x90, 0x40, 0x90, 0x41, 0x90,\n\t0x42, 0x90, 0x43, 0x90, 0x44, 0x90, 0x45, 0x90, 0x46, 0x90, 0x47, 0x90, 0x48, 0x90, 0x49, 0x90, 0x4A, 0x90, 0x4B, 0x90, 0x4C, 0x90, 0x4D, 0x90, 0x4E, 0x90, 0x4F, 0x90, 0x50, 0x90, 0x51, 0x90,\n\t0x52, 0x90, 0x53, 0x90, 0x54, 0x90, 0x55, 0x90, 0x56, 0x90, 0x57, 0x90, 0x58, 0x90, 0x59, 0x90, 0x5A, 0x90, 0x5B, 0x90, 0x5C, 0x90, 0x5D, 0x90, 0x5E, 0x90, 0x5F, 0x90, 0x60, 0x90, 0x61, 0x90,\n\t0x62, 0x90, 0x63, 0x90, 0x64, 0x90, 0x65, 0x90, 0x66, 0x90, 0x67, 0x90, 0x68, 0x90, 0x69, 0x90, 0x6A, 0x90, 0x6B, 0x90, 0x6C, 0x90, 0x6D, 0x90, 0x6E, 0x90, 0x6F, 0x90, 0x70, 0x90, 0x71, 0x90,\n\t0x72, 0x90, 0x73, 0x90, 0x74, 0x90, 0x75, 0x90, 0x76, 0x90, 0x77, 0x90, 0x78, 0x90, 0x79, 0x90, 0x7A, 0x90, 0x7B, 0x90, 0x7C, 0x90, 0x7D, 0x90, 0x7E, 0x90, 0x7F, 0x90, 0x80, 0x90, 0x81, 0x90,\n\t0x82, 0x90, 0x83, 0x90, 0x84, 0x90, 0x85, 0x90, 0x86, 0x90, 0x87, 0x90, 0x88, 0x90, 0x89, 0x90, 0x8A, 0x90, 0x8B, 0x90, 0x8C, 0x90, 0x8D, 0x90, 0x8E, 0x90, 0x8F, 0x90, 0x90, 0x90, 0x91, 0x90,\n\t0x92, 0x90, 0x93, 0x90, 0x94, 0x90, 0x95, 0x90, 0x96, 0x90, 0x97, 0x90, 0x98, 0x90, 0x99, 0x90, 0x9A, 0x90, 0x9B, 0x90, 0x9C, 0x90, 0x9D, 0x90, 0x9E, 0x90, 0x9F, 0x90, 0xA0, 0x90, 0xA1, 0x90,\n\t0xA2, 0x90, 0xA3, 0x90, 0xA4, 0x90, 0xA5, 0x90, 0xA6, 0x90, 0xA7, 0x90, 0xA8, 0x90, 0xA9, 0x90, 0xAA, 0x90, 0xAB, 0x90, 0xAC, 0x90, 0xAD, 0x90, 0xAE, 0x90, 0xAF, 0x90, 0xB0, 0x90, 0xB1, 0x90,\n\t0xB2, 0x90, 0xB3, 0x90, 0xB4, 0x90, 0xB5, 0x90, 0xB6, 0x90, 0xB7, 0x90, 0xB8, 0x90, 0xB9, 0x90, 0xBA, 0x90, 0xBB, 0x90, 0xBC, 0x90, 0xBD, 0x90, 0xBE, 0x90, 0xBF, 0x90, 0xC0, 0x90, 0xC1, 0x90,\n\t0xC2, 0x90, 0xC3, 0x90, 0xC4, 0x90, 0xC5, 0x90, 0xC6, 0x90, 0xC7, 0x90, 0xC8, 0x90, 0xC9, 0x90, 0xCA, 0x90, 0xCB, 0x90, 0xCC, 0x90, 0xCD, 0x90, 0xCE, 0x90, 0xCF, 0x90, 0xD0, 0x90, 0xD1, 0x90,\n\t0xD2, 0x90, 0xD3, 0x90, 0xD4, 0x90, 0xD5, 0x90, 0xD6, 0x90, 0xD7, 0x90, 0xD8, 0x90, 0xD9, 0x90, 0xDA, 0x90, 0xDB, 0x90, 0xDC, 0x90, 0xDD, 0x90, 0xDE, 0x90, 0xDF, 0x90, 0xE0, 0x90, 0xE1, 0x90,\n\t0xE2, 0x90, 0xE3, 0x90, 0xE4, 0x90, 0xE5, 0x90, 0xE6, 0x90, 0xE7, 0x90, 0xE8, 0x90, 0xE9, 0x90, 0xEA, 0x90, 0xEB, 0x90, 0xEC, 0x90, 0xED, 0x90, 0xEE, 0x90, 0xEF, 0x90, 0xF0, 0x90, 0xF1, 0x90,\n\t0xF2, 0x90, 0xF3, 0x90, 0xF4, 0x90, 0xF5, 0x90, 0xF6, 0x90, 0xF7, 0x90, 0xF8, 0x90, 0xF9, 0x90, 0xFA, 0x90, 0xFB, 0x90, 0xFC, 0x90, 0xFD, 0x90, 0xFE, 0x90, 0xFF, 0x90, 0x00, 0x91, 0x01, 0x91,\n\t0x02, 0x91, 0x03, 0x91, 0x04, 0x91, 0x05, 0x91, 0x06, 0x91, 0x07, 0x91, 0x08, 0x91, 0x09, 0x91, 0x0A, 0x91, 0x0B, 0x91, 0x0C, 0x91, 0x0D, 0x91, 0x0E, 0x91, 0x0F, 0x91, 0x10, 0x91, 0x11, 0x91,\n\t0x12, 0x91, 0x13, 0x91, 0x14, 0x91, 0x15, 0x91, 0x16, 0x91, 0x17, 0x91, 0x18, 0x91, 0x19, 0x91, 0x1A, 0x91, 0x1B, 0x91, 0x1C, 0x91, 0x1D, 0x91, 0x1E, 0x91, 0x1F, 0x91, 0x20, 0x91, 0x21, 0x91,\n\t0x22, 0x91, 0x23, 0x91, 0x24, 0x91, 0x25, 0x91, 0x26, 0x91, 0x27, 0x91, 0x28, 0x91, 0x29, 0x91, 0x2A, 0x91, 0x2B, 0x91, 0x2C, 0x91, 0x2D, 0x91, 0x2E, 0x91, 0x2F, 0x91, 0x30, 0x91, 0x31, 0x91,\n\t0x32, 0x91, 0x33, 0x91, 0x34, 0x91, 0x35, 0x91, 0x36, 0x91, 0x37, 0x91, 0x38, 0x91, 0x39, 0x91, 0x3A, 0x91, 0x3B, 0x91, 0x3C, 0x91, 0x3D, 0x91, 0x3E, 0x91, 0x3F, 0x91, 0x40, 0x91, 0x41, 0x91,\n\t0x42, 0x91, 0x43, 0x91, 0x44, 0x91, 0x45, 0x91, 0x46, 0x91, 0x47, 0x91, 0x48, 0x91, 0x49, 0x91, 0x4A, 0x91, 0x4B, 0x91, 0x4C, 0x91, 0x4D, 0x91, 0x4E, 0x91, 0x4F, 0x91, 0x50, 0x91, 0x51, 0x91,\n\t0x52, 0x91, 0x53, 0x91, 0x54, 0x91, 0x55, 0x91, 0x56, 0x91, 0x57, 0x91, 0x58, 0x91, 0x59, 0x91, 0x5A, 0x91, 0x5B, 0x91, 0x5C, 0x91, 0x5D, 0x91, 0x5E, 0x91, 0x5F, 0x91, 0x60, 0x91, 0x61, 0x91,\n\t0x62, 0x91, 0x63, 0x91, 0x64, 0x91, 0x65, 0x91, 0x66, 0x91, 0x67, 0x91, 0x68, 0x91, 0x69, 0x91, 0x6A, 0x91, 0x6B, 0x91, 0x6C, 0x91, 0x6D, 0x91, 0x6E, 0x91, 0x6F, 0x91, 0x70, 0x91, 0x71, 0x91,\n\t0x72, 0x91, 0x73, 0x91, 0x74, 0x91, 0x75, 0x91, 0x76, 0x91, 0x77, 0x91, 0x78, 0x91, 0x79, 0x91, 0x7A, 0x91, 0x7A, 0x91, 0x7B, 0x91, 0x7C, 0x91, 0x7D, 0x91, 0x7E, 0x91, 0x7F, 0x91, 0x80, 0x91,\n\t0x81, 0x91, 0x82, 0x91, 0x83, 0x91, 0x84, 0x91, 0x85, 0x91, 0x86, 0x91, 0x87, 0x91, 0x88, 0x91, 0x89, 0x91, 0x8A, 0x91, 0x8B, 0x91, 0x8C, 0x91, 0x8D, 0x91, 0x8E, 0x91, 0x8F, 0x91, 0x90, 0x91,\n\t0x91, 0x91, 0x92, 0x91, 0x93, 0x91, 0x94, 0x91, 0x95, 0x91, 0x96, 0x91, 0x97, 0x91, 0x98, 0x91, 0x99, 0x91, 0x9A, 0x91, 0x9B, 0x91, 0x9C, 0x91, 0x9D, 0x91, 0x9E, 0x91, 0x9F, 0x91, 0xA0, 0x91,\n\t0xA1, 0x91, 0xA2, 0x91, 0xA3, 0x91, 0xA4, 0x91, 0xA5, 0x91, 0xA6, 0x91, 0xA7, 0x91, 0xA8, 0x91, 0xA9, 0x91, 0xAA, 0x91, 0xAB, 0x91, 0xAC, 0x91, 0xAD, 0x91, 0xAE, 0x91, 0xAF, 0x91, 0xB0, 0x91,\n\t0xB1, 0x91, 0xB2, 0x91, 0xB3, 0x91, 0xB4, 0x91, 0xB5, 0x91, 0xB6, 0x91, 0xB7, 0x91, 0xB8, 0x91, 0xB9, 0x91, 0xBA, 0x91, 0xBB, 0x91, 0xBC, 0x91, 0xBD, 0x91, 0xBE, 0x91, 0xBF, 0x91, 0xC0, 0x91,\n\t0xC1, 0x91, 0xC2, 0x91, 0xC3, 0x91, 0xC4, 0x91, 0xC5, 0x91, 0xC6, 0x91, 0xC7, 0x91, 0xC8, 0x91, 0xC9, 0x91, 0xCA, 0x91, 0xCB, 0x91, 0xCC, 0x91, 0xCD, 0x91, 0xCE, 0x91, 0xCF, 0x91, 0xD0, 0x91,\n\t0xD1, 0x91, 0xD2, 0x91, 0xD3, 0x91, 0xD4, 0x91, 0xD5, 0x91, 0xD6, 0x91, 0xD7, 0x91, 0xD8, 0x91, 0xD9, 0x91, 0xDA, 0x91, 0xDB, 0x91, 0xDC, 0x91, 0xDD, 0x91, 0xDE, 0x91, 0xDF, 0x91, 0xE0, 0x91,\n\t0xE1, 0x91, 0xE2, 0x91, 0xE3, 0x91, 0xE4, 0x91, 0xE5, 0x91, 0xE6, 0x91, 0xE7, 0x91, 0xE8, 0x91, 0xE9, 0x91, 0xEA, 0x91, 0xEB, 0x91, 0xEC, 0x91, 0xED, 0x91, 0xEE, 0x91, 0xEF, 0x91, 0xF0, 0x91,\n\t0xF1, 0x91, 0xF2, 0x91, 0xF3, 0x91, 0xF4, 0x91, 0xF5, 0x91, 0xF6, 0x91, 0xF7, 0x91, 0xF8, 0x91, 0xF9, 0x91, 0xFA, 0x91, 0xFB, 0x91, 0xFC, 0x91, 0xFD, 0x91, 0xFE, 0x91, 0xFF, 0x91, 0x00, 0x92,\n\t0x01, 0x92, 0x02, 0x92, 0x03, 0x92, 0x04, 0x92, 0x05, 0x92, 0x06, 0x92, 0x07, 0x92, 0x08, 0x92, 0x09, 0x92, 0x0A, 0x92, 0x0B, 0x92, 0x0C, 0x92, 0x0D, 0x92, 0x0E, 0x92, 0x0F, 0x92, 0x10, 0x92,\n\t0x11, 0x92, 0x12, 0x92, 0x13, 0x92, 0x14, 0x92, 0x15, 0x92, 0x16, 0x92, 0x17, 0x92, 0x18, 0x92, 0x19, 0x92, 0x1A, 0x92, 0x1B, 0x92, 0x1C, 0x92, 0x1D, 0x92, 0x1E, 0x92, 0x1F, 0x92, 0x20, 0x92,\n\t0x21, 0x92, 0x22, 0x92, 0x23, 0x92, 0x24, 0x92, 0x25, 0x92, 0x26, 0x92, 0x27, 0x92, 0x28, 0x92, 0x29, 0x92, 0x2A, 0x92, 0x2B, 0x92, 0x2C, 0x92, 0x2D, 0x92, 0x2E, 0x92, 0x2F, 0x92, 0x30, 0x92,\n\t0x31, 0x92, 0x32, 0x92, 0x33, 0x92, 0x34, 0x92, 0x35, 0x92, 0x36, 0x92, 0x37, 0x92, 0x38, 0x92, 0x39, 0x92, 0x3A, 0x92, 0x3B, 0x92, 0x3C, 0x92, 0x3D, 0x92, 0x3E, 0x92, 0x3F, 0x92, 0x40, 0x92,\n\t0x41, 0x92, 0x42, 0x92, 0x43, 0x92, 0x44, 0x92, 0x45, 0x92, 0x46, 0x92, 0x47, 0x92, 0x48, 0x92, 0x49, 0x92, 0x4A, 0x92, 0x4B, 0x92, 0x4C, 0x92, 0x4D, 0x92, 0x4E, 0x92, 0x4F, 0x92, 0x50, 0x92,\n\t0x51, 0x92, 0x52, 0x92, 0x53, 0x92, 0x54, 0x92, 0x55, 0x92, 0x56, 0x92, 0x57, 0x92, 0x58, 0x92, 0x59, 0x92, 0x5A, 0x92, 0x5B, 0x92, 0x5C, 0x92, 0x5D, 0x92, 0x5E, 0x92, 0x5F, 0x92, 0x60, 0x92,\n\t0x61, 0x92, 0x62, 0x92, 0x63, 0x92, 0x64, 0x92, 0x65, 0x92, 0x66, 0x92, 0x67, 0x92, 0x68, 0x92, 0x69, 0x92, 0x6A, 0x92, 0x6B, 0x92, 0x6C, 0x92, 0x6D, 0x92, 0x6E, 0x92, 0x6F, 0x92, 0x70, 0x92,\n\t0x71, 0x92, 0x72, 0x92, 0x73, 0x92, 0x74, 0x92, 0x75, 0x92, 0x76, 0x92, 0x77, 0x92, 0x78, 0x92, 0x79, 0x92, 0x7A, 0x92, 0x7B, 0x92, 0x7C, 0x92, 0x7D, 0x92, 0x7E, 0x92, 0x7F, 0x92, 0x80, 0x92,\n\t0x81, 0x92, 0x82, 0x92, 0x83, 0x92, 0x84, 0x92, 0x85, 0x92, 0x86, 0x92, 0x87, 0x92, 0x88, 0x92, 0x89, 0x92, 0x8A, 0x92, 0x8B, 0x92, 0x8C, 0x92, 0x8D, 0x92, 0x8E, 0x92, 0x8F, 0x92, 0x90, 0x92,\n\t0x91, 0x92, 0x92, 0x92, 0x93, 0x92, 0x94, 0x92, 0x95, 0x92, 0x96, 0x92, 0x97, 0x92, 0x98, 0x92, 0x99, 0x92, 0x9A, 0x92, 0x9B, 0x92, 0x9C, 0x92, 0x9D, 0x92, 0x9E, 0x92, 0x9F, 0x92, 0xA0, 0x92,\n\t0xA1, 0x92, 0xA2, 0x92, 0xA3, 0x92, 0xA4, 0x92, 0xA5, 0x92, 0xA6, 0x92, 0xA7, 0x92, 0xA8, 0x92, 0xA9, 0x92, 0xAA, 0x92, 0xAB, 0x92, 0xAC, 0x92, 0xAD, 0x92, 0xAE, 0x92, 0xAF, 0x92, 0xB0, 0x92,\n\t0xB1, 0x92, 0xB2, 0x92, 0xB3, 0x92, 0xB4, 0x92, 0xB5, 0x92, 0xB6, 0x92, 0xB7, 0x92, 0xB8, 0x92, 0xB9, 0x92, 0xBA, 0x92, 0xBB, 0x92, 0xBC, 0x92, 0xBD, 0x92, 0xBE, 0x92, 0xBF, 0x92, 0xC0, 0x92,\n\t0xC1, 0x92, 0xC2, 0x92, 0xC3, 0x92, 0xC4, 0x92, 0xC5, 0x92, 0xC6, 0x92, 0xC7, 0x92, 0xC8, 0x92, 0xC9, 0x92, 0xCA, 0x92, 0xCB, 0x92, 0xCC, 0x92, 0xCD, 0x92, 0xCE, 0x92, 0xCF, 0x92, 0xD0, 0x92,\n\t0xD1, 0x92, 0xD2, 0x92, 0xD3, 0x92, 0xD4, 0x92, 0xD5, 0x92, 0xD6, 0x92, 0xD7, 0x92, 0xD8, 0x92, 0xD9, 0x92, 0xDA, 0x92, 0xDB, 0x92, 0xDC, 0x92, 0xDD, 0x92, 0xDE, 0x92, 0xDF, 0x92, 0xE0, 0x92,\n\t0xE1, 0x92, 0xE2, 0x92, 0xE3, 0x92, 0xE4, 0x92, 0xE5, 0x92, 0xE6, 0x92, 0xE7, 0x92, 0xE8, 0x92, 0xE9, 0x92, 0xEA, 0x92, 0xEB, 0x92, 0xEC, 0x92, 0xED, 0x92, 0xEE, 0x92, 0xEF, 0x92, 0xF0, 0x92,\n\t0xF1, 0x92, 0xF2, 0x92, 0xF3, 0x92, 0xF4, 0x92, 0xF5, 0x92, 0xF6, 0x92, 0xF7, 0x92, 0xF8, 0x92, 0xF9, 0x92, 0xFA, 0x92, 0xFB, 0x92, 0xFC, 0x92, 0xFD, 0x92, 0xFE, 0x92, 0xFF, 0x92, 0x00, 0x93,\n\t0x01, 0x93, 0x02, 0x93, 0x03, 0x93, 0x04, 0x93, 0x05, 0x93, 0x06, 0x93, 0x07, 0x93, 0x08, 0x93, 0x09, 0x93, 0x0A, 0x93, 0x0B, 0x93, 0x0C, 0x93, 0x0D, 0x93, 0x0E, 0x93, 0x0F, 0x93, 0x10, 0x93,\n\t0x11, 0x93, 0x12, 0x93, 0x12, 0x93, 0x13, 0x93, 0x14, 0x93, 0x15, 0x93, 0x16, 0x93, 0x17, 0x93, 0x18, 0x93, 0x19, 0x93, 0x1A, 0x93, 0x1B, 0x93, 0x1C, 0x93, 0x1D, 0x93, 0x1E, 0x93, 0x1F, 0x93,\n\t0x20, 0x93, 0x21, 0x93, 0x22, 0x93, 0x23, 0x93, 0x24, 0x93, 0x25, 0x93, 0x26, 0x93, 0x27, 0x93, 0x28, 0x93, 0x29, 0x93, 0x2A, 0x93, 0x2B, 0x93, 0x2C, 0x93, 0x2D, 0x93, 0x2E, 0x93, 0x2F, 0x93,\n\t0x30, 0x93, 0x31, 0x93, 0x32, 0x93, 0x33, 0x93, 0x34, 0x93, 0x35, 0x93, 0x36, 0x93, 0x37, 0x93, 0x38, 0x93, 0x39, 0x93, 0x3A, 0x93, 0x3B, 0x93, 0x3C, 0x93, 0x3D, 0x93, 0x3E, 0x93, 0x3F, 0x93,\n\t0x40, 0x93, 0x41, 0x93, 0x42, 0x93, 0x43, 0x93, 0x44, 0x93, 0x45, 0x93, 0x46, 0x93, 0x47, 0x93, 0x48, 0x93, 0x49, 0x93, 0x4A, 0x93, 0x4B, 0x93, 0x4C, 0x93, 0x4D, 0x93, 0x4E, 0x93, 0x4F, 0x93,\n\t0x50, 0x93, 0x51, 0x93, 0x52, 0x93, 0x53, 0x93, 0x54, 0x93, 0x55, 0x93, 0x56, 0x93, 0x57, 0x93, 0x58, 0x93, 0x59, 0x93, 0x5A, 0x93, 0x5B, 0x93, 0x5C, 0x93, 0x5D, 0x93, 0x5E, 0x93, 0x5F, 0x93,\n\t0x60, 0x93, 0x61, 0x93, 0x62, 0x93, 0x63, 0x93, 0x64, 0x93, 0x65, 0x93, 0x66, 0x93, 0x67, 0x93, 0x68, 0x93, 0x69, 0x93, 0x6A, 0x93, 0x6B, 0x93, 0x6C, 0x93, 0x6D, 0x93, 0x6E, 0x93, 0x6F, 0x93,\n\t0x70, 0x93, 0x71, 0x93, 0x72, 0x93, 0x73, 0x93, 0x74, 0x93, 0x75, 0x93, 0x76, 0x93, 0x77, 0x93, 0x78, 0x93, 0x79, 0x93, 0x7A, 0x93, 0x7B, 0x93, 0x7C, 0x93, 0x7D, 0x93, 0x7E, 0x93, 0x7F, 0x93,\n\t0x80, 0x93, 0x81, 0x93, 0x82, 0x93, 0x83, 0x93, 0x84, 0x93, 0x85, 0x93, 0x86, 0x93, 0x87, 0x93, 0x88, 0x93, 0x89, 0x93, 0x8A, 0x93, 0x8B, 0x93, 0x8C, 0x93, 0x8D, 0x93, 0x8E, 0x93, 0x8F, 0x93,\n\t0x90, 0x93, 0x91, 0x93, 0x92, 0x93, 0x93, 0x93, 0x94, 0x93, 0x95, 0x93, 0x96, 0x93, 0x97, 0x93, 0x98, 0x93, 0x99, 0x93, 0x9A, 0x93, 0x9B, 0x93, 0x9C, 0x93, 0x9D, 0x93, 0x9E, 0x93, 0x9F, 0x93,\n\t0xA0, 0x93, 0xA1, 0x93, 0xA2, 0x93, 0xA3, 0x93, 0xA4, 0x93, 0xA5, 0x93, 0xA6, 0x93, 0xA7, 0x93, 0xA8, 0x93, 0xA9, 0x93, 0xAA, 0x93, 0xAB, 0x93, 0xAC, 0x93, 0xAD, 0x93, 0xAE, 0x93, 0xAF, 0x93,\n\t0xB0, 0x93, 0xB1, 0x93, 0xB2, 0x93, 0xB3, 0x93, 0xB4, 0x93, 0xB5, 0x93, 0xB6, 0x93, 0xB7, 0x93, 0xB8, 0x93, 0xB9, 0x93, 0xBA, 0x93, 0xBB, 0x93, 0xBC, 0x93, 0xBD, 0x93, 0xBE, 0x93, 0xBF, 0x93,\n\t0xC0, 0x93, 0xC1, 0x93, 0xC2, 0x93, 0xC3, 0x93, 0xC4, 0x93, 0xC5, 0x93, 0xC6, 0x93, 0xC7, 0x93, 0xC8, 0x93, 0xC9, 0x93, 0xCA, 0x93, 0xCB, 0x93, 0xCC, 0x93, 0xCD, 0x93, 0xCE, 0x93, 0xCF, 0x93,\n\t0xD0, 0x93, 0xD1, 0x93, 0xD2, 0x93, 0xD3, 0x93, 0xD4, 0x93, 0xD5, 0x93, 0xD6, 0x93, 0xD7, 0x93, 0xD8, 0x93, 0xD9, 0x93, 0xDA, 0x93, 0xDB, 0x93, 0xDC, 0x93, 0xDD, 0x93, 0xDE, 0x93, 0xDF, 0x93,\n\t0xE0, 0x93, 0xE1, 0x93, 0xE2, 0x93, 0xE3, 0x93, 0xE4, 0x93, 0xE5, 0x93, 0xE6, 0x93, 0xE7, 0x93, 0xE8, 0x93, 0xE9, 0x93, 0xEA, 0x93, 0xEB, 0x93, 0xEC, 0x93, 0xED, 0x93, 0xEE, 0x93, 0xEF, 0x93,\n\t0xF0, 0x93, 0xF1, 0x93, 0xF2, 0x93, 0xF3, 0x93, 0xF4, 0x93, 0xF5, 0x93, 0xF6, 0x93, 0xF7, 0x93, 0xF8, 0x93, 0xF9, 0x93, 0xFA, 0x93, 0xFB, 0x93, 0xFC, 0x93, 0xFD, 0x93, 0xFF, 0x93, 0x00, 0x94,\n\t0x01, 0x94, 0x02, 0x94, 0x03, 0x94, 0x04, 0x94, 0x05, 0x94, 0x06, 0x94, 0x07, 0x94, 0x08, 0x94, 0x09, 0x94, 0x0A, 0x94, 0x0B, 0x94, 0x0C, 0x94, 0x0D, 0x94, 0x0E, 0x94, 0x0F, 0x94, 0x10, 0x94,\n\t0x11, 0x94, 0x12, 0x94, 0x13, 0x94, 0x14, 0x94, 0x15, 0x94, 0x16, 0x94, 0x17, 0x94, 0x18, 0x94, 0x19, 0x94, 0x1A, 0x94, 0x1B, 0x94, 0x1C, 0x94, 0x1D, 0x94, 0x1E, 0x94, 0x1F, 0x94, 0x20, 0x94,\n\t0x21, 0x94, 0x22, 0x94, 0x23, 0x94, 0x24, 0x94, 0x25, 0x94, 0x26, 0x94, 0x27, 0x94, 0x28, 0x94, 0x29, 0x94, 0x2A, 0x94, 0x2B, 0x94, 0x2C, 0x94, 0x2D, 0x94, 0x2E, 0x94, 0x2F, 0x94, 0x30, 0x94,\n\t0x31, 0x94, 0x32, 0x94, 0x33, 0x94, 0x34, 0x94, 0x35, 0x94, 0x36, 0x94, 0x37, 0x94, 0x38, 0x94, 0x39, 0x94, 0x3A, 0x94, 0x3B, 0x94, 0x3C, 0x94, 0x3D, 0x94, 0x3E, 0x94, 0x3F, 0x94, 0x40, 0x94,\n\t0x41, 0x94, 0x42, 0x94, 0x43, 0x94, 0x44, 0x94, 0x45, 0x94, 0x46, 0x94, 0x47, 0x94, 0x48, 0x94, 0x49, 0x94, 0x4A, 0x94, 0x4B, 0x94, 0x4C, 0x94, 0x4D, 0x94, 0x4E, 0x94, 0x4F, 0x94, 0x50, 0x94,\n\t0x51, 0x94, 0x52, 0x94, 0x53, 0x94, 0x54, 0x94, 0x55, 0x94, 0x56, 0x94, 0x57, 0x94, 0x58, 0x94, 0x59, 0x94, 0x5A, 0x94, 0x5B, 0x94, 0x5C, 0x94, 0x5D, 0x94, 0x5E, 0x94, 0x5F, 0x94, 0x60, 0x94,\n\t0x61, 0x94, 0x62, 0x94, 0x63, 0x94, 0x64, 0x94, 0x65, 0x94, 0x66, 0x94, 0x67, 0x94, 0x68, 0x94, 0x69, 0x94, 0x6A, 0x94, 0x6B, 0x94, 0x6C, 0x94, 0x6D, 0x94, 0x6E, 0x94, 0x6F, 0x94, 0x70, 0x94,\n\t0x71, 0x94, 0x72, 0x94, 0x73, 0x94, 0x74, 0x94, 0x75, 0x94, 0x76, 0x94, 0x77, 0x94, 0x78, 0x94, 0x79, 0x94, 0x7A, 0x94, 0x7B, 0x94, 0x7C, 0x94, 0x7D, 0x94, 0x7E, 0x94, 0x7F, 0x94, 0x80, 0x94,\n\t0x81, 0x94, 0x82, 0x94, 0x83, 0x94, 0x84, 0x94, 0x85, 0x94, 0x86, 0x94, 0x87, 0x94, 0x88, 0x94, 0x89, 0x94, 0x8A, 0x94, 0x8B, 0x94, 0x8C, 0x94, 0x8D, 0x94, 0x8E, 0x94, 0x8F, 0x94, 0x90, 0x94,\n\t0x91, 0x94, 0x92, 0x94, 0x93, 0x94, 0x94, 0x94, 0x95, 0x94, 0x96, 0x94, 0x97, 0x94, 0x98, 0x94, 0x99, 0x94, 0x9A, 0x94, 0x9B, 0x94, 0x9C, 0x94, 0x9D, 0x94, 0x9E, 0x94, 0x9F, 0x94, 0xA0, 0x94,\n\t0xA1, 0x94, 0xA2, 0x94, 0xA3, 0x94, 0xA4, 0x94, 0xA5, 0x94, 0xA6, 0x94, 0xA7, 0x94, 0xA8, 0x94, 0xA9, 0x94, 0xAA, 0x94, 0xAB, 0x94, 0xAC, 0x94, 0xAD, 0x94, 0xAE, 0x94, 0xAF, 0x94, 0xB0, 0x94,\n\t0xB1, 0x94, 0xB2, 0x94, 0xB3, 0x94, 0xB4, 0x94, 0xB5, 0x94, 0xB6, 0x94, 0xB7, 0x94, 0xB8, 0x94, 0xB9, 0x94, 0xBA, 0x94, 0xBB, 0x94, 0xBC, 0x94, 0xBD, 0x94, 0xBE, 0x94, 0xBF, 0x94, 0xC0, 0x94,\n\t0xC1, 0x94, 0xC2, 0x94, 0xC3, 0x94, 0xC4, 0x94, 0xC5, 0x94, 0xC6, 0x94, 0xC7, 0x94, 0xC8, 0x94, 0xC9, 0x94, 0xCA, 0x94, 0xCB, 0x94, 0xCC, 0x94, 0xCD, 0x94, 0xCE, 0x94, 0xCF, 0x94, 0xD0, 0x94,\n\t0xD1, 0x94, 0xD2, 0x94, 0xD3, 0x94, 0xD4, 0x94, 0xD5, 0x94, 0xD6, 0x94, 0xD7, 0x94, 0xD8, 0x94, 0xD9, 0x94, 0xDA, 0x94, 0xDB, 0x94, 0xDC, 0x94, 0xDD, 0x94, 0xDE, 0x94, 0xDF, 0x94, 0xE0, 0x94,\n\t0xE1, 0x94, 0xE2, 0x94, 0xE3, 0x94, 0xE4, 0x94, 0xE5, 0x94, 0xE6, 0x94, 0xE7, 0x94, 0xE8, 0x94, 0xE9, 0x94, 0xEA, 0x94, 0xEB, 0x94, 0xEC, 0x94, 0xED, 0x94, 0xEE, 0x94, 0xEF, 0x94, 0xF0, 0x94,\n\t0xF1, 0x94, 0xF2, 0x94, 0xF3, 0x94, 0xF4, 0x94, 0xF5, 0x94, 0xF6, 0x94, 0xF7, 0x94, 0xF8, 0x94, 0xF9, 0x94, 0xFA, 0x94, 0xFB, 0x94, 0xFC, 0x94, 0xFD, 0x94, 0xFE, 0x94, 0xFF, 0x94, 0xFF, 0x94,\n\t0x00, 0x95, 0x01, 0x95, 0x02, 0x95, 0x03, 0x95, 0x04, 0x95, 0x05, 0x95, 0x06, 0x95, 0x07, 0x95, 0x08, 0x95, 0x09, 0x95, 0x0A, 0x95, 0x0B, 0x95, 0x0C, 0x95, 0x0D, 0x95, 0x0E, 0x95, 0x0F, 0x95,\n\t0x10, 0x95, 0x11, 0x95, 0x12, 0x95, 0x13, 0x95, 0x14, 0x95, 0x15, 0x95, 0x16, 0x95, 0x17, 0x95, 0x18, 0x95, 0x19, 0x95, 0x1A, 0x95, 0x1B, 0x95, 0x1C, 0x95, 0x1D, 0x95, 0x1E, 0x95, 0x1F, 0x95,\n\t0x20, 0x95, 0x21, 0x95, 0x22, 0x95, 0x23, 0x95, 0x24, 0x95, 0x25, 0x95, 0x26, 0x95, 0x27, 0x95, 0x28, 0x95, 0x29, 0x95, 0x2A, 0x95, 0x2B, 0x95, 0x2C, 0x95, 0x2D, 0x95, 0x2E, 0x95, 0x2F, 0x95,\n\t0x30, 0x95, 0x31, 0x95, 0x32, 0x95, 0x33, 0x95, 0x34, 0x95, 0x35, 0x95, 0x36, 0x95, 0x37, 0x95, 0x38, 0x95, 0x39, 0x95, 0x3A, 0x95, 0x3B, 0x95, 0x3C, 0x95, 0x3D, 0x95, 0x3E, 0x95, 0x3F, 0x95,\n\t0x40, 0x95, 0x41, 0x95, 0x42, 0x95, 0x43, 0x95, 0x44, 0x95, 0x45, 0x95, 0x46, 0x95, 0x47, 0x95, 0x48, 0x95, 0x49, 0x95, 0x4A, 0x95, 0x4B, 0x95, 0x4C, 0x95, 0x4D, 0x95, 0x4E, 0x95, 0x4F, 0x95,\n\t0x50, 0x95, 0x51, 0x95, 0x52, 0x95, 0x53, 0x95, 0x54, 0x95, 0x55, 0x95, 0x56, 0x95, 0x57, 0x95, 0x58, 0x95, 0x59, 0x95, 0x5A, 0x95, 0x5B, 0x95, 0x5C, 0x95, 0x5D, 0x95, 0x5E, 0x95, 0x5F, 0x95,\n\t0x60, 0x95, 0x61, 0x95, 0x62, 0x95, 0x63, 0x95, 0x64, 0x95, 0x65, 0x95, 0x66, 0x95, 0x67, 0x95, 0x68, 0x95, 0x69, 0x95, 0x6A, 0x95, 0x6B, 0x95, 0x6C, 0x95, 0x6D, 0x95, 0x6E, 0x95, 0x6F, 0x95,\n\t0x70, 0x95, 0x71, 0x95, 0x72, 0x95, 0x73, 0x95, 0x74, 0x95, 0x75, 0x95, 0x76, 0x95, 0x77, 0x95, 0x78, 0x95, 0x79, 0x95, 0x7A, 0x95, 0x7B, 0x95, 0x7C, 0x95, 0x7D, 0x95, 0x7E, 0x95, 0x7F, 0x95,\n\t0x80, 0x95, 0x81, 0x95, 0x82, 0x95, 0x83, 0x95, 0x84, 0x95, 0x85, 0x95, 0x86, 0x95, 0x87, 0x95, 0x88, 0x95, 0x89, 0x95, 0x8A, 0x95, 0x8B, 0x95, 0x8C, 0x95, 0x8D, 0x95, 0x8E, 0x95, 0x8F, 0x95,\n\t0x90, 0x95, 0x91, 0x95, 0x92, 0x95, 0x93, 0x95, 0x94, 0x95, 0x95, 0x95, 0x96, 0x95, 0x97, 0x95, 0x98, 0x95, 0x99, 0x95, 0x9A, 0x95, 0x9B, 0x95, 0x9C, 0x95, 0x9D, 0x95, 0x9E, 0x95, 0x9F, 0x95,\n\t0xA0, 0x95, 0xA1, 0x95, 0xA2, 0x95, 0xA3, 0x95, 0xA4, 0x95, 0xA5, 0x95, 0xA6, 0x95, 0xA7, 0x95, 0xA8, 0x95, 0xA9, 0x95, 0xAA, 0x95, 0xAB, 0x95, 0xAC, 0x95, 0xAD, 0x95, 0xAE, 0x95, 0xAF, 0x95,\n\t0xB0, 0x95, 0xB1, 0x95, 0xB2, 0x95, 0xB3, 0x95, 0xB4, 0x95, 0xB5, 0x95, 0xB6, 0x95, 0xB7, 0x95, 0xB8, 0x95, 0xB9, 0x95, 0xBA, 0x95, 0xBB, 0x95, 0xBC, 0x95, 0xBD, 0x95, 0xBE, 0x95, 0xBF, 0x95,\n\t0xC0, 0x95, 0xC1, 0x95, 0xC2, 0x95, 0xC3, 0x95, 0xC4, 0x95, 0xC5, 0x95, 0xC6, 0x95, 0xC7, 0x95, 0xC8, 0x95, 0xC9, 0x95, 0xCA, 0x95, 0xCB, 0x95, 0xCC, 0x95, 0xCD, 0x95, 0xCE, 0x95, 0xCF, 0x95,\n\t0xD0, 0x95, 0xD1, 0x95, 0xD2, 0x95, 0xD3, 0x95, 0xD4, 0x95, 0xD5, 0x95, 0xD6, 0x95, 0xD7, 0x95, 0xD8, 0x95, 0xD9, 0x95, 0xDA, 0x95, 0xDB, 0x95, 0xDC, 0x95, 0xDD, 0x95, 0xDE, 0x95, 0xDF, 0x95,\n\t0xE0, 0x95, 0xE1, 0x95, 0xE2, 0x95, 0xE3, 0x95, 0xE4, 0x95, 0xE5, 0x95, 0xE6, 0x95, 0xE7, 0x95, 0xE8, 0x95, 0xE9, 0x95, 0xE9, 0x95, 0xEA, 0x95, 0xEB, 0x95, 0xEC, 0x95, 0xED, 0x95, 0xEE, 0x95,\n\t0xEF, 0x95, 0xF0, 0x95, 0xF1, 0x95, 0xF2, 0x95, 0xF3, 0x95, 0xF4, 0x95, 0xF5, 0x95, 0xF6, 0x95, 0xF7, 0x95, 0xF8, 0x95, 0xF9, 0x95, 0xFA, 0x95, 0xFB, 0x95, 0xFC, 0x95, 0xFD, 0x95, 0xFE, 0x95,\n\t0xFF, 0x95, 0x00, 0x96, 0x01, 0x96, 0x02, 0x96, 0x03, 0x96, 0x04, 0x96, 0x05, 0x96, 0x06, 0x96, 0x07, 0x96, 0x08, 0x96, 0x09, 0x96, 0x0A, 0x96, 0x0B, 0x96, 0x0C, 0x96, 0x0D, 0x96, 0x0E, 0x96,\n\t0x0F, 0x96, 0x10, 0x96, 0x11, 0x96, 0x12, 0x96, 0x13, 0x96, 0x14, 0x96, 0x15, 0x96, 0x16, 0x96, 0x17, 0x96, 0x18, 0x96, 0x19, 0x96, 0x1A, 0x96, 0x1B, 0x96, 0x1C, 0x96, 0x1D, 0x96, 0x1E, 0x96,\n\t0x1F, 0x96, 0x20, 0x96, 0x21, 0x96, 0x22, 0x96, 0x23, 0x96, 0x24, 0x96, 0x25, 0x96, 0x26, 0x96, 0x27, 0x96, 0x28, 0x96, 0x29, 0x96, 0x2A, 0x96, 0x2B, 0x96, 0x2C, 0x96, 0x2D, 0x96, 0x2E, 0x96,\n\t0x2F, 0x96, 0x30, 0x96, 0x31, 0x96, 0x32, 0x96, 0x33, 0x96, 0x34, 0x96, 0x35, 0x96, 0x36, 0x96, 0x37, 0x96, 0x38, 0x96, 0x39, 0x96, 0x3A, 0x96, 0x3B, 0x96, 0x3C, 0x96, 0x3D, 0x96, 0x3E, 0x96,\n\t0x3F, 0x96, 0x40, 0x96, 0x41, 0x96, 0x42, 0x96, 0x43, 0x96, 0x44, 0x96, 0x45, 0x96, 0x46, 0x96, 0x47, 0x96, 0x48, 0x96, 0x49, 0x96, 0x4A, 0x96, 0x4B, 0x96, 0x4C, 0x96, 0x4D, 0x96, 0x4E, 0x96,\n\t0x4F, 0x96, 0x50, 0x96, 0x51, 0x96, 0x52, 0x96, 0x53, 0x96, 0x54, 0x96, 0x55, 0x96, 0x56, 0x96, 0x57, 0x96, 0x58, 0x96, 0x59, 0x96, 0x5A, 0x96, 0x5B, 0x96, 0x5C, 0x96, 0x5D, 0x96, 0x5E, 0x96,\n\t0x5F, 0x96, 0x60, 0x96, 0x61, 0x96, 0x62, 0x96, 0x63, 0x96, 0x64, 0x96, 0x65, 0x96, 0x66, 0x96, 0x67, 0x96, 0x68, 0x96, 0x69, 0x96, 0x6A, 0x96, 0x6B, 0x96, 0x6C, 0x96, 0x6D, 0x96, 0x6E, 0x96,\n\t0x6F, 0x96, 0x70, 0x96, 0x71, 0x96, 0x72, 0x96, 0x73, 0x96, 0x74, 0x96, 0x75, 0x96, 0x76, 0x96, 0x77, 0x96, 0x78, 0x96, 0x79, 0x96, 0x7A, 0x96, 0x7B, 0x96, 0x7C, 0x96, 0x7D, 0x96, 0x7E, 0x96,\n\t0x7F, 0x96, 0x80, 0x96, 0x81, 0x96, 0x82, 0x96, 0x83, 0x96, 0x84, 0x96, 0x85, 0x96, 0x86, 0x96, 0x87, 0x96, 0x88, 0x96, 0x89, 0x96, 0x8A, 0x96, 0x8B, 0x96, 0x8C, 0x96, 0x8D, 0x96, 0x8E, 0x96,\n\t0x8F, 0x96, 0x90, 0x96, 0x91, 0x96, 0x92, 0x96, 0x93, 0x96, 0x94, 0x96, 0x95, 0x96, 0x96, 0x96, 0x97, 0x96, 0x98, 0x96, 0x99, 0x96, 0x9A, 0x96, 0x9B, 0x96, 0x9C, 0x96, 0x9D, 0x96, 0x9E, 0x96,\n\t0x9F, 0x96, 0xA0, 0x96, 0xA1, 0x96, 0xA2, 0x96, 0xA3, 0x96, 0xA4, 0x96, 0xA5, 0x96, 0xA6, 0x96, 0xA7, 0x96, 0xA8, 0x96, 0xA9, 0x96, 0xAA, 0x96, 0xAB, 0x96, 0xAC, 0x96, 0xAD, 0x96, 0xAE, 0x96,\n\t0xAF, 0x96, 0xB0, 0x96, 0xB1, 0x96, 0xB2, 0x96, 0xB3, 0x96, 0xB3, 0x96, 0xB4, 0x96, 0xB5, 0x96, 0xB6, 0x96, 0xB7, 0x96, 0xB8, 0x96, 0xB9, 0x96, 0xBA, 0x96, 0xBB, 0x96, 0xBC, 0x96, 0xBD, 0x96,\n\t0xBE, 0x96, 0xBF, 0x96, 0xC0, 0x96, 0xC1, 0x96, 0xC2, 0x96, 0xC3, 0x96, 0xC4, 0x96, 0xC5, 0x96, 0xC6, 0x96, 0xC7, 0x96, 0xC8, 0x96, 0xC9, 0x96, 0xCA, 0x96, 0xCB, 0x96, 0xCC, 0x96, 0xCD, 0x96,\n\t0xCE, 0x96, 0xCF, 0x96, 0xD0, 0x96, 0xD1, 0x96, 0xD2, 0x96, 0xD3, 0x96, 0xD4, 0x96, 0xD5, 0x96, 0xD6, 0x96, 0xD7, 0x96, 0xD8, 0x96, 0xD9, 0x96, 0xDA, 0x96, 0xDB, 0x96, 0xDC, 0x96, 0xDD, 0x96,\n\t0xDE, 0x96, 0xDF, 0x96, 0xE0, 0x96, 0xE1, 0x96, 0xE2, 0x96, 0xE3, 0x96, 0xE4, 0x96, 0xE5, 0x96, 0xE6, 0x96, 0xE7, 0x96, 0xE8, 0x96, 0xE9, 0x96, 0xEA, 0x96, 0xEB, 0x96, 0xEC, 0x96, 0xED, 0x96,\n\t0xEE, 0x96, 0xEF, 0x96, 0xF0, 0x96, 0xF1, 0x96, 0xF2, 0x96, 0xF3, 0x96, 0xF4, 0x96, 0xF5, 0x96, 0xF6, 0x96, 0xF7, 0x96, 0xF8, 0x96, 0xF9, 0x96, 0xFA, 0x96, 0xFB, 0x96, 0xFC, 0x96, 0xFD, 0x96,\n\t0xFE, 0x96, 0xFF, 0x96, 0x00, 0x97, 0x01, 0x97, 0x02, 0x97, 0x03, 0x97, 0x04, 0x97, 0x05, 0x97, 0x06, 0x97, 0x07, 0x97, 0x08, 0x97, 0x09, 0x97, 0x0A, 0x97, 0x0B, 0x97, 0x0C, 0x97, 0x0D, 0x97,\n\t0x0E, 0x97, 0x0F, 0x97, 0x10, 0x97, 0x11, 0x97, 0x12, 0x97, 0x13, 0x97, 0x14, 0x97, 0x15, 0x97, 0x16, 0x97, 0x17, 0x97, 0x18, 0x97, 0x19, 0x97, 0x1A, 0x97, 0x1B, 0x97, 0x1C, 0x97, 0x1D, 0x97,\n\t0x1E, 0x97, 0x1F, 0x97, 0x20, 0x97, 0x21, 0x97, 0x22, 0x97, 0x23, 0x97, 0x24, 0x97, 0x25, 0x97, 0x26, 0x97, 0x27, 0x97, 0x28, 0x97, 0x29, 0x97, 0x2A, 0x97, 0x2B, 0x97, 0x2C, 0x97, 0x2D, 0x97,\n\t0x2E, 0x97, 0x2F, 0x97, 0x30, 0x97, 0x31, 0x97, 0x32, 0x97, 0x33, 0x97, 0x34, 0x97, 0x35, 0x97, 0x36, 0x97, 0x37, 0x97, 0x38, 0x97, 0x39, 0x97, 0x3A, 0x97, 0x3B, 0x97, 0x3C, 0x97, 0x3D, 0x97,\n\t0x3E, 0x97, 0x3F, 0x97, 0x40, 0x97, 0x41, 0x97, 0x42, 0x97, 0x43, 0x97, 0x44, 0x97, 0x45, 0x97, 0x46, 0x97, 0x47, 0x97, 0x48, 0x97, 0x49, 0x97, 0x4A, 0x97, 0x4B, 0x97, 0x4C, 0x97, 0x4D, 0x97,\n\t0x4E, 0x97, 0x4F, 0x97, 0x50, 0x97, 0x51, 0x97, 0x52, 0x97, 0x53, 0x97, 0x54, 0x97, 0x55, 0x97, 0x56, 0x97, 0x57, 0x97, 0x58, 0x97, 0x59, 0x97, 0x5A, 0x97, 0x5B, 0x97, 0x5C, 0x97, 0x5D, 0x97,\n\t0x5E, 0x97, 0x5F, 0x97, 0x60, 0x97, 0x61, 0x97, 0x62, 0x97, 0x63, 0x97, 0x64, 0x97, 0x65, 0x97, 0x66, 0x97, 0x67, 0x97, 0x68, 0x97, 0x68, 0x97, 0x69, 0x97, 0x6A, 0x97, 0x6B, 0x97, 0x6C, 0x97,\n\t0x6D, 0x97, 0x6E, 0x97, 0x6F, 0x97, 0x70, 0x97, 0x71, 0x97, 0x72, 0x97, 0x73, 0x97, 0x74, 0x97, 0x75, 0x97, 0x76, 0x97, 0x77, 0x97, 0x78, 0x97, 0x79, 0x97, 0x7A, 0x97, 0x7B, 0x97, 0x7C, 0x97,\n\t0x7D, 0x97, 0x7E, 0x97, 0x7F, 0x97, 0x80, 0x97, 0x81, 0x97, 0x82, 0x97, 0x83, 0x97, 0x84, 0x97, 0x85, 0x97, 0x86, 0x97, 0x87, 0x97, 0x88, 0x97, 0x89, 0x97, 0x8A, 0x97, 0x8B, 0x97, 0x8C, 0x97,\n\t0x8D, 0x97, 0x8E, 0x97, 0x8F, 0x97, 0x90, 0x97, 0x91, 0x97, 0x92, 0x97, 0x93, 0x97, 0x94, 0x97, 0x95, 0x97, 0x96, 0x97, 0x97, 0x97, 0x98, 0x97, 0x99, 0x97, 0x9A, 0x97, 0x9B, 0x97, 0x9C, 0x97,\n\t0x9D, 0x97, 0x9E, 0x97, 0x9F, 0x97, 0xA0, 0x97, 0xA1, 0x97, 0xA2, 0x97, 0xA3, 0x97, 0xA4, 0x97, 0xA5, 0x97, 0xA6, 0x97, 0xA7, 0x97, 0xA8, 0x97, 0xA9, 0x97, 0xAA, 0x97, 0xAB, 0x97, 0xAC, 0x97,\n\t0xAD, 0x97, 0xAE, 0x97, 0xAF, 0x97, 0xB0, 0x97, 0xB1, 0x97, 0xB2, 0x97, 0xB3, 0x97, 0xB4, 0x97, 0xB5, 0x97, 0xB6, 0x97, 0xB7, 0x97, 0xB8, 0x97, 0xB9, 0x97, 0xBA, 0x97, 0xBB, 0x97, 0xBC, 0x97,\n\t0xBD, 0x97, 0xBE, 0x97, 0xBF, 0x97, 0xC0, 0x97, 0xC1, 0x97, 0xC2, 0x97, 0xC3, 0x97, 0xC4, 0x97, 0xC5, 0x97, 0xC6, 0x97, 0xC7, 0x97, 0xC8, 0x97, 0xC9, 0x97, 0xCA, 0x97, 0xCB, 0x97, 0xCC, 0x97,\n\t0xCD, 0x97, 0xCE, 0x97, 0xCF, 0x97, 0xD0, 0x97, 0xD1, 0x97, 0xD2, 0x97, 0xD3, 0x97, 0xD4, 0x97, 0xD5, 0x97, 0xD6, 0x97, 0xD7, 0x97, 0xD8, 0x97, 0xD9, 0x97, 0xDA, 0x97, 0xDB, 0x97, 0xDC, 0x97,\n\t0xDD, 0x97, 0xDE, 0x97, 0xDF, 0x97, 0xE0, 0x97, 0xE1, 0x97, 0xE2, 0x97, 0xE3, 0x97, 0xE4, 0x97, 0xE5, 0x97, 0xE6, 0x97, 0xE7, 0x97, 0xE8, 0x97, 0xE9, 0x97, 0xEA, 0x97, 0xEB, 0x97, 0xEC, 0x97,\n\t0xED, 0x97, 0xEE, 0x97, 0xEF, 0x97, 0xF0, 0x97, 0xF1, 0x97, 0xF2, 0x97, 0xF3, 0x97, 0xF4, 0x97, 0xF5, 0x97, 0xF6, 0x97, 0xF7, 0x97, 0xF8, 0x97, 0xF9, 0x97, 0xFA, 0x97, 0xFC, 0x97, 0xFE, 0x97,\n\t0x00, 0x98, 0x01, 0x98, 0x02, 0x98, 0x03, 0x98, 0x04, 0x98, 0x05, 0x98, 0x06, 0x98, 0x07, 0x98, 0x08, 0x98, 0x09, 0x98, 0x0A, 0x98, 0x0B, 0x98, 0x0C, 0x98, 0x0D, 0x98, 0x0E, 0x98, 0x0F, 0x98,\n\t0x10, 0x98, 0x11, 0x98, 0x12, 0x98, 0x13, 0x98, 0x14, 0x98, 0x15, 0x98, 0x16, 0x98, 0x17, 0x98, 0x18, 0x98, 0x19, 0x98, 0x1A, 0x98, 0x1B, 0x98, 0x1C, 0x98, 0x1D, 0x98, 0x1E, 0x98, 0x1F, 0x98,\n\t0x20, 0x98, 0x21, 0x98, 0x22, 0x98, 0x23, 0x98, 0x24, 0x98, 0x25, 0x98, 0x26, 0x98, 0x27, 0x98, 0x28, 0x98, 0x29, 0x98, 0x2A, 0x98, 0x2B, 0x98, 0x2C, 0x98, 0x2D, 0x98, 0x2D, 0x98, 0x2E, 0x98,\n\t0x2F, 0x98, 0x30, 0x98, 0x31, 0x98, 0x32, 0x98, 0x33, 0x98, 0x34, 0x98, 0x35, 0x98, 0x36, 0x98, 0x37, 0x98, 0x38, 0x98, 0x39, 0x98, 0x3A, 0x98, 0x3B, 0x98, 0x3C, 0x98, 0x3D, 0x98, 0x3E, 0x98,\n\t0x3F, 0x98, 0x40, 0x98, 0x41, 0x98, 0x42, 0x98, 0x43, 0x98, 0x44, 0x98, 0x45, 0x98, 0x46, 0x98, 0x47, 0x98, 0x48, 0x98, 0x49, 0x98, 0x4A, 0x98, 0x4B, 0x98, 0x4C, 0x98, 0x4D, 0x98, 0x4E, 0x98,\n\t0x4F, 0x98, 0x50, 0x98, 0x51, 0x98, 0x52, 0x98, 0x53, 0x98, 0x54, 0x98, 0x55, 0x98, 0x56, 0x98, 0x57, 0x98, 0x58, 0x98, 0x59, 0x98, 0x5A, 0x98, 0x5B, 0x98, 0x5C, 0x98, 0x5D, 0x98, 0x5E, 0x98,\n\t0x5F, 0x98, 0x60, 0x98, 0x61, 0x98, 0x62, 0x98, 0x63, 0x98, 0x64, 0x98, 0x65, 0x98, 0x66, 0x98, 0x67, 0x98, 0x68, 0x98, 0x69, 0x98, 0x6A, 0x98, 0x6B, 0x98, 0x6C, 0x98, 0x6D, 0x98, 0x6E, 0x98,\n\t0x6F, 0x98, 0x70, 0x98, 0x71, 0x98, 0x72, 0x98, 0x73, 0x98, 0x74, 0x98, 0x75, 0x98, 0x76, 0x98, 0x77, 0x98, 0x78, 0x98, 0x79, 0x98, 0x7A, 0x98, 0x7B, 0x98, 0x7C, 0x98, 0x7D, 0x98, 0x7E, 0x98,\n\t0x7F, 0x98, 0x80, 0x98, 0x81, 0x98, 0x82, 0x98, 0x83, 0x98, 0x84, 0x98, 0x85, 0x98, 0x86, 0x98, 0x87, 0x98, 0x88, 0x98, 0x89, 0x98, 0x8A, 0x98, 0x8B, 0x98, 0x8C, 0x98, 0x8D, 0x98, 0x8E, 0x98,\n\t0x8F, 0x98, 0x90, 0x98, 0x91, 0x98, 0x92, 0x98, 0x93, 0x98, 0x94, 0x98, 0x95, 0x98, 0x96, 0x98, 0x97, 0x98, 0x98, 0x98, 0x99, 0x98, 0x9A, 0x98, 0x9B, 0x98, 0x9C, 0x98, 0x9D, 0x98, 0x9E, 0x98,\n\t0x9F, 0x98, 0xA0, 0x98, 0xA1, 0x98, 0xA2, 0x98, 0xA3, 0x98, 0xA4, 0x98, 0xA5, 0x98, 0xA6, 0x98, 0xA7, 0x98, 0xA8, 0x98, 0xA9, 0x98, 0xAA, 0x98, 0xAB, 0x98, 0xAC, 0x98, 0xAD, 0x98, 0xAE, 0x98,\n\t0xAF, 0x98, 0xB0, 0x98, 0xB1, 0x98, 0xB2, 0x98, 0xB3, 0x98, 0xB4, 0x98, 0xB5, 0x98, 0xB6, 0x98, 0xB7, 0x98, 0xB8, 0x98, 0xB9, 0x98, 0xBA, 0x98, 0xBB, 0x98, 0xBB, 0x98, 0xBC, 0x98, 0xBD, 0x98,\n\t0xBE, 0x98, 0xBF, 0x98, 0xC0, 0x98, 0xC1, 0x98, 0xC2, 0x98, 0xC3, 0x98, 0xC4, 0x98, 0xC5, 0x98, 0xC6, 0x98, 0xC7, 0x98, 0xC8, 0x98, 0xC9, 0x98, 0xCA, 0x98, 0xCB, 0x98, 0xCC, 0x98, 0xCD, 0x98,\n\t0xCE, 0x98, 0xCF, 0x98, 0xD0, 0x98, 0xD1, 0x98, 0xD2, 0x98, 0xD3, 0x98, 0xD4, 0x98, 0xD5, 0x98, 0xD6, 0x98, 0xD7, 0x98, 0xD8, 0x98, 0xD9, 0x98, 0xDA, 0x98, 0xDB, 0x98, 0xDC, 0x98, 0xDD, 0x98,\n\t0xDE, 0x98, 0xDF, 0x98, 0xE0, 0x98, 0xE1, 0x98, 0xE2, 0x98, 0xE3, 0x98, 0xE4, 0x98, 0xE5, 0x98, 0xE6, 0x98, 0xE7, 0x98, 0xE8, 0x98, 0xE9, 0x98, 0xEA, 0x98, 0xEB, 0x98, 0xEC, 0x98, 0xED, 0x98,\n\t0xEE, 0x98, 0xEF, 0x98, 0xF0, 0x98, 0xF1, 0x98, 0xF2, 0x98, 0xF3, 0x98, 0xF4, 0x98, 0xF5, 0x98, 0xF6, 0x98, 0xF7, 0x98, 0xF8, 0x98, 0xF9, 0x98, 0xFA, 0x98, 0xFB, 0x98, 0xFC, 0x98, 0xFD, 0x98,\n\t0xFE, 0x98, 0xFF, 0x98, 0x00, 0x99, 0x01, 0x99, 0x02, 0x99, 0x03, 0x99, 0x04, 0x99, 0x05, 0x99, 0x06, 0x99, 0x07, 0x99, 0x08, 0x99, 0x09, 0x99, 0x0A, 0x99, 0x0B, 0x99, 0x0C, 0x99, 0x0D, 0x99,\n\t0x0E, 0x99, 0x0F, 0x99, 0x10, 0x99, 0x11, 0x99, 0x12, 0x99, 0x13, 0x99, 0x14, 0x99, 0x15, 0x99, 0x16, 0x99, 0x17, 0x99, 0x18, 0x99, 0x19, 0x99, 0x1A, 0x99, 0x1B, 0x99, 0x1C, 0x99, 0x1D, 0x99,\n\t0x1E, 0x99, 0x1F, 0x99, 0x20, 0x99, 0x21, 0x99, 0x22, 0x99, 0x23, 0x99, 0x24, 0x99, 0x25, 0x99, 0x26, 0x99, 0x27, 0x99, 0x28, 0x99, 0x29, 0x99, 0x2A, 0x99, 0x2B, 0x99, 0x2C, 0x99, 0x2D, 0x99,\n\t0x2E, 0x99, 0x2F, 0x99, 0x30, 0x99, 0x31, 0x99, 0x32, 0x99, 0x33, 0x99, 0x34, 0x99, 0x35, 0x99, 0x36, 0x99, 0x37, 0x99, 0x38, 0x99, 0x39, 0x99, 0x3A, 0x99, 0x3B, 0x99, 0x3B, 0x99, 0x3C, 0x99,\n\t0x3D, 0x99, 0x3E, 0x99, 0x3F, 0x99, 0x40, 0x99, 0x41, 0x99, 0x42, 0x99, 0x43, 0x99, 0x44, 0x99, 0x45, 0x99, 0x46, 0x99, 0x47, 0x99, 0x48, 0x99, 0x49, 0x99, 0x4A, 0x99, 0x4B, 0x99, 0x4C, 0x99,\n\t0x4D, 0x99, 0x4E, 0x99, 0x4F, 0x99, 0x50, 0x99, 0x51, 0x99, 0x52, 0x99, 0x53, 0x99, 0x54, 0x99, 0x55, 0x99, 0x56, 0x99, 0x57, 0x99, 0x58, 0x99, 0x59, 0x99, 0x5A, 0x99, 0x5B, 0x99, 0x5C, 0x99,\n\t0x5D, 0x99, 0x5E, 0x99, 0x5F, 0x99, 0x60, 0x99, 0x61, 0x99, 0x62, 0x99, 0x63, 0x99, 0x64, 0x99, 0x65, 0x99, 0x66, 0x99, 0x67, 0x99, 0x68, 0x99, 0x69, 0x99, 0x6A, 0x99, 0x6B, 0x99, 0x6C, 0x99,\n\t0x6D, 0x99, 0x6E, 0x99, 0x6F, 0x99, 0x70, 0x99, 0x71, 0x99, 0x72, 0x99, 0x73, 0x99, 0x74, 0x99, 0x75, 0x99, 0x76, 0x99, 0x77, 0x99, 0x78, 0x99, 0x79, 0x99, 0x7A, 0x99, 0x7B, 0x99, 0x7C, 0x99,\n\t0x7D, 0x99, 0x7E, 0x99, 0x7F, 0x99, 0x80, 0x99, 0x81, 0x99, 0x82, 0x99, 0x83, 0x99, 0x84, 0x99, 0x85, 0x99, 0x86, 0x99, 0x87, 0x99, 0x88, 0x99, 0x89, 0x99, 0x8A, 0x99, 0x8B, 0x99, 0x8C, 0x99,\n\t0x8D, 0x99, 0x8E, 0x99, 0x8F, 0x99, 0x90, 0x99, 0x91, 0x99, 0x92, 0x99, 0x93, 0x99, 0x94, 0x99, 0x95, 0x99, 0x96, 0x99, 0x97, 0x99, 0x98, 0x99, 0x99, 0x99, 0x9A, 0x99, 0x9B, 0x99, 0x9C, 0x99,\n\t0x9D, 0x99, 0x9E, 0x99, 0x9F, 0x99, 0xA0, 0x99, 0xA1, 0x99, 0xA2, 0x99, 0xA3, 0x99, 0xA4, 0x99, 0xA5, 0x99, 0xA6, 0x99, 0xA7, 0x99, 0xA8, 0x99, 0xA9, 0x99, 0xAA, 0x99, 0xAB, 0x99, 0xAC, 0x99,\n\t0xAD, 0x99, 0xAE, 0x99, 0xAF, 0x99, 0xAF, 0x99, 0xB0, 0x99, 0xB1, 0x99, 0xB2, 0x99, 0xB3, 0x99, 0xB4, 0x99, 0xB5, 0x99, 0xB6, 0x99, 0xB7, 0x99, 0xB8, 0x99, 0xB9, 0x99, 0xBA, 0x99, 0xBB, 0x99,\n\t0xBC, 0x99, 0xBD, 0x99, 0xBE, 0x99, 0xBF, 0x99, 0xC0, 0x99, 0xC1, 0x99, 0xC2, 0x99, 0xC3, 0x99, 0xC4, 0x99, 0xC5, 0x99, 0xC6, 0x99, 0xC7, 0x99, 0xC8, 0x99, 0xC9, 0x99, 0xCA, 0x99, 0xCB, 0x99,\n\t0xCC, 0x99, 0xCD, 0x99, 0xCE, 0x99, 0xCF, 0x99, 0xD0, 0x99, 0xD1, 0x99, 0xD2, 0x99, 0xD3, 0x99, 0xD4, 0x99, 0xD5, 0x99, 0xD6, 0x99, 0xD7, 0x99, 0xD8, 0x99, 0xD9, 0x99, 0xDA, 0x99, 0xDB, 0x99,\n\t0xDC, 0x99, 0xDD, 0x99, 0xDE, 0x99, 0xDF, 0x99, 0xE0, 0x99, 0xE1, 0x99, 0xE2, 0x99, 0xE3, 0x99, 0xE4, 0x99, 0xE5, 0x99, 0xE6, 0x99, 0xE7, 0x99, 0xE8, 0x99, 0xE9, 0x99, 0xEA, 0x99, 0xEB, 0x99,\n\t0xEC, 0x99, 0xED, 0x99, 0xEE, 0x99, 0xEF, 0x99, 0xF0, 0x99, 0xF1, 0x99, 0xF2, 0x99, 0xF3, 0x99, 0xF4, 0x99, 0xF5, 0x99, 0xF6, 0x99, 0xF7, 0x99, 0xF8, 0x99, 0xF9, 0x99, 0xFA, 0x99, 0xFB, 0x99,\n\t0xFC, 0x99, 0xFD, 0x99, 0xFE, 0x99, 0xFF, 0x99, 0x00, 0x9A, 0x01, 0x9A, 0x02, 0x9A, 0x03, 0x9A, 0x04, 0x9A, 0x05, 0x9A, 0x06, 0x9A, 0x07, 0x9A, 0x08, 0x9A, 0x09, 0x9A, 0x0A, 0x9A, 0x0B, 0x9A,\n\t0x0C, 0x9A, 0x0D, 0x9A, 0x0E, 0x9A, 0x0F, 0x9A, 0x10, 0x9A, 0x11, 0x9A, 0x12, 0x9A, 0x13, 0x9A, 0x14, 0x9A, 0x15, 0x9A, 0x16, 0x9A, 0x17, 0x9A, 0x18, 0x9A, 0x19, 0x9A, 0x1A, 0x9A, 0x1A, 0x9A,\n\t0x1B, 0x9A, 0x1C, 0x9A, 0x1D, 0x9A, 0x1E, 0x9A, 0x1F, 0x9A, 0x20, 0x9A, 0x21, 0x9A, 0x22, 0x9A, 0x23, 0x9A, 0x24, 0x9A, 0x25, 0x9A, 0x26, 0x9A, 0x27, 0x9A, 0x28, 0x9A, 0x29, 0x9A, 0x2A, 0x9A,\n\t0x2B, 0x9A, 0x2C, 0x9A, 0x2D, 0x9A, 0x2E, 0x9A, 0x2F, 0x9A, 0x30, 0x9A, 0x31, 0x9A, 0x32, 0x9A, 0x33, 0x9A, 0x34, 0x9A, 0x35, 0x9A, 0x36, 0x9A, 0x37, 0x9A, 0x38, 0x9A, 0x39, 0x9A, 0x3A, 0x9A,\n\t0x3B, 0x9A, 0x3C, 0x9A, 0x3D, 0x9A, 0x3E, 0x9A, 0x3F, 0x9A, 0x40, 0x9A, 0x41, 0x9A, 0x42, 0x9A, 0x43, 0x9A, 0x44, 0x9A, 0x45, 0x9A, 0x46, 0x9A, 0x47, 0x9A, 0x48, 0x9A, 0x49, 0x9A, 0x4A, 0x9A,\n\t0x4B, 0x9A, 0x4C, 0x9A, 0x4D, 0x9A, 0x4E, 0x9A, 0x4F, 0x9A, 0x50, 0x9A, 0x51, 0x9A, 0x52, 0x9A, 0x53, 0x9A, 0x54, 0x9A, 0x55, 0x9A, 0x56, 0x9A, 0x57, 0x9A, 0x58, 0x9A, 0x59, 0x9A, 0x5A, 0x9A,\n\t0x5B, 0x9A, 0x5C, 0x9A, 0x5D, 0x9A, 0x5E, 0x9A, 0x5F, 0x9A, 0x60, 0x9A, 0x61, 0x9A, 0x62, 0x9A, 0x63, 0x9A, 0x64, 0x9A, 0x65, 0x9A, 0x66, 0x9A, 0x67, 0x9A, 0x68, 0x9A, 0x69, 0x9A, 0x6A, 0x9A,\n\t0x6B, 0x9A, 0x6C, 0x9A, 0x6D, 0x9A, 0x6E, 0x9A, 0x6F, 0x9A, 0x70, 0x9A, 0x71, 0x9A, 0x72, 0x9A, 0x73, 0x9A, 0x74, 0x9A, 0x75, 0x9A, 0x76, 0x9A, 0x77, 0x9A, 0x78, 0x9A, 0x79, 0x9A, 0x7A, 0x9A,\n\t0x7B, 0x9A, 0x7C, 0x9A, 0x7D, 0x9A, 0x7E, 0x9A, 0x7F, 0x9A, 0x7F, 0x9A, 0x80, 0x9A, 0x81, 0x9A, 0x82, 0x9A, 0x83, 0x9A, 0x84, 0x9A, 0x85, 0x9A, 0x86, 0x9A, 0x87, 0x9A, 0x88, 0x9A, 0x89, 0x9A,\n\t0x8A, 0x9A, 0x8B, 0x9A, 0x8C, 0x9A, 0x8D, 0x9A, 0x8E, 0x9A, 0x8F, 0x9A, 0x90, 0x9A, 0x91, 0x9A, 0x92, 0x9A, 0x93, 0x9A, 0x94, 0x9A, 0x95, 0x9A, 0x96, 0x9A, 0x97, 0x9A, 0x98, 0x9A, 0x99, 0x9A,\n\t0x9A, 0x9A, 0x9B, 0x9A, 0x9C, 0x9A, 0x9D, 0x9A, 0x9E, 0x9A, 0x9F, 0x9A, 0xA0, 0x9A, 0xA1, 0x9A, 0xA2, 0x9A, 0xA3, 0x9A, 0xA4, 0x9A, 0xA5, 0x9A, 0xA6, 0x9A, 0xA7, 0x9A, 0xA8, 0x9A, 0xA9, 0x9A,\n\t0xAA, 0x9A, 0xAB, 0x9A, 0xAC, 0x9A, 0xAD, 0x9A, 0xAE, 0x9A, 0xAF, 0x9A, 0xB0, 0x9A, 0xB1, 0x9A, 0xB2, 0x9A, 0xB3, 0x9A, 0xB4, 0x9A, 0xB5, 0x9A, 0xB6, 0x9A, 0xB7, 0x9A, 0xB8, 0x9A, 0xB9, 0x9A,\n\t0xBA, 0x9A, 0xBB, 0x9A, 0xBC, 0x9A, 0xBD, 0x9A, 0xBE, 0x9A, 0xBF, 0x9A, 0xC0, 0x9A, 0xC1, 0x9A, 0xC2, 0x9A, 0xC3, 0x9A, 0xC4, 0x9A, 0xC5, 0x9A, 0xC6, 0x9A, 0xC7, 0x9A, 0xC8, 0x9A, 0xC9, 0x9A,\n\t0xCA, 0x9A, 0xCB, 0x9A, 0xCC, 0x9A, 0xCD, 0x9A, 0xCE, 0x9A, 0xCF, 0x9A, 0xD0, 0x9A, 0xD1, 0x9A, 0xD2, 0x9A, 0xD3, 0x9A, 0xD4, 0x9A, 0xD5, 0x9A, 0xD6, 0x9A, 0xD7, 0x9A, 0xD8, 0x9A, 0xD9, 0x9A,\n\t0xDA, 0x9A, 0xDB, 0x9A, 0xDC, 0x9A, 0xDD, 0x9A, 0xDD, 0x9A, 0xDE, 0x9A, 0xDF, 0x9A, 0xE0, 0x9A, 0xE1, 0x9A, 0xE2, 0x9A, 0xE3, 0x9A, 0xE4, 0x9A, 0xE5, 0x9A, 0xE6, 0x9A, 0xE7, 0x9A, 0xE8, 0x9A,\n\t0xE9, 0x9A, 0xEA, 0x9A, 0xEB, 0x9A, 0xEC, 0x9A, 0xED, 0x9A, 0xEE, 0x9A, 0xEF, 0x9A, 0xF0, 0x9A, 0xF1, 0x9A, 0xF2, 0x9A, 0xF3, 0x9A, 0xF4, 0x9A, 0xF5, 0x9A, 0xF6, 0x9A, 0xF7, 0x9A, 0xF8, 0x9A,\n\t0xF9, 0x9A, 0xFA, 0x9A, 0xFB, 0x9A, 0xFC, 0x9A, 0xFD, 0x9A, 0xFE, 0x9A, 0xFF, 0x9A, 0x00, 0x9B, 0x01, 0x9B, 0x02, 0x9B, 0x03, 0x9B, 0x04, 0x9B, 0x05, 0x9B, 0x06, 0x9B, 0x07, 0x9B, 0x08, 0x9B,\n\t0x09, 0x9B, 0x0A, 0x9B, 0x0B, 0x9B, 0x0C, 0x9B, 0x0D, 0x9B, 0x0E, 0x9B, 0x0F, 0x9B, 0x10, 0x9B, 0x11, 0x9B, 0x12, 0x9B, 0x13, 0x9B, 0x14, 0x9B, 0x15, 0x9B, 0x16, 0x9B, 0x17, 0x9B, 0x18, 0x9B,\n\t0x19, 0x9B, 0x1A, 0x9B, 0x1B, 0x9B, 0x1C, 0x9B, 0x1D, 0x9B, 0x1E, 0x9B, 0x1F, 0x9B, 0x20, 0x9B, 0x21, 0x9B, 0x22, 0x9B, 0x23, 0x9B, 0x24, 0x9B, 0x25, 0x9B, 0x26, 0x9B, 0x27, 0x9B, 0x28, 0x9B,\n\t0x29, 0x9B, 0x2A, 0x9B, 0x2B, 0x9B, 0x2C, 0x9B, 0x2D, 0x9B, 0x2E, 0x9B, 0x2F, 0x9B, 0x30, 0x9B, 0x31, 0x9B, 0x32, 0x9B, 0x33, 0x9B, 0x34, 0x9B, 0x35, 0x9B, 0x36, 0x9B, 0x37, 0x9B, 0x37, 0x9B,\n\t0x38, 0x9B, 0x39, 0x9B, 0x3A, 0x9B, 0x3B, 0x9B, 0x3C, 0x9B, 0x3D, 0x9B, 0x3E, 0x9B, 0x3F, 0x9B, 0x40, 0x9B, 0x41, 0x9B, 0x42, 0x9B, 0x43, 0x9B, 0x44, 0x9B, 0x45, 0x9B, 0x46, 0x9B, 0x47, 0x9B,\n\t0x48, 0x9B, 0x49, 0x9B, 0x4A, 0x9B, 0x4B, 0x9B, 0x4C, 0x9B, 0x4D, 0x9B, 0x4E, 0x9B, 0x4F, 0x9B, 0x50, 0x9B, 0x51, 0x9B, 0x52, 0x9B, 0x53, 0x9B, 0x54, 0x9B, 0x55, 0x9B, 0x56, 0x9B, 0x57, 0x9B,\n\t0x58, 0x9B, 0x59, 0x9B, 0x5A, 0x9B, 0x5B, 0x9B, 0x5C, 0x9B, 0x5D, 0x9B, 0x5E, 0x9B, 0x5F, 0x9B, 0x60, 0x9B, 0x61, 0x9B, 0x62, 0x9B, 0x63, 0x9B, 0x64, 0x9B, 0x65, 0x9B, 0x66, 0x9B, 0x67, 0x9B,\n\t0x68, 0x9B, 0x69, 0x9B, 0x6A, 0x9B, 0x6B, 0x9B, 0x6C, 0x9B, 0x6D, 0x9B, 0x6E, 0x9B, 0x6F, 0x9B, 0x70, 0x9B, 0x71, 0x9B, 0x72, 0x9B, 0x73, 0x9B, 0x74, 0x9B, 0x75, 0x9B, 0x76, 0x9B, 0x77, 0x9B,\n\t0x78, 0x9B, 0x79, 0x9B, 0x7A, 0x9B, 0x7B, 0x9B, 0x7C, 0x9B, 0x7D, 0x9B, 0x7E, 0x9B, 0x7F, 0x9B, 0x80, 0x9B, 0x81, 0x9B, 0x82, 0x9B, 0x83, 0x9B, 0x84, 0x9B, 0x85, 0x9B, 0x86, 0x9B, 0x87, 0x9B,\n\t0x88, 0x9B, 0x89, 0x9B, 0x8A, 0x9B, 0x8B, 0x9B, 0x8C, 0x9B, 0x8C, 0x9B, 0x8D, 0x9B, 0x8E, 0x9B, 0x8F, 0x9B, 0x90, 0x9B, 0x91, 0x9B, 0x92, 0x9B, 0x93, 0x9B, 0x94, 0x9B, 0x95, 0x9B, 0x96, 0x9B,\n\t0x97, 0x9B, 0x98, 0x9B, 0x99, 0x9B, 0x9A, 0x9B, 0x9B, 0x9B, 0x9C, 0x9B, 0x9D, 0x9B, 0x9E, 0x9B, 0x9F, 0x9B, 0xA0, 0x9B, 0xA1, 0x9B, 0xA2, 0x9B, 0xA3, 0x9B, 0xA4, 0x9B, 0xA5, 0x9B, 0xA6, 0x9B,\n\t0xA7, 0x9B, 0xA8, 0x9B, 0xA9, 0x9B, 0xAA, 0x9B, 0xAB, 0x9B, 0xAC, 0x9B, 0xAD, 0x9B, 0xAE, 0x9B, 0xAF, 0x9B, 0xB0, 0x9B, 0xB1, 0x9B, 0xB2, 0x9B, 0xB3, 0x9B, 0xB4, 0x9B, 0xB5, 0x9B, 0xB6, 0x9B,\n\t0xB7, 0x9B, 0xB8, 0x9B, 0xB9, 0x9B, 0xBA, 0x9B, 0xBB, 0x9B, 0xBC, 0x9B, 0xBD, 0x9B, 0xBE, 0x9B, 0xBF, 0x9B, 0xC0, 0x9B, 0xC1, 0x9B, 0xC2, 0x9B, 0xC3, 0x9B, 0xC4, 0x9B, 0xC5, 0x9B, 0xC6, 0x9B,\n\t0xC7, 0x9B, 0xC8, 0x9B, 0xC9, 0x9B, 0xCA, 0x9B, 0xCB, 0x9B, 0xCC, 0x9B, 0xCD, 0x9B, 0xCE, 0x9B, 0xCF, 0x9B, 0xD0, 0x9B, 0xD1, 0x9B, 0xD2, 0x9B, 0xD3, 0x9B, 0xD4, 0x9B, 0xD5, 0x9B, 0xD6, 0x9B,\n\t0xD7, 0x9B, 0xD8, 0x9B, 0xD9, 0x9B, 0xDA, 0x9B, 0xDB, 0x9B, 0xDC, 0x9B, 0xDD, 0x9B, 0xDE, 0x9B, 0xDE, 0x9B, 0xDF, 0x9B, 0xE0, 0x9B, 0xE1, 0x9B, 0xE2, 0x9B, 0xE3, 0x9B, 0xE4, 0x9B, 0xE5, 0x9B,\n\t0xE6, 0x9B, 0xE7, 0x9B, 0xE8, 0x9B, 0xE9, 0x9B, 0xEA, 0x9B, 0xEB, 0x9B, 0xEC, 0x9B, 0xED, 0x9B, 0xEE, 0x9B, 0xEF, 0x9B, 0xF0, 0x9B, 0xF1, 0x9B, 0xF2, 0x9B, 0xF3, 0x9B, 0xF5, 0x9B, 0xF7, 0x9B,\n\t0xF9, 0x9B, 0xFB, 0x9B, 0xFD, 0x9B, 0xFF, 0x9B, 0x01, 0x9C, 0x02, 0x9C, 0x03, 0x9C, 0x03, 0x9C, 0x04, 0x9C, 0x05, 0x9C, 0x06, 0x9C, 0x07, 0x9C, 0x08, 0x9C, 0x09, 0x9C, 0x0A, 0x9C, 0x0B, 0x9C,\n\t0x0C, 0x9C, 0x0D, 0x9C, 0x0E, 0x9C, 0x0F, 0x9C, 0x10, 0x9C, 0x11, 0x9C, 0x12, 0x9C, 0x13, 0x9C, 0x14, 0x9C, 0x15, 0x9C, 0x16, 0x9C, 0x17, 0x9C, 0x18, 0x9C, 0x19, 0x9C, 0x1A, 0x9C, 0x1B, 0x9C,\n\t0x1C, 0x9C, 0x1D, 0x9C, 0x1E, 0x9C, 0x1F, 0x9C, 0x20, 0x9C, 0x21, 0x9C, 0x22, 0x9C, 0x23, 0x9C, 0x24, 0x9C, 0x25, 0x9C, 0x26, 0x9C, 0x27, 0x9C, 0x28, 0x9C, 0x29, 0x9C, 0x2A, 0x9C, 0x2B, 0x9C,\n\t0x2C, 0x9C, 0x2D, 0x9C, 0x2E, 0x9C, 0x2F, 0x9C, 0x30, 0x9C, 0x31, 0x9C, 0x32, 0x9C, 0x33, 0x9C, 0x34, 0x9C, 0x35, 0x9C, 0x36, 0x9C, 0x37, 0x9C, 0x38, 0x9C, 0x39, 0x9C, 0x3A, 0x9C, 0x3B, 0x9C,\n\t0x3C, 0x9C, 0x3D, 0x9C, 0x3E, 0x9C, 0x3F, 0x9C, 0x40, 0x9C, 0x41, 0x9C, 0x42, 0x9C, 0x43, 0x9C, 0x44, 0x9C, 0x45, 0x9C, 0x46, 0x9C, 0x47, 0x9C, 0x48, 0x9C, 0x49, 0x9C, 0x4A, 0x9C, 0x4B, 0x9C,\n\t0x4C, 0x9C, 0x4D, 0x9C, 0x4E, 0x9C, 0x4E, 0x9C, 0x4F, 0x9C, 0x50, 0x9C, 0x51, 0x9C, 0x52, 0x9C, 0x53, 0x9C, 0x54, 0x9C, 0x55, 0x9C, 0x56, 0x9C, 0x57, 0x9C, 0x58, 0x9C, 0x59, 0x9C, 0x5A, 0x9C,\n\t0x5B, 0x9C, 0x5C, 0x9C, 0x5D, 0x9C, 0x5E, 0x9C, 0x5F, 0x9C, 0x60, 0x9C, 0x61, 0x9C, 0x62, 0x9C, 0x63, 0x9C, 0x64, 0x9C, 0x65, 0x9C, 0x66, 0x9C, 0x67, 0x9C, 0x68, 0x9C, 0x69, 0x9C, 0x6A, 0x9C,\n\t0x6B, 0x9C, 0x6C, 0x9C, 0x6D, 0x9C, 0x6E, 0x9C, 0x6F, 0x9C, 0x70, 0x9C, 0x71, 0x9C, 0x72, 0x9C, 0x73, 0x9C, 0x74, 0x9C, 0x75, 0x9C, 0x76, 0x9C, 0x77, 0x9C, 0x78, 0x9C, 0x79, 0x9C, 0x7A, 0x9C,\n\t0x7B, 0x9C, 0x7C, 0x9C, 0x7D, 0x9C, 0x7E, 0x9C, 0x7F, 0x9C, 0x80, 0x9C, 0x81, 0x9C, 0x82, 0x9C, 0x83, 0x9C, 0x84, 0x9C, 0x85, 0x9C, 0x86, 0x9C, 0x87, 0x9C, 0x88, 0x9C, 0x89, 0x9C, 0x8A, 0x9C,\n\t0x8B, 0x9C, 0x8C, 0x9C, 0x8D, 0x9C, 0x8E, 0x9C, 0x8F, 0x9C, 0x90, 0x9C, 0x91, 0x9C, 0x92, 0x9C, 0x93, 0x9C, 0x94, 0x9C, 0x95, 0x9C, 0x95, 0x9C, 0x96, 0x9C, 0x97, 0x9C, 0x98, 0x9C, 0x99, 0x9C,\n\t0x9A, 0x9C, 0x9B, 0x9C, 0x9C, 0x9C, 0x9D, 0x9C, 0x9E, 0x9C, 0x9F, 0x9C, 0xA0, 0x9C, 0xA1, 0x9C, 0xA2, 0x9C, 0xA3, 0x9C, 0xA4, 0x9C, 0xA5, 0x9C, 0xA6, 0x9C, 0xA7, 0x9C, 0xA8, 0x9C, 0xA9, 0x9C,\n\t0xAA, 0x9C, 0xAB, 0x9C, 0xAC, 0x9C, 0xAD, 0x9C, 0xAE, 0x9C, 0xAF, 0x9C, 0xB0, 0x9C, 0xB1, 0x9C, 0xB2, 0x9C, 0xB3, 0x9C, 0xB4, 0x9C, 0xB5, 0x9C, 0xB6, 0x9C, 0xB7, 0x9C, 0xB8, 0x9C, 0xB9, 0x9C,\n\t0xBA, 0x9C, 0xBB, 0x9C, 0xBC, 0x9C, 0xBD, 0x9C, 0xBE, 0x9C, 0xBF, 0x9C, 0xC0, 0x9C, 0xC1, 0x9C, 0xC2, 0x9C, 0xC3, 0x9C, 0xC4, 0x9C, 0xC5, 0x9C, 0xC6, 0x9C, 0xC7, 0x9C, 0xC8, 0x9C, 0xC9, 0x9C,\n\t0xCA, 0x9C, 0xCB, 0x9C, 0xCC, 0x9C, 0xCD, 0x9C, 0xCE, 0x9C, 0xCF, 0x9C, 0xD0, 0x9C, 0xD1, 0x9C, 0xD2, 0x9C, 0xD3, 0x9C, 0xD4, 0x9C, 0xD5, 0x9C, 0xD6, 0x9C, 0xD7, 0x9C, 0xD8, 0x9C, 0xD8, 0x9C,\n\t0xD9, 0x9C, 0xDA, 0x9C, 0xDB, 0x9C, 0xDC, 0x9C, 0xDD, 0x9C, 0xDE, 0x9C, 0xDF, 0x9C, 0xE0, 0x9C, 0xE1, 0x9C, 0xE2, 0x9C, 0xE3, 0x9C, 0xE4, 0x9C, 0xE5, 0x9C, 0xE6, 0x9C, 0xE7, 0x9C, 0xE8, 0x9C,\n\t0xE9, 0x9C, 0xEA, 0x9C, 0xEB, 0x9C, 0xEC, 0x9C, 0xED, 0x9C, 0xEE, 0x9C, 0xEF, 0x9C, 0xF0, 0x9C, 0xF1, 0x9C, 0xF2, 0x9C, 0xF3, 0x9C, 0xF4, 0x9C, 0xF5, 0x9C, 0xF6, 0x9C, 0xF7, 0x9C, 0xF8, 0x9C,\n\t0xF9, 0x9C, 0xFA, 0x9C, 0xFB, 0x9C, 0xFC, 0x9C, 0xFD, 0x9C, 0xFE, 0x9C, 0xFF, 0x9C, 0x00, 0x9D, 0x01, 0x9D, 0x02, 0x9D, 0x03, 0x9D, 0x04, 0x9D, 0x05, 0x9D, 0x06, 0x9D, 0x07, 0x9D, 0x08, 0x9D,\n\t0x09, 0x9D, 0x0A, 0x9D, 0x0B, 0x9D, 0x0C, 0x9D, 0x0D, 0x9D, 0x0E, 0x9D, 0x0F, 0x9D, 0x10, 0x9D, 0x11, 0x9D, 0x12, 0x9D, 0x13, 0x9D, 0x14, 0x9D, 0x15, 0x9D, 0x16, 0x9D, 0x17, 0x9D, 0x17, 0x9D,\n\t0x18, 0x9D, 0x19, 0x9D, 0x1A, 0x9D, 0x1B, 0x9D, 0x1C, 0x9D, 0x1D, 0x9D, 0x1E, 0x9D, 0x1F, 0x9D, 0x20, 0x9D, 0x21, 0x9D, 0x22, 0x9D, 0x23, 0x9D, 0x24, 0x9D, 0x25, 0x9D, 0x26, 0x9D, 0x27, 0x9D,\n\t0x28, 0x9D, 0x29, 0x9D, 0x2A, 0x9D, 0x2B, 0x9D, 0x2C, 0x9D, 0x2D, 0x9D, 0x2E, 0x9D, 0x2F, 0x9D, 0x30, 0x9D, 0x31, 0x9D, 0x32, 0x9D, 0x33, 0x9D, 0x34, 0x9D, 0x35, 0x9D, 0x36, 0x9D, 0x37, 0x9D,\n\t0x38, 0x9D, 0x39, 0x9D, 0x3A, 0x9D, 0x3B, 0x9D, 0x3C, 0x9D, 0x3D, 0x9D, 0x3E, 0x9D, 0x3F, 0x9D, 0x40, 0x9D, 0x41, 0x9D, 0x42, 0x9D, 0x43, 0x9D, 0x44, 0x9D, 0x45, 0x9D, 0x46, 0x9D, 0x47, 0x9D,\n\t0x48, 0x9D, 0x49, 0x9D, 0x4A, 0x9D, 0x4B, 0x9D, 0x4C, 0x9D, 0x4D, 0x9D, 0x4E, 0x9D, 0x4F, 0x9D, 0x50, 0x9D, 0x51, 0x9D, 0x52, 0x9D, 0x53, 0x9D, 0x53, 0x9D, 0x54, 0x9D, 0x55, 0x9D, 0x56, 0x9D,\n\t0x57, 0x9D, 0x58, 0x9D, 0x59, 0x9D, 0x5A, 0x9D, 0x5B, 0x9D, 0x5C, 0x9D, 0x5D, 0x9D, 0x5E, 0x9D, 0x5F, 0x9D, 0x60, 0x9D, 0x61, 0x9D, 0x62, 0x9D, 0x63, 0x9D, 0x64, 0x9D, 0x65, 0x9D, 0x66, 0x9D,\n\t0x67, 0x9D, 0x68, 0x9D, 0x69, 0x9D, 0x6A, 0x9D, 0x6B, 0x9D, 0x6C, 0x9D, 0x6D, 0x9D, 0x6E, 0x9D, 0x6F, 0x9D, 0x70, 0x9D, 0x71, 0x9D, 0x72, 0x9D, 0x73, 0x9D, 0x74, 0x9D, 0x75, 0x9D, 0x76, 0x9D,\n\t0x77, 0x9D, 0x78, 0x9D, 0x79, 0x9D, 0x7A, 0x9D, 0x7B, 0x9D, 0x7C, 0x9D, 0x7D, 0x9D, 0x7E, 0x9D, 0x7F, 0x9D, 0x80, 0x9D, 0x81, 0x9D, 0x82, 0x9D, 0x83, 0x9D, 0x84, 0x9D, 0x85, 0x9D, 0x86, 0x9D,\n\t0x87, 0x9D, 0x88, 0x9D, 0x89, 0x9D, 0x8A, 0x9D, 0x8B, 0x9D, 0x8C, 0x9D, 0x8C, 0x9D, 0x8D, 0x9D, 0x8E, 0x9D, 0x8F, 0x9D, 0x90, 0x9D, 0x91, 0x9D, 0x92, 0x9D, 0x93, 0x9D, 0x94, 0x9D, 0x95, 0x9D,\n\t0x96, 0x9D, 0x97, 0x9D, 0x98, 0x9D, 0x99, 0x9D, 0x9A, 0x9D, 0x9B, 0x9D, 0x9C, 0x9D, 0x9D, 0x9D, 0x9E, 0x9D, 0x9F, 0x9D, 0xA0, 0x9D, 0xA1, 0x9D, 0xA2, 0x9D, 0xA3, 0x9D, 0xA4, 0x9D, 0xA5, 0x9D,\n\t0xA6, 0x9D, 0xA7, 0x9D, 0xA8, 0x9D, 0xA9, 0x9D, 0xAA, 0x9D, 0xAB, 0x9D, 0xAC, 0x9D, 0xAD, 0x9D, 0xAE, 0x9D, 0xAF, 0x9D, 0xB0, 0x9D, 0xB1, 0x9D, 0xB2, 0x9D, 0xB3, 0x9D, 0xB4, 0x9D, 0xB5, 0x9D,\n\t0xB6, 0x9D, 0xB7, 0x9D, 0xB8, 0x9D, 0xB9, 0x9D, 0xBA, 0x9D, 0xBB, 0x9D, 0xBC, 0x9D, 0xBD, 0x9D, 0xBE, 0x9D, 0xBF, 0x9D, 0xC0, 0x9D, 0xC1, 0x9D, 0xC2, 0x9D, 0xC3, 0x9D, 0xC4, 0x9D, 0xC4, 0x9D,\n\t0xC5, 0x9D, 0xC6, 0x9D, 0xC7, 0x9D, 0xC8, 0x9D, 0xC9, 0x9D, 0xCA, 0x9D, 0xCB, 0x9D, 0xCC, 0x9D, 0xCD, 0x9D, 0xCE, 0x9D, 0xCF, 0x9D, 0xD0, 0x9D, 0xD1, 0x9D, 0xD2, 0x9D, 0xD3, 0x9D, 0xD4, 0x9D,\n\t0xD5, 0x9D, 0xD6, 0x9D, 0xD7, 0x9D, 0xD8, 0x9D, 0xD9, 0x9D, 0xDA, 0x9D, 0xDB, 0x9D, 0xDC, 0x9D, 0xDD, 0x9D, 0xDE, 0x9D, 0xDF, 0x9D, 0xE0, 0x9D, 0xE1, 0x9D, 0xE2, 0x9D, 0xE3, 0x9D, 0xE4, 0x9D,\n\t0xE5, 0x9D, 0xE6, 0x9D, 0xE7, 0x9D, 0xE8, 0x9D, 0xE9, 0x9D, 0xEA, 0x9D, 0xEB, 0x9D, 0xEC, 0x9D, 0xED, 0x9D, 0xEE, 0x9D, 0xEF, 0x9D, 0xF0, 0x9D, 0xF1, 0x9D, 0xF2, 0x9D, 0xF3, 0x9D, 0xF4, 0x9D,\n\t0xF5, 0x9D, 0xF6, 0x9D, 0xF7, 0x9D, 0xF8, 0x9D, 0xF9, 0x9D, 0xF9, 0x9D, 0xFA, 0x9D, 0xFB, 0x9D, 0xFC, 0x9D, 0xFD, 0x9D, 0xFE, 0x9D, 0xFF, 0x9D, 0x00, 0x9E, 0x01, 0x9E, 0x02, 0x9E, 0x03, 0x9E,\n\t0x04, 0x9E, 0x05, 0x9E, 0x06, 0x9E, 0x07, 0x9E, 0x08, 0x9E, 0x09, 0x9E, 0x0A, 0x9E, 0x0B, 0x9E, 0x0C, 0x9E, 0x0D, 0x9E, 0x0E, 0x9E, 0x0F, 0x9E, 0x10, 0x9E, 0x11, 0x9E, 0x12, 0x9E, 0x13, 0x9E,\n\t0x14, 0x9E, 0x15, 0x9E, 0x16, 0x9E, 0x17, 0x9E, 0x18, 0x9E, 0x19, 0x9E, 0x1A, 0x9E, 0x1B, 0x9E, 0x1C, 0x9E, 0x1D, 0x9E, 0x1E, 0x9E, 0x1F, 0x9E, 0x20, 0x9E, 0x21, 0x9E, 0x22, 0x9E, 0x23, 0x9E,\n\t0x24, 0x9E, 0x25, 0x9E, 0x26, 0x9E, 0x27, 0x9E, 0x28, 0x9E, 0x29, 0x9E, 0x2A, 0x9E, 0x2B, 0x9E, 0x2C, 0x9E, 0x2C, 0x9E, 0x2D, 0x9E, 0x2E, 0x9E, 0x2F, 0x9E, 0x30, 0x9E, 0x31, 0x9E, 0x32, 0x9E,\n\t0x33, 0x9E, 0x34, 0x9E, 0x35, 0x9E, 0x36, 0x9E, 0x37, 0x9E, 0x38, 0x9E, 0x39, 0x9E, 0x3A, 0x9E, 0x3B, 0x9E, 0x3C, 0x9E, 0x3D, 0x9E, 0x3E, 0x9E, 0x3F, 0x9E, 0x40, 0x9E, 0x41, 0x9E, 0x42, 0x9E,\n\t0x43, 0x9E, 0x44, 0x9E, 0x45, 0x9E, 0x46, 0x9E, 0x47, 0x9E, 0x48, 0x9E, 0x49, 0x9E, 0x4A, 0x9E, 0x4B, 0x9E, 0x4C, 0x9E, 0x4D, 0x9E, 0x4E, 0x9E, 0x4F, 0x9E, 0x50, 0x9E, 0x51, 0x9E, 0x52, 0x9E,\n\t0x53, 0x9E, 0x54, 0x9E, 0x55, 0x9E, 0x56, 0x9E, 0x57, 0x9E, 0x58, 0x9E, 0x59, 0x9E, 0x5A, 0x9E, 0x5B, 0x9E, 0x5C, 0x9E, 0x5D, 0x9E, 0x5E, 0x9E, 0x5E, 0x9E, 0x5F, 0x9E, 0x60, 0x9E, 0x61, 0x9E,\n\t0x62, 0x9E, 0x63, 0x9E, 0x64, 0x9E, 0x65, 0x9E, 0x66, 0x9E, 0x67, 0x9E, 0x68, 0x9E, 0x69, 0x9E, 0x6A, 0x9E, 0x6B, 0x9E, 0x6C, 0x9E, 0x6D, 0x9E, 0x6E, 0x9E, 0x6F, 0x9E, 0x70, 0x9E, 0x71, 0x9E,\n\t0x72, 0x9E, 0x73, 0x9E, 0x74, 0x9E, 0x75, 0x9E, 0x76, 0x9E, 0x77, 0x9E, 0x78, 0x9E, 0x79, 0x9E, 0x7A, 0x9E, 0x7B, 0x9E, 0x7C, 0x9E, 0x7D, 0x9E, 0x7E, 0x9E, 0x7F, 0x9E, 0x80, 0x9E, 0x81, 0x9E,\n\t0x82, 0x9E, 0x83, 0x9E, 0x84, 0x9E, 0x85, 0x9E, 0x86, 0x9E, 0x87, 0x9E, 0x88, 0x9E, 0x89, 0x9E, 0x8A, 0x9E, 0x8B, 0x9E, 0x8C, 0x9E, 0x8D, 0x9E, 0x8E, 0x9E, 0x8E, 0x9E, 0x8F, 0x9E, 0x90, 0x9E,\n\t0x91, 0x9E, 0x92, 0x9E, 0x93, 0x9E, 0x94, 0x9E, 0x95, 0x9E, 0x96, 0x9E, 0x97, 0x9E, 0x98, 0x9E, 0x99, 0x9E, 0x9A, 0x9E, 0x9B, 0x9E, 0x9C, 0x9E, 0x9D, 0x9E, 0x9E, 0x9E, 0x9F, 0x9E, 0xA0, 0x9E,\n\t0xA1, 0x9E, 0xA2, 0x9E, 0xA3, 0x9E, 0xA4, 0x9E, 0xA5, 0x9E, 0xA6, 0x9E, 0xA7, 0x9E, 0xA8, 0x9E, 0xA9, 0x9E, 0xAA, 0x9E, 0xAB, 0x9E, 0xAC, 0x9E, 0xAD, 0x9E, 0xAE, 0x9E, 0xAF, 0x9E, 0xB0, 0x9E,\n\t0xB1, 0x9E, 0xB2, 0x9E, 0xB3, 0x9E, 0xB4, 0x9E, 0xB5, 0x9E, 0xB6, 0x9E, 0xB7, 0x9E, 0xB8, 0x9E, 0xB9, 0x9E, 0xBA, 0x9E, 0xBB, 0x9E, 0xBC, 0x9E, 0xBD, 0x9E, 0xBD, 0x9E, 0xBE, 0x9E, 0xBF, 0x9E,\n\t0xC0, 0x9E, 0xC1, 0x9E, 0xC2, 0x9E, 0xC3, 0x9E, 0xC4, 0x9E, 0xC5, 0x9E, 0xC6, 0x9E, 0xC7, 0x9E, 0xC8, 0x9E, 0xC9, 0x9E, 0xCA, 0x9E, 0xCB, 0x9E, 0xCC, 0x9E, 0xCD, 0x9E, 0xCE, 0x9E, 0xCF, 0x9E,\n\t0xD0, 0x9E, 0xD1, 0x9E, 0xD2, 0x9E, 0xD3, 0x9E, 0xD4, 0x9E, 0xD5, 0x9E, 0xD6, 0x9E, 0xD7, 0x9E, 0xD8, 0x9E, 0xD9, 0x9E, 0xDA, 0x9E, 0xDB, 0x9E, 0xDC, 0x9E, 0xDD, 0x9E, 0xDE, 0x9E, 0xDF, 0x9E,\n\t0xE0, 0x9E, 0xE1, 0x9E, 0xE2, 0x9E, 0xE3, 0x9E, 0xE4, 0x9E, 0xE5, 0x9E, 0xE6, 0x9E, 0xE7, 0x9E, 0xE8, 0x9E, 0xE9, 0x9E, 0xEA, 0x9E, 0xEA, 0x9E, 0xEB, 0x9E, 0xEC, 0x9E, 0xED, 0x9E, 0xEE, 0x9E,\n\t0xEF, 0x9E, 0xF0, 0x9E, 0xF1, 0x9E, 0xF2, 0x9E, 0xF3, 0x9E, 0xF4, 0x9E, 0xF5, 0x9E, 0xF6, 0x9E, 0xF7, 0x9E, 0xF8, 0x9E, 0xF9, 0x9E, 0xFA, 0x9E, 0xFB, 0x9E, 0xFC, 0x9E, 0xFD, 0x9E, 0xFE, 0x9E,\n\t0xFF, 0x9E, 0x00, 0x9F, 0x01, 0x9F, 0x02, 0x9F, 0x03, 0x9F, 0x04, 0x9F, 0x05, 0x9F, 0x06, 0x9F, 0x07, 0x9F, 0x08, 0x9F, 0x09, 0x9F, 0x0A, 0x9F, 0x0B, 0x9F, 0x0C, 0x9F, 0x0D, 0x9F, 0x0E, 0x9F,\n\t0x0F, 0x9F, 0x10, 0x9F, 0x11, 0x9F, 0x12, 0x9F, 0x13, 0x9F, 0x14, 0x9F, 0x15, 0x9F, 0x16, 0x9F, 0x17, 0x9F, 0x17, 0x9F, 0x18, 0x9F, 0x19, 0x9F, 0x1A, 0x9F, 0x1B, 0x9F, 0x1C, 0x9F, 0x1D, 0x9F,\n\t0x1E, 0x9F, 0x1F, 0x9F, 0x20, 0x9F, 0x21, 0x9F, 0x22, 0x9F, 0x23, 0x9F, 0x24, 0x9F, 0x25, 0x9F, 0x26, 0x9F, 0x27, 0x9F, 0x28, 0x9F, 0x29, 0x9F, 0x2A, 0x9F, 0x2B, 0x9F, 0x2C, 0x9F, 0x2D, 0x9F,\n\t0x2E, 0x9F, 0x2F, 0x9F, 0x30, 0x9F, 0x31, 0x9F, 0x32, 0x9F, 0x33, 0x9F, 0x34, 0x9F, 0x35, 0x9F, 0x36, 0x9F, 0x37, 0x9F, 0x38, 0x9F, 0x39, 0x9F, 0x3A, 0x9F, 0x3B, 0x9F, 0x3C, 0x9F, 0x3D, 0x9F,\n\t0x3E, 0x9F, 0x3F, 0x9F, 0x40, 0x9F, 0x41, 0x9F, 0x42, 0x9F, 0x42, 0x9F, 0x43, 0x9F, 0x44, 0x9F, 0x45, 0x9F, 0x46, 0x9F, 0x47, 0x9F, 0x48, 0x9F, 0x49, 0x9F, 0x4A, 0x9F, 0x4B, 0x9F, 0x4C, 0x9F,\n\t0x4D, 0x9F, 0x4E, 0x9F, 0x4F, 0x9F, 0x50, 0x9F, 0x51, 0x9F, 0x52, 0x9F, 0x53, 0x9F, 0x54, 0x9F, 0x55, 0x9F, 0x56, 0x9F, 0x57, 0x9F, 0x58, 0x9F, 0x59, 0x9F, 0x5A, 0x9F, 0x5B, 0x9F, 0x5C, 0x9F,\n\t0x5D, 0x9F, 0x5E, 0x9F, 0x5F, 0x9F, 0x60, 0x9F, 0x61, 0x9F, 0x62, 0x9F, 0x63, 0x9F, 0x64, 0x9F, 0x65, 0x9F, 0x66, 0x9F, 0x67, 0x9F, 0x68, 0x9F, 0x69, 0x9F, 0x6A, 0x9F, 0x6B, 0x9F, 0x6C, 0x9F,\n\t0x6C, 0x9F, 0x6D, 0x9F, 0x6E, 0x9F, 0x6F, 0x9F, 0x70, 0x9F, 0x71, 0x9F, 0x72, 0x9F, 0x73, 0x9F, 0x74, 0x9F, 0x75, 0x9F, 0x76, 0x9F, 0x77, 0x9F, 0x78, 0x9F, 0x79, 0x9F, 0x7A, 0x9F, 0x7B, 0x9F,\n\t0x7C, 0x9F, 0x7D, 0x9F, 0x7E, 0x9F, 0x7F, 0x9F, 0x80, 0x9F, 0x81, 0x9F, 0x82, 0x9F, 0x83, 0x9F, 0x84, 0x9F, 0x85, 0x9F, 0x86, 0x9F, 0x87, 0x9F, 0x88, 0x9F, 0x89, 0x9F, 0x8A, 0x9F, 0x8B, 0x9F,\n\t0x8C, 0x9F, 0x8D, 0x9F, 0x8E, 0x9F, 0x8F, 0x9F, 0x90, 0x9F, 0x91, 0x9F, 0x92, 0x9F, 0x93, 0x9F, 0x94, 0x9F, 0x95, 0x9F, 0x95, 0x9F, 0x96, 0x9F, 0x97, 0x9F, 0x98, 0x9F, 0x99, 0x9F, 0x9A, 0x9F,\n\t0x9B, 0x9F, 0x9C, 0x9F, 0x9D, 0x9F, 0x9E, 0x9F, 0x9F, 0x9F, 0xA0, 0x9F, 0xA1, 0x9F, 0xA2, 0x9F, 0xA3, 0x9F, 0xA4, 0x9F, 0xA5, 0x9F, 0xA6, 0x9F, 0xA7, 0x9F, 0xA8, 0x9F, 0xA9, 0x9F, 0xAA, 0x9F,\n\t0xAB, 0x9F, 0xAC, 0x9F, 0xAD, 0x9F, 0xAE, 0x9F, 0xAF, 0x9F, 0xB0, 0x9F, 0xB1, 0x9F, 0xB2, 0x9F, 0xB3, 0x9F, 0xB4, 0x9F, 0xB5, 0x9F, 0xB6, 0x9F, 0xB7, 0x9F, 0xB8, 0x9F, 0xB9, 0x9F, 0xBA, 0x9F,\n\t0xBB, 0x9F, 0xBC, 0x9F, 0xBD, 0x9F, 0xBE, 0x9F, 0xBE, 0x9F, 0xBF, 0x9F, 0xC0, 0x9F, 0xC1, 0x9F, 0xC2, 0x9F, 0xC3, 0x9F, 0xC4, 0x9F, 0xC5, 0x9F, 0xC6, 0x9F, 0xC7, 0x9F, 0xC8, 0x9F, 0xC9, 0x9F,\n\t0xCA, 0x9F, 0xCB, 0x9F, 0xCC, 0x9F, 0xCD, 0x9F, 0xCE, 0x9F, 0xCF, 0x9F, 0xD0, 0x9F, 0xD1, 0x9F, 0xD2, 0x9F, 0xD3, 0x9F, 0xD4, 0x9F, 0xD5, 0x9F, 0xD6, 0x9F, 0xD7, 0x9F, 0xD8, 0x9F, 0xD9, 0x9F,\n\t0xDA, 0x9F, 0xDB, 0x9F, 0xDC, 0x9F, 0xDD, 0x9F, 0xDE, 0x9F, 0xDF, 0x9F, 0xE0, 0x9F, 0xE1, 0x9F, 0xE2, 0x9F, 0xE3, 0x9F, 0xE4, 0x9F, 0xE5, 0x9F, 0xE5, 0x9F, 0xE6, 0x9F, 0xE8, 0x9F, 0xEA, 0x9F,\n\t0xEC, 0x9F, 0xEE, 0x9F, 0xF0, 0x9F, 0xF2, 0x9F, 0xF4, 0x9F, 0xF6, 0x9F, 0xF8, 0x9F, 0xFA, 0x9F, 0xFC, 0x9F, 0xFE, 0x9F, 0x00, 0xA0, 0x01, 0xA0, 0x02, 0xA0, 0x03, 0xA0, 0x04, 0xA0, 0x05, 0xA0,\n\t0x06, 0xA0, 0x07, 0xA0, 0x08, 0xA0, 0x09, 0xA0, 0x0A, 0xA0, 0x0B, 0xA0, 0x0C, 0xA0, 0x0D, 0xA0, 0x0E, 0xA0, 0x0F, 0xA0, 0x10, 0xA0, 0x10, 0xA0, 0x11, 0xA0, 0x12, 0xA0, 0x13, 0xA0, 0x14, 0xA0,\n\t0x15, 0xA0, 0x16, 0xA0, 0x17, 0xA0, 0x18, 0xA0, 0x19, 0xA0, 0x1A, 0xA0, 0x1B, 0xA0, 0x1C, 0xA0, 0x1D, 0xA0, 0x1E, 0xA0, 0x1F, 0xA0, 0x20, 0xA0, 0x21, 0xA0, 0x22, 0xA0, 0x23, 0xA0, 0x24, 0xA0,\n\t0x25, 0xA0, 0x26, 0xA0, 0x27, 0xA0, 0x28, 0xA0, 0x29, 0xA0, 0x2A, 0xA0, 0x2B, 0xA0, 0x2C, 0xA0, 0x2D, 0xA0, 0x2E, 0xA0, 0x2F, 0xA0, 0x30, 0xA0, 0x31, 0xA0, 0x32, 0xA0, 0x33, 0xA0, 0x34, 0xA0,\n\t0x35, 0xA0, 0x35, 0xA0, 0x36, 0xA0, 0x37, 0xA0, 0x38, 0xA0, 0x39, 0xA0, 0x3A, 0xA0, 0x3B, 0xA0, 0x3C, 0xA0, 0x3D, 0xA0, 0x3E, 0xA0, 0x3F, 0xA0, 0x40, 0xA0, 0x41, 0xA0, 0x42, 0xA0, 0x43, 0xA0,\n\t0x44, 0xA0, 0x45, 0xA0, 0x46, 0xA0, 0x47, 0xA0, 0x48, 0xA0, 0x49, 0xA0, 0x4A, 0xA0, 0x4B, 0xA0, 0x4C, 0xA0, 0x4D, 0xA0, 0x4E, 0xA0, 0x4F, 0xA0, 0x50, 0xA0, 0x51, 0xA0, 0x52, 0xA0, 0x53, 0xA0,\n\t0x54, 0xA0, 0x55, 0xA0, 0x56, 0xA0, 0x57, 0xA0, 0x58, 0xA0, 0x59, 0xA0, 0x59, 0xA0, 0x5A, 0xA0, 0x5B, 0xA0, 0x5C, 0xA0, 0x5D, 0xA0, 0x5E, 0xA0, 0x5F, 0xA0, 0x60, 0xA0, 0x61, 0xA0, 0x62, 0xA0,\n\t0x63, 0xA0, 0x64, 0xA0, 0x65, 0xA0, 0x66, 0xA0, 0x67, 0xA0, 0x68, 0xA0, 0x69, 0xA0, 0x6A, 0xA0, 0x6B, 0xA0, 0x6C, 0xA0, 0x6D, 0xA0, 0x6E, 0xA0, 0x6F, 0xA0, 0x70, 0xA0, 0x71, 0xA0, 0x72, 0xA0,\n\t0x73, 0xA0, 0x74, 0xA0, 0x75, 0xA0, 0x76, 0xA0, 0x77, 0xA0, 0x78, 0xA0, 0x79, 0xA0, 0x7A, 0xA0, 0x7B, 0xA0, 0x7C, 0xA0, 0x7C, 0xA0, 0x7D, 0xA0, 0x7E, 0xA0, 0x7F, 0xA0, 0x80, 0xA0, 0x81, 0xA0,\n\t0x82, 0xA0, 0x83, 0xA0, 0x84, 0xA0, 0x85, 0xA0, 0x86, 0xA0, 0x87, 0xA0, 0x88, 0xA0, 0x89, 0xA0, 0x8A, 0xA0, 0x8B, 0xA0, 0x8C, 0xA0, 0x8D, 0xA0, 0x8E, 0xA0, 0x8F, 0xA0, 0x90, 0xA0, 0x91, 0xA0,\n\t0x92, 0xA0, 0x93, 0xA0, 0x94, 0xA0, 0x95, 0xA0, 0x96, 0xA0, 0x97, 0xA0, 0x98, 0xA0, 0x99, 0xA0, 0x9A, 0xA0, 0x9B, 0xA0, 0x9C, 0xA0, 0x9D, 0xA0, 0x9D, 0xA0, 0x9E, 0xA0, 0x9F, 0xA0, 0xA0, 0xA0,\n\t0xA1, 0xA0, 0xA2, 0xA0, 0xA3, 0xA0, 0xA4, 0xA0, 0xA5, 0xA0, 0xA6, 0xA0, 0xA7, 0xA0, 0xA8, 0xA0, 0xA9, 0xA0, 0xAA, 0xA0, 0xAB, 0xA0, 0xAC, 0xA0, 0xAD, 0xA0, 0xAE, 0xA0, 0xAF, 0xA0, 0xB0, 0xA0,\n\t0xB1, 0xA0, 0xB2, 0xA0, 0xB3, 0xA0, 0xB4, 0xA0, 0xB5, 0xA0, 0xB6, 0xA0, 0xB7, 0xA0, 0xB8, 0xA0, 0xB9, 0xA0, 0xBA, 0xA0, 0xBB, 0xA0, 0xBC, 0xA0, 0xBD, 0xA0, 0xBE, 0xA0, 0xBE, 0xA0, 0xBF, 0xA0,\n\t0xC0, 0xA0, 0xC1, 0xA0, 0xC2, 0xA0, 0xC3, 0xA0, 0xC4, 0xA0, 0xC5, 0xA0, 0xC6, 0xA0, 0xC7, 0xA0, 0xC8, 0xA0, 0xC9, 0xA0, 0xCA, 0xA0, 0xCB, 0xA0, 0xCC, 0xA0, 0xCD, 0xA0, 0xCE, 0xA0, 0xCF, 0xA0,\n\t0xD0, 0xA0, 0xD1, 0xA0, 0xD2, 0xA0, 0xD3, 0xA0, 0xD4, 0xA0, 0xD5, 0xA0, 0xD6, 0xA0, 0xD7, 0xA0, 0xD8, 0xA0, 0xD9, 0xA0, 0xDA, 0xA0, 0xDB, 0xA0, 0xDC, 0xA0, 0xDD, 0xA0, 0xDE, 0xA0, 0xDE, 0xA0,\n\t0xDF, 0xA0, 0xE0, 0xA0, 0xE1, 0xA0, 0xE2, 0xA0, 0xE3, 0xA0, 0xE4, 0xA0, 0xE5, 0xA0, 0xE6, 0xA0, 0xE7, 0xA0, 0xE8, 0xA0, 0xE9, 0xA0, 0xEA, 0xA0, 0xEB, 0xA0, 0xEC, 0xA0, 0xED, 0xA0, 0xEE, 0xA0,\n\t0xEF, 0xA0, 0xF0, 0xA0, 0xF1, 0xA0, 0xF2, 0xA0, 0xF3, 0xA0, 0xF4, 0xA0, 0xF5, 0xA0, 0xF6, 0xA0, 0xF7, 0xA0, 0xF8, 0xA0, 0xF9, 0xA0, 0xFA, 0xA0, 0xFB, 0xA0, 0xFC, 0xA0, 0xFD, 0xA0, 0xFD, 0xA0,\n\t0xFE, 0xA0, 0xFF, 0xA0, 0x00, 0xA1, 0x01, 0xA1, 0x02, 0xA1, 0x03, 0xA1, 0x04, 0xA1, 0x05, 0xA1, 0x06, 0xA1, 0x07, 0xA1, 0x08, 0xA1, 0x09, 0xA1, 0x0A, 0xA1, 0x0B, 0xA1, 0x0C, 0xA1, 0x0D, 0xA1,\n\t0x0E, 0xA1, 0x0F, 0xA1, 0x10, 0xA1, 0x11, 0xA1, 0x12, 0xA1, 0x13, 0xA1, 0x14, 0xA1, 0x15, 0xA1, 0x16, 0xA1, 0x17, 0xA1, 0x18, 0xA1, 0x19, 0xA1, 0x1A, 0xA1, 0x1B, 0xA1, 0x1B, 0xA1, 0x1C, 0xA1,\n\t0x1D, 0xA1, 0x1E, 0xA1, 0x1F, 0xA1, 0x20, 0xA1, 0x21, 0xA1, 0x22, 0xA1, 0x23, 0xA1, 0x24, 0xA1, 0x25, 0xA1, 0x26, 0xA1, 0x27, 0xA1, 0x28, 0xA1, 0x29, 0xA1, 0x2A, 0xA1, 0x2B, 0xA1, 0x2C, 0xA1,\n\t0x2D, 0xA1, 0x2E, 0xA1, 0x2F, 0xA1, 0x30, 0xA1, 0x31, 0xA1, 0x32, 0xA1, 0x33, 0xA1, 0x34, 0xA1, 0x35, 0xA1, 0x36, 0xA1, 0x37, 0xA1, 0x38, 0xA1, 0x39, 0xA1, 0x39, 0xA1, 0x3A, 0xA1, 0x3B, 0xA1,\n\t0x3C, 0xA1, 0x3D, 0xA1, 0x3E, 0xA1, 0x3F, 0xA1, 0x40, 0xA1, 0x41, 0xA1, 0x42, 0xA1, 0x43, 0xA1, 0x44, 0xA1, 0x45, 0xA1, 0x46, 0xA1, 0x47, 0xA1, 0x48, 0xA1, 0x49, 0xA1, 0x4A, 0xA1, 0x4B, 0xA1,\n\t0x4C, 0xA1, 0x4D, 0xA1, 0x4E, 0xA1, 0x4F, 0xA1, 0x50, 0xA1, 0x51, 0xA1, 0x52, 0xA1, 0x53, 0xA1, 0x54, 0xA1, 0x55, 0xA1, 0x56, 0xA1, 0x56, 0xA1, 0x57, 0xA1, 0x58, 0xA1, 0x59, 0xA1, 0x5A, 0xA1,\n\t0x5B, 0xA1, 0x5C, 0xA1, 0x5D, 0xA1, 0x5E, 0xA1, 0x5F, 0xA1, 0x60, 0xA1, 0x61, 0xA1, 0x62, 0xA1, 0x63, 0xA1, 0x64, 0xA1, 0x65, 0xA1, 0x66, 0xA1, 0x67, 0xA1, 0x68, 0xA1, 0x69, 0xA1, 0x6A, 0xA1,\n\t0x6B, 0xA1, 0x6C, 0xA1, 0x6D, 0xA1, 0x6E, 0xA1, 0x6F, 0xA1, 0x70, 0xA1, 0x71, 0xA1, 0x72, 0xA1, 0x72, 0xA1, 0x73, 0xA1, 0x74, 0xA1, 0x75, 0xA1, 0x76, 0xA1, 0x77, 0xA1, 0x78, 0xA1, 0x79, 0xA1,\n\t0x7A, 0xA1, 0x7B, 0xA1, 0x7C, 0xA1, 0x7D, 0xA1, 0x7E, 0xA1, 0x7F, 0xA1, 0x80, 0xA1, 0x81, 0xA1, 0x82, 0xA1, 0x83, 0xA1, 0x84, 0xA1, 0x85, 0xA1, 0x86, 0xA1, 0x87, 0xA1, 0x88, 0xA1, 0x89, 0xA1,\n\t0x8A, 0xA1, 0x8B, 0xA1, 0x8C, 0xA1, 0x8D, 0xA1, 0x8E, 0xA1, 0x8E, 0xA1, 0x8F, 0xA1, 0x90, 0xA1, 0x91, 0xA1, 0x92, 0xA1, 0x93, 0xA1, 0x94, 0xA1, 0x95, 0xA1, 0x96, 0xA1, 0x97, 0xA1, 0x98, 0xA1,\n\t0x99, 0xA1, 0x9A, 0xA1, 0x9B, 0xA1, 0x9C, 0xA1, 0x9D, 0xA1, 0x9E, 0xA1, 0x9F, 0xA1, 0xA0, 0xA1, 0xA1, 0xA1, 0xA2, 0xA1, 0xA3, 0xA1, 0xA4, 0xA1, 0xA5, 0xA1, 0xA6, 0xA1, 0xA7, 0xA1, 0xA8, 0xA1,\n\t0xA9, 0xA1, 0xA9, 0xA1, 0xAA, 0xA1, 0xAB, 0xA1, 0xAC, 0xA1, 0xAD, 0xA1, 0xAE, 0xA1, 0xAF, 0xA1, 0xB0, 0xA1, 0xB1, 0xA1, 0xB2, 0xA1, 0xB3, 0xA1, 0xB4, 0xA1, 0xB5, 0xA1, 0xB6, 0xA1, 0xB7, 0xA1,\n\t0xB8, 0xA1, 0xB9, 0xA1, 0xBA, 0xA1, 0xBB, 0xA1, 0xBC, 0xA1, 0xBD, 0xA1, 0xBE, 0xA1, 0xBF, 0xA1, 0xC0, 0xA1, 0xC1, 0xA1, 0xC2, 0xA1, 0xC3, 0xA1, 0xC3, 0xA1, 0xC4, 0xA1, 0xC5, 0xA1, 0xC6, 0xA1,\n\t0xC7, 0xA1, 0xC8, 0xA1, 0xC9, 0xA1, 0xCA, 0xA1, 0xCB, 0xA1, 0xCC, 0xA1, 0xCD, 0xA1, 0xCE, 0xA1, 0xCF, 0xA1, 0xD0, 0xA1, 0xD1, 0xA1, 0xD2, 0xA1, 0xD3, 0xA1, 0xD4, 0xA1, 0xD5, 0xA1, 0xD6, 0xA1,\n\t0xD7, 0xA1, 0xD8, 0xA1, 0xD9, 0xA1, 0xDA, 0xA1, 0xDB, 0xA1, 0xDC, 0xA1, 0xDD, 0xA1, 0xDE, 0xA1, 0xDE, 0xA1, 0xDF, 0xA1, 0xE0, 0xA1, 0xE1, 0xA1, 0xE2, 0xA1, 0xE3, 0xA1, 0xE4, 0xA1, 0xE5, 0xA1,\n\t0xE6, 0xA1, 0xE7, 0xA1, 0xE8, 0xA1, 0xE9, 0xA1, 0xEA, 0xA1, 0xEB, 0xA1, 0xEC, 0xA1, 0xED, 0xA1, 0xEE, 0xA1, 0xEF, 0xA1, 0xF0, 0xA1, 0xF1, 0xA1, 0xF2, 0xA1, 0xF3, 0xA1, 0xF4, 0xA1, 0xF5, 0xA1,\n\t0xF6, 0xA1, 0xF7, 0xA1, 0xF7, 0xA1, 0xF8, 0xA1, 0xF9, 0xA1, 0xFA, 0xA1, 0xFB, 0xA1, 0xFC, 0xA1, 0xFD, 0xA1, 0xFE, 0xA1, 0xFF, 0xA1, 0x00, 0xA2, 0x01, 0xA2, 0x02, 0xA2, 0x03, 0xA2, 0x04, 0xA2,\n\t0x05, 0xA2, 0x06, 0xA2, 0x07, 0xA2, 0x08, 0xA2, 0x09, 0xA2, 0x0A, 0xA2, 0x0B, 0xA2, 0x0C, 0xA2, 0x0D, 0xA2, 0x0E, 0xA2, 0x0F, 0xA2, 0x10, 0xA2, 0x10, 0xA2, 0x11, 0xA2, 0x12, 0xA2, 0x13, 0xA2,\n\t0x14, 0xA2, 0x15, 0xA2, 0x16, 0xA2, 0x17, 0xA2, 0x18, 0xA2, 0x19, 0xA2, 0x1A, 0xA2, 0x1B, 0xA2, 0x1C, 0xA2, 0x1D, 0xA2, 0x1E, 0xA2, 0x1F, 0xA2, 0x20, 0xA2, 0x21, 0xA2, 0x22, 0xA2, 0x23, 0xA2,\n\t0x24, 0xA2, 0x25, 0xA2, 0x26, 0xA2, 0x27, 0xA2, 0x28, 0xA2, 0x29, 0xA2, 0x29, 0xA2, 0x2A, 0xA2, 0x2B, 0xA2, 0x2C, 0xA2, 0x2D, 0xA2, 0x2E, 0xA2, 0x2F, 0xA2, 0x30, 0xA2, 0x31, 0xA2, 0x32, 0xA2,\n\t0x33, 0xA2, 0x34, 0xA2, 0x35, 0xA2, 0x36, 0xA2, 0x37, 0xA2, 0x38, 0xA2, 0x39, 0xA2, 0x3A, 0xA2, 0x3B, 0xA2, 0x3C, 0xA2, 0x3D, 0xA2, 0x3E, 0xA2, 0x3F, 0xA2, 0x40, 0xA2, 0x41, 0xA2, 0x41, 0xA2,\n\t0x42, 0xA2, 0x43, 0xA2, 0x44, 0xA2, 0x45, 0xA2, 0x46, 0xA2, 0x47, 0xA2, 0x48, 0xA2, 0x49, 0xA2, 0x4A, 0xA2, 0x4B, 0xA2, 0x4C, 0xA2, 0x4D, 0xA2, 0x4E, 0xA2, 0x4F, 0xA2, 0x50, 0xA2, 0x51, 0xA2,\n\t0x52, 0xA2, 0x53, 0xA2, 0x54, 0xA2, 0x55, 0xA2, 0x56, 0xA2, 0x57, 0xA2, 0x58, 0xA2, 0x59, 0xA2, 0x59, 0xA2, 0x5A, 0xA2, 0x5B, 0xA2, 0x5C, 0xA2, 0x5D, 0xA2, 0x5E, 0xA2, 0x5F, 0xA2, 0x60, 0xA2,\n\t0x61, 0xA2, 0x62, 0xA2, 0x63, 0xA2, 0x64, 0xA2, 0x65, 0xA2, 0x66, 0xA2, 0x67, 0xA2, 0x68, 0xA2, 0x69, 0xA2, 0x6A, 0xA2, 0x6B, 0xA2, 0x6C, 0xA2, 0x6D, 0xA2, 0x6E, 0xA2, 0x6F, 0xA2, 0x70, 0xA2,\n\t0x71, 0xA2, 0x71, 0xA2, 0x72, 0xA2, 0x73, 0xA2, 0x74, 0xA2, 0x75, 0xA2, 0x76, 0xA2, 0x77, 0xA2, 0x78, 0xA2, 0x79, 0xA2, 0x7A, 0xA2, 0x7B, 0xA2, 0x7C, 0xA2, 0x7D, 0xA2, 0x7E, 0xA2, 0x7F, 0xA2,\n\t0x80, 0xA2, 0x81, 0xA2, 0x82, 0xA2, 0x83, 0xA2, 0x84, 0xA2, 0x85, 0xA2, 0x86, 0xA2, 0x87, 0xA2, 0x88, 0xA2, 0x88, 0xA2, 0x89, 0xA2, 0x8A, 0xA2, 0x8B, 0xA2, 0x8C, 0xA2, 0x8D, 0xA2, 0x8E, 0xA2,\n\t0x8F, 0xA2, 0x90, 0xA2, 0x91, 0xA2, 0x92, 0xA2, 0x93, 0xA2, 0x94, 0xA2, 0x95, 0xA2, 0x96, 0xA2, 0x97, 0xA2, 0x98, 0xA2, 0x99, 0xA2, 0x9A, 0xA2, 0x9B, 0xA2, 0x9C, 0xA2, 0x9D, 0xA2, 0x9E, 0xA2,\n\t0x9F, 0xA2, 0x9F, 0xA2, 0xA0, 0xA2, 0xA1, 0xA2, 0xA2, 0xA2, 0xA3, 0xA2, 0xA4, 0xA2, 0xA5, 0xA2, 0xA6, 0xA2, 0xA7, 0xA2, 0xA8, 0xA2, 0xA9, 0xA2, 0xAA, 0xA2, 0xAB, 0xA2, 0xAC, 0xA2, 0xAD, 0xA2,\n\t0xAE, 0xA2, 0xAF, 0xA2, 0xB0, 0xA2, 0xB1, 0xA2, 0xB2, 0xA2, 0xB3, 0xA2, 0xB4, 0xA2, 0xB5, 0xA2, 0xB6, 0xA2, 0xB6, 0xA2, 0xB7, 0xA2, 0xB8, 0xA2, 0xB9, 0xA2, 0xBA, 0xA2, 0xBB, 0xA2, 0xBC, 0xA2,\n\t0xBD, 0xA2, 0xBE, 0xA2, 0xBF, 0xA2, 0xC0, 0xA2, 0xC1, 0xA2, 0xC2, 0xA2, 0xC3, 0xA2, 0xC4, 0xA2, 0xC5, 0xA2, 0xC6, 0xA2, 0xC7, 0xA2, 0xC8, 0xA2, 0xC9, 0xA2, 0xCA, 0xA2, 0xCB, 0xA2, 0xCC, 0xA2,\n\t0xCC, 0xA2, 0xCD, 0xA2, 0xCE, 0xA2, 0xCF, 0xA2, 0xD0, 0xA2, 0xD1, 0xA2, 0xD2, 0xA2, 0xD3, 0xA2, 0xD4, 0xA2, 0xD5, 0xA2, 0xD6, 0xA2, 0xD7, 0xA2, 0xD8, 0xA2, 0xD9, 0xA2, 0xDA, 0xA2, 0xDB, 0xA2,\n\t0xDC, 0xA2, 0xDD, 0xA2, 0xDE, 0xA2, 0xDF, 0xA2, 0xE0, 0xA2, 0xE1, 0xA2, 0xE2, 0xA2, 0xE2, 0xA2, 0xE3, 0xA2, 0xE4, 0xA2, 0xE5, 0xA2, 0xE6, 0xA2, 0xE7, 0xA2, 0xE8, 0xA2, 0xE9, 0xA2, 0xEA, 0xA2,\n\t0xEB, 0xA2, 0xEC, 0xA2, 0xED, 0xA2, 0xEE, 0xA2, 0xEF, 0xA2, 0xF0, 0xA2, 0xF1, 0xA2, 0xF2, 0xA2, 0xF3, 0xA2, 0xF4, 0xA2, 0xF5, 0xA2, 0xF6, 0xA2, 0xF7, 0xA2, 0xF8, 0xA2, 0xF8, 0xA2, 0xF9, 0xA2,\n\t0xFA, 0xA2, 0xFB, 0xA2, 0xFC, 0xA2, 0xFD, 0xA2, 0xFE, 0xA2, 0xFF, 0xA2, 0x00, 0xA3, 0x01, 0xA3, 0x02, 0xA3, 0x03, 0xA3, 0x04, 0xA3, 0x05, 0xA3, 0x06, 0xA3, 0x07, 0xA3, 0x08, 0xA3, 0x09, 0xA3,\n\t0x0A, 0xA3, 0x0B, 0xA3, 0x0C, 0xA3, 0x0D, 0xA3, 0x0D, 0xA3, 0x0E, 0xA3, 0x0F, 0xA3, 0x10, 0xA3, 0x11, 0xA3, 0x12, 0xA3, 0x13, 0xA3, 0x14, 0xA3, 0x15, 0xA3, 0x16, 0xA3, 0x17, 0xA3, 0x18, 0xA3,\n\t0x19, 0xA3, 0x1A, 0xA3, 0x1B, 0xA3, 0x1C, 0xA3, 0x1D, 0xA3, 0x1E, 0xA3, 0x1F, 0xA3, 0x20, 0xA3, 0x21, 0xA3, 0x22, 0xA3, 0x22, 0xA3, 0x23, 0xA3, 0x24, 0xA3, 0x25, 0xA3, 0x26, 0xA3, 0x27, 0xA3,\n\t0x28, 0xA3, 0x29, 0xA3, 0x2A, 0xA3, 0x2B, 0xA3, 0x2C, 0xA3, 0x2D, 0xA3, 0x2E, 0xA3, 0x2F, 0xA3, 0x30, 0xA3, 0x31, 0xA3, 0x32, 0xA3, 0x33, 0xA3, 0x34, 0xA3, 0x35, 0xA3, 0x36, 0xA3, 0x37, 0xA3,\n\t0x37, 0xA3, 0x38, 0xA3, 0x39, 0xA3, 0x3A, 0xA3, 0x3B, 0xA3, 0x3C, 0xA3, 0x3D, 0xA3, 0x3E, 0xA3, 0x3F, 0xA3, 0x40, 0xA3, 0x41, 0xA3, 0x42, 0xA3, 0x43, 0xA3, 0x44, 0xA3, 0x45, 0xA3, 0x46, 0xA3,\n\t0x47, 0xA3, 0x48, 0xA3, 0x49, 0xA3, 0x4A, 0xA3, 0x4B, 0xA3, 0x4B, 0xA3, 0x4C, 0xA3, 0x4D, 0xA3, 0x4E, 0xA3, 0x4F, 0xA3, 0x50, 0xA3, 0x51, 0xA3, 0x52, 0xA3, 0x53, 0xA3, 0x54, 0xA3, 0x55, 0xA3,\n\t0x56, 0xA3, 0x57, 0xA3, 0x58, 0xA3, 0x59, 0xA3, 0x5A, 0xA3, 0x5B, 0xA3, 0x5C, 0xA3, 0x5D, 0xA3, 0x5E, 0xA3, 0x5F, 0xA3, 0x60, 0xA3, 0x60, 0xA3, 0x61, 0xA3, 0x62, 0xA3, 0x63, 0xA3, 0x64, 0xA3,\n\t0x65, 0xA3, 0x66, 0xA3, 0x67, 0xA3, 0x68, 0xA3, 0x69, 0xA3, 0x6A, 0xA3, 0x6B, 0xA3, 0x6C, 0xA3, 0x6D, 0xA3, 0x6E, 0xA3, 0x6F, 0xA3, 0x70, 0xA3, 0x71, 0xA3, 0x72, 0xA3, 0x73, 0xA3, 0x74, 0xA3,\n\t0x74, 0xA3, 0x75, 0xA3, 0x76, 0xA3, 0x77, 0xA3, 0x78, 0xA3, 0x79, 0xA3, 0x7A, 0xA3, 0x7B, 0xA3, 0x7C, 0xA3, 0x7D, 0xA3, 0x7E, 0xA3, 0x7F, 0xA3, 0x80, 0xA3, 0x81, 0xA3, 0x82, 0xA3, 0x83, 0xA3,\n\t0x84, 0xA3, 0x85, 0xA3, 0x86, 0xA3, 0x87, 0xA3, 0x88, 0xA3, 0x88, 0xA3, 0x89, 0xA3, 0x8A, 0xA3, 0x8B, 0xA3, 0x8C, 0xA3, 0x8D, 0xA3, 0x8E, 0xA3, 0x8F, 0xA3, 0x90, 0xA3, 0x91, 0xA3, 0x92, 0xA3,\n\t0x93, 0xA3, 0x94, 0xA3, 0x95, 0xA3, 0x96, 0xA3, 0x97, 0xA3, 0x98, 0xA3, 0x99, 0xA3, 0x9A, 0xA3, 0x9B, 0xA3, 0x9C, 0xA3, 0x9C, 0xA3, 0x9D, 0xA3, 0x9E, 0xA3, 0x9F, 0xA3, 0xA0, 0xA3, 0xA1, 0xA3,\n\t0xA2, 0xA3, 0xA3, 0xA3, 0xA4, 0xA3, 0xA5, 0xA3, 0xA6, 0xA3, 0xA7, 0xA3, 0xA8, 0xA3, 0xA9, 0xA3, 0xAA, 0xA3, 0xAB, 0xA3, 0xAC, 0xA3, 0xAD, 0xA3, 0xAE, 0xA3, 0xAF, 0xA3, 0xAF, 0xA3, 0xB0, 0xA3,\n\t0xB1, 0xA3, 0xB2, 0xA3, 0xB3, 0xA3, 0xB4, 0xA3, 0xB5, 0xA3, 0xB6, 0xA3, 0xB7, 0xA3, 0xB8, 0xA3, 0xB9, 0xA3, 0xBA, 0xA3, 0xBB, 0xA3, 0xBC, 0xA3, 0xBD, 0xA3, 0xBE, 0xA3, 0xBF, 0xA3, 0xC0, 0xA3,\n\t0xC1, 0xA3, 0xC2, 0xA3, 0xC2, 0xA3, 0xC3, 0xA3, 0xC4, 0xA3, 0xC5, 0xA3, 0xC6, 0xA3, 0xC7, 0xA3, 0xC8, 0xA3, 0xC9, 0xA3, 0xCA, 0xA3, 0xCB, 0xA3, 0xCC, 0xA3, 0xCD, 0xA3, 0xCF, 0xA3, 0xD1, 0xA3,\n\t0xD3, 0xA3, 0xD5, 0xA3, 0xD6, 0xA3, 0xD8, 0xA3, 0xDA, 0xA3, 0xDC, 0xA3, 0xDE, 0xA3, 0xE0, 0xA3, 0xE2, 0xA3, 0xE4, 0xA3, 0xE6, 0xA3, 0xE8, 0xA3, 0xE9, 0xA3, 0xEB, 0xA3, 0xED, 0xA3, 0xEF, 0xA3,\n\t0xF1, 0xA3, 0xF3, 0xA3, 0xF5, 0xA3, 0xF7, 0xA3, 0xF9, 0xA3, 0xFB, 0xA3, 0xFC, 0xA3, 0xFE, 0xA3, 0x00, 0xA4, 0x01, 0xA4, 0x02, 0xA4, 0x03, 0xA4, 0x04, 0xA4, 0x05, 0xA4, 0x06, 0xA4, 0x07, 0xA4,\n\t0x08, 0xA4, 0x09, 0xA4, 0x0A, 0xA4, 0x0B, 0xA4, 0x0B, 0xA4, 0x0C, 0xA4, 0x0D, 0xA4, 0x0E, 0xA4, 0x0F, 0xA4, 0x10, 0xA4, 0x11, 0xA4, 0x12, 0xA4, 0x13, 0xA4, 0x14, 0xA4, 0x15, 0xA4, 0x16, 0xA4,\n\t0x17, 0xA4, 0x18, 0xA4, 0x19, 0xA4, 0x1A, 0xA4, 0x1B, 0xA4, 0x1C, 0xA4, 0x1D, 0xA4, 0x1D, 0xA4, 0x1E, 0xA4, 0x1F, 0xA4, 0x20, 0xA4, 0x21, 0xA4, 0x22, 0xA4, 0x23, 0xA4, 0x24, 0xA4, 0x25, 0xA4,\n\t0x26, 0xA4, 0x27, 0xA4, 0x28, 0xA4, 0x29, 0xA4, 0x2A, 0xA4, 0x2B, 0xA4, 0x2C, 0xA4, 0x2D, 0xA4, 0x2E, 0xA4, 0x2F, 0xA4, 0x2F, 0xA4, 0x30, 0xA4, 0x31, 0xA4, 0x32, 0xA4, 0x33, 0xA4, 0x34, 0xA4,\n\t0x35, 0xA4, 0x36, 0xA4, 0x37, 0xA4, 0x38, 0xA4, 0x39, 0xA4, 0x3A, 0xA4, 0x3B, 0xA4, 0x3C, 0xA4, 0x3D, 0xA4, 0x3E, 0xA4, 0x3F, 0xA4, 0x40, 0xA4, 0x41, 0xA4, 0x41, 0xA4, 0x42, 0xA4, 0x43, 0xA4,\n\t0x44, 0xA4, 0x45, 0xA4, 0x46, 0xA4, 0x47, 0xA4, 0x48, 0xA4, 0x49, 0xA4, 0x4A, 0xA4, 0x4B, 0xA4, 0x4C, 0xA4, 0x4D, 0xA4, 0x4E, 0xA4, 0x4F, 0xA4, 0x50, 0xA4, 0x51, 0xA4, 0x52, 0xA4, 0x52, 0xA4,\n\t0x53, 0xA4, 0x54, 0xA4, 0x55, 0xA4, 0x56, 0xA4, 0x57, 0xA4, 0x58, 0xA4, 0x59, 0xA4, 0x5A, 0xA4, 0x5B, 0xA4, 0x5C, 0xA4, 0x5D, 0xA4, 0x5E, 0xA4, 0x5F, 0xA4, 0x60, 0xA4, 0x61, 0xA4, 0x62, 0xA4,\n\t0x63, 0xA4, 0x63, 0xA4, 0x64, 0xA4, 0x65, 0xA4, 0x66, 0xA4, 0x67, 0xA4, 0x68, 0xA4, 0x69, 0xA4, 0x6A, 0xA4, 0x6B, 0xA4, 0x6C, 0xA4, 0x6D, 0xA4, 0x6E, 0xA4, 0x6F, 0xA4, 0x70, 0xA4, 0x71, 0xA4,\n\t0x72, 0xA4, 0x73, 0xA4, 0x74, 0xA4, 0x74, 0xA4, 0x75, 0xA4, 0x76, 0xA4, 0x77, 0xA4, 0x78, 0xA4, 0x79, 0xA4, 0x7A, 0xA4, 0x7B, 0xA4, 0x7C, 0xA4, 0x7D, 0xA4, 0x7E, 0xA4, 0x7F, 0xA4, 0x80, 0xA4,\n\t0x81, 0xA4, 0x82, 0xA4, 0x83, 0xA4, 0x84, 0xA4, 0x84, 0xA4, 0x85, 0xA4, 0x86, 0xA4, 0x87, 0xA4, 0x88, 0xA4, 0x89, 0xA4, 0x8A, 0xA4, 0x8B, 0xA4, 0x8C, 0xA4, 0x8D, 0xA4, 0x8E, 0xA4, 0x8F, 0xA4,\n\t0x90, 0xA4, 0x91, 0xA4, 0x92, 0xA4, 0x93, 0xA4, 0x94, 0xA4, 0x94, 0xA4, 0x95, 0xA4, 0x96, 0xA4, 0x97, 0xA4, 0x98, 0xA4, 0x99, 0xA4, 0x9A, 0xA4, 0x9B, 0xA4, 0x9C, 0xA4, 0x9D, 0xA4, 0x9E, 0xA4,\n\t0x9F, 0xA4, 0xA0, 0xA4, 0xA1, 0xA4, 0xA2, 0xA4, 0xA3, 0xA4, 0x01, 0x00, 0xF0, 0xFF, 0xFF, 0xFF, 0xF2, 0xA5, 0xA4, 0xA6, 0xA4, 0xA7, 0xA4, 0xA8, 0xA4, 0xA9, 0xA4, 0xAA, 0xA4, 0xAB, 0xA4, 0xAC,\n\t0xA4, 0xAD, 0xA4, 0xAE, 0xA4, 0xAF, 0xA4, 0xB0, 0xA4, 0xB1, 0xA4, 0xB2, 0xA4, 0xB3, 0xA4, 0xB4, 0xA4, 0xB4, 0xA4, 0xB5, 0xA4, 0xB6, 0xA4, 0xB7, 0xA4, 0xB8, 0xA4, 0xB9, 0xA4, 0xBA, 0xA4, 0xBB,\n\t0xA4, 0xBC, 0xA4, 0xBD, 0xA4, 0xBE, 0xA4, 0xBF, 0xA4, 0xC0, 0xA4, 0xC1, 0xA4, 0xC2, 0xA4, 0xC3, 0xA4, 0xC3, 0xA4, 0xC4, 0xA4, 0xC5, 0xA4, 0xC6, 0xA4, 0xC7, 0xA4, 0xC8, 0xA4, 0xC9, 0xA4, 0xCA,\n\t0xA4, 0xCB, 0xA4, 0xCC, 0xA4, 0xCD, 0xA4, 0xCE, 0xA4, 0xCF, 0xA4, 0xD0, 0xA4, 0xD1, 0xA4, 0xD2, 0xA4, 0xD2, 0xA4, 0xD3, 0xA4, 0xD4, 0xA4, 0xD5, 0xA4, 0xD6, 0xA4, 0xD7, 0xA4, 0xD8, 0xA4, 0xD9,\n\t0xA4, 0xDA, 0xA4, 0xDB, 0xA4, 0xDC, 0xA4, 0xDD, 0xA4, 0xDE, 0xA4, 0xDF, 0xA4, 0xE0, 0xA4, 0xE1, 0xA4, 0xE1, 0xA4, 0xE2, 0xA4, 0xE3, 0xA4, 0xE4, 0xA4, 0xE5, 0xA4, 0xE6, 0xA4, 0xE7, 0xA4, 0xE8,\n\t0xA4, 0xE9, 0xA4, 0xEA, 0xA4, 0xEB, 0xA4, 0xEC, 0xA4, 0xED, 0xA4, 0xEE, 0xA4, 0xEF, 0xA4, 0xF0, 0xA4, 0xF0, 0xA4, 0xF1, 0xA4, 0xF2, 0xA4, 0xF3, 0xA4, 0xF4, 0xA4, 0xF5, 0xA4, 0xF6, 0xA4, 0xF7,\n\t0xA4, 0xF8, 0xA4, 0xF9, 0xA4, 0xFA, 0xA4, 0xFB, 0xA4, 0xFC, 0xA4, 0xFD, 0xA4, 0xFE, 0xA4, 0xFF, 0xA4, 0xFF, 0xA4, 0x00, 0xA5, 0x01, 0xA5, 0x02, 0xA5, 0x03, 0xA5, 0x04, 0xA5, 0x05, 0xA5, 0x06,\n\t0xA5, 0x07, 0xA5, 0x08, 0xA5, 0x09, 0xA5, 0x0A, 0xA5, 0x0B, 0xA5, 0x0C, 0xA5, 0x0D, 0xA5, 0x0D, 0xA5, 0x0E, 0xA5, 0x0F, 0xA5, 0x10, 0xA5, 0x11, 0xA5, 0x12, 0xA5, 0x13, 0xA5, 0x14, 0xA5, 0x15,\n\t0xA5, 0x16, 0xA5, 0x17, 0xA5, 0x18, 0xA5, 0x19, 0xA5, 0x1A, 0xA5, 0x1B, 0xA5, 0x1C, 0xA5, 0x1C, 0xA5, 0x1D, 0xA5, 0x1E, 0xA5, 0x1F, 0xA5, 0x20, 0xA5, 0x21, 0xA5, 0x22, 0xA5, 0x23, 0xA5, 0x24,\n\t0xA5, 0x25, 0xA5, 0x26, 0xA5, 0x27, 0xA5, 0x28, 0xA5, 0x29, 0xA5, 0x2A, 0xA5, 0x2A, 0xA5, 0x2B, 0xA5, 0x2C, 0xA5, 0x2D, 0xA5, 0x2E, 0xA5, 0x2F, 0xA5, 0x30, 0xA5, 0x31, 0xA5, 0x32, 0xA5, 0x33,\n\t0xA5, 0x34, 0xA5, 0x35, 0xA5, 0x36, 0xA5, 0x37, 0xA5, 0x38, 0xA5, 0x38, 0xA5, 0x39, 0xA5, 0x3A, 0xA5, 0x3B, 0xA5, 0x3C, 0xA5, 0x3D, 0xA5, 0x3E, 0xA5, 0x3F, 0xA5, 0x40, 0xA5, 0x41, 0xA5, 0x42,\n\t0xA5, 0x43, 0xA5, 0x44, 0xA5, 0x45, 0xA5, 0x45, 0xA5, 0x46, 0xA5, 0x47, 0xA5, 0x48, 0xA5, 0x49, 0xA5, 0x4A, 0xA5, 0x4B, 0xA5, 0x4C, 0xA5, 0x4D, 0xA5, 0x4E, 0xA5, 0x4F, 0xA5, 0x50, 0xA5, 0x51,\n\t0xA5, 0x52, 0xA5, 0x53, 0xA5, 0x53, 0xA5, 0x54, 0xA5, 0x55, 0xA5, 0x56, 0xA5, 0x57, 0xA5, 0x58, 0xA5, 0x59, 0xA5, 0x5A, 0xA5, 0x5B, 0xA5, 0x5C, 0xA5, 0x5D, 0xA5, 0x5E, 0xA5, 0x5F, 0xA5, 0x60,\n\t0xA5, 0x61, 0xA5, 0x61, 0xA5, 0x62, 0xA5, 0x63, 0xA5, 0x64, 0xA5, 0x65, 0xA5, 0x66, 0xA5, 0x67, 0xA5, 0x68, 0xA5, 0x69, 0xA5, 0x6A, 0xA5, 0x6B, 0xA5, 0x6C, 0xA5, 0x6D, 0xA5, 0x6E, 0xA5, 0x6E,\n\t0xA5, 0x6F, 0xA5, 0x70, 0xA5, 0x71, 0xA5, 0x72, 0xA5, 0x73, 0xA5, 0x74, 0xA5, 0x75, 0xA5, 0x76, 0xA5, 0x77, 0xA5, 0x78, 0xA5, 0x79, 0xA5, 0x7A, 0xA5, 0x7B, 0xA5, 0x7B, 0xA5, 0x7C, 0xA5, 0x7D,\n\t0xA5, 0x7E, 0xA5, 0x7F, 0xA5, 0x80, 0xA5, 0x81, 0xA5, 0x82, 0xA5, 0x83, 0xA5, 0x84, 0xA5, 0x85, 0xA5, 0x86, 0xA5, 0x87, 0xA5, 0x88, 0xA5, 0x88, 0xA5, 0x89, 0xA5, 0x8A, 0xA5, 0x8B, 0xA5, 0x8C,\n\t0xA5, 0x8D, 0xA5, 0x8E, 0xA5, 0x8F, 0xA5, 0x90, 0xA5, 0x91, 0xA5, 0x92, 0xA5, 0x93, 0xA5, 0x94, 0xA5, 0x95, 0xA5, 0x95, 0xA5, 0x96, 0xA5, 0x97, 0xA5, 0x98, 0xA5, 0x99, 0xA5, 0x9A, 0xA5, 0x9B,\n\t0xA5, 0x9C, 0xA5, 0x9D, 0xA5, 0x9E, 0xA5, 0x9F, 0xA5, 0xA0, 0xA5, 0xA1, 0xA5, 0xA2, 0xA5, 0xA2, 0xA5, 0xA3, 0xA5, 0xA4, 0xA5, 0xA5, 0xA5, 0xA6, 0xA5, 0xA7, 0xA5, 0xA8, 0xA5, 0xA9, 0xA5, 0xAA,\n\t0xA5, 0xAB, 0xA5, 0xAC, 0xA5, 0xAD, 0xA5, 0xAE, 0xA5, 0xAF, 0xA5, 0xAF, 0xA5, 0xB0, 0xA5, 0xB1, 0xA5, 0xB2, 0xA5, 0xB3, 0xA5, 0xB4, 0xA5, 0xB5, 0xA5, 0xB6, 0xA5, 0xB7, 0xA5, 0xB8, 0xA5, 0xB9,\n\t0xA5, 0xBA, 0xA5, 0xBB, 0xA5, 0xBB, 0xA5, 0xBC, 0xA5, 0xBD, 0xA5, 0xBE, 0xA5, 0xBF, 0xA5, 0xC0, 0xA5, 0xC1, 0xA5, 0xC2, 0xA5, 0xC3, 0xA5, 0xC4, 0xA5, 0xC5, 0xA5, 0xC6, 0xA5, 0xC7, 0xA5, 0xC7,\n\t0xA5, 0xC8, 0xA5, 0xC9, 0xA5, 0xCA, 0xA5, 0xCB, 0xA5, 0xCC, 0xA5, 0xCD, 0xA5, 0xCE, 0xA5, 0xCF, 0xA5, 0xD0, 0xA5, 0xD1, 0xA5, 0xD2, 0xA5, 0xD3, 0xA5, 0xD4, 0xA5, 0xD4, 0xA5, 0xD5, 0xA5, 0xD6,\n\t0xA5, 0xD7, 0xA5, 0xD8, 0xA5, 0xD9, 0xA5, 0xDA, 0xA5, 0xDB, 0xA5, 0xDC, 0xA5, 0xDD, 0xA5, 0xDE, 0xA5, 0xDF, 0xA5, 0xE0, 0xA5, 0xE0, 0xA5, 0xE1, 0xA5, 0xE2, 0xA5, 0xE3, 0xA5, 0xE4, 0xA5, 0xE5,\n\t0xA5, 0xE6, 0xA5, 0xE7, 0xA5, 0xE8, 0xA5, 0xE9, 0xA5, 0xEA, 0xA5, 0xEB, 0xA5, 0xEC, 0xA5, 0xEC, 0xA5, 0xED, 0xA5, 0xEE, 0xA5, 0xEF, 0xA5, 0xF0, 0xA5, 0xF1, 0xA5, 0xF2, 0xA5, 0xF3, 0xA5, 0xF4,\n\t0xA5, 0xF5, 0xA5, 0xF6, 0xA5, 0xF7, 0xA5, 0xF8, 0xA5, 0xF8, 0xA5, 0xF9, 0xA5, 0xFA, 0xA5, 0xFB, 0xA5, 0xFC, 0xA5, 0xFD, 0xA5, 0xFE, 0xA5, 0xFF, 0xA5, 0x00, 0xA6, 0x01, 0xA6, 0x02, 0xA6, 0x03,\n\t0xA6, 0x04, 0xA6, 0x04, 0xA6, 0x05, 0xA6, 0x06, 0xA6, 0x07, 0xA6, 0x08, 0xA6, 0x09, 0xA6, 0x0A, 0xA6, 0x0B, 0xA6, 0x0C, 0xA6, 0x0D, 0xA6, 0x0E, 0xA6, 0x0F, 0xA6, 0x10, 0xA6, 0x10, 0xA6, 0x11,\n\t0xA6, 0x12, 0xA6, 0x13, 0xA6, 0x14, 0xA6, 0x15, 0xA6, 0x16, 0xA6, 0x17, 0xA6, 0x18, 0xA6, 0x19, 0xA6, 0x1A, 0xA6, 0x1B, 0xA6, 0x1B, 0xA6, 0x1C, 0xA6, 0x1D, 0xA6, 0x1E, 0xA6, 0x1F, 0xA6, 0x20,\n\t0xA6, 0x21, 0xA6, 0x22, 0xA6, 0x23, 0xA6, 0x24, 0xA6, 0x25, 0xA6, 0x26, 0xA6, 0x27, 0xA6, 0x27, 0xA6, 0x28, 0xA6, 0x29, 0xA6, 0x2A, 0xA6, 0x2B, 0xA6, 0x2C, 0xA6, 0x2D, 0xA6, 0x2E, 0xA6, 0x2F,\n\t0xA6, 0x30, 0xA6, 0x31, 0xA6, 0x32, 0xA6, 0x32, 0xA6, 0x33, 0xA6, 0x34, 0xA6, 0x35, 0xA6, 0x36, 0xA6, 0x37, 0xA6, 0x38, 0xA6, 0x39, 0xA6, 0x3A, 0xA6, 0x3B, 0xA6, 0x3C, 0xA6, 0x3D, 0xA6, 0x3E,\n\t0xA6, 0x3E, 0xA6, 0x3F, 0xA6, 0x40, 0xA6, 0x41, 0xA6, 0x42, 0xA6, 0x43, 0xA6, 0x44, 0xA6, 0x45, 0xA6, 0x46, 0xA6, 0x47, 0xA6, 0x48, 0xA6, 0x49, 0xA6, 0x49, 0xA6, 0x4A, 0xA6, 0x4B, 0xA6, 0x4C,\n\t0xA6, 0x4D, 0xA6, 0x4E, 0xA6, 0x4F, 0xA6, 0x50, 0xA6, 0x51, 0xA6, 0x52, 0xA6, 0x53, 0xA6, 0x54, 0xA6, 0x54, 0xA6, 0x55, 0xA6, 0x56, 0xA6, 0x57, 0xA6, 0x58, 0xA6, 0x59, 0xA6, 0x5A, 0xA6, 0x5B,\n\t0xA6, 0x5C, 0xA6, 0x5D, 0xA6, 0x5E, 0xA6, 0x5F, 0xA6, 0x5F, 0xA6, 0x60, 0xA6, 0x61, 0xA6, 0x62, 0xA6, 0x63, 0xA6, 0x64, 0xA6, 0x65, 0xA6, 0x66, 0xA6, 0x67, 0xA6, 0x68, 0xA6, 0x69, 0xA6, 0x6A,\n\t0xA6, 0x6A, 0xA6, 0x6B, 0xA6, 0x6C, 0xA6, 0x6D, 0xA6, 0x6E, 0xA6, 0x6F, 0xA6, 0x70, 0xA6, 0x71, 0xA6, 0x72, 0xA6, 0x73, 0xA6, 0x74, 0xA6, 0x75, 0xA6, 0x75, 0xA6, 0x76, 0xA6, 0x77, 0xA6, 0x78,\n\t0xA6, 0x79, 0xA6, 0x7A, 0xA6, 0x7B, 0xA6, 0x7C, 0xA6, 0x7D, 0xA6, 0x7E, 0xA6, 0x7F, 0xA6, 0xFB, 0x52, 0xF0, 0xFF, 0xFF, 0x29, 0x81, 0xA6, 0x82, 0xA6, 0x83, 0xA6, 0x84, 0xA6, 0x85, 0xA6, 0x86,\n\t0xA6, 0x87, 0xA6, 0x88, 0xA6, 0x89, 0xA6, 0x8A, 0xA6, 0x8B, 0xA6, 0x8B, 0xA6, 0x8C, 0xA6, 0x8D, 0xA6, 0x8E, 0xA6, 0x8F, 0xA6, 0x90, 0xA6, 0x91, 0xA6, 0x92, 0xA6, 0x93, 0xA6, 0x94, 0xA6, 0x95,\n\t0xA6, 0x96, 0xA6, 0x96, 0xA6, 0x97, 0xA6, 0x98, 0xA6, 0x99, 0xA6, 0x9A, 0xA6, 0x9B, 0xA6, 0x9C, 0xA6, 0x9D, 0xA6, 0x9E, 0xA6, 0x9F, 0xA6, 0xA0, 0xA6, 0xA0, 0xA6, 0xA1, 0xA6, 0xA2, 0xA6, 0xA3,\n\t0xA6, 0xA4, 0xA6, 0xA5, 0xA6, 0xA6, 0xA6, 0xA7, 0xA6, 0xA8, 0xA6, 0xA9, 0xA6, 0xAA, 0xA6, 0xAB, 0xA6, 0xAB, 0xA6, 0xAC, 0xA6, 0xAD, 0xA6, 0xAE, 0xA6, 0xAF, 0xA6, 0xB0, 0xA6, 0xB1, 0xA6, 0xB2,\n\t0xA6, 0xB3, 0xA6, 0xB4, 0xA6, 0xB5, 0xA6, 0xB5, 0xA6, 0xB6, 0xA6, 0xB7, 0xA6, 0xB8, 0xA6, 0xB9, 0xA6, 0xBA, 0xA6, 0xBB, 0xA6, 0xBC, 0xA6, 0xBD, 0xA6, 0xBE, 0xA6, 0xBF, 0xA6, 0xC0, 0xA6, 0xC0,\n\t0xA6, 0xC1, 0xA6, 0xC2, 0xA6, 0xC3, 0xA6, 0xC4, 0xA6, 0xC5, 0xA6, 0xC6, 0xA6, 0xC7, 0xA6, 0xC8, 0xA6, 0xC9, 0xA6, 0xCA, 0xA6, 0xCA, 0xA6, 0xCB, 0xA6, 0xCC, 0xA6, 0xCD, 0xA6, 0xCE, 0xA6, 0xCF,\n\t0xA6, 0xD0, 0xA6, 0xD1, 0xA6, 0xD2, 0xA6, 0xD3, 0xA6, 0xD4, 0xA6, 0xD4, 0xA6, 0xD5, 0xA6, 0xD6, 0xA6, 0xD7, 0xA6, 0xD8, 0xA6, 0xD9, 0xA6, 0xDA, 0xA6, 0xDB, 0xA6, 0xDC, 0xA6, 0xDD, 0xA6, 0xDE,\n\t0xA6, 0xDF, 0xA6, 0xDF, 0xA6, 0xE0, 0xA6, 0xE1, 0xA6, 0xE2, 0xA6, 0xE3, 0xA6, 0xE4, 0xA6, 0xE5, 0xA6, 0xE6, 0xA6, 0xE7, 0xA6, 0xE8, 0xA6, 0xE9, 0xA6, 0xE9, 0xA6, 0xEA, 0xA6, 0xEB, 0xA6, 0xEC,\n\t0xA6, 0xED, 0xA6, 0xEE, 0xA6, 0xEF, 0xA6, 0xF0, 0xA6, 0xF1, 0xA6, 0xF2, 0xA6, 0xF3, 0xA6, 0xF3, 0xA6, 0xF4, 0xA6, 0xF5, 0xA6, 0xF6, 0xA6, 0xF7, 0xA6, 0xF8, 0xA6, 0xF9, 0xA6, 0xFA, 0xA6, 0xFB,\n\t0xA6, 0xFC, 0xA6, 0xFD, 0xA6, 0xFD, 0xA6, 0xFE, 0xA6, 0xFF, 0xA6, 0x00, 0xA7, 0x01, 0xA7, 0x02, 0xA7, 0x03, 0xA7, 0x04, 0xA7, 0x05, 0xA7, 0x06, 0xA7, 0x07, 0xA7, 0x07, 0xA7, 0x08, 0xA7, 0x09,\n\t0xA7, 0x0A, 0xA7, 0x0B, 0xA7, 0x0C, 0xA7, 0x0D, 0xA7, 0x0E, 0xA7, 0x0F, 0xA7, 0x10, 0xA7, 0x11, 0xA7, 0x11, 0xA7, 0x12, 0xA7, 0x13, 0xA7, 0x14, 0xA7, 0x15, 0xA7, 0x16, 0xA7, 0x17, 0xA7, 0x18,\n\t0xA7, 0x19, 0xA7, 0x1A, 0xA7, 0x1A, 0xA7, 0x1B, 0xA7, 0x1C, 0xA7, 0x1D, 0xA7, 0x1E, 0xA7, 0x1F, 0xA7, 0x20, 0xA7, 0x21, 0xA7, 0x22, 0xA7, 0x23, 0xA7, 0x24, 0xA7, 0x24, 0xA7, 0x25, 0xA7, 0x26,\n\t0xA7, 0x27, 0xA7, 0x28, 0xA7, 0x29, 0xA7, 0x2A, 0xA7, 0x2B, 0xA7, 0x2C, 0xA7, 0x2D, 0xA7, 0x2E, 0xA7, 0x2E, 0xA7, 0x2F, 0xA7, 0x30, 0xA7, 0x31, 0xA7, 0x32, 0xA7, 0x33, 0xA7, 0x34, 0xA7, 0x35,\n\t0xA7, 0x36, 0xA7, 0x37, 0xA7, 0x38, 0xA7, 0x38, 0xA7, 0x39, 0xA7, 0x3A, 0xA7, 0x3B, 0xA7, 0x3C, 0xA7, 0x3D, 0xA7, 0x3E, 0xA7, 0x3F, 0xA7, 0x40, 0xA7, 0x41, 0xA7, 0x41, 0xA7, 0x42, 0xA7, 0x43,\n\t0xA7, 0x44, 0xA7, 0x45, 0xA7, 0x46, 0xA7, 0x47, 0xA7, 0x48, 0xA7, 0x49, 0xA7, 0x4A, 0xA7, 0x4B, 0xA7, 0x4B, 0xA7, 0x4C, 0xA7, 0x4D, 0xA7, 0x4E, 0xA7, 0x4F, 0xA7, 0x50, 0xA7, 0x51, 0xA7, 0x52,\n\t0xA7, 0x53, 0xA7, 0x54, 0xA7, 0x54, 0xA7, 0x55, 0xA7, 0x56, 0xA7, 0x57, 0xA7, 0x58, 0xA7, 0x59, 0xA7, 0x5A, 0xA7, 0x5B, 0xA7, 0x5C, 0xA7, 0x5D, 0xA7, 0x5E, 0xA7, 0x5E, 0xA7, 0x5F, 0xA7, 0x60,\n\t0xA7, 0x61, 0xA7, 0x62, 0xA7, 0x63, 0xA7, 0x64, 0xA7, 0x65, 0xA7, 0x66, 0xA7, 0x67, 0xA7, 0x67, 0xA7, 0x68, 0xA7, 0x69, 0xA7, 0x6A, 0xA7, 0x6B, 0xA7, 0x6C, 0xA7, 0x6D, 0xA7, 0x6E, 0xA7, 0x6F,\n\t0xA7, 0x70, 0xA7, 0x70, 0xA7, 0x71, 0xA7, 0x72, 0xA7, 0x73, 0xA7, 0x74, 0xA7, 0x75, 0xA7, 0x76, 0xA7, 0x77, 0xA7, 0x78, 0xA7, 0x79, 0xA7, 0x79, 0xA7, 0x7A, 0xA7, 0x7B, 0xA7, 0x7C, 0xA7, 0x7D,\n\t0xA7, 0x7E, 0xA7, 0x7F, 0xA7, 0x80, 0xA7, 0x81, 0xA7, 0x82, 0xA7, 0x31, 0x49, 0xF0, 0xFF, 0xFF, 0x0F, 0x84, 0xA7, 0x85, 0xA7, 0x86, 0xA7, 0x87, 0xA7, 0x88, 0xA7, 0x89, 0xA7, 0x8A, 0xA7, 0x8B,\n\t0xA7, 0x8C, 0xA7, 0x8C, 0xA7, 0x8D, 0xA7, 0x8E, 0xA7, 0x8F, 0xA7, 0x90, 0xA7, 0x91, 0xA7, 0x92, 0xA7, 0x93, 0xA7, 0x94, 0xA7, 0x95, 0xA7, 0x95, 0xA7, 0x96, 0xA7, 0x97, 0xA7, 0x98, 0xA7, 0x99,\n\t0xA7, 0x9A, 0xA7, 0x9C, 0xA7, 0x9E, 0xA7, 0x9F, 0xA7, 0xA1, 0xA7, 0xA3, 0xA7, 0xA5, 0xA7, 0xA7, 0xA7, 0xA8, 0xA7, 0xAA, 0xA7, 0xAC, 0xA7, 0xAE, 0xA7, 0xB0, 0xA7, 0xB1, 0xA7, 0xB3, 0xA7, 0xB5,\n\t0xA7, 0xB7, 0xA7, 0xB9, 0xA7, 0xBA, 0xA7, 0xBC, 0xA7, 0xBE, 0xA7, 0xC0, 0xA7, 0xC2, 0xA7, 0xC3, 0xA7, 0xC5, 0xA7, 0xC7, 0xA7, 0xC9, 0xA7, 0xCA, 0xA7, 0xCC, 0xA7, 0xCE, 0xA7, 0xD0, 0xA7, 0xD2,\n\t0xA7, 0xD3, 0xA7, 0xD5, 0xA7, 0xD7, 0xA7, 0xD9, 0xA7, 0xDB, 0xA7, 0xDC, 0xA7, 0xDE, 0xA7, 0xE0, 0xA7, 0xE2, 0xA7, 0xE4, 0xA7, 0xE5, 0xA7, 0xE7, 0xA7, 0xE9, 0xA7, 0xEB, 0xA7, 0xED, 0xA7, 0xEE,\n\t0xA7, 0xF0, 0xA7, 0xF2, 0xA7, 0xF4, 0xA7, 0xF6, 0xA7, 0xF7, 0xA7, 0xF9, 0xA7, 0xFB, 0xA7, 0xFD, 0xA7, 0xFE, 0xA7, 0x00, 0xA8, 0x01, 0xA8, 0x02, 0xA8, 0x03, 0xA8, 0x04, 0xA8, 0x05, 0xA8, 0x06,\n\t0xA8, 0x06, 0xA8, 0x07, 0xA8, 0x08, 0xA8, 0x09, 0xA8, 0x0A, 0xA8, 0x0B, 0xA8, 0x0C, 0xA8, 0x0D, 0xA8, 0x0E, 0xA8, 0x0E, 0xA8, 0x0F, 0xA8, 0x10, 0xA8, 0x11, 0xA8, 0x12, 0xA8, 0x13, 0xA8, 0x14,\n\t0xA8, 0x15, 0xA8, 0x16, 0xA8, 0x16, 0xA8, 0x17, 0xA8, 0x18, 0xA8, 0x19, 0xA8, 0x1A, 0xA8, 0x1B, 0xA8, 0x1C, 0xA8, 0x1D, 0xA8, 0x1E, 0xA8, 0x1F, 0xA8, 0x1F, 0xA8, 0x20, 0xA8, 0x21, 0xA8, 0x22,\n\t0xA8, 0x23, 0xA8, 0x24, 0xA8, 0x25, 0xA8, 0x26, 0xA8, 0x27, 0xA8, 0x27, 0xA8, 0x28, 0xA8, 0x29, 0xA8, 0x2A, 0xA8, 0x2B, 0xA8, 0x2C, 0xA8, 0x2D, 0xA8, 0x2E, 0xA8, 0x2F, 0xA8, 0x2F, 0xA8, 0x30,\n\t0xA8, 0x31, 0xA8, 0x32, 0xA8, 0x33, 0xA8, 0x34, 0xA8, 0x35, 0xA8, 0x36, 0xA8, 0x37, 0xA8, 0x37, 0xA8, 0x38, 0xA8, 0x39, 0xA8, 0x3A, 0xA8, 0x3B, 0xA8, 0x3C, 0xA8, 0x3D, 0xA8, 0x3E, 0xA8, 0x3F,\n\t0xA8, 0x3F, 0xA8, 0x40, 0xA8, 0x41, 0xA8, 0x42, 0xA8, 0x43, 0xA8, 0x44, 0xA8, 0x45, 0xA8, 0x46, 0xA8, 0x47, 0xA8, 0x47, 0xA8, 0x48, 0xA8, 0x49, 0xA8, 0x4A, 0xA8, 0x4B, 0xA8, 0x4C, 0xA8, 0x4D,\n\t0xA8, 0x4E, 0xA8, 0x4F, 0xA8, 0x4F, 0xA8, 0x50, 0xA8, 0x51, 0xA8, 0x52, 0xA8, 0x53, 0xA8, 0x54, 0xA8, 0x55, 0xA8, 0x56, 0xA8, 0x56, 0xA8, 0x57, 0xA8, 0x58, 0xA8, 0x59, 0xA8, 0x5A, 0xA8, 0x5B,\n\t0xA8, 0x5C, 0xA8, 0x5D, 0xA8, 0x5E, 0xA8, 0x5E, 0xA8, 0x5F, 0xA8, 0x60, 0xA8, 0x61, 0xA8, 0x62, 0xA8, 0x63, 0xA8, 0x64, 0xA8, 0x65, 0xA8, 0x66, 0xA8, 0x66, 0xA8, 0x67, 0xA8, 0x68, 0xA8, 0x69,\n\t0xA8, 0x6A, 0xA8, 0x6B, 0xA8, 0x6C, 0xA8, 0x6D, 0xA8, 0x6D, 0xA8, 0x6E, 0xA8, 0x6F, 0xA8, 0x70, 0xA8, 0x71, 0xA8, 0x72, 0xA8, 0x73, 0xA8, 0x74, 0xA8, 0x75, 0xA8, 0x75, 0xA8, 0x76, 0xA8, 0x77,\n\t0xA8, 0x78, 0xA8, 0x79, 0xA8, 0x7A, 0xA8, 0x7B, 0xA8, 0x7C, 0xA8, 0x7C, 0xA8, 0x7D, 0xA8, 0x7E, 0xA8, 0x7F, 0xA8, 0x80, 0xA8, 0x81, 0xA8, 0x82, 0xA8, 0x83, 0xA8, 0x84, 0xA8, 0x84, 0xA8, 0x85,\n\t0xA8, 0x86, 0xA8, 0x87, 0xA8, 0x88, 0xA8, 0x89, 0xA8, 0x8A, 0xA8, 0x8B, 0xA8, 0x8B, 0xA8, 0x8C, 0xA8, 0x8D, 0xA8, 0x8E, 0xA8, 0x8F, 0xA8, 0x90, 0xA8, 0x91, 0xA8, 0x92, 0xA8, 0x92, 0xA8, 0x93,\n\t0xA8, 0x94, 0xA8, 0x95, 0xA8, 0x96, 0xA8, 0x97, 0xA8, 0x98, 0xA8, 0x99, 0xA8, 0x99, 0xA8, 0x9A, 0xA8, 0x9B, 0xA8, 0x9C, 0xA8, 0x9D, 0xA8, 0x9E, 0xA8, 0x9F, 0xA8, 0xA0, 0xA8, 0xA1, 0xA8, 0xA1,\n\t0xA8, 0xA2, 0xA8, 0xA3, 0xA8, 0xA4, 0xA8, 0xA5, 0xA8, 0xA6, 0xA8, 0xA7, 0xA8, 0x01, 0x00, 0xF0, 0xFF, 0xE4, 0xA9, 0xA8, 0xAA, 0xA8, 0xAB, 0xA8, 0xAC, 0xA8, 0xAD, 0xA8, 0xAE, 0xA8, 0xAF, 0xA8,\n\t0xAF, 0xA8, 0xB0, 0xA8, 0xB1, 0xA8, 0xB2, 0xA8, 0xB3, 0xA8, 0xB4, 0xA8, 0xB5, 0xA8, 0xB6, 0xA8, 0xB6, 0xA8, 0xB7, 0xA8, 0xB8, 0xA8, 0xB9, 0xA8, 0xBA, 0xA8, 0xBB, 0xA8, 0xBC, 0xA8, 0xBD, 0xA8,\n\t0xBD, 0xA8, 0xBE, 0xA8, 0xBF, 0xA8, 0xC0, 0xA8, 0xC1, 0xA8, 0xC2, 0xA8, 0xC3, 0xA8, 0xC4, 0xA8, 0xC4, 0xA8, 0xC5, 0xA8, 0xC6, 0xA8, 0xC7, 0xA8, 0xC8, 0xA8, 0xC9, 0xA8, 0xCA, 0xA8, 0xCB, 0xA8,\n\t0xCB, 0xA8, 0xCC, 0xA8, 0xCD, 0xA8, 0xCE, 0xA8, 0xCF, 0xA8, 0xD0, 0xA8, 0xD1, 0xA8, 0xD1, 0xA8, 0xD2, 0xA8, 0xD3, 0xA8, 0xD4, 0xA8, 0xD5, 0xA8, 0xD6, 0xA8, 0xD7, 0xA8, 0xD8, 0xA8, 0xD8, 0xA8,\n\t0xD9, 0xA8, 0xDA, 0xA8, 0xDB, 0xA8, 0xDC, 0xA8, 0xDD, 0xA8, 0xDE, 0xA8, 0xDF, 0xA8, 0xDF, 0xA8, 0xE0, 0xA8, 0xE1, 0xA8, 0xE2, 0xA8, 0xE3, 0xA8, 0xE4, 0xA8, 0xE5, 0xA8, 0xE6, 0xA8, 0xE6, 0xA8,\n\t0xE7, 0xA8, 0xE8, 0xA8, 0xE9, 0xA8, 0xEA, 0xA8, 0xEB, 0xA8, 0xEC, 0xA8, 0xEC, 0xA8, 0xED, 0xA8, 0xEE, 0xA8, 0xEF, 0xA8, 0xF0, 0xA8, 0xF1, 0xA8, 0xF2, 0xA8, 0xF3, 0xA8, 0xF3, 0xA8, 0xF4, 0xA8,\n\t0xF5, 0xA8, 0xF6, 0xA8, 0xF7, 0xA8, 0xF8, 0xA8, 0xF9, 0xA8, 0xFA, 0xA8, 0xFA, 0xA8, 0xFB, 0xA8, 0xFC, 0xA8, 0xFD, 0xA8, 0xFE, 0xA8, 0xFF, 0xA8, 0x00, 0xA9, 0x00, 0xA9, 0x01, 0xA9, 0x02, 0xA9,\n\t0x03, 0xA9, 0x04, 0xA9, 0x05, 0xA9, 0x06, 0xA9, 0x07, 0xA9, 0x07, 0xA9, 0x08, 0xA9, 0x09, 0xA9, 0x0A, 0xA9, 0x0B, 0xA9, 0x0C, 0xA9, 0x0D, 0xA9, 0x0D, 0xA9, 0x0E, 0xA9, 0x0F, 0xA9, 0x10, 0xA9,\n\t0x11, 0xA9, 0x12, 0xA9, 0x13, 0xA9, 0x13, 0xA9, 0x14, 0xA9, 0x15, 0xA9, 0x16, 0xA9, 0x17, 0xA9, 0x18, 0xA9, 0x19, 0xA9, 0x1A, 0xA9, 0x1A, 0xA9, 0x1B, 0xA9, 0x1C, 0xA9, 0x1D, 0xA9, 0x1E, 0xA9,\n\t0x1F, 0xA9, 0x20, 0xA9, 0x20, 0xA9, 0x21, 0xA9, 0x22, 0xA9, 0x23, 0xA9, 0x24, 0xA9, 0x25, 0xA9, 0x26, 0xA9, 0x26, 0xA9, 0x27, 0xA9, 0x28, 0xA9, 0x29, 0xA9, 0x2A, 0xA9, 0x2B, 0xA9, 0x2C, 0xA9,\n\t0x2D, 0xA9, 0x2D, 0xA9, 0x2E, 0xA9, 0x2F, 0xA9, 0x30, 0xA9, 0x31, 0xA9, 0x32, 0xA9, 0x33, 0xA9, 0x33, 0xA9, 0x34, 0xA9, 0x35, 0xA9, 0x36, 0xA9, 0x37, 0xA9, 0x38, 0xA9, 0x39, 0xA9, 0x39, 0xA9,\n\t0x3A, 0xA9, 0x3B, 0xA9, 0x3C, 0xA9, 0x3D, 0xA9, 0x3E, 0xA9, 0x3F, 0xA9, 0x3F, 0xA9, 0x40, 0xA9, 0x41, 0xA9, 0x42, 0xA9, 0x43, 0xA9, 0x44, 0xA9, 0x45, 0xA9, 0x45, 0xA9, 0x46, 0xA9, 0x47, 0xA9,\n\t0x48, 0xA9, 0x49, 0xA9, 0x4A, 0xA9, 0x4B, 0xA9, 0x4B, 0xA9, 0x4C, 0xA9, 0x4D, 0xA9, 0x4E, 0xA9, 0x4F, 0xA9, 0x50, 0xA9, 0x51, 0xA9, 0x51, 0xA9, 0x52, 0xA9, 0x53, 0xA9, 0x54, 0xA9, 0x55, 0xA9,\n\t0x56, 0xA9, 0x57, 0xA9, 0x57, 0xA9, 0x58, 0xA9, 0x59, 0xA9, 0x5A, 0xA9, 0x5B, 0xA9, 0x5C, 0xA9, 0x5D, 0xA9, 0x5D, 0xA9, 0x5E, 0xA9, 0x5F, 0xA9, 0x60, 0xA9, 0x61, 0xA9, 0x62, 0xA9, 0x63, 0xA9,\n\t0x63, 0xA9, 0x64, 0xA9, 0x65, 0xA9, 0x66, 0xA9, 0x67, 0xA9, 0x68, 0xA9, 0x69, 0xA9, 0x69, 0xA9, 0x6A, 0xA9, 0x6B, 0xA9, 0x6C, 0xA9, 0x6D, 0xA9, 0x6E, 0xA9, 0x6F, 0xA9, 0x6F, 0xA9, 0x70, 0xA9,\n\t0x71, 0xA9, 0x72, 0xA9, 0x73, 0xA9, 0x74, 0xA9, 0x75, 0xA9, 0x75, 0xA9, 0x76, 0xA9, 0x77, 0xA9, 0x78, 0xA9, 0x79, 0xA9, 0x7A, 0xA9, 0x7B, 0xA9, 0x7B, 0xA9, 0x7C, 0xA9, 0x7D, 0xA9, 0x7E, 0xA9,\n\t0x7F, 0xA9, 0x80, 0xA9, 0x3F, 0x55, 0xF0, 0xFF, 0xFF, 0x55, 0x82, 0xA9, 0x83, 0xA9, 0x84, 0xA9, 0x85, 0xA9, 0x86, 0xA9, 0x86, 0xA9, 0x87, 0xA9, 0x88, 0xA9, 0x89, 0xA9, 0x8A, 0xA9, 0x8B, 0xA9,\n\t0x8C, 0xA9, 0x8C, 0xA9, 0x8D, 0xA9, 0x8E, 0xA9, 0x8F, 0xA9, 0x90, 0xA9, 0x91, 0xA9, 0x92, 0xA9, 0x92, 0xA9, 0x93, 0xA9, 0x94, 0xA9, 0x95, 0xA9, 0x96, 0xA9, 0x97, 0xA9, 0x97, 0xA9, 0x98, 0xA9,\n\t0x99, 0xA9, 0x9A, 0xA9, 0x9B, 0xA9, 0x9C, 0xA9, 0x9D, 0xA9, 0x9D, 0xA9, 0x9E, 0xA9, 0x9F, 0xA9, 0xA0, 0xA9, 0xA1, 0xA9, 0xA2, 0xA9, 0xA3, 0xA9, 0xA3, 0xA9, 0xA4, 0xA9, 0xA5, 0xA9, 0xA6, 0xA9,\n\t0xA7, 0xA9, 0xA8, 0xA9, 0xA8, 0xA9, 0xA9, 0xA9, 0xAA, 0xA9, 0xAB, 0xA9, 0xAC, 0xA9, 0xAD, 0xA9, 0xAE, 0xA9, 0xAE, 0xA9, 0xAF, 0xA9, 0xB0, 0xA9, 0xB1, 0xA9, 0xB2, 0xA9, 0xB3, 0xA9, 0xB3, 0xA9,\n\t0xB4, 0xA9, 0xB5, 0xA9, 0xB6, 0xA9, 0xB7, 0xA9, 0xB8, 0xA9, 0xB9, 0xA9, 0xB9, 0xA9, 0xBA, 0xA9, 0xBB, 0xA9, 0xBC, 0xA9, 0xBD, 0xA9, 0xBE, 0xA9, 0xBE, 0xA9, 0xBF, 0xA9, 0xC0, 0xA9, 0xC1, 0xA9,\n\t0xC2, 0xA9, 0xC3, 0xA9, 0xC4, 0xA9, 0xC4, 0xA9, 0xC5, 0xA9, 0xC6, 0xA9, 0xC7, 0xA9, 0xC8, 0xA9, 0xC9, 0xA9, 0xC9, 0xA9, 0xCA, 0xA9, 0xCB, 0xA9, 0xCC, 0xA9, 0xCD, 0xA9, 0xCE, 0xA9, 0xCF, 0xA9,\n\t0xCF, 0xA9, 0xD0, 0xA9, 0xD1, 0xA9, 0xD2, 0xA9, 0xD3, 0xA9, 0xD4, 0xA9, 0xD4, 0xA9, 0xD5, 0xA9, 0xD6, 0xA9, 0xD7, 0xA9, 0xD8, 0xA9, 0xD9, 0xA9, 0xD9, 0xA9, 0xDA, 0xA9, 0xDB, 0xA9, 0xDC, 0xA9,\n\t0xDD, 0xA9, 0xDE, 0xA9, 0xDF, 0xA9, 0xDF, 0xA9, 0xE0, 0xA9, 0xE1, 0xA9, 0xE2, 0xA9, 0xE3, 0xA9, 0xE4, 0xA9, 0xE4, 0xA9, 0xE5, 0xA9, 0xE6, 0xA9, 0xE7, 0xA9, 0xE8, 0xA9, 0xE9, 0xA9, 0xE9, 0xA9,\n\t0xEA, 0xA9, 0xEB, 0xA9, 0xEC, 0xA9, 0xED, 0xA9, 0xEE, 0xA9, 0xEE, 0xA9, 0xEF, 0xA9, 0xF0, 0xA9, 0xF1, 0xA9, 0xF2, 0xA9, 0xF3, 0xA9, 0xF4, 0xA9, 0xF4, 0xA9, 0xF5, 0xA9, 0xF6, 0xA9, 0xF7, 0xA9,\n\t0xF8, 0xA9, 0xF9, 0xA9, 0xF9, 0xA9, 0xFA, 0xA9, 0xFB, 0xA9, 0xFC, 0xA9, 0xFD, 0xA9, 0xFE, 0xA9, 0xFE, 0xA9, 0xFF, 0xA9, 0x00, 0xAA, 0x01, 0xAA, 0x02, 0xAA, 0x03, 0xAA, 0x03, 0xAA, 0x04, 0xAA,\n\t0x05, 0xAA, 0x06, 0xAA, 0x07, 0xAA, 0x08, 0xAA, 0x08, 0xAA, 0x09, 0xAA, 0x0A, 0xAA, 0x0B, 0xAA, 0x0C, 0xAA, 0x0D, 0xAA, 0x0D, 0xAA, 0x0E, 0xAA, 0x0F, 0xAA, 0x10, 0xAA, 0x11, 0xAA, 0x12, 0xAA,\n\t0x13, 0xAA, 0x13, 0xAA, 0x14, 0xAA, 0x15, 0xAA, 0x16, 0xAA, 0x17, 0xAA, 0x18, 0xAA, 0x18, 0xAA, 0x19, 0xAA, 0x1A, 0xAA, 0x1B, 0xAA, 0x1C, 0xAA, 0x1D, 0xAA, 0x1D, 0xAA, 0x1E, 0xAA, 0x1F, 0xAA,\n\t0x20, 0xAA, 0x21, 0xAA, 0x22, 0xAA, 0x22, 0xAA, 0x23, 0xAA, 0x24, 0xAA, 0x25, 0xAA, 0x26, 0xAA, 0x27, 0xAA, 0x27, 0xAA, 0x28, 0xAA, 0x29, 0xAA, 0x2A, 0xAA, 0x2B, 0xAA, 0x2C, 0xAA, 0x2C, 0xAA,\n\t0x2D, 0xAA, 0x2E, 0xAA, 0x2F, 0xAA, 0x30, 0xAA, 0x31, 0xAA, 0x31, 0xAA, 0x32, 0xAA, 0x33, 0xAA, 0x34, 0xAA, 0x35, 0xAA, 0x36, 0xAA, 0x36, 0xAA, 0x37, 0xAA, 0x38, 0xAA, 0x39, 0xAA, 0x3A, 0xAA,\n\t0x3A, 0xAA, 0x3B, 0xAA, 0x3C, 0xAA, 0x3D, 0xAA, 0x3E, 0xAA, 0x3F, 0xAA, 0x3F, 0xAA, 0x40, 0xAA, 0x41, 0xAA, 0x42, 0xAA, 0x43, 0xAA, 0x44, 0xAA, 0x44, 0xAA, 0x45, 0xAA, 0x46, 0xAA, 0x47, 0xAA,\n\t0x48, 0xAA, 0x49, 0xAA, 0x49, 0xAA, 0x4A, 0xAA, 0x4B, 0xAA, 0x4C, 0xAA, 0x4D, 0xAA, 0x4E, 0xAA, 0x4E, 0xAA, 0x4F, 0xAA, 0x50, 0xAA, 0x51, 0xAA, 0x52, 0xAA, 0x53, 0xAA, 0x53, 0xAA, 0x54, 0xAA,\n\t0x55, 0xAA, 0x56, 0xAA, 0x57, 0xAA, 0x58, 0xAA, 0x58, 0xAA, 0x59, 0xAA, 0x5A, 0xAA, 0x5B, 0xAA, 0x5C, 0xAA, 0x5C, 0xAA, 0x5D, 0xAA, 0x5E, 0xAA, 0x5F, 0xAA, 0x60, 0xAA, 0x61, 0xAA, 0x61, 0xAA,\n\t0x62, 0xAA, 0x63, 0xAA, 0x64, 0xAA, 0x65, 0xAA, 0x66, 0xAA, 0x66, 0xAA, 0x67, 0xAA, 0x68, 0xAA, 0x69, 0xAA, 0x6A, 0xAA, 0x6B, 0xAA, 0x6B, 0xAA, 0x6C, 0xAA, 0x6D, 0xAA, 0x6E, 0xAA, 0x6F, 0xAA,\n\t0x6F, 0xAA, 0x70, 0xAA, 0x71, 0xAA, 0x72, 0xAA, 0x73, 0xAA, 0x74, 0xAA, 0x74, 0xAA, 0x75, 0xAA, 0x76, 0xAA, 0x77, 0xAA, 0x78, 0xAA, 0x79, 0xAA, 0x79, 0xAA, 0x7A, 0xAA, 0x7B, 0xAA, 0x7C, 0xAA,\n\t0x7D, 0xAA, 0x7D, 0xAA, 0x7E, 0xAA, 0x7F, 0xAA, 0x80, 0xAA, 0x81, 0xAA, 0xA1, 0x53, 0xF0, 0xFF, 0xFF, 0xFF, 0xFC, 0x83, 0xAA, 0x84, 0xAA, 0x85, 0xAA, 0x86, 0xAA, 0x87, 0xAA, 0x87, 0xAA, 0x88,\n\t0xAA, 0x89, 0xAA, 0x8A, 0xAA, 0x8B, 0xAA, 0x8B, 0xAA, 0x8C, 0xAA, 0x8D, 0xAA, 0x8E, 0xAA, 0x8F, 0xAA, 0x90, 0xAA, 0x90, 0xAA, 0x91, 0xAA, 0x92, 0xAA, 0x93, 0xAA, 0x94, 0xAA, 0x95, 0xAA, 0x95,\n\t0xAA, 0x96, 0xAA, 0x97, 0xAA, 0x98, 0xAA, 0x99, 0xAA, 0x99, 0xAA, 0x9A, 0xAA, 0x9B, 0xAA, 0x9C, 0xAA, 0x9D, 0xAA, 0x9E, 0xAA, 0x9E, 0xAA, 0x9F, 0xAA, 0xA0, 0xAA, 0xA1, 0xAA, 0xA2, 0xAA, 0xA2,\n\t0xAA, 0xA3, 0xAA, 0xA4, 0xAA, 0xA5, 0xAA, 0xA6, 0xAA, 0xA7, 0xAA, 0xA7, 0xAA, 0xA8, 0xAA, 0xA9, 0xAA, 0xAA, 0xAA, 0xAB, 0xAA, 0xAB, 0xAA, 0xAC, 0xAA, 0xAD, 0xAA, 0xAE, 0xAA, 0xAF, 0xAA, 0xB0,\n\t0xAA, 0xB0, 0xAA, 0xB1, 0xAA, 0xB2, 0xAA, 0xB3, 0xAA, 0xB4, 0xAA, 0xB4, 0xAA, 0xB5, 0xAA, 0xB6, 0xAA, 0xB7, 0xAA, 0xB8, 0xAA, 0xB9, 0xAA, 0xB9, 0xAA, 0xBA, 0xAA, 0xBB, 0xAA, 0xBC, 0xAA, 0xBD,\n\t0xAA, 0xBD, 0xAA, 0xBE, 0xAA, 0xBF, 0xAA, 0xC0, 0xAA, 0xC1, 0xAA, 0xC1, 0xAA, 0xC2, 0xAA, 0xC3, 0xAA, 0xC4, 0xAA, 0xC5, 0xAA, 0xC6, 0xAA, 0xC6, 0xAA, 0xC7, 0xAA, 0xC8, 0xAA, 0xC9, 0xAA, 0xCA,\n\t0xAA, 0xCA, 0xAA, 0xCB, 0xAA, 0xCC, 0xAA, 0xCD, 0xAA, 0xCE, 0xAA, 0xCF, 0xAA, 0xCF, 0xAA, 0xD0, 0xAA, 0xD1, 0xAA, 0xD2, 0xAA, 0xD3, 0xAA, 0xD3, 0xAA, 0xD4, 0xAA, 0xD5, 0xAA, 0xD6, 0xAA, 0xD7,\n\t0xAA, 0xD7, 0xAA, 0xD8, 0xAA, 0xD9, 0xAA, 0xDA, 0xAA, 0xDB, 0xAA, 0xDC, 0xAA, 0xDC, 0xAA, 0xDD, 0xAA, 0xDE, 0xAA, 0xDF, 0xAA, 0xE0, 0xAA, 0xE0, 0xAA, 0xE1, 0xAA, 0xE2, 0xAA, 0xE3, 0xAA, 0xE4,\n\t0xAA, 0xE4, 0xAA, 0xE5, 0xAA, 0xE6, 0xAA, 0xE7, 0xAA, 0xE8, 0xAA, 0xE9, 0xAA, 0xE9, 0xAA, 0xEA, 0xAA, 0xEB, 0xAA, 0xEC, 0xAA, 0xED, 0xAA, 0xED, 0xAA, 0xEE, 0xAA, 0xEF, 0xAA, 0xF0, 0xAA, 0xF1,\n\t0xAA, 0xF1, 0xAA, 0xF2, 0xAA, 0xF3, 0xAA, 0xF4, 0xAA, 0xF5, 0xAA, 0xF5, 0xAA, 0xF6, 0xAA, 0xF7, 0xAA, 0xF8, 0xAA, 0xF9, 0xAA, 0xFA, 0xAA, 0xFA, 0xAA, 0xFB, 0xAA, 0xFC, 0xAA, 0xFD, 0xAA, 0xFE,\n\t0xAA, 0xFE, 0xAA, 0xFF, 0xAA, 0x00, 0xAB, 0x01, 0xAB, 0x02, 0xAB, 0x02, 0xAB, 0x03, 0xAB, 0x04, 0xAB, 0x05, 0xAB, 0x06, 0xAB, 0x06, 0xAB, 0x07, 0xAB, 0x08, 0xAB, 0x09, 0xAB, 0x0A, 0xAB, 0x0A,\n\t0xAB, 0x0B, 0xAB, 0x0C, 0xAB, 0x0D, 0xAB, 0x0E, 0xAB, 0x0E, 0xAB, 0x0F, 0xAB, 0x10, 0xAB, 0x11, 0xAB, 0x12, 0xAB, 0x13, 0xAB, 0x13, 0xAB, 0x14, 0xAB, 0x15, 0xAB, 0x16, 0xAB, 0x17, 0xAB, 0x17,\n\t0xAB, 0x18, 0xAB, 0x19, 0xAB, 0x1A, 0xAB, 0x1B, 0xAB, 0x1B, 0xAB, 0x1C, 0xAB, 0x1D, 0xAB, 0x1E, 0xAB, 0x1F, 0xAB, 0x1F, 0xAB, 0x20, 0xAB, 0x21, 0xAB, 0x22, 0xAB, 0x23, 0xAB, 0x23, 0xAB, 0x24,\n\t0xAB, 0x25, 0xAB, 0x26, 0xAB, 0x27, 0xAB, 0x27, 0xAB, 0x28, 0xAB, 0x29, 0xAB, 0x2A, 0xAB, 0x2B, 0xAB, 0x2B, 0xAB, 0x2C, 0xAB, 0x2D, 0xAB, 0x2E, 0xAB, 0x2F, 0xAB, 0x2F, 0xAB, 0x30, 0xAB, 0x31,\n\t0xAB, 0x32, 0xAB, 0x33, 0xAB, 0x33, 0xAB, 0x34, 0xAB, 0x36, 0xAB, 0x37, 0xAB, 0x39, 0xAB, 0x3B, 0xAB, 0x3C, 0xAB, 0x3E, 0xAB, 0x3F, 0xAB, 0x41, 0xAB, 0x43, 0xAB, 0x44, 0xAB, 0x46, 0xAB, 0x47,\n\t0xAB, 0x49, 0xAB, 0x4B, 0xAB, 0x4C, 0xAB, 0x4E, 0xAB, 0x4F, 0xAB, 0x51, 0xAB, 0x53, 0xAB, 0x54, 0xAB, 0x56, 0xAB, 0x57, 0xAB, 0x59, 0xAB, 0x5B, 0xAB, 0x5C, 0xAB, 0x5E, 0xAB, 0x5F, 0xAB, 0x61,\n\t0xAB, 0x63, 0xAB, 0x64, 0xAB, 0x66, 0xAB, 0x67, 0xAB, 0x69, 0xAB, 0x6B, 0xAB, 0x6C, 0xAB, 0x6E, 0xAB, 0x6F, 0xAB, 0x71, 0xAB, 0x73, 0xAB, 0x74, 0xAB, 0x76, 0xAB, 0x77, 0xAB, 0x79, 0xAB, 0x7A,\n\t0xAB, 0x7C, 0xAB, 0x7E, 0xAB, 0x7F, 0xAB, 0x81, 0xAB, 0x82, 0xAB, 0x84, 0xAB, 0x86, 0xAB, 0x87, 0xAB, 0x89, 0xAB, 0x8A, 0xAB, 0x8C, 0xAB, 0x8D, 0xAB, 0x8F, 0xAB, 0x91, 0xAB, 0x92, 0xAB, 0x94,\n\t0xAB, 0x95, 0xAB, 0x97, 0xAB, 0x99, 0xAB, 0x9A, 0xAB, 0x9C, 0xAB, 0x9D, 0xAB, 0x9F, 0xAB, 0xA0, 0xAB, 0xA2, 0xAB, 0xA4, 0xAB, 0xA5, 0xAB, 0xA7, 0xAB, 0xA8, 0xAB, 0xAA, 0xAB, 0xAB, 0xAB, 0xAD,\n\t0xAB, 0xAF, 0xAB, 0xB0, 0xAB, 0xB2, 0xAB, 0xB3, 0xAB, 0xB5, 0xAB, 0xB6, 0xAB, 0xB8, 0xAB, 0xBA, 0xAB, 0xBB, 0xAB, 0xBD, 0xAB, 0xBE, 0xAB, 0xC0, 0xAB, 0xC1, 0xAB, 0xC3, 0xAB, 0xC5, 0xAB, 0xC6,\n\t0xAB, 0xC8, 0xAB, 0xC9, 0xAB, 0xCB, 0xAB, 0xCC, 0xAB, 0xCE, 0xAB, 0xD0, 0xAB, 0xD1, 0xAB, 0xD3, 0xAB, 0xD4, 0xAB, 0xD6, 0xAB, 0xD7, 0xAB, 0xD9, 0xAB, 0xDA, 0xAB, 0xDC, 0xAB, 0xDE, 0xAB, 0xDF,\n\t0xAB, 0xE1, 0xAB, 0xE2, 0xAB, 0xE4, 0xAB, 0xE5, 0xAB, 0xE7, 0xAB, 0xE9, 0xAB, 0xEA, 0xAB, 0xEC, 0xAB, 0xED, 0xAB, 0xEF, 0xAB, 0xF0, 0xAB, 0xF2, 0xAB, 0xF3, 0xAB, 0xF5, 0xAB, 0xF7, 0xAB, 0xF8,\n\t0xAB, 0xFA, 0xAB, 0xFB, 0xAB, 0xFD, 0xAB, 0xFE, 0xAB, 0x00, 0xAC, 0x01, 0xAC, 0x01, 0xAC, 0x02, 0xAC, 0x03, 0xAC, 0x04, 0xAC, 0x05, 0xAC, 0x05, 0xAC, 0x06, 0xAC, 0x07, 0xAC, 0x08, 0xAC, 0x08,\n\t0xAC, 0x09, 0xAC, 0x0A, 0xAC, 0x0B, 0xAC, 0x0C, 0xAC, 0x0C, 0xAC, 0x0D, 0xAC, 0x0E, 0xAC, 0x0F, 0xAC, 0x0F, 0xAC, 0x10, 0xAC, 0x11, 0xAC, 0x12, 0xAC, 0x13, 0xAC, 0x13, 0xAC, 0x14, 0xAC, 0x15,\n\t0xAC, 0x16, 0xAC, 0x16, 0xAC, 0x17, 0xAC, 0x18, 0xAC, 0x19, 0xAC, 0x19, 0xAC, 0x1A, 0xAC, 0x1B, 0xAC, 0x1C, 0xAC, 0x1D, 0xAC, 0x1D, 0xAC, 0x1E, 0xAC, 0x1F, 0xAC, 0x20, 0xAC, 0x20, 0xAC, 0x21,\n\t0xAC, 0x22, 0xAC, 0x23, 0xAC, 0x23, 0xAC, 0x24, 0xAC, 0x25, 0xAC, 0x26, 0xAC, 0x27, 0xAC, 0x27, 0xAC, 0x28, 0xAC, 0x29, 0xAC, 0x2A, 0xAC, 0x2A, 0xAC, 0x2B, 0xAC, 0x2C, 0xAC, 0x2D, 0xAC, 0x2D,\n\t0xAC, 0x2E, 0xAC, 0x2F, 0xAC, 0x30, 0xAC, 0x31, 0xAC, 0x31, 0xAC, 0x32, 0xAC, 0x33, 0xAC, 0x34, 0xAC, 0x34, 0xAC, 0x35, 0xAC, 0x36, 0xAC, 0x37, 0xAC, 0x37, 0xAC, 0x38, 0xAC, 0x39, 0xAC, 0x3A,\n\t0xAC, 0x3A, 0xAC, 0x3B, 0xAC, 0x3C, 0xAC, 0x3D, 0xAC, 0x3D, 0xAC, 0x3E, 0xAC, 0x3F, 0xAC, 0x40, 0xAC, 0x41, 0xAC, 0x41, 0xAC, 0x42, 0xAC, 0x43, 0xAC, 0x44, 0xAC, 0x44, 0xAC, 0x45, 0xAC, 0x46,\n\t0xAC, 0x47, 0xAC, 0x47, 0xAC, 0x48, 0xAC, 0x49, 0xAC, 0x4A, 0xAC, 0x4A, 0xAC, 0x4B, 0xAC, 0x4C, 0xAC, 0x4D, 0xAC, 0x4D, 0xAC, 0x4E, 0xAC, 0x4F, 0xAC, 0x50, 0xAC, 0x50, 0xAC, 0x51, 0xAC, 0x52,\n\t0xAC, 0x53, 0xAC, 0x53, 0xAC, 0x54, 0xAC, 0x55, 0xAC, 0x56, 0xAC, 0x57, 0xAC, 0x57, 0xAC, 0x58, 0xAC, 0x59, 0xAC, 0x5A, 0xAC, 0x5A, 0xAC, 0x5B, 0xAC, 0x5C, 0xAC, 0x5D, 0xAC, 0x5D, 0xAC, 0x5E,\n\t0xAC, 0x5F, 0xAC, 0x60, 0xAC, 0x60, 0xAC, 0x61, 0xAC, 0x62, 0xAC, 0x63, 0xAC, 0x63, 0xAC, 0x64, 0xAC, 0x65, 0xAC, 0x66, 0xAC, 0x66, 0xAC, 0x67, 0xAC, 0x68, 0xAC, 0x69, 0xAC, 0x69, 0xAC, 0x6A,\n\t0xAC, 0x6B, 0xAC, 0x6C, 0xAC, 0x6C, 0xAC, 0x6D, 0xAC, 0x6E, 0xAC, 0x6F, 0xAC, 0x6F, 0xAC, 0x70, 0xAC, 0x71, 0xAC, 0x72, 0xAC, 0x72, 0xAC, 0x73, 0xAC, 0x74, 0xAC, 0x75, 0xAC, 0x75, 0xAC, 0x76,\n\t0xAC, 0x77, 0xAC, 0x78, 0xAC, 0x78, 0xAC, 0x79, 0xAC, 0x7A, 0xAC, 0x7B, 0xAC, 0x7B, 0xAC, 0x7C, 0xAC, 0x7D, 0xAC, 0x7E, 0xAC, 0x7E, 0xAC, 0x7F, 0xAC, 0x80, 0xAC, 0xA5, 0x5B, 0x20, 0x82, 0xAC,\n\t0xAB, 0x53, 0xF0, 0xFF, 0xFF, 0xB7, 0x84, 0xAC, 0x85, 0xAC, 0x86, 0xAC, 0x86, 0xAC, 0x87, 0xAC, 0x88, 0xAC, 0x89, 0xAC, 0x89, 0xAC, 0x8A, 0xAC, 0x8B, 0xAC, 0x8C, 0xAC, 0x8C, 0xAC, 0x8D, 0xAC,\n\t0x8E, 0xAC, 0x8F, 0xAC, 0x8F, 0xAC, 0x90, 0xAC, 0x91, 0xAC, 0x92, 0xAC, 0x92, 0xAC, 0x93, 0xAC, 0x94, 0xAC, 0x95, 0xAC, 0x95, 0xAC, 0x96, 0xAC, 0x97, 0xAC, 0x98, 0xAC, 0x98, 0xAC, 0x99, 0xAC,\n\t0x9A, 0xAC, 0x9A, 0xAC, 0x9B, 0xAC, 0x9C, 0xAC, 0x9D, 0xAC, 0x9D, 0xAC, 0x9E, 0xAC, 0x9F, 0xAC, 0xA0, 0xAC, 0xA0, 0xAC, 0xA1, 0xAC, 0xA2, 0xAC, 0xA3, 0xAC, 0xA3, 0xAC, 0xA4, 0xAC, 0xA5, 0xAC,\n\t0xA6, 0xAC, 0xA6, 0xAC, 0xA7, 0xAC, 0xA8, 0xAC, 0xA8, 0xAC, 0xA9, 0xAC, 0xAA, 0xAC, 0xAB, 0xAC, 0xAB, 0xAC, 0xAC, 0xAC, 0xAD, 0xAC, 0xAE, 0xAC, 0xAE, 0xAC, 0xAF, 0xAC, 0xB0, 0xAC, 0xB1, 0xAC,\n\t0xB1, 0xAC, 0xB2, 0xAC, 0xB3, 0xAC, 0xB3, 0xAC, 0xB4, 0xAC, 0xB5, 0xAC, 0xB6, 0xAC, 0xB6, 0xAC, 0xB7, 0xAC, 0xB8, 0xAC, 0xB9, 0xAC, 0xB9, 0xAC, 0xBA, 0xAC, 0xBB, 0xAC, 0xBB, 0xAC, 0xBC, 0xAC,\n\t0xBD, 0xAC, 0xBE, 0xAC, 0xBE, 0xAC, 0xBF, 0xAC, 0xC0, 0xAC, 0xC1, 0xAC, 0xC1, 0xAC, 0xC2, 0xAC, 0xC3, 0xAC, 0xC4, 0xAC, 0xC4, 0xAC, 0xC5, 0xAC, 0xC6, 0xAC, 0xC6, 0xAC, 0xC7, 0xAC, 0xC8, 0xAC,\n\t0xC9, 0xAC, 0xC9, 0xAC, 0xCA, 0xAC, 0xCB, 0xAC, 0xCB, 0xAC, 0xCC, 0xAC, 0xCD, 0xAC, 0xCE, 0xAC, 0xCE, 0xAC, 0xCF, 0xAC, 0xD0, 0xAC, 0xD1, 0xAC, 0xD1, 0xAC, 0xD2, 0xAC, 0xD3, 0xAC, 0xD3, 0xAC,\n\t0xD4, 0xAC, 0xD5, 0xAC, 0xD6, 0xAC, 0xD6, 0xAC, 0xD7, 0xAC, 0xD8, 0xAC, 0xD9, 0xAC, 0xD9, 0xAC, 0xDA, 0xAC, 0xDB, 0xAC, 0xDB, 0xAC, 0xDC, 0xAC, 0xDD, 0xAC, 0xDE, 0xAC, 0xDE, 0xAC, 0xDF, 0xAC,\n\t0xE0, 0xAC, 0xE0, 0xAC, 0xE1, 0xAC, 0xE2, 0xAC, 0xE3, 0xAC, 0xE3, 0xAC, 0xE4, 0xAC, 0xE5, 0xAC, 0xE5, 0xAC, 0xE6, 0xAC, 0xE7, 0xAC, 0xE8, 0xAC, 0xE8, 0xAC, 0xE9, 0xAC, 0xEA, 0xAC, 0xEB, 0xAC,\n\t0xEB, 0xAC, 0xEC, 0xAC, 0xED, 0xAC, 0xED, 0xAC, 0xEE, 0xAC, 0xEF, 0xAC, 0xF0, 0xAC, 0xF0, 0xAC, 0xF1, 0xAC, 0xF2, 0xAC, 0xF2, 0xAC, 0xF3, 0xAC, 0xF4, 0xAC, 0xF5, 0xAC, 0xF5, 0xAC, 0xF6, 0xAC,\n\t0xF7, 0xAC, 0xF7, 0xAC, 0xF8, 0xAC, 0xF9, 0xAC, 0xFA, 0xAC, 0xFA, 0xAC, 0xFB, 0xAC, 0xFC, 0xAC, 0xFC, 0xAC, 0xFD, 0xAC, 0xFE, 0xAC, 0xFF, 0xAC, 0xFF, 0xAC, 0x00, 0xAD, 0x01, 0xAD, 0x01, 0xAD,\n\t0x02, 0xAD, 0x03, 0xAD, 0x04, 0xAD, 0x04, 0xAD, 0x05, 0xAD, 0x06, 0xAD, 0x06, 0xAD, 0x07, 0xAD, 0x08, 0xAD, 0x08, 0xAD, 0x09, 0xAD, 0x0A, 0xAD, 0x0B, 0xAD, 0x0B, 0xAD, 0x0C, 0xAD, 0x0D, 0xAD,\n\t0x0D, 0xAD, 0x0E, 0xAD, 0x0F, 0xAD, 0x10, 0xAD, 0x10, 0xAD, 0x11, 0xAD, 0x12, 0xAD, 0x12, 0xAD, 0x13, 0xAD, 0x14, 0xAD, 0x15, 0xAD, 0x15, 0xAD, 0x16, 0xAD, 0x17, 0xAD, 0x17, 0xAD, 0x18, 0xAD,\n\t0x19, 0xAD, 0x19, 0xAD, 0x1A, 0xAD, 0x1B, 0xAD, 0x1C, 0xAD, 0x1C, 0xAD, 0x1D, 0xAD, 0x1E, 0xAD, 0x1E, 0xAD, 0x1F, 0xAD, 0x20, 0xAD, 0x20, 0xAD, 0x21, 0xAD, 0x22, 0xAD, 0x23, 0xAD, 0x23, 0xAD,\n\t0x24, 0xAD, 0x25, 0xAD, 0x25, 0xAD, 0x26, 0xAD, 0x27, 0xAD, 0x28, 0xAD, 0x28, 0xAD, 0x29, 0xAD, 0x2A, 0xAD, 0x2A, 0xAD, 0x2B, 0xAD, 0x2C, 0xAD, 0x2C, 0xAD, 0x2D, 0xAD, 0x2E, 0xAD, 0x2F, 0xAD,\n\t0x2F, 0xAD, 0x30, 0xAD, 0x31, 0xAD, 0x31, 0xAD, 0x32, 0xAD, 0x33, 0xAD, 0x33, 0xAD, 0x34, 0xAD, 0x35, 0xAD, 0x35, 0xAD, 0x36, 0xAD, 0x37, 0xAD, 0x38, 0xAD, 0x38, 0xAD, 0x39, 0xAD, 0x3A, 0xAD,\n\t0x3A, 0xAD, 0x3B, 0xAD, 0x3C, 0xAD, 0x3C, 0xAD, 0x3D, 0xAD, 0x3E, 0xAD, 0x3F, 0xAD, 0x3F, 0xAD, 0x40, 0xAD, 0x41, 0xAD, 0x41, 0xAD, 0x42, 0xAD, 0x43, 0xAD, 0x43, 0xAD, 0x44, 0xAD, 0x45, 0xAD,\n\t0x45, 0xAD, 0x46, 0xAD, 0x47, 0xAD, 0x48, 0xAD, 0x48, 0xAD, 0x49, 0xAD, 0x4A, 0xAD, 0x4A, 0xAD, 0x4B, 0xAD, 0x4C, 0xAD, 0x4C, 0xAD, 0x4D, 0xAD, 0x4E, 0xAD, 0x4E, 0xAD, 0x4F, 0xAD, 0x50, 0xAD,\n\t0x51, 0xAD, 0x51, 0xAD, 0x52, 0xAD, 0x53, 0xAD, 0x53, 0xAD, 0x54, 0xAD, 0x55, 0xAD, 0x55, 0xAD, 0x56, 0xAD, 0x57, 0xAD, 0x57, 0xAD, 0x58, 0xAD, 0x59, 0xAD, 0x59, 0xAD, 0x5A, 0xAD, 0x5B, 0xAD,\n\t0x5C, 0xAD, 0x5C, 0xAD, 0x5D, 0xAD, 0x5E, 0xAD, 0x5E, 0xAD, 0x5F, 0xAD, 0x60, 0xAD, 0x60, 0xAD, 0x61, 0xAD, 0x62, 0xAD, 0x62, 0xAD, 0x63, 0xAD, 0x64, 0xAD, 0x64, 0xAD, 0x65, 0xAD, 0x66, 0xAD,\n\t0x67, 0xAD, 0x67, 0xAD, 0x68, 0xAD, 0x69, 0xAD, 0x69, 0xAD, 0x6A, 0xAD, 0x6B, 0xAD, 0x6B, 0xAD, 0x6C, 0xAD, 0x6D, 0xAD, 0x6D, 0xAD, 0x6E, 0xAD, 0x6F, 0xAD, 0x6F, 0xAD, 0x70, 0xAD, 0x71, 0xAD,\n\t0x71, 0xAD, 0x72, 0xAD, 0x73, 0xAD, 0x73, 0xAD, 0x74, 0xAD, 0x75, 0xAD, 0x76, 0xAD, 0x76, 0xAD, 0x77, 0xAD, 0x78, 0xAD, 0x78, 0xAD, 0x79, 0xAD, 0x7A, 0xAD, 0x7A, 0xAD, 0x7B, 0xAD, 0x7C, 0xAD,\n\t0x7C, 0xAD, 0x7D, 0xAD, 0x7E, 0xAD, 0x7E, 0xAD, 0x7F, 0xAD, 0x6F, 0x62, 0x20, 0x81, 0xAD, 0x75, 0x5A, 0xF0, 0x6D, 0x83, 0xAD, 0x84, 0xAD, 0x84, 0xAD, 0x85, 0xAD, 0x86, 0xAD, 0x86, 0xAD, 0x87,\n\t0xAD, 0x88, 0xAD, 0x88, 0xAD, 0x89, 0xAD, 0x8A, 0xAD, 0x8B, 0xAD, 0x8B, 0xAD, 0x8C, 0xAD, 0x8D, 0xAD, 0x8D, 0xAD, 0x8E, 0xAD, 0x8F, 0xAD, 0x8F, 0xAD, 0x90, 0xAD, 0x91, 0xAD, 0x91, 0xAD, 0x92,\n\t0xAD, 0x93, 0xAD, 0x93, 0xAD, 0x94, 0xAD, 0x95, 0xAD, 0x95, 0xAD, 0x96, 0xAD, 0x97, 0xAD, 0x97, 0xAD, 0x98, 0xAD, 0x99, 0xAD, 0x99, 0xAD, 0x9A, 0xAD, 0x9B, 0xAD, 0x9B, 0xAD, 0x9C, 0xAD, 0x9D,\n\t0xAD, 0x9D, 0xAD, 0x9E, 0xAD, 0x9F, 0xAD, 0x9F, 0xAD, 0xA0, 0xAD, 0xA1, 0xAD, 0xA1, 0xAD, 0xA2, 0xAD, 0xA3, 0xAD, 0xA3, 0xAD, 0xA4, 0xAD, 0xA5, 0xAD, 0xA5, 0xAD, 0xA6, 0xAD, 0xA7, 0xAD, 0xA7,\n\t0xAD, 0xA8, 0xAD, 0xA9, 0xAD, 0xA9, 0xAD, 0xAA, 0xAD, 0xAB, 0xAD, 0xAB, 0xAD, 0xAC, 0xAD, 0x01, 0x00, 0xF0, 0xFF, 0x84, 0xAE, 0xAD, 0xAF, 0xAD, 0xAF, 0xAD, 0xB0, 0xAD, 0xB1, 0xAD, 0xB1, 0xAD,\n\t0xB2, 0xAD, 0xB3, 0xAD, 0xB3, 0xAD, 0xB4, 0xAD, 0xB5, 0xAD, 0xB5, 0xAD, 0xB6, 0xAD, 0xB7, 0xAD, 0xB7, 0xAD, 0xB8, 0xAD, 0xB9, 0xAD, 0xB9, 0xAD, 0xBA, 0xAD, 0xBB, 0xAD, 0xBB, 0xAD, 0xBC, 0xAD,\n\t0xBD, 0xAD, 0xBD, 0xAD, 0xBE, 0xAD, 0xBF, 0xAD, 0xBF, 0xAD, 0xC0, 0xAD, 0xC1, 0xAD, 0xC1, 0xAD, 0xC2, 0xAD, 0xC2, 0xAD, 0xC3, 0xAD, 0xC4, 0xAD, 0xC4, 0xAD, 0xC5, 0xAD, 0xC6, 0xAD, 0xC6, 0xAD,\n\t0xC7, 0xAD, 0xC8, 0xAD, 0xC8, 0xAD, 0xC9, 0xAD, 0xCA, 0xAD, 0xCA, 0xAD, 0xCB, 0xAD, 0xCC, 0xAD, 0xCC, 0xAD, 0xCD, 0xAD, 0xCE, 0xAD, 0xCE, 0xAD, 0xCF, 0xAD, 0xD0, 0xAD, 0xD0, 0xAD, 0xD1, 0xAD,\n\t0xD2, 0xAD, 0xD2, 0xAD, 0xD3, 0xAD, 0xD4, 0xAD, 0xD4, 0xAD, 0xD5, 0xAD, 0xD6, 0xAD, 0xD6, 0xAD, 0xD7, 0xAD, 0xD7, 0xAD, 0xD8, 0xAD, 0xD9, 0xAD, 0xD9, 0xAD, 0xDA, 0xAD, 0xDB, 0xAD, 0xDB, 0xAD,\n\t0xDC, 0xAD, 0xDD, 0xAD, 0xDD, 0xAD, 0xDE, 0xAD, 0xDF, 0xAD, 0xDF, 0xAD, 0xE0, 0xAD, 0xE1, 0xAD, 0xE1, 0xAD, 0xE2, 0xAD, 0xE3, 0xAD, 0xE3, 0xAD, 0xE4, 0xAD, 0xE4, 0xAD, 0xE5, 0xAD, 0xE6, 0xAD,\n\t0xE6, 0xAD, 0xE7, 0xAD, 0xE8, 0xAD, 0xE8, 0xAD, 0xE9, 0xAD, 0xEA, 0xAD, 0xEA, 0xAD, 0xEB, 0xAD, 0xEC, 0xAD, 0xEC, 0xAD, 0xED, 0xAD, 0xEE, 0xAD, 0xEE, 0xAD, 0xEF, 0xAD, 0xEF, 0xAD, 0xF0, 0xAD,\n\t0xF1, 0xAD, 0xF1, 0xAD, 0xF2, 0xAD, 0xF3, 0xAD, 0xF3, 0xAD, 0xF4, 0xAD, 0xF5, 0xAD, 0xF5, 0xAD, 0xF6, 0xAD, 0xF7, 0xAD, 0xF7, 0xAD, 0xF8, 0xAD, 0xF9, 0xAD, 0xF9, 0xAD, 0xFA, 0xAD, 0xFA, 0xAD,\n\t0xFB, 0xAD, 0xFC, 0xAD, 0xFC, 0xAD, 0xFD, 0xAD, 0xFE, 0xAD, 0xFE, 0xAD, 0xFF, 0xAD, 0x00, 0xAE, 0x00, 0xAE, 0x01, 0xAE, 0x01, 0xAE, 0x02, 0xAE, 0x03, 0xAE, 0x03, 0xAE, 0x04, 0xAE, 0x05, 0xAE,\n\t0x05, 0xAE, 0x06, 0xAE, 0x07, 0xAE, 0x07, 0xAE, 0x08, 0xAE, 0x09, 0xAE, 0x09, 0xAE, 0x0A, 0xAE, 0x0A, 0xAE, 0x0B, 0xAE, 0x0C, 0xAE, 0x0C, 0xAE, 0x0D, 0xAE, 0x0E, 0xAE, 0x0E, 0xAE, 0x0F, 0xAE,\n\t0x10, 0xAE, 0x10, 0xAE, 0x11, 0xAE, 0x11, 0xAE, 0x12, 0xAE, 0x13, 0xAE, 0x13, 0xAE, 0x14, 0xAE, 0x15, 0xAE, 0x15, 0xAE, 0x16, 0xAE, 0x17, 0xAE, 0x17, 0xAE, 0x18, 0xAE, 0x18, 0xAE, 0x19, 0xAE,\n\t0x1A, 0xAE, 0x1A, 0xAE, 0x1B, 0xAE, 0x1C, 0xAE, 0x1C, 0xAE, 0x1D, 0xAE, 0x1D, 0xAE, 0x1E, 0xAE, 0x1F, 0xAE, 0x1F, 0xAE, 0x20, 0xAE, 0x21, 0xAE, 0x21, 0xAE, 0x22, 0xAE, 0x23, 0xAE, 0x23, 0xAE,\n\t0x24, 0xAE, 0x24, 0xAE, 0x25, 0xAE, 0x26, 0xAE, 0x26, 0xAE, 0x27, 0xAE, 0x28, 0xAE, 0x28, 0xAE, 0x29, 0xAE, 0x29, 0xAE, 0x2A, 0xAE, 0x2B, 0xAE, 0x2B, 0xAE, 0x2C, 0xAE, 0x2D, 0xAE, 0x2D, 0xAE,\n\t0x2E, 0xAE, 0x2F, 0xAE, 0x2F, 0xAE, 0x91, 0xFF, 0xF1, 0xFF, 0xFF, 0xFF, 0xA3, 0x31, 0xAE, 0x32, 0xAE, 0x32, 0xAE, 0x33, 0xAE, 0x34, 0xAE, 0x34, 0xAE, 0x35, 0xAE, 0x35, 0xAE, 0x36, 0xAE, 0x37,\n\t0xAE, 0x37, 0xAE, 0x38, 0xAE, 0x39, 0xAE, 0x39, 0xAE, 0x3A, 0xAE, 0x3A, 0xAE, 0x3B, 0xAE, 0x3C, 0xAE, 0x3C, 0xAE, 0x3D, 0xAE, 0x3E, 0xAE, 0x3E, 0xAE, 0x3F, 0xAE, 0x3F, 0xAE, 0x40, 0xAE, 0x41,\n\t0xAE, 0x41, 0xAE, 0x42, 0xAE, 0x42, 0xAE, 0x43, 0xAE, 0x44, 0xAE, 0x44, 0xAE, 0x45, 0xAE, 0x46, 0xAE, 0x46, 0xAE, 0x47, 0xAE, 0x47, 0xAE, 0x48, 0xAE, 0x49, 0xAE, 0x49, 0xAE, 0x4A, 0xAE, 0x4B,\n\t0xAE, 0x4B, 0xAE, 0x4C, 0xAE, 0x4C, 0xAE, 0x4D, 0xAE, 0x4E, 0xAE, 0x4E, 0xAE, 0x4F, 0xAE, 0x4F, 0xAE, 0x50, 0xAE, 0x51, 0xAE, 0x51, 0xAE, 0x52, 0xAE, 0x53, 0xAE, 0x53, 0xAE, 0x54, 0xAE, 0x54,\n\t0xAE, 0x55, 0xAE, 0x56, 0xAE, 0x56, 0xAE, 0x57, 0xAE, 0x58, 0xAE, 0x58, 0xAE, 0x59, 0xAE, 0x59, 0xAE, 0x5A, 0xAE, 0x5B, 0xAE, 0x5B, 0xAE, 0x5C, 0xAE, 0x5C, 0xAE, 0x5D, 0xAE, 0x5E, 0xAE, 0x5E,\n\t0xAE, 0x5F, 0xAE, 0x5F, 0xAE, 0x60, 0xAE, 0x61, 0xAE, 0x61, 0xAE, 0x62, 0xAE, 0x63, 0xAE, 0x63, 0xAE, 0x64, 0xAE, 0x64, 0xAE, 0x65, 0xAE, 0x66, 0xAE, 0x66, 0xAE, 0x67, 0xAE, 0x67, 0xAE, 0x68,\n\t0xAE, 0x69, 0xAE, 0x69, 0xAE, 0x6A, 0xAE, 0x6B, 0xAE, 0x6B, 0xAE, 0x6C, 0xAE, 0x6D, 0xAE, 0x6E, 0xAE, 0x6F, 0xAE, 0x71, 0xAE, 0x72, 0xAE, 0x73, 0xAE, 0x74, 0xAE, 0x75, 0xAE, 0x77, 0xAE, 0x78,\n\t0xAE, 0x79, 0xAE, 0x7A, 0xAE, 0x7B, 0xAE, 0x7D, 0xAE, 0x7E, 0xAE, 0x7F, 0xAE, 0x80, 0xAE, 0x82, 0xAE, 0x83, 0xAE, 0x84, 0xAE, 0x85, 0xAE, 0x86, 0xAE, 0x88, 0xAE, 0x89, 0xAE, 0x8A, 0xAE, 0x8B,\n\t0xAE, 0x8C, 0xAE, 0x8E, 0xAE, 0x8F, 0xAE, 0x90, 0xAE, 0x91, 0xAE, 0x92, 0xAE, 0x94, 0xAE, 0x95, 0xAE, 0x96, 0xAE, 0x97, 0xAE, 0x98, 0xAE, 0x99, 0xAE, 0x9B, 0xAE, 0x9C, 0xAE, 0x9D, 0xAE, 0x9E,\n\t0xAE, 0x9F, 0xAE, 0xA1, 0xAE, 0xA2, 0xAE, 0xA3, 0xAE, 0xA4, 0xAE, 0xA5, 0xAE, 0xA7, 0xAE, 0xA8, 0xAE, 0xA9, 0xAE, 0xAA, 0xAE, 0xAB, 0xAE, 0xAC, 0xAE, 0xAE, 0xAE, 0xAF, 0xAE, 0xB0, 0xAE, 0xB1,\n\t0xAE, 0xB2, 0xAE, 0xB4, 0xAE, 0xB5, 0xAE, 0xB6, 0xAE, 0xB7, 0xAE, 0xB8, 0xAE, 0xB9, 0xAE, 0xBB, 0xAE, 0xBC, 0xAE, 0xBD, 0xAE, 0xBE, 0xAE, 0xBF, 0xAE, 0xC0, 0xAE, 0xC2, 0xAE, 0xC3, 0xAE, 0xC4,\n\t0xAE, 0xC5, 0xAE, 0xC6, 0xAE, 0xC7, 0xAE, 0xC9, 0xAE, 0xCA, 0xAE, 0xCB, 0xAE, 0xCC, 0xAE, 0xCD, 0xAE, 0xCE, 0xAE, 0xD0, 0xAE, 0xD1, 0xAE, 0xD2, 0xAE, 0xD3, 0xAE, 0xD4, 0xAE, 0xD5, 0xAE, 0xD6,\n\t0xAE, 0xD8, 0xAE, 0xD9, 0xAE, 0xDA, 0xAE, 0xDB, 0xAE, 0xDC, 0xAE, 0xDD, 0xAE, 0xDE, 0xAE, 0xE0, 0xAE, 0xE1, 0xAE, 0xE2, 0xAE, 0xE3, 0xAE, 0xE4, 0xAE, 0xE5, 0xAE, 0xE7, 0xAE, 0xE8, 0xAE, 0xE9,\n\t0xAE, 0xEA, 0xAE, 0xEB, 0xAE, 0xEC, 0xAE, 0xED, 0xAE, 0xEE, 0xAE, 0xF0, 0xAE, 0xF1, 0xAE, 0xF2, 0xAE, 0xF3, 0xAE, 0xF4, 0xAE, 0xF5, 0xAE, 0xF6, 0xAE, 0xF8, 0xAE, 0xF9, 0xAE, 0xFA, 0xAE, 0xFB,\n\t0xAE, 0xFC, 0xAE, 0xFD, 0xAE, 0xFE, 0xAE, 0xFF, 0xAE, 0x01, 0xAF, 0x02, 0xAF, 0x03, 0xAF, 0x04, 0xAF, 0x05, 0xAF, 0x06, 0xAF, 0x07, 0xAF, 0x08, 0xAF, 0x0A, 0xAF, 0x0B, 0xAF, 0x0C, 0xAF, 0x0D,\n\t0xAF, 0x0E, 0xAF, 0x0F, 0xAF, 0x10, 0xAF, 0x11, 0xAF, 0x13, 0xAF, 0x14, 0xAF, 0x15, 0xAF, 0x16, 0xAF, 0x17, 0xAF, 0x18, 0xAF, 0x19, 0xAF, 0x1A, 0xAF, 0x1B, 0xAF, 0x1D, 0xAF, 0x1E, 0xAF, 0x1F,\n\t0xAF, 0x20, 0xAF, 0x21, 0xAF, 0x22, 0xAF, 0x23, 0xAF, 0x24, 0xAF, 0x25, 0xAF, 0x26, 0xAF, 0x28, 0xAF, 0x29, 0xAF, 0x2A, 0xAF, 0x2B, 0xAF, 0x2C, 0xAF, 0x2D, 0xAF, 0x2E, 0xAF, 0x2F, 0xAF, 0x30,\n\t0xAF, 0x31, 0xAF, 0x33, 0xAF, 0x34, 0xAF, 0x35, 0xAF, 0x36, 0xAF, 0x37, 0xAF, 0x38, 0xAF, 0x39, 0xAF, 0x3A, 0xAF, 0x3B, 0xAF, 0x3C, 0xAF, 0x3D, 0xAF, 0x3F, 0xAF, 0x40, 0xAF, 0x41, 0xAF, 0x42,\n\t0xAF, 0x43, 0xAF, 0x44, 0xAF, 0x45, 0xAF, 0x46, 0xAF, 0x47, 0xAF, 0x48, 0xAF, 0x49, 0xAF, 0x4A, 0xAF, 0x4C, 0xAF, 0x4D, 0xAF, 0x4E, 0xAF, 0x4F, 0xAF, 0x50, 0xAF, 0x51, 0xAF, 0x52, 0xAF, 0x53,\n\t0xAF, 0x54, 0xAF, 0x55, 0xAF, 0x56, 0xAF, 0x57, 0xAF, 0x58, 0xAF, 0x5A, 0xAF, 0x5B, 0xAF, 0x5C, 0xAF, 0x5D, 0xAF, 0x5E, 0xAF, 0x5F, 0xAF, 0x60, 0xAF, 0x61, 0xAF, 0x62, 0xAF, 0x63, 0xAF, 0x64,\n\t0xAF, 0x65, 0xAF, 0x66, 0xAF, 0x67, 0xAF, 0x68, 0xAF, 0x69, 0xAF, 0x6B, 0xAF, 0x6C, 0xAF, 0x6D, 0xAF, 0x6E, 0xAF, 0x6F, 0xAF, 0x70, 0xAF, 0x71, 0xAF, 0x72, 0xAF, 0x73, 0xAF, 0x74, 0xAF, 0x75,\n\t0xAF, 0x76, 0xAF, 0x77, 0xAF, 0x78, 0xAF, 0x79, 0xAF, 0x7A, 0xAF, 0x7B, 0xAF, 0x7C, 0xAF, 0x7D, 0xAF, 0x7E, 0xAF, 0x80, 0xAF, 0x81, 0xAF, 0x82, 0xAF, 0x83, 0xAF, 0x84, 0xAF, 0x85, 0xAF, 0x86,\n\t0xAF, 0x87, 0xAF, 0x88, 0xAF, 0x89, 0xAF, 0x8A, 0xAF, 0x8B, 0xAF, 0x8C, 0xAF, 0x8D, 0xAF, 0x8E, 0xAF, 0x8F, 0xAF, 0x90, 0xAF, 0x91, 0xAF, 0x92, 0xAF, 0x93, 0xAF, 0x94, 0xAF, 0x95, 0xAF, 0x96,\n\t0xAF, 0x97, 0xAF, 0x98, 0xAF, 0x99, 0xAF, 0x9A, 0xAF, 0x9B, 0xAF, 0x9C, 0xAF, 0x9D, 0xAF, 0x9E, 0xAF, 0xA0, 0xAF, 0xA1, 0xAF, 0xA2, 0xAF, 0xA3, 0xAF, 0xA4, 0xAF, 0xA5, 0xAF, 0xA6, 0xAF, 0xA7,\n\t0xAF, 0xA8, 0xAF, 0xA9, 0xAF, 0xAA, 0xAF, 0xAB, 0xAF, 0xAC, 0xAF, 0xAD, 0xAF, 0xAE, 0xAF, 0xAF, 0xAF, 0xB0, 0xAF, 0xB1, 0xAF, 0xB2, 0xAF, 0xB3, 0xAF, 0xB4, 0xAF, 0xB5, 0xAF, 0xB6, 0xAF, 0xB7,\n\t0xAF, 0xB8, 0xAF, 0xB9, 0xAF, 0xBA, 0xAF, 0xBB, 0xAF, 0xBC, 0xAF, 0xBD, 0xAF, 0xBE, 0xAF, 0xBF, 0xAF, 0xC0, 0xAF, 0xC1, 0xAF, 0xC2, 0xAF, 0xC3, 0xAF, 0xC4, 0xAF, 0xC5, 0xAF, 0xC6, 0xAF, 0xC7,\n\t0xAF, 0xC8, 0xAF, 0xC9, 0xAF, 0xCA, 0xAF, 0xCB, 0xAF, 0xCC, 0xAF, 0xCD, 0xAF, 0xCE, 0xAF, 0xCF, 0xAF, 0xD0, 0xAF, 0xD1, 0xAF, 0xD2, 0xAF, 0xD3, 0xAF, 0xD4, 0xAF, 0xD5, 0xAF, 0xD6, 0xAF, 0xD7,\n\t0xAF, 0xD8, 0xAF, 0xD9, 0xAF, 0xDA, 0xAF, 0xDB, 0xAF, 0xDC, 0xAF, 0xDC, 0xAF, 0xDD, 0xAF, 0xDE, 0xAF, 0xDF, 0xAF, 0xE0, 0xAF, 0xE1, 0xAF, 0xE2, 0xAF, 0xE3, 0xAF, 0xE4, 0xAF, 0xE5, 0xAF, 0xE6,\n\t0xAF, 0xE7, 0xAF, 0xE8, 0xAF, 0xE9, 0xAF, 0xEA, 0xAF, 0xEB, 0xAF, 0xEC, 0xAF, 0xED, 0xAF, 0xEE, 0xAF, 0xEF, 0xAF, 0xF0, 0xAF, 0xF1, 0xAF, 0xF2, 0xAF, 0xF3, 0xAF, 0xF4, 0xAF, 0xF5, 0xAF, 0xF6,\n\t0xAF, 0xF7, 0xAF, 0xF8, 0xAF, 0xF9, 0xAF, 0xFA, 0xAF, 0xFA, 0xAF, 0xFB, 0xAF, 0xFC, 0xAF, 0xFD, 0xAF, 0xFE, 0xAF, 0xFF, 0xAF, 0x00, 0xB0, 0x01, 0xB0, 0x01, 0xB0, 0x02, 0x02, 0x00, 0xF1, 0x12,\n\t0x03, 0xB0, 0x03, 0xB0, 0x04, 0xB0, 0x04, 0xB0, 0x05, 0xB0, 0x05, 0xB0, 0x06, 0xB0, 0x06, 0xB0, 0x07, 0xB0, 0x07, 0xB0, 0x08, 0xB0, 0x08, 0xB0, 0x09, 0xB0, 0x09, 0xB0, 0x0A, 0xB0, 0x0A, 0xB0,\n\t0x0B, 0x02, 0x00, 0xF1, 0x0A, 0x0C, 0xB0, 0x0C, 0xB0, 0x0D, 0xB0, 0x0D, 0xB0, 0x0E, 0xB0, 0x0E, 0xB0, 0x0F, 0xB0, 0x0F, 0xB0, 0x10, 0xB0, 0x10, 0xB0, 0x11, 0xB0, 0x11, 0xB0, 0x12, 0x02, 0x00,\n\t0xF1, 0x06, 0x13, 0xB0, 0x13, 0xB0, 0x14, 0xB0, 0x14, 0xB0, 0x15, 0xB0, 0x15, 0xB0, 0x16, 0xB0, 0x16, 0xB0, 0x17, 0xB0, 0x17, 0xB0, 0x18, 0x02, 0x00, 0xF1, 0x02, 0x19, 0xB0, 0x19, 0xB0, 0x1A,\n\t0xB0, 0x1A, 0xB0, 0x1B, 0xB0, 0x1B, 0xB0, 0x1C, 0xB0, 0x1C, 0xB0, 0x1D, 0x02, 0x00, 0xF1, 0x02, 0x1E, 0xB0, 0x1E, 0xB0, 0x1F, 0xB0, 0x1F, 0xB0, 0x20, 0xB0, 0x20, 0xB0, 0x21, 0xB0, 0x21, 0xB0,\n\t0x22, 0x02, 0x00, 0xD1, 0x23, 0xB0, 0x23, 0xB0, 0x24, 0xB0, 0x24, 0xB0, 0x25, 0xB0, 0x25, 0xB0, 0x26, 0x02, 0x00, 0xD1, 0x27, 0xB0, 0x27, 0xB0, 0x28, 0xB0, 0x28, 0xB0, 0x29, 0xB0, 0x29, 0xB0,\n\t0x2A, 0x02, 0x00, 0xD1, 0x2B, 0xB0, 0x2B, 0xB0, 0x2C, 0xB0, 0x2C, 0xB0, 0x2D, 0xB0, 0x2D, 0xB0, 0x2E, 0x02, 0x00, 0xD1, 0x2F, 0xB0, 0x2F, 0xB0, 0x30, 0xB0, 0x30, 0xB0, 0x31, 0xB0, 0x31, 0xB0,\n\t0x32, 0x02, 0x00, 0x91, 0x33, 0xB0, 0x33, 0xB0, 0x34, 0xB0, 0x34, 0xB0, 0x35, 0x02, 0x00, 0xD1, 0x36, 0xB0, 0x36, 0xB0, 0x37, 0xB0, 0x37, 0xB0, 0x38, 0xB0, 0x38, 0xB0, 0x39, 0x02, 0x00, 0x91,\n\t0x3A, 0xB0, 0x3A, 0xB0, 0x3B, 0xB0, 0x3B, 0xB0, 0x3C, 0x02, 0x00, 0x91, 0x3D, 0xB0, 0x3D, 0xB0, 0x3E, 0xB0, 0x3E, 0xB0, 0x3F, 0x02, 0x00, 0x91, 0x40, 0xB0, 0x40, 0xB0, 0x41, 0xB0, 0x41, 0xB0,\n\t0x42, 0x02, 0x00, 0x91, 0x43, 0xB0, 0x43, 0xB0, 0x44, 0xB0, 0x44, 0xB0, 0x45, 0x02, 0x00, 0x91, 0x46, 0xB0, 0x46, 0xB0, 0x47, 0xB0, 0x47, 0xB0, 0x48, 0x02, 0x00, 0x51, 0x49, 0xB0, 0x49, 0xB0,\n\t0x4A, 0x02, 0x00, 0x91, 0x4B, 0xB0, 0x4B, 0xB0, 0x4C, 0xB0, 0x4C, 0xB0, 0x4D, 0x02, 0x00, 0x51, 0x4E, 0xB0, 0x4E, 0xB0, 0x4F, 0x02, 0x00, 0x91, 0x50, 0xB0, 0x50, 0xB0, 0x51, 0xB0, 0x51, 0xB0,\n\t0x52, 0x02, 0x00, 0x51, 0x53, 0xB0, 0x53, 0xB0, 0x54, 0x02, 0x00, 0x91, 0x55, 0xB0, 0x55, 0xB0, 0x56, 0xB0, 0x56, 0xB0, 0x57, 0x02, 0x00, 0x51, 0x58, 0xB0, 0x58, 0xB0, 0x59, 0x02, 0x00, 0x51,\n\t0x5A, 0xB0, 0x5A, 0xB0, 0x5B, 0x02, 0x00, 0x51, 0x5C, 0xB0, 0x5C, 0xB0, 0x5D, 0x02, 0x00, 0x51, 0x5E, 0xB0, 0x5E, 0xB0, 0x5F, 0x02, 0x00, 0x51, 0x60, 0xB0, 0x60, 0xB0, 0x61, 0x02, 0x00, 0x51,\n\t0x62, 0xB0, 0x62, 0xB0, 0x63, 0x02, 0x00, 0x51, 0x64, 0xB0, 0x64, 0xB0, 0x65, 0x02, 0x00, 0x51, 0x66, 0xB0, 0x66, 0xB0, 0x67, 0x02, 0x00, 0x51, 0x68, 0xB0, 0x68, 0xB0, 0x69, 0x02, 0x00, 0x51,\n\t0x6A, 0xB0, 0x6A, 0xB0, 0x6B, 0x02, 0x00, 0x51, 0x6C, 0xB0, 0x6C, 0xB0, 0x6D, 0x02, 0x00, 0x51, 0x6E, 0xB0, 0x6E, 0xB0, 0x6F, 0x02, 0x00, 0x11, 0x70, 0x02, 0x00, 0x51, 0x71, 0xB0, 0x71, 0xB0,\n\t0x72, 0x02, 0x00, 0x51, 0x73, 0xB0, 0x73, 0xB0, 0x74, 0x02, 0x00, 0x11, 0x75, 0x02, 0x00, 0x51, 0x76, 0xB0, 0x76, 0xB0, 0x77, 0x02, 0x00, 0x51, 0x78, 0xB0, 0x78, 0xB0, 0x79, 0x02, 0x00, 0x11,\n\t0x7A, 0x02, 0x00, 0x51, 0x7B, 0xB0, 0x7B, 0xB0, 0x7C, 0x02, 0x00, 0x11, 0x7D, 0x02, 0x00, 0x51, 0x7E, 0xB0, 0x7E, 0xB0, 0x7F, 0x02, 0x00, 0x11, 0x80, 0x02, 0x00, 0x00, 0x89, 0x66, 0x11, 0x82,\n\t0x02, 0x00, 0x11, 0x83, 0x02, 0x00, 0x51, 0x84, 0xB0, 0x84, 0xB0, 0x85, 0x02, 0x00, 0x11, 0x86, 0x02, 0x00, 0x51, 0x87, 0xB0, 0x87, 0xB0, 0x88, 0x02, 0x00, 0x11, 0x89, 0x02, 0x00, 0x11, 0x8A,\n\t0x02, 0x00, 0x51, 0x8B, 0xB0, 0x8B, 0xB0, 0x8C, 0x02, 0x00, 0x11, 0x8D, 0x02, 0x00, 0x11, 0x8E, 0x02, 0x00, 0x51, 0x8F, 0xB0, 0x8F, 0xB0, 0x90, 0x02, 0x00, 0x11, 0x91, 0x02, 0x00, 0x11, 0x92,\n\t0x02, 0x00, 0x51, 0x93, 0xB0, 0x93, 0xB0, 0x94, 0x02, 0x00, 0x11, 0x95, 0x02, 0x00, 0x11, 0x96, 0x02, 0x00, 0x11, 0x97, 0x02, 0x00, 0x51, 0x98, 0xB0, 0x98, 0xB0, 0x99, 0x02, 0x00, 0x11, 0x9A,\n\t0x02, 0x00, 0x11, 0x9B, 0x02, 0x00, 0x11, 0x9C, 0x02, 0x00, 0x11, 0x9D, 0x02, 0x00, 0x11, 0x9E, 0x02, 0x00, 0x51, 0x9F, 0xB0, 0x9F, 0xB0, 0xA0, 0x02, 0x00, 0x11, 0xA1, 0x02, 0x00, 0x11, 0xA2,\n\t0x02, 0x00, 0x11, 0xA3, 0x02, 0x00, 0x11, 0xA4, 0x02, 0x00, 0x11, 0xA5, 0x02, 0x00, 0x11, 0xA6, 0x02, 0x00, 0x11, 0xA7, 0x02, 0x00, 0x11, 0xA8, 0x02, 0x00, 0x11, 0xA9, 0x02, 0x00, 0x11, 0xAA,\n\t0x02, 0x00, 0x11, 0xAB, 0x02, 0x00, 0x11, 0xAC, 0x02, 0x00, 0x11, 0xAD, 0x02, 0x00, 0x11, 0xAE, 0x02, 0x00, 0x11, 0xAF, 0x02, 0x00, 0x02, 0x01, 0x00, 0x11, 0xB1, 0x02, 0x00, 0x11, 0xB2, 0x02,\n\t0x00, 0x11, 0xB3, 0x02, 0x00, 0x11, 0xB4, 0x02, 0x00, 0x11, 0xB5, 0x02, 0x00, 0x11, 0xB6, 0x02, 0x00, 0x11, 0xB7, 0x02, 0x00, 0x11, 0xB8, 0x02, 0x00, 0x11, 0xB9, 0x02, 0x00, 0x11, 0xBA, 0x02,\n\t0x00, 0x11, 0xBB, 0x02, 0x00, 0x13, 0xBC, 0x02, 0x00, 0x11, 0xBD, 0x02, 0x00, 0x11, 0xBE, 0x02, 0x00, 0x11, 0xBF, 0x02, 0x00, 0x11, 0xC0, 0x02, 0x00, 0x11, 0xC1, 0x02, 0x00, 0x11, 0xC2, 0x02,\n\t0x00, 0x13, 0xC3, 0x02, 0x00, 0x11, 0xC4, 0x02, 0x00, 0x11, 0xC5, 0x02, 0x00, 0x11, 0xC6, 0x02, 0x00, 0x13, 0xC7, 0x02, 0x00, 0x11, 0xC8, 0x02, 0x00, 0x11, 0xC9, 0x02, 0x00, 0x11, 0xCA, 0x02,\n\t0x00, 0x13, 0xCB, 0x02, 0x00, 0x11, 0xCC, 0x02, 0x00, 0x11, 0xCD, 0x02, 0x00, 0x13, 0xCE, 0x02, 0x00, 0x11, 0xCF, 0x02, 0x00, 0x11, 0xD0, 0x02, 0x00, 0x13, 0xD1, 0x02, 0x00, 0x11, 0xD2, 0x02,\n\t0x00, 0x11, 0xD3, 0x02, 0x00, 0x13, 0xD4, 0x02, 0x00, 0x11, 0xD5, 0x02, 0x00, 0x11, 0xD6, 0x02, 0x00, 0x13, 0xD7, 0x02, 0x00, 0x11, 0xD8, 0x02, 0x00, 0x13, 0xD9, 0x02, 0x00, 0x11, 0xDA, 0x02,\n\t0x00, 0x13, 0xDB, 0x02, 0x00, 0x11, 0xDC, 0x02, 0x00, 0x13, 0xDD, 0x02, 0x00, 0x11, 0xDE, 0x02, 0x00, 0x13, 0xDF, 0x02, 0x00, 0x11, 0xE0, 0x02, 0x00, 0x13, 0xE1, 0x02, 0x00, 0x11, 0xE2, 0x02,\n\t0x00, 0x13, 0xE3, 0x02, 0x00, 0x11, 0xE4, 0x02, 0x00, 0x13, 0xE5, 0x02, 0x00, 0x13, 0xE6, 0x02, 0x00, 0x11, 0xE7, 0x02, 0x00, 0x13, 0xE8, 0x02, 0x00, 0x13, 0xE9, 0x02, 0x00, 0x11, 0xEA, 0x02,\n\t0x00, 0x13, 0xEB, 0x02, 0x00, 0x13, 0xEC, 0x02, 0x00, 0x11, 0xED, 0x02, 0x00, 0x13, 0xEE, 0x02, 0x00, 0x13, 0xEF, 0x02, 0x00, 0x11, 0xF0, 0x02, 0x00, 0xF1, 0x4C, 0xF1, 0xB0, 0xF1, 0xB0, 0xF2,\n\t0xB0, 0xF3, 0xB0, 0xF3, 0xB0, 0xF4, 0xB0, 0xF4, 0xB0, 0xF5, 0xB0, 0xF5, 0xB0, 0xF6, 0xB0, 0xF6, 0xB0, 0xF7, 0xB0, 0xF7, 0xB0, 0xF8, 0xB0, 0xF8, 0xB0, 0xF9, 0xB0, 0xF9, 0xB0, 0xFA, 0xB0, 0xFA,\n\t0xB0, 0xFB, 0xB0, 0xFB, 0xB0, 0xFC, 0xB0, 0xFC, 0xB0, 0xFD, 0xB0, 0xFD, 0xB0, 0xFE, 0xB0, 0xFE, 0xB0, 0xFF, 0xB0, 0xFF, 0xB0, 0x00, 0xB1, 0x00, 0xB1, 0x01, 0xB1, 0x01, 0xB1, 0x02, 0xB1, 0x02,\n\t0xB1, 0x03, 0xB1, 0x03, 0xB1, 0x04, 0xB1, 0x04, 0xB1, 0x05, 0xB1, 0x05, 0xB1, 0x06, 0xB1, 0x06, 0xB1, 0x07, 0xB1, 0x07, 0xB1, 0x08, 0x02, 0x00, 0xF1, 0x0A, 0x09, 0xB1, 0x09, 0xB1, 0x0A, 0xB1,\n\t0x0A, 0xB1, 0x0B, 0xB1, 0x0B, 0xB1, 0x0C, 0xB1, 0x0C, 0xB1, 0x0D, 0xB1, 0x0D, 0xB1, 0x0E, 0xB1, 0x0E, 0xB1, 0x0F, 0x02, 0x00, 0xD1, 0x10, 0xB1, 0x10, 0xB1, 0x11, 0xB1, 0x11, 0xB1, 0x12, 0xB1,\n\t0x12, 0xB1, 0x13, 0x02, 0x00, 0xD1, 0x14, 0xB1, 0x14, 0xB1, 0x15, 0xB1, 0x15, 0xB1, 0x16, 0xB1, 0x16, 0xB1, 0x17, 0x02, 0x00, 0x91, 0x18, 0xB1, 0x18, 0xB1, 0x19, 0xB1, 0x19, 0xB1, 0x1A, 0x02,\n\t0x00, 0x91, 0x1B, 0xB1, 0x1B, 0xB1, 0x1C, 0xB1, 0x1C, 0xB1, 0x1D, 0x02, 0x00, 0x51, 0x1E, 0xB1, 0x1E, 0xB1, 0x1F, 0x02, 0x00, 0x91, 0x20, 0xB1, 0x20, 0xB1, 0x21, 0xB1, 0x21, 0xB1, 0x22, 0x02,\n\t0x00, 0x51, 0x23, 0xB1, 0x23, 0xB1, 0x24, 0x02, 0x00, 0x51, 0x25, 0xB1, 0x25, 0xB1, 0x26, 0x02, 0x00, 0x11, 0x27, 0x02, 0x00, 0x51, 0x28, 0xB1, 0x28, 0xB1, 0x29, 0x02, 0x00, 0x51, 0x2A, 0xB1,\n\t0x2A, 0xB1, 0x2B, 0x02, 0x00, 0x11, 0x2C, 0x02, 0x00, 0x51, 0x2D, 0xB1, 0x2D, 0xB1, 0x2E, 0x02, 0x00, 0x11, 0x2F, 0x02, 0x00, 0x51, 0x30, 0xB1, 0x30, 0xB1, 0x31, 0x02, 0x00, 0x11, 0x32, 0x02,\n\t0x00, 0x11, 0x33, 0x02, 0x00, 0x11, 0x34, 0x02, 0x00, 0x11, 0x35, 0x02, 0x00, 0x51, 0x36, 0xB1, 0x36, 0xB1, 0x37, 0x02, 0x00, 0x11, 0x38, 0x02, 0x00, 0x11, 0x39, 0x02, 0x00, 0x11, 0x3A, 0x02,\n\t0x00, 0x11, 0x3B, 0x02, 0x00, 0x11, 0x3C, 0x02, 0x00, 0x11, 0x3D, 0x02, 0x00, 0x13, 0x3E, 0x02, 0x00, 0x11, 0x3F, 0x02, 0x00, 0x11, 0x40, 0x02, 0x00, 0x11, 0x41, 0x02, 0x00, 0x11, 0x42, 0x02,\n\t0x00, 0x13, 0x43, 0x02, 0x00, 0x11, 0x44, 0x02, 0x00, 0x11, 0x45, 0x02, 0x00, 0x13, 0x46, 0x02, 0x00, 0x11, 0x47, 0x02, 0x00, 0x13, 0x48, 0x02, 0x00, 0x11, 0x49, 0x02, 0x00, 0x13, 0x4A, 0x02,\n\t0x00, 0x13, 0x4B, 0x02, 0x00, 0x11, 0x4C, 0x02, 0x00, 0x13, 0x4D, 0x02, 0x00, 0x13, 0x4E, 0x02, 0x00, 0x13, 0x4F, 0x02, 0x00, 0x13, 0x50, 0x02, 0x00, 0x13, 0x51, 0x02, 0x00, 0x13, 0x52, 0x02,\n\t0x00, 0x13, 0x53, 0x02, 0x00, 0x13, 0x54, 0x02, 0x00, 0x15, 0x55, 0x02, 0x00, 0x13, 0x56, 0x02, 0x00, 0x13, 0x57, 0x02, 0x00, 0x15, 0x58, 0x02, 0x00, 0x15, 0x59, 0x02, 0x00, 0x15, 0x5A, 0x02,\n\t0x00, 0x13, 0x5B, 0x02, 0x00, 0x15, 0x5C, 0x02, 0x00, 0x17, 0x5D, 0x02, 0x00, 0x15, 0x5E, 0x02, 0x00, 0x15, 0x5F, 0x02, 0x00, 0x17, 0x60, 0x02, 0x00, 0x17, 0x61, 0x02, 0x00, 0x17, 0x62, 0x02,\n\t0x00, 0x17, 0x63, 0x02, 0x00, 0x17, 0x64, 0x02, 0x00, 0x19, 0x65, 0x02, 0x00, 0x19, 0x66, 0x02, 0x00, 0x1B, 0x67, 0x02, 0x00, 0x19, 0x68, 0x02, 0x00, 0x1D, 0x69, 0x02, 0x00, 0x1D, 0x6A, 0x02,\n\t0x00, 0x1D, 0x6B, 0x02, 0x00, 0x1F, 0x6C, 0x02, 0x00, 0x02, 0x1F, 0x6D, 0x02, 0x00, 0x04, 0x1F, 0x6E, 0x02, 0x00, 0x08, 0x1F, 0x6F, 0x02, 0x00, 0x0E, 0x1F, 0x70, 0x02, 0x00, 0x1E, 0x1F, 0x71,\n\t0x02, 0x00, 0x72, 0x1F, 0x70, 0x02, 0x00, 0x20, 0x1F, 0x6F, 0x02, 0x00, 0x10, 0x1F, 0x6E, 0x02, 0x00, 0x0A, 0x0F, 0x84, 0x01, 0x05, 0x1F, 0x6C, 0x02, 0x00, 0x04, 0x2E, 0x6B, 0xB1, 0x02, 0x00,\n\t0x1E, 0x6A, 0x02, 0x00, 0x0E, 0x28, 0x02, 0x0B, 0x48, 0x02, 0x1D, 0x68, 0x68, 0x02, 0x1B, 0x66, 0x02, 0x00, 0x0A, 0xA4, 0x02, 0x1B, 0x64, 0x02, 0x00, 0x19, 0x63, 0x02, 0x00, 0x08, 0xF4, 0x02,\n\t0x19, 0x61, 0x02, 0x00, 0x08, 0x26, 0x03, 0x17, 0x5F, 0x02, 0x00, 0x17, 0x5E, 0x02, 0x00, 0x08, 0x6A, 0x03, 0x17, 0x5C, 0x02, 0x00, 0x15, 0x5B, 0x02, 0x00, 0x17, 0x5A, 0x02, 0x00, 0x06, 0xBE,\n\t0x03, 0x17, 0x58, 0x02, 0x00, 0x15, 0x57, 0x02, 0x00, 0x15, 0x56, 0x02, 0x00, 0x06, 0x0C, 0x04, 0x15, 0x54, 0x02, 0x00, 0x15, 0x53, 0x02, 0x00, 0x04, 0x42, 0x04, 0x15, 0x51, 0x02, 0x00, 0x15,\n\t0x50, 0x02, 0x00, 0x04, 0x76, 0x04, 0x15, 0x4E, 0x02, 0x00, 0x04, 0x98, 0x04, 0x15, 0x4C, 0x02, 0x00, 0x04, 0xB8, 0x04, 0x15, 0x4A, 0x02, 0x00, 0x13, 0x49, 0x02, 0x00, 0x04, 0xE8, 0x04, 0x13,\n\t0x47, 0x02, 0x00, 0x04, 0x06, 0x05, 0x15, 0x45, 0x02, 0x00, 0x13, 0x44, 0x02, 0x00, 0x04, 0x34, 0x05, 0x13, 0x42, 0x02, 0x00, 0x13, 0x41, 0x02, 0x00, 0x02, 0x5E, 0x05, 0x13, 0x3F, 0x02, 0x00,\n\t0x04, 0x7A, 0x05, 0x13, 0x3D, 0x02, 0x00, 0x13, 0x3C, 0x02, 0x00, 0x13, 0x3B, 0x02, 0x00, 0x02, 0xB2, 0x05, 0x13, 0x39, 0x02, 0x00, 0x13, 0x38, 0x02, 0x00, 0x02, 0xDA, 0x05, 0x13, 0x36, 0x02,\n\t0x00, 0x13, 0x35, 0x02, 0x00, 0x02, 0x00, 0x06, 0x13, 0x33, 0x02, 0x00, 0x02, 0x1A, 0x06, 0x13, 0x31, 0x02, 0x00, 0x11, 0x30, 0x02, 0x00, 0x13, 0x2F, 0x02, 0x00, 0x02, 0x4C, 0x06, 0x11, 0x2D,\n\t0x02, 0x00, 0x13, 0x2C, 0x02, 0x00, 0x02, 0x70, 0x06, 0x11, 0x2A, 0x02, 0x00, 0x13, 0x29, 0x02, 0x00, 0x11, 0x28, 0x02, 0x00, 0x02, 0x9E, 0x06, 0x13, 0x26, 0x02, 0x00, 0x11, 0x25, 0x02, 0x00,\n\t0x02, 0xC2, 0x06, 0x11, 0x23, 0x02, 0x00, 0x13, 0x22, 0x02, 0x00, 0x11, 0x21, 0x02, 0x00, 0x11, 0x20, 0x02, 0x00, 0x02, 0xFA, 0x06, 0x11, 0x1E, 0x02, 0x00, 0x02, 0x10, 0x07, 0x11, 0x1C, 0x02,\n\t0x00, 0x13, 0x1B, 0x02, 0x00, 0x02, 0x32, 0x07, 0x11, 0x19, 0x02, 0x00, 0x11, 0x18, 0x02, 0x00, 0x02, 0x52, 0x07, 0x11, 0x16, 0x02, 0x00, 0x00, 0x66, 0x07, 0x00, 0x6E, 0x07, 0x11, 0x13, 0x7E,\n\t0x07, 0x11, 0x11, 0x8C, 0x07, 0x11, 0x0F, 0x9C, 0x07, 0x31, 0x0D, 0xB1, 0x0C, 0xB0, 0x07, 0x11, 0x0A, 0xBE, 0x07, 0x11, 0x08, 0xCE, 0x07, 0x31, 0x06, 0xB1, 0x05, 0xE2, 0x07, 0x11, 0x03, 0xF0,\n\t0x07, 0x40, 0x01, 0xB1, 0x00, 0xB1, 0x04, 0x08, 0x11, 0xFE, 0x12, 0x08, 0x31, 0xFC, 0xB0, 0xFB, 0x26, 0x08, 0x31, 0xF9, 0xB0, 0xF8, 0x3A, 0x08, 0x11, 0xF6, 0x48, 0x08, 0xD1, 0xF4, 0xB0, 0xF3,\n\t0xB0, 0xF2, 0xB0, 0xF2, 0xB0, 0xF1, 0xB0, 0xF0, 0xB0, 0xEF, 0x7A, 0x08, 0x31, 0xED, 0xB0, 0xEC, 0x98, 0x08, 0x31, 0xEA, 0xB0, 0xE9, 0xB6, 0x08, 0x31, 0xE7, 0xB0, 0xE6, 0xD4, 0x08, 0x51, 0xE4,\n\t0xB0, 0xE3, 0xB0, 0xE2, 0xFA, 0x08, 0x51, 0xE0, 0xB0, 0xDF, 0xB0, 0xDE, 0x20, 0x09, 0x51, 0xDC, 0xB0, 0xDB, 0xB0, 0xDA, 0x46, 0x09, 0x51, 0xD8, 0xB0, 0xD7, 0xB0, 0xD6, 0x6C, 0x09, 0x51, 0xD4,\n\t0xB0, 0xD3, 0xB0, 0xD2, 0x90, 0x09, 0x71, 0xD0, 0xB0, 0xCF, 0xB0, 0xCE, 0xB0, 0xCD, 0xBE, 0x09, 0x51, 0xCB, 0xB0, 0xCA, 0xB0, 0xC9, 0xE2, 0x09, 0x71, 0xC7, 0xB0, 0xC6, 0xB0, 0xC5, 0xB0, 0xC4,\n\t0x0E, 0x0A, 0x91, 0xC2, 0xB0, 0xC1, 0xB0, 0xC0, 0xB0, 0xBF, 0xB0, 0xBE, 0x42, 0x0A, 0x71, 0xBC, 0xB0, 0xBB, 0xB0, 0xBA, 0xB0, 0xB9, 0x6E, 0x0A, 0x91, 0xB7, 0xB0, 0xB6, 0xB0, 0xB5, 0xB0, 0xB4,\n\t0xB0, 0xB3, 0xA0, 0x0A, 0xB1, 0xB1, 0xB0, 0xB0, 0xB0, 0xAF, 0xB0, 0xAE, 0xB0, 0xAD, 0xB0, 0xAC, 0xDA, 0x0A, 0xB1, 0xAA, 0xB0, 0xA9, 0xB0, 0xA8, 0xB0, 0xA7, 0xB0, 0xA6, 0xB0, 0xA5, 0x14, 0x0B,\n\t0xD1, 0xA3, 0xB0, 0xA2, 0xB0, 0xA1, 0xB0, 0xA0, 0xB0, 0x9F, 0xB0, 0x9E, 0xB0, 0x9D, 0x54, 0x0B, 0xF1, 0x00, 0x9B, 0xB0, 0x9A, 0xB0, 0x99, 0xB0, 0x98, 0xB0, 0x97, 0xB0, 0x96, 0xB0, 0x95, 0xB0,\n\t0x94, 0x9C, 0x0B, 0xF1, 0x00, 0x92, 0xB0, 0x91, 0xB0, 0x90, 0xB0, 0x8F, 0xB0, 0x8E, 0xB0, 0x8D, 0xB0, 0x8C, 0xB0, 0x8B, 0xE0, 0x0B, 0xF1, 0x06, 0x89, 0xB0, 0x88, 0xB0, 0x87, 0xB0, 0x86, 0xB0,\n\t0x85, 0xB0, 0x84, 0xB0, 0x83, 0xB0, 0x82, 0xB0, 0x81, 0xB0, 0x80, 0xB0, 0x7F, 0x3C, 0x0C, 0xF1, 0x08, 0x7D, 0xB0, 0x7C, 0xB0, 0x7B, 0xB0, 0x7A, 0xB0, 0x79, 0xB0, 0x78, 0xB0, 0x77, 0xB0, 0x76,\n\t0xB0, 0x75, 0xB0, 0x74, 0xB0, 0x73, 0xB0, 0x72, 0x9C, 0x0C, 0xF1, 0x12, 0x70, 0xB0, 0x6F, 0xB0, 0x6E, 0xB0, 0x6D, 0xB0, 0x6C, 0xB0, 0x6B, 0xB0, 0x6A, 0xB0, 0x69, 0xB0, 0x68, 0xB0, 0x67, 0xB0,\n\t0x66, 0xB0, 0x65, 0xB0, 0x64, 0xB0, 0x63, 0xB0, 0x62, 0xB0, 0x61, 0xB0, 0x60, 0x1C, 0x0D, 0xF1, 0x24, 0x5E, 0xB0, 0x5D, 0xB0, 0x5C, 0xB0, 0x5B, 0xB0, 0x5A, 0xB0, 0x59, 0xB0, 0x58, 0xB0, 0x57,\n\t0xB0, 0x56, 0xB0, 0x55, 0xB0, 0x54, 0xB0, 0x53, 0xB0, 0x52, 0xB0, 0x51, 0xB0, 0x50, 0xB0, 0x4F, 0xB0, 0x4E, 0xB0, 0x4D, 0xB0, 0x4C, 0xB0, 0x4B, 0xB0, 0x4A, 0xB0, 0x49, 0xB0, 0x48, 0xB0, 0x47,\n\t0xB0, 0x46, 0xB0, 0x45, 0xD8, 0x0D, 0xF1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x5D, 0x43, 0xB0, 0x42, 0xB0, 0x41, 0xB0, 0x40, 0xB0, 0x3F,\n\t0xB0, 0x3E, 0xB0, 0x3D, 0xB0, 0x3C, 0xB0, 0x3B, 0xB0, 0x3A, 0xB0, 0x39, 0xB0, 0x38, 0xB0, 0x37, 0xB0, 0x36, 0xB0, 0x35, 0xB0, 0x34, 0xB0, 0x33, 0xB0, 0x32, 0xB0, 0x31, 0xB0, 0x30, 0xB0, 0x2F,\n\t0xB0, 0x2E, 0xB0, 0x2D, 0xB0, 0x2C, 0xB0, 0x2B, 0xB0, 0x2A, 0xB0, 0x29, 0xB0, 0x28, 0xB0, 0x27, 0xB0, 0x26, 0xB0, 0x25, 0xB0, 0x24, 0xB0, 0x23, 0xB0, 0x22, 0xB0, 0x21, 0xB0, 0x20, 0xB0, 0x1F,\n\t0xB0, 0x1E, 0xB0, 0x1D, 0xB0, 0x1C, 0xB0, 0x1B, 0xB0, 0x1A, 0xB0, 0x19, 0xB0, 0x18, 0xB0, 0x17, 0xB0, 0x16, 0xB0, 0x15, 0xB0, 0x14, 0xB0, 0x13, 0xB0, 0x12, 0xB0, 0x11, 0xB0, 0x10, 0xB0, 0x0F,\n\t0xB0, 0x0E, 0xB0, 0x0D, 0xB0, 0x0C, 0xB0, 0x0B, 0xB0, 0x0A, 0xB0, 0x09, 0xB0, 0x08, 0xB0, 0x07, 0xB0, 0x06, 0xB0, 0x05, 0xB0, 0x04, 0xB0, 0x03, 0xB0, 0x02, 0xB0, 0x01, 0xB0, 0x00, 0xB0, 0xFE,\n\t0xAF, 0xFC, 0xAF, 0xFA, 0xAF, 0xF8, 0xAF, 0xF6, 0xAF, 0xF4, 0xAF, 0xF2, 0xAF, 0xF0, 0xAF, 0xEE, 0xAF, 0xEC, 0xAF, 0xEA, 0xAF, 0xE7, 0xAF, 0xE5, 0xAF, 0xE3, 0xAF, 0xE1, 0xAF, 0xDF, 0xAF, 0xDD,\n\t0xAF, 0xDB, 0xAF, 0xD9, 0xAF, 0xD7, 0xAF, 0xD5, 0xAF, 0xD3, 0xAF, 0xD1, 0xAF, 0xCF, 0xAF, 0xCD, 0xAF, 0xCB, 0xAF, 0xC9, 0xAF, 0xC7, 0xAF, 0xC5, 0xAF, 0xC3, 0xAF, 0xC1, 0xAF, 0xBF, 0xAF, 0xBD,\n\t0xAF, 0xBB, 0xAF, 0xB9, 0xAF, 0xB7, 0xAF, 0xB5, 0xAF, 0xB2, 0xAF, 0xB0, 0xAF, 0xAE, 0xAF, 0xAC, 0xAF, 0xAA, 0xAF, 0xA8, 0xAF, 0xA6, 0xAF, 0xA4, 0xAF, 0xA2, 0xAF, 0xA0, 0xAF, 0x9E, 0xAF, 0x9C,\n\t0xAF, 0x9A, 0xAF, 0x98, 0xAF, 0x96, 0xAF, 0x94, 0xAF, 0x92, 0xAF, 0x90, 0xAF, 0x8E, 0xAF, 0x8C, 0xAF, 0x89, 0xAF, 0x87, 0xAF, 0x85, 0xAF, 0x83, 0xAF, 0x81, 0xAF, 0x7F, 0xAF, 0x7D, 0xAF, 0x7B,\n\t0xAF, 0x79, 0xAF, 0x77, 0xAF, 0x75, 0xAF, 0x73, 0xAF, 0x71, 0xAF, 0x6F, 0xAF, 0x6D, 0xAF, 0x6B, 0xAF, 0x69, 0xAF, 0x67, 0xAF, 0x64, 0xAF, 0x62, 0xAF, 0x60, 0xAF, 0x5E, 0xAF, 0x5C, 0xAF, 0x5A,\n\t0xAF, 0x58, 0xAF, 0x56, 0xAF, 0x54, 0xAF, 0x52, 0xAF, 0x50, 0xAF, 0x4E, 0xAF, 0x4C, 0xAF, 0x4A, 0xAF, 0x48, 0xAF, 0x46, 0xAF, 0x43, 0xAF, 0x41, 0xAF, 0x3F, 0xAF, 0x3D, 0xAF, 0x3B, 0xAF, 0x39,\n\t0xAF, 0x37, 0xAF, 0x35, 0xAF, 0x33, 0xAF, 0x31, 0xAF, 0x2F, 0xAF, 0x2D, 0xAF, 0x2B, 0xAF, 0x29, 0xAF, 0x27, 0xAF, 0x25, 0xAF, 0x22, 0xAF, 0x20, 0xAF, 0x1E, 0xAF, 0x1C, 0xAF, 0x1A, 0xAF, 0x18,\n\t0xAF, 0x16, 0xAF, 0x14, 0xAF, 0x12, 0xAF, 0x10, 0xAF, 0x0E, 0xAF, 0x0C, 0xAF, 0x0A, 0xAF, 0x08, 0xAF, 0x06, 0xAF, 0x04, 0xAF, 0x01, 0xAF, 0xFF, 0xAE, 0xFD, 0xAE, 0xFB, 0xAE, 0xF9, 0xAE, 0xF7,\n\t0xAE, 0xF5, 0xAE, 0xF3, 0xAE, 0xF1, 0xAE, 0xEF, 0xAE, 0xED, 0xAE, 0xEB, 0xAE, 0xE9, 0xAE, 0xE7, 0xAE, 0xE5, 0xAE, 0xE3, 0xAE, 0xE0, 0xAE, 0xDE, 0xAE, 0xDC, 0xAE, 0xDA, 0xAE, 0xD8, 0xAE, 0xD6,\n\t0xAE, 0xD4, 0xAE, 0xD2, 0xAE, 0xD0, 0xAE, 0xCE, 0xAE, 0xCC, 0xAE, 0xCA, 0xAE, 0xC8, 0xAE, 0xC6, 0xAE, 0xC4, 0xAE, 0xC2, 0xAE, 0xBF, 0xAE, 0xBD, 0xAE, 0xBB, 0xAE, 0xB9, 0xAE, 0xB7, 0xAE, 0xB5,\n\t0xAE, 0xB3, 0xAE, 0xB1, 0xAE, 0xAF, 0xAE, 0xAD, 0xAE, 0xAB, 0xAE, 0xA9, 0xAE, 0xA7, 0xAE, 0xA5, 0xAE, 0xA3, 0xAE, 0xA1, 0xAE, 0x9F, 0xAE, 0x9D, 0xAE, 0x9A, 0xAE, 0x98, 0xAE, 0x96, 0xAE, 0x94,\n\t0xAE, 0x92, 0xAE, 0x90, 0xAE, 0x8E, 0xAE, 0x8C, 0xAE, 0x8A, 0xAE, 0x88, 0xAE, 0x86, 0xAE, 0x84, 0xAE, 0x82, 0xAE, 0x80, 0xAE, 0x7E, 0xAE, 0x7C, 0xAE, 0x7A, 0xAE, 0x78, 0xAE, 0x76, 0xAE, 0x74,\n\t0xAE, 0x72, 0xAE, 0x6F, 0xAE, 0x6D, 0xAE, 0x6B, 0xAE, 0x69, 0xAE, 0x67, 0xAE, 0x65, 0xAE, 0x63, 0xAE, 0x61, 0xAE, 0x5F, 0xAE, 0x5D, 0xAE, 0x5B, 0xAE, 0x59, 0xAE, 0x57, 0xAE, 0x55, 0xAE, 0x53,\n\t0xAE, 0x51, 0xAE, 0x4F, 0xAE, 0x4D, 0xAE, 0x4B, 0xAE, 0x49, 0xAE, 0x47, 0xAE, 0x45, 0xAE, 0x43, 0xAE, 0x41, 0xAE, 0x3F, 0xAE, 0x3D, 0xAE, 0x3A, 0xAE, 0x38, 0xAE, 0x36, 0xAE, 0x34, 0xAE, 0x32,\n\t0xAE, 0x30, 0xAE, 0x2E, 0xAE, 0x2C, 0xAE, 0x2A, 0xAE, 0x28, 0xAE, 0x26, 0xAE, 0x24, 0xAE, 0x22, 0xAE, 0x20, 0xAE, 0x1E, 0xAE, 0x1C, 0xAE, 0x1A, 0xAE, 0x18, 0xAE, 0x16, 0xAE, 0x14, 0xAE, 0x12,\n\t0xAE, 0x10, 0xAE, 0x0E, 0xAE, 0x0C, 0xAE, 0x0A, 0xAE, 0x08, 0xAE, 0x06, 0xAE, 0x04, 0xAE, 0x02, 0xAE, 0x00, 0xAE, 0xFE, 0xAD, 0xFC, 0xAD, 0xFA, 0xAD, 0xF8, 0xAD, 0xF6, 0xAD, 0xF4, 0xAD, 0xF2,\n\t0xAD, 0xF0, 0xAD, 0xEE, 0xAD, 0xEC, 0xAD, 0xEA, 0xAD, 0xE8, 0xAD, 0xE6, 0xAD, 0xE4, 0xAD, 0xE2, 0xAD, 0xE0, 0xAD, 0xDE, 0xAD, 0xDC, 0xAD, 0xDA, 0xAD, 0xD8, 0xAD, 0xD6, 0xAD, 0xD4, 0xAD, 0xD2,\n\t0xAD, 0xD0, 0xAD, 0xCE, 0xAD, 0xCC, 0xAD, 0xCA, 0xAD, 0xC8, 0xAD, 0xC6, 0xAD, 0xC4, 0xAD, 0xC2, 0xAD, 0xC0, 0xAD, 0xBE, 0xAD, 0xBC, 0xAD, 0xBA, 0xAD, 0xB8, 0xAD, 0xB6, 0xAD, 0xB4, 0xAD, 0xB2,\n\t0xAD, 0xB0, 0xAD, 0xAE, 0xAD, 0xAC, 0xAD, 0xAA, 0xAD, 0xA8, 0xAD, 0xA6, 0xAD, 0xA4, 0xAD, 0xA2, 0xAD, 0xA0, 0xAD, 0x9E, 0xAD, 0x9C, 0xAD, 0x9A, 0xAD, 0x98, 0xAD, 0x96, 0xAD, 0x95, 0xAD, 0x93,\n\t0xAD, 0x91, 0xAD, 0x8F, 0xAD, 0x8D, 0xAD, 0x8B, 0xAD, 0x89, 0xAD, 0x87, 0xAD, 0x85, 0xAD, 0x83, 0xAD, 0x81, 0xAD, 0x7F, 0xAD, 0x7D, 0xAD, 0x7B, 0xAD, 0x79, 0xAD, 0x77, 0xAD, 0x75, 0xAD, 0x73,\n\t0xAD, 0x71, 0xAD, 0x6F, 0xAD, 0x6D, 0xAD, 0x6C, 0xAD, 0x6A, 0xAD, 0x68, 0xAD, 0x66, 0xAD, 0x64, 0xAD, 0x62, 0xAD, 0x60, 0xAD, 0x5E, 0xAD, 0x5C, 0xAD, 0x5A, 0xAD, 0x58, 0xAD, 0x56, 0xAD, 0x54,\n\t0xAD, 0x52, 0xAD, 0x50, 0xAD, 0x4E, 0xAD, 0x4D, 0xAD, 0x4B, 0xAD, 0x49, 0xAD, 0x47, 0xAD, 0x45, 0xAD, 0x43, 0xAD, 0x41, 0xAD, 0x3F, 0xAD, 0x3D, 0xAD, 0x3B, 0xAD, 0x39, 0xAD, 0x37, 0xAD, 0x36,\n\t0xAD, 0x34, 0xAD, 0x32, 0xAD, 0x30, 0xAD, 0x2E, 0xAD, 0x2C, 0xAD, 0x2A, 0xAD, 0x28, 0xAD, 0x26, 0xAD, 0x24, 0xAD, 0x22, 0xAD, 0x21, 0xAD, 0x1F, 0xAD, 0x1D, 0xAD, 0x1B, 0xAD, 0x19, 0xAD, 0x17,\n\t0xAD, 0x15, 0xAD, 0x13, 0xAD, 0x11, 0xAD, 0x0F, 0xAD, 0x0E, 0xAD, 0x0C, 0xAD, 0x0A, 0xAD, 0x08, 0xAD, 0x06, 0xAD, 0x04, 0xAD, 0x02, 0xAD, 0x00, 0xAD, 0xFF, 0xAC, 0xFD, 0xAC, 0xFB, 0xAC, 0xF9,\n\t0xAC, 0xF7, 0xAC, 0xF5, 0xAC, 0xF3, 0xAC, 0xF1, 0xAC, 0xF0, 0xAC, 0xEE, 0xAC, 0xEC, 0xAC, 0xEA, 0xAC, 0xE8, 0xAC, 0xE6, 0xAC, 0xE4, 0xAC, 0xE3, 0xAC, 0xE1, 0xAC, 0xDF, 0xAC, 0xDD, 0xAC, 0xDB,\n\t0xAC, 0xD9, 0xAC, 0xD7, 0xAC, 0xD6, 0xAC, 0xD4, 0xAC, 0xD2, 0xAC, 0xD0, 0xAC, 0xCE, 0xAC, 0xCC, 0xAC, 0xCA, 0xAC, 0xC9, 0xAC, 0xC7, 0xAC, 0xC5, 0xAC, 0xC3, 0xAC, 0xC1, 0xAC, 0xBF, 0xAC, 0xBE,\n\t0xAC, 0xBC, 0xAC, 0xBA, 0xAC, 0xB8, 0xAC, 0xB6, 0xAC, 0xB4, 0xAC, 0xB3, 0xAC, 0xB1, 0xAC, 0xAF, 0xAC, 0xAD, 0xAC, 0xAB, 0xAC, 0xA9, 0xAC, 0xA8, 0xAC, 0xA6, 0xAC, 0xA4, 0xAC, 0xA2, 0xAC, 0xA0,\n\t0xAC, 0x9F, 0xAC, 0x9D, 0xAC, 0x9B, 0xAC, 0x99, 0xAC, 0x97, 0xAC, 0x96, 0xAC, 0x94, 0xAC, 0x92, 0xAC, 0x90, 0xAC, 0x8E, 0xAC, 0x8D, 0xAC, 0x8B, 0xAC, 0x89, 0xAC, 0x87, 0xAC, 0x85, 0xAC, 0x84,\n\t0xAC, 0x82, 0xAC, 0x80, 0xAC, 0x7E, 0xAC, 0x7C, 0xAC, 0x7B, 0xAC, 0x79, 0xAC, 0x77, 0xAC, 0x75, 0xAC, 0x74, 0xAC, 0x72, 0xAC, 0x70, 0xAC, 0x6E, 0xAC, 0x6C, 0xAC, 0x6B, 0xAC, 0x69, 0xAC, 0x67,\n\t0xAC, 0x65, 0xAC, 0x64, 0xAC, 0x62, 0xAC, 0x60, 0xAC, 0x5E, 0xAC, 0x5D, 0xAC, 0x5B, 0xAC, 0x59, 0xAC, 0x57, 0xAC, 0x56, 0xAC, 0x54, 0xAC, 0x52, 0xAC, 0x50, 0xAC, 0x4F, 0xAC, 0x4D, 0xAC, 0x4B,\n\t0xAC, 0x49, 0xAC, 0x48, 0xAC, 0x46, 0xAC, 0x44, 0xAC, 0x42, 0xAC, 0x41, 0xAC, 0x3F, 0xAC, 0x3D, 0xAC, 0x3B, 0xAC, 0x3A, 0xAC, 0x38, 0xAC, 0x36, 0xAC, 0x35, 0xAC, 0x33, 0xAC, 0x31, 0xAC, 0x2F,\n\t0xAC, 0x2E, 0xAC, 0x2C, 0xAC, 0x2A, 0xAC, 0x28, 0xAC, 0x27, 0xAC, 0x25, 0xAC, 0x23, 0xAC, 0x22, 0xAC, 0x20, 0xAC, 0x1E, 0xAC, 0x1D, 0xAC, 0x1B, 0xAC, 0x19, 0xAC, 0x17, 0xAC, 0x16, 0xAC, 0x14,\n\t0xAC, 0x12, 0xAC, 0x11, 0xAC, 0x0F, 0xAC, 0x0D, 0xAC, 0x0C, 0xAC, 0x0A, 0xAC, 0x08, 0xAC, 0x06, 0xAC, 0x05, 0xAC, 0x03, 0xAC, 0x01, 0xAC, 0x00, 0xAC, 0xFC, 0xAB, 0xF9, 0xAB, 0xF6, 0xAB, 0xF2,\n\t0xAB, 0xEF, 0xAB, 0xEB, 0xAB, 0xE8, 0xAB, 0xE5, 0xAB, 0xE1, 0xAB, 0xDE, 0xAB, 0xDB, 0xAB, 0xD8, 0xAB, 0xD4, 0xAB, 0xD1, 0xAB, 0xCE, 0xAB, 0xCA, 0xAB, 0xC7, 0xAB, 0xC4, 0xAB, 0xC0, 0xAB, 0xBD,\n\t0xAB, 0xBA, 0xAB, 0xB7, 0xAB, 0xB3, 0xAB, 0xB0, 0xAB, 0xAD, 0xAB, 0xA9, 0xAB, 0xA6, 0xAB, 0xA3, 0xAB, 0xA0, 0xAB, 0x9C, 0xAB, 0x99, 0xAB, 0x96, 0xAB, 0x93, 0xAB, 0x8F, 0xAB, 0x8C, 0xAB, 0x89,\n\t0xAB, 0x86, 0xAB, 0x82, 0xAB, 0x7F, 0xAB, 0x7C, 0xAB, 0x79, 0xAB, 0x75, 0xAB, 0x72, 0xAB, 0x6F, 0xAB, 0x6C, 0xAB, 0x69, 0xAB, 0x65, 0xAB, 0x62, 0xAB, 0x5F, 0xAB, 0x5C, 0xAB, 0x59, 0xAB, 0x55,\n\t0xAB, 0x52, 0xAB, 0x4F, 0xAB, 0x4C, 0xAB, 0x49, 0xAB, 0x45, 0xAB, 0x42, 0xAB, 0x3F, 0xAB, 0x3C, 0xAB, 0x39, 0xAB, 0x36, 0xAB, 0x32, 0xAB, 0x2F, 0xAB, 0x2C, 0xAB, 0x29, 0xAB, 0x26, 0xAB, 0x23,\n\t0xAB, 0x20, 0xAB, 0x1C, 0xAB, 0x19, 0xAB, 0x16, 0xAB, 0x13, 0xAB, 0x10, 0xAB, 0x0D, 0xAB, 0x0A, 0xAB, 0x07, 0xAB, 0x04, 0xAB, 0x00, 0xAB, 0xFD, 0xAA, 0xFA, 0xAA, 0xF7, 0xAA, 0xF4, 0xAA, 0xF1,\n\t0xAA, 0xEE, 0xAA, 0xEB, 0xAA, 0xE8, 0xAA, 0xE5, 0xAA, 0xE2, 0xAA, 0xDE, 0xAA, 0xDB, 0xAA, 0xD8, 0xAA, 0xD5, 0xAA, 0xD2, 0xAA, 0xCF, 0xAA, 0xCC, 0xAA, 0xC9, 0xAA, 0xC6, 0xAA, 0xC3, 0xAA, 0xC0,\n\t0xAA, 0xBD, 0xAA, 0xBA, 0xAA, 0xB7, 0xAA, 0xB4, 0xAA, 0xB1, 0xAA, 0xAE, 0xAA, 0xAB, 0xAA, 0xA8, 0xAA, 0xA5, 0xAA, 0xA2, 0xAA, 0x9F, 0xAA, 0x9C, 0xAA, 0x99, 0xAA, 0x96, 0xAA, 0x93, 0xAA, 0x90,\n\t0xAA, 0x8D, 0xAA, 0x8A, 0xAA, 0x87, 0xAA, 0x84, 0xAA, 0x81, 0xAA, 0x7E, 0xAA, 0x7B, 0xAA, 0x78, 0xAA, 0x75, 0xAA, 0x72, 0xAA, 0x6F, 0xAA, 0x6C, 0xAA, 0x69, 0xAA, 0x66, 0xAA, 0x64, 0xAA, 0x61,\n\t0xAA, 0x5E, 0xAA, 0x5B, 0xAA, 0x58, 0xAA, 0x55, 0xAA, 0x52, 0xAA, 0x4F, 0xAA, 0x4C, 0xAA, 0x49, 0xAA, 0x46, 0xAA, 0x44, 0xAA, 0x41, 0xAA, 0x3E, 0xAA, 0x3B, 0xAA, 0x38, 0xAA, 0x35, 0xAA, 0x32,\n\t0xAA, 0x2F, 0xAA, 0x2C, 0xAA, 0x2A, 0xAA, 0x27, 0xAA, 0x24, 0xAA, 0x21, 0xAA, 0x1E, 0xAA, 0x1B, 0xAA, 0x19, 0xAA, 0x16, 0xAA, 0x13, 0xAA, 0x10, 0xAA, 0x0D, 0xAA, 0x0A, 0xAA, 0x08, 0xAA, 0x05,\n\t0xAA, 0x02, 0xAA, 0xFF, 0xA9, 0xFC, 0xA9, 0xF9, 0xA9, 0xF7, 0xA9, 0xF4, 0xA9, 0xF1, 0xA9, 0xEE, 0xA9, 0xEB, 0xA9, 0xE9, 0xA9, 0xE6, 0xA9, 0xE3, 0xA9, 0xE0, 0xA9, 0xDE, 0xA9, 0xDB, 0xA9, 0xD8,\n\t0xA9, 0xD5, 0xA9, 0xD2, 0xA9, 0xD0, 0xA9, 0xCA, 0xA9, 0xC5, 0xA9, 0xBF, 0xA9, 0xBA, 0xA9, 0xB4, 0xA9, 0xAF, 0xA9, 0xAA, 0xA9, 0xA4, 0xA9, 0x9F, 0xA9, 0x99, 0xA9, 0x94, 0xA9, 0x8F, 0xA9, 0x89,\n\t0xA9, 0x84, 0xA9, 0x7F, 0xA9, 0x79, 0xA9, 0x74, 0xA9, 0x6F, 0xA9, 0x69, 0xA9, 0x64, 0xA9, 0x5F, 0xA9, 0x5A, 0xA9, 0x55, 0xA9, 0x4F, 0xA9, 0x4A, 0xA9, 0x45, 0xA9, 0x40, 0xA9, 0x3B, 0xA9, 0x36,\n\t0xA9, 0x30, 0xA9, 0x2B, 0xA9, 0x26, 0xA9, 0x21, 0xA9, 0x1C, 0xA9, 0x17, 0xA9, 0x12, 0xA9, 0x0D, 0xA9, 0x08, 0xA9, 0x03, 0xA9, 0xFE, 0xA8, 0xF9, 0xA8, 0xF4, 0xA8, 0xEF, 0xA8, 0xEA, 0xA8, 0xE5,\n\t0xA8, 0xE1, 0xA8, 0xDC, 0xA8, 0xD7, 0xA8, 0xD2, 0xA8, 0xCD, 0xA8, 0xC8, 0xA8, 0xC3, 0xA8, 0xBF, 0xA8, 0xBA, 0xA8, 0xB5, 0xA8, 0xB0, 0xA8, 0xAC, 0xA8, 0xA7, 0xA8, 0xA2, 0xA8, 0x9D, 0xA8, 0x99,\n\t0xA8, 0x94, 0xA8, 0x8F, 0xA8, 0x8B, 0xA8, 0x86, 0xA8, 0x81, 0xA8, 0x7D, 0xA8, 0x78, 0xA8, 0x74, 0xA8, 0x6F, 0xA8, 0x6A, 0xA8, 0x66, 0xA8, 0x61, 0xA8, 0x5D, 0xA8, 0x58, 0xA8, 0x54, 0xA8, 0x4F,\n\t0xA8, 0x4B, 0xA8, 0x46, 0xA8, 0x42, 0xA8, 0x3D, 0xA8, 0x39, 0xA8, 0x35, 0xA8, 0x30, 0xA8, 0x2C, 0xA8, 0x27, 0xA8, 0x23, 0xA8, 0x1F, 0xA8, 0x1A, 0xA8, 0x16, 0xA8, 0x12, 0xA8, 0x0D, 0xA8, 0x09,\n\t0xA8, 0x05, 0xA8, 0x01, 0xA8, 0xF9, 0xA7, 0xF0, 0xA7, 0xE8, 0xA7, 0xE0, 0xA7, 0xD7, 0xA7, 0xCF, 0xA7, 0xC6, 0xA7, 0xBE, 0xA7, 0xB6, 0xA7, 0xAE, 0xA7, 0xA5, 0xA7, 0x9D, 0xA7, 0x95, 0xA7, 0x8D,\n\t0xA7, 0x85, 0xA7, 0x7D, 0xA7, 0x75, 0xA7, 0x6C, 0xA7, 0x64, 0xA7, 0x5C, 0xA7, 0x54, 0xA7, 0x4D, 0xA7, 0x45, 0xA7, 0x3D, 0xA7, 0x35, 0xA7, 0x2D, 0xA7, 0x25, 0xA7, 0x1D, 0xA7, 0x16, 0xA7, 0x0E,\n\t0xA7, 0x06, 0xA7, 0xFE, 0xA6, 0xF7, 0xA6, 0xEF, 0xA6, 0xE7, 0xA6, 0xE0, 0xA6, 0xD8, 0xA6, 0xD1, 0xA6, 0xC9, 0xA6, 0xC2, 0xA6, 0xBA, 0xA6, 0xB3, 0xA6, 0xAB, 0xA6, 0xA4, 0xA6, 0x9C, 0xA6, 0x95,\n\t0xA6, 0x8E, 0xA6, 0x86, 0xA6, 0x7F, 0xA6, 0x78, 0xA6, 0x71, 0xA6, 0x69, 0xA6, 0x62, 0xA6, 0x5B, 0xA6, 0x54, 0xA6, 0x4D, 0xA6, 0x46, 0xA6, 0x3F, 0xA6, 0x37, 0xA6, 0x30, 0xA6, 0x29, 0xA6, 0x22,\n\t0xA6, 0x1B, 0xA6, 0x15, 0xA6, 0x0E, 0xA6, 0x07, 0xA6, 0x00, 0xA6, 0xF9, 0xA5, 0xF2, 0xA5, 0xEB, 0xA5, 0xE5, 0xA5, 0xDE, 0xA5, 0xD7, 0xA5, 0xD0, 0xA5, 0xCA, 0xA5, 0xC3, 0xA5, 0xBD, 0xA5, 0xB6,\n\t0xA5, 0xAF, 0xA5, 0xA9, 0xA5, 0xA2, 0xA5, 0x9C, 0xA5, 0x95, 0xA5, 0x8F, 0xA5, 0x88, 0xA5, 0x82, 0xA5, 0x7B, 0xA5, 0x75, 0xA5, 0x6F, 0xA5, 0x68, 0xA5, 0x62, 0xA5, 0x5C, 0xA5, 0x55, 0xA5, 0x4F,\n\t0xA5, 0x49, 0xA5, 0x43, 0xA5, 0x3D, 0xA5, 0x36, 0xA5, 0x30, 0xA5, 0x2A, 0xA5, 0x24, 0xA5, 0x1E, 0xA5, 0x18, 0xA5, 0x12, 0xA5, 0x0C, 0xA5, 0x06, 0xA5, 0x00, 0xA5, 0xFA, 0xA4, 0xF4, 0xA4, 0xEE,\n\t0xA4, 0xE8, 0xA4, 0xE2, 0xA4, 0xDD, 0xA4, 0xD7, 0xA4, 0xD1, 0xA4, 0xCB, 0xA4, 0xC5, 0xA4, 0xC0, 0xA4, 0xBA, 0xA4, 0xB4, 0xA4, 0xAF, 0xA4, 0xA9, 0xA4, 0xA3, 0xA4, 0x9E, 0xA4, 0x98, 0xA4, 0x93,\n\t0xA4, 0x8D, 0xA4, 0x88, 0xA4, 0x82, 0xA4, 0x7D, 0xA4, 0x77, 0xA4, 0x72, 0xA4, 0x6C, 0xA4, 0x67, 0xA4, 0x61, 0xA4, 0x5C, 0xA4, 0x57, 0xA4, 0x51, 0xA4, 0x4C, 0xA4, 0x47, 0xA4, 0x42, 0xA4, 0x3C,\n\t0xA4, 0x37, 0xA4, 0x32, 0xA4, 0x2D, 0xA4, 0x28, 0xA4, 0x22, 0xA4, 0x1D, 0xA4, 0x18, 0xA4, 0x13, 0xA4, 0x0E, 0xA4, 0x09, 0xA4, 0x04, 0xA4, 0xFE, 0xA3, 0xF4, 0xA3, 0xEA, 0xA3, 0xE0, 0xA3, 0xD6,\n\t0xA3, 0xCD, 0xA3, 0xC3, 0xA3, 0xB9, 0xA3, 0xAF, 0xA3, 0xA6, 0xA3, 0x9C, 0xA3, 0x93, 0xA3, 0x89, 0xA3, 0x80, 0xA3, 0x76, 0xA3, 0x6D, 0xA3, 0x63, 0xA3, 0x5A, 0xA3, 0x51, 0xA3, 0x47, 0xA3, 0x3E,\n\t0xA3, 0x35, 0xA3, 0x2C, 0xA3, 0x22, 0xA3, 0x19, 0xA3, 0x10, 0xA3, 0x07, 0xA3, 0xFE, 0xA2, 0xF5, 0xA2, 0xEC, 0xA2, 0xE4, 0xA2, 0xDB, 0xA2, 0xD2, 0xA2, 0xC9, 0xA2, 0xC0, 0xA2, 0xB8, 0xA2, 0xAF,\n\t0xA2, 0xA6, 0xA2, 0x9E, 0xA2, 0x95, 0xA2, 0x8D, 0xA2, 0x84, 0xA2, 0x7C, 0xA2, 0x73, 0xA2, 0x6B, 0xA2, 0x63, 0xA2, 0x5A, 0xA2, 0x52, 0xA2, 0x4A, 0xA2, 0x42, 0xA2, 0x39, 0xA2, 0x31, 0xA2, 0x29,\n\t0xA2, 0x21, 0xA2, 0x19, 0xA2, 0x11, 0xA2, 0x09, 0xA2, 0x01, 0xA2, 0xF9, 0xA1, 0xF1, 0xA1, 0xE9, 0xA1, 0xE2, 0xA1, 0xDA, 0xA1, 0xD2, 0xA1, 0xCA, 0xA1, 0xC3, 0xA1, 0xBB, 0xA1, 0xB3, 0xA1, 0xAC,\n\t0xA1, 0xA4, 0xA1, 0x9D, 0xA1, 0x95, 0xA1, 0x8E, 0xA1, 0x86, 0xA1, 0x7F, 0xA1, 0x78, 0xA1, 0x70, 0xA1, 0x69, 0xA1, 0x62, 0xA1, 0x5A, 0xA1, 0x53, 0xA1, 0x4C, 0xA1, 0x45, 0xA1, 0x3E, 0xA1, 0x37,\n\t0xA1, 0x30, 0xA1, 0x29, 0xA1, 0x22, 0xA1, 0x1B, 0xA1, 0x14, 0xA1, 0x0D, 0xA1, 0x06, 0xA1, 0xFF, 0xA0, 0xF8, 0xA0, 0xF2, 0xA0, 0xEB, 0xA0, 0xE4, 0xA0, 0xDD, 0xA0, 0xD7, 0xA0, 0xD0, 0xA0, 0xCA,\n\t0xA0, 0xC3, 0xA0, 0xBC, 0xA0, 0xB6, 0xA0, 0xAF, 0xA0, 0xA9, 0xA0, 0xA3, 0xA0, 0x9C, 0xA0, 0x96, 0xA0, 0x8F, 0xA0, 0x89, 0xA0, 0x83, 0xA0, 0x7D, 0xA0, 0x76, 0xA0, 0x70, 0xA0, 0x6A, 0xA0, 0x64,\n\t0xA0, 0x5E, 0xA0, 0x58, 0xA0, 0x52, 0xA0, 0x4C, 0xA0, 0x46, 0xA0, 0x40, 0xA0, 0x3A, 0xA0, 0x34, 0xA0, 0x2E, 0xA0, 0x28, 0xA0, 0x22, 0xA0, 0x1C, 0xA0, 0x16, 0xA0, 0x11, 0xA0, 0x0B, 0xA0, 0x05,\n\t0xA0, 0xFF, 0x9F, 0xF4, 0x9F, 0xE9, 0x9F, 0xDD, 0x9F, 0xD2, 0x9F, 0xC7, 0x9F, 0xBC, 0x9F, 0xB1, 0x9F, 0xA6, 0x9F, 0x9B, 0x9F, 0x90, 0x9F, 0x86, 0x9F, 0x7B, 0x9F, 0x70, 0x9F, 0x65, 0x9F, 0x5B,\n\t0x9F, 0x50, 0x9F, 0x46, 0x9F, 0x3B, 0x9F, 0x31, 0x9F, 0x27, 0x9F, 0x1C, 0x9F, 0x12, 0x9F, 0x08, 0x9F, 0xFE, 0x9E, 0xF4, 0x9E, 0xE9, 0x9E, 0xDF, 0x9E, 0xD5, 0x9E, 0xCC, 0x9E, 0xC2, 0x9E, 0xB8,\n\t0x9E, 0xAE, 0x9E, 0xA4, 0x9E, 0x9B, 0x9E, 0x91, 0x9E, 0x87, 0x9E, 0x7E, 0x9E, 0x74, 0x9E, 0x6B, 0x9E, 0x62, 0x9E, 0x58, 0x9E, 0x4F, 0x9E, 0x46, 0x9E, 0x3C, 0x9E, 0x33, 0x9E, 0x2A, 0x9E, 0x21,\n\t0x9E, 0x18, 0x9E, 0x0F, 0x9E, 0x06, 0x9E, 0xFD, 0x9D, 0xF4, 0x9D, 0xEB, 0x9D, 0xE2, 0x9D, 0xDA, 0x9D, 0xD1, 0x9D, 0xC8, 0x9D, 0xC0, 0x9D, 0xB7, 0x9D, 0xAF, 0x9D, 0xA6, 0x9D, 0x9E, 0x9D, 0x95,\n\t0x9D, 0x8D, 0x9D, 0x85, 0x9D, 0x7C, 0x9D, 0x74, 0x9D, 0x6C, 0x9D, 0x64, 0x9D, 0x5C, 0x9D, 0x54, 0x9D, 0x4C, 0x9D, 0x44, 0x9D, 0x3C, 0x9D, 0x34, 0x9D, 0x2C, 0x9D, 0x24, 0x9D, 0x1C, 0x9D, 0x14,\n\t0x9D, 0x0D, 0x9D, 0x05, 0x9D, 0xFD, 0x9C, 0xF6, 0x9C, 0xEE, 0x9C, 0xE7, 0x9C, 0xDF, 0x9C, 0xD8, 0x9C, 0xD0, 0x9C, 0xC9, 0x9C, 0xC2, 0x9C, 0xBA, 0x9C, 0xB3, 0x9C, 0xAC, 0x9C, 0xA5, 0x9C, 0x9E,\n\t0x9C, 0x96, 0x9C, 0x8F, 0x9C, 0x88, 0x9C, 0x81, 0x9C, 0x7A, 0x9C, 0x73, 0x9C, 0x6D, 0x9C, 0x66, 0x9C, 0x5F, 0x9C, 0x58, 0x9C, 0x51, 0x9C, 0x4B, 0x9C, 0x44, 0x9C, 0x3D, 0x9C, 0x37, 0x9C, 0x30,\n\t0x9C, 0x2A, 0x9C, 0x23, 0x9C, 0x1D, 0x9C, 0x16, 0x9C, 0x10, 0x9C, 0x09, 0x9C, 0x03, 0x9C, 0xF9, 0x9B, 0xED, 0x9B, 0xE0, 0x9B, 0xD4, 0x9B, 0xC8, 0x9B, 0xBB, 0x9B, 0xAF, 0x9B, 0xA3, 0x9B, 0x97,\n\t0x9B, 0x8B, 0x9B, 0x7F, 0x9B, 0x73, 0x9B, 0x67, 0x9B, 0x5B, 0x9B, 0x50, 0x9B, 0x44, 0x9B, 0x39, 0x9B, 0x2D, 0x9B, 0x22, 0x9B, 0x16, 0x9B, 0x0B, 0x9B, 0x00, 0x9B, 0xF4, 0x9A, 0xE9, 0x9A, 0xDE,\n\t0x9A, 0xD3, 0x9A, 0xC8, 0x9A, 0xBD, 0x9A, 0xB2, 0x9A, 0xA8, 0x9A, 0x9D, 0x9A, 0x92, 0x9A, 0x88, 0x9A, 0x7D, 0x9A, 0x72, 0x9A, 0x68, 0x9A, 0x5E, 0x9A, 0x53, 0x9A, 0x49, 0x9A, 0x3F, 0x9A, 0x35,\n\t0x9A, 0x2B, 0x9A, 0x21, 0x9A, 0x17, 0x9A, 0x0D, 0x9A, 0x03, 0x9A, 0xF9, 0x99, 0xEF, 0x99, 0xE5, 0x99, 0xDC, 0x99, 0xD2, 0x99, 0xC9, 0x99, 0xBF, 0x99, 0xB6, 0x99, 0xAC, 0x99, 0xA3, 0x99, 0x9A,\n\t0x99, 0x90, 0x99, 0x87, 0x99, 0x7E, 0x99, 0x75, 0x99, 0x6C, 0x99, 0x63, 0x99, 0x5A, 0x99, 0x51, 0x99, 0x48, 0x99, 0x3F, 0x99, 0x37, 0x99, 0x2E, 0x99, 0x25, 0x99, 0x1D, 0x99, 0x14, 0x99, 0x0C,\n\t0x99, 0x03, 0x99, 0xFB, 0x98, 0xF3, 0x98, 0xEA, 0x98, 0xE2, 0x98, 0xDA, 0x98, 0xD2, 0x98, 0xCA, 0x98, 0xC2, 0x98, 0xBA, 0x98, 0xB2, 0x98, 0xAA, 0x98, 0xA2, 0x98, 0x9A, 0x98, 0x92, 0x98, 0x8A,\n\t0x98, 0x83, 0x98, 0x7B, 0x98, 0x73, 0x98, 0x6C, 0x98, 0x64, 0x98, 0x5D, 0x98, 0x55, 0x98, 0x4E, 0x98, 0x47, 0x98, 0x3F, 0x98, 0x38, 0x98, 0x31, 0x98, 0x2A, 0x98, 0x23, 0x98, 0x1C, 0x98, 0x14,\n\t0x98, 0x0D, 0x98, 0x06, 0x98, 0xFF, 0x97, 0xF1, 0x97, 0xE4, 0x97, 0xD6, 0x97, 0xC9, 0x97, 0xBB, 0x97, 0xAE, 0x97, 0xA0, 0x97, 0x93, 0x97, 0x86, 0x97, 0x79, 0x97, 0x6C, 0x97, 0x5F, 0x97, 0x52,\n\t0x97, 0x46, 0x97, 0x39, 0x97, 0x2C, 0x97, 0x20, 0x97, 0x13, 0x97, 0x07, 0x97, 0xFB, 0x96, 0xEE, 0x96, 0xE2, 0x96, 0xD6, 0x96, 0xCA, 0x96, 0xBE, 0x96, 0xB2, 0x96, 0xA7, 0x96, 0x9B, 0x96, 0x8F,\n\t0x96, 0x84, 0x96, 0x78, 0x96, 0x6D, 0x96, 0x61, 0x96, 0x56, 0x96, 0x4B, 0x96, 0x40, 0x96, 0x35, 0x96, 0x2A, 0x96, 0x1F, 0x96, 0x14, 0x96, 0x09, 0x96, 0xFE, 0x95, 0xF4, 0x95, 0xE9, 0x95, 0xDE,\n\t0x95, 0xD4, 0x95, 0xCA, 0x95, 0xBF, 0x95, 0xB5, 0x95, 0xAB, 0x95, 0xA0, 0x95, 0x96, 0x95, 0x8C, 0x95, 0x82, 0x95, 0x78, 0x95, 0x6F, 0x95, 0x65, 0x95, 0x5B, 0x95, 0x51, 0x95, 0x48, 0x95, 0x3E,\n\t0x95, 0x35, 0x95, 0x2B, 0x95, 0x22, 0x95, 0x19, 0x95, 0x0F, 0x95, 0x06, 0x95, 0xFD, 0x94, 0xF4, 0x94, 0xEB, 0x94, 0xE2, 0x94, 0xD9, 0x94, 0xD0, 0x94, 0xC7, 0x94, 0xBF, 0x94, 0xB6, 0x94, 0xAD,\n\t0x94, 0xA5, 0x94, 0x9C, 0x94, 0x94, 0x94, 0x8B, 0x94, 0x83, 0x94, 0x7B, 0x94, 0x72, 0x94, 0x6A, 0x94, 0x62, 0x94, 0x5A, 0x94, 0x52, 0x94, 0x4A, 0x94, 0x42, 0x94, 0x3A, 0x94, 0x32, 0x94, 0x2A,\n\t0x94, 0x23, 0x94, 0x1B, 0x94, 0x13, 0x94, 0x0C, 0x94, 0x04, 0x94, 0xF9, 0x93, 0xEB, 0x93, 0xDC, 0x93, 0xCD, 0x93, 0xBF, 0x93, 0xB0, 0x93, 0xA2, 0x93, 0x93, 0x93, 0x85, 0x93, 0x77, 0x93, 0x69,\n\t0x93, 0x5B, 0x93, 0x4D, 0x93, 0x3F, 0x93, 0x32, 0x93, 0x24, 0x93, 0x17, 0x93, 0x09, 0x93, 0xFC, 0x92, 0xEF, 0x92, 0xE2, 0x92, 0xD4, 0x92, 0xC8, 0x92, 0xBB, 0x92, 0xAE, 0x92, 0xA1, 0x92, 0x95,\n\t0x92, 0x88, 0x92, 0x7C, 0x92, 0x6F, 0x92, 0x63, 0x92, 0x57, 0x92, 0x4B, 0x92, 0x3E, 0x92, 0x33, 0x92, 0x27, 0x92, 0x1B, 0x92, 0x0F, 0x92, 0x03, 0x92, 0xF8, 0x91, 0xEC, 0x91, 0xE1, 0x91, 0xD6,\n\t0x91, 0xCA, 0x91, 0xBF, 0x91, 0xB4, 0x91, 0xA9, 0x91, 0x9E, 0x91, 0x93, 0x91, 0x88, 0x91, 0x7E, 0x91, 0x73, 0x91, 0x68, 0x91, 0x5E, 0x91, 0x53, 0x91, 0x49, 0x91, 0x3F, 0x91, 0x35, 0x91, 0x2A,\n\t0x91, 0x20, 0x91, 0x16, 0x91, 0x0C, 0x91, 0x02, 0x91, 0xF9, 0x90, 0xEF, 0x90, 0xE5, 0x90, 0xDB, 0x90, 0xD2, 0x90, 0xC8, 0x90, 0xBF, 0x90, 0xB6, 0x90, 0xAC, 0x90, 0xA3, 0x90, 0x9A, 0x90, 0x91,\n\t0x90, 0x88, 0x90, 0x7F, 0x90, 0x76, 0x90, 0x6D, 0x90, 0x64, 0x90, 0x5C, 0x90, 0x53, 0x90, 0x4A, 0x90, 0x42, 0x90, 0x39, 0x90, 0x31, 0x90, 0x29, 0x90, 0x20, 0x90, 0x18, 0x90, 0x10, 0x90, 0x08,\n\t0x90, 0xFF, 0x8F, 0xEF, 0x8F, 0xDF, 0x8F, 0xCF, 0x8F, 0xC0, 0x8F, 0xB0, 0x8F, 0xA1, 0x8F, 0x91, 0x8F, 0x82, 0x8F, 0x73, 0x8F, 0x64, 0x8F, 0x55, 0x8F, 0x46, 0x8F, 0x37, 0x8F, 0x29, 0x8F, 0x1A,\n\t0x8F, 0x0C, 0x8F, 0xFD, 0x8E, 0xEF, 0x8E, 0xE1, 0x8E, 0xD3, 0x8E, 0xC5, 0x8E, 0xB7, 0x8E, 0xAA, 0x8E, 0x9C, 0x8E, 0x8F, 0x8E, 0x81, 0x8E, 0x74, 0x8E, 0x67, 0x8E, 0x5A, 0x8E, 0x4D, 0x8E, 0x40,\n\t0x8E, 0x33, 0x8E, 0x26, 0x8E, 0x19, 0x8E, 0x0D, 0x8E, 0x00, 0x8E, 0xF4, 0x8D, 0xE8, 0x8D, 0xDC, 0x8D, 0xCF, 0x8D, 0xC3, 0x8D, 0xB8, 0x8D, 0xAC, 0x8D, 0xA0, 0x8D, 0x94, 0x8D, 0x89, 0x8D, 0x7D,\n\t0x8D, 0x72, 0x8D, 0x66, 0x8D, 0x5B, 0x8D, 0x50, 0x8D, 0x45, 0x8D, 0x3A, 0x8D, 0x2F, 0x8D, 0x24, 0x8D, 0x19, 0x8D, 0x0F, 0x8D, 0x04, 0x8D, 0xFA, 0x8C, 0xEF, 0x8C, 0xE5, 0x8C, 0xDB, 0x8C, 0xD0,\n\t0x8C, 0xC6, 0x8C, 0xBC, 0x8C, 0xB2, 0x8C, 0xA8, 0x8C, 0x9E, 0x8C, 0x95, 0x8C, 0x8B, 0x8C, 0x81, 0x8C, 0x78, 0x8C, 0x6E, 0x8C, 0x65, 0x8C, 0x5C, 0x8C, 0x52, 0x8C, 0x49, 0x8C, 0x40, 0x8C, 0x37,\n\t0x8C, 0x2E, 0x8C, 0x25, 0x8C, 0x1C, 0x8C, 0x14, 0x8C, 0x0B, 0x8C, 0x02, 0x8C, 0xF3, 0x8B, 0xE2, 0x8B, 0xD1, 0x8B, 0xC0, 0x8B, 0xB0, 0x8B, 0x9F, 0x8B, 0x8F, 0x8B, 0x7F, 0x8B, 0x6F, 0x8B, 0x5F,\n\t0x8B, 0x4F, 0x8B, 0x3F, 0x8B, 0x2F, 0x8B, 0x20, 0x8B, 0x10, 0x8B, 0x01, 0x8B, 0xF2, 0x8A, 0xE3, 0x8A, 0xD4, 0x8A, 0xC5, 0x8A, 0xB6, 0x8A, 0xA8, 0x8A, 0x99, 0x8A, 0x8B, 0x8A, 0x7D, 0x8A, 0x6E,\n\t0x8A, 0x60, 0x8A, 0x52, 0x8A, 0x45, 0x8A, 0x37, 0x8A, 0x29, 0x8A, 0x1C, 0x8A, 0x0E, 0x8A, 0x01, 0x8A, 0xF4, 0x89, 0xE7, 0x89, 0xDA, 0x89, 0xCD, 0x89, 0xC0, 0x89, 0xB4, 0x89, 0xA7, 0x89, 0x9B,\n\t0x89, 0x8E, 0x89, 0x82, 0x89, 0x76, 0x89, 0x6A, 0x89, 0x5E, 0x89, 0x52, 0x89, 0x46, 0x89, 0x3A, 0x89, 0x2F, 0x89, 0x23, 0x89, 0x18, 0x89, 0x0C, 0x89, 0x01, 0x89, 0xF6, 0x88, 0xEB, 0x88, 0xE0,\n\t0x88, 0xD5, 0x88, 0xCA, 0x88, 0xBF, 0x88, 0xB5, 0x88, 0xAA, 0x88, 0xA0, 0x88, 0x95, 0x88, 0x8B, 0x88, 0x81, 0x88, 0x77, 0x88, 0x6D, 0x88, 0x63, 0x88, 0x59, 0x88, 0x4F, 0x88, 0x45, 0x88, 0x3C,\n\t0x88, 0x32, 0x88, 0x29, 0x88, 0x1F, 0x88, 0x16, 0x88, 0x0D, 0x88, 0x03, 0x88, 0xF5, 0x87, 0xE2, 0x87, 0xD1, 0x87, 0xBF, 0x87, 0xAD, 0x87, 0x9C, 0x87, 0x8A, 0x87, 0x79, 0x87, 0x68, 0x87, 0x57,\n\t0x87, 0x46, 0x87, 0x36, 0x87, 0x25, 0x87, 0x15, 0x87, 0x04, 0x87, 0xF4, 0x86, 0xE4, 0x86, 0xD4, 0x86, 0xC5, 0x86, 0xB5, 0x86, 0xA6, 0x86, 0x96, 0x86, 0x87, 0x86, 0x78, 0x86, 0x69, 0x86, 0x5A,\n\t0x86, 0x4C, 0x86, 0x3D, 0x86, 0x2E, 0x86, 0x20, 0x86, 0x12, 0x86, 0x04, 0x86, 0xF6, 0x85, 0xE8, 0x85, 0xDA, 0x85, 0xCD, 0x85, 0xBF, 0x85, 0xB2, 0x85, 0xA4, 0x85, 0x97, 0x85, 0x8A, 0x85, 0x7D,\n\t0x85, 0x70, 0x85, 0x63, 0x85, 0x57, 0x85, 0x4A, 0x85, 0x3E, 0x85, 0x32, 0x85, 0x25, 0x85, 0x19, 0x85, 0x0D, 0x85, 0x01, 0x85, 0xF5, 0x84, 0xEA, 0x84, 0xDE, 0x84, 0xD3, 0x84, 0xC7, 0x84, 0xBC,\n\t0x84, 0xB1, 0x84, 0xA6, 0x84, 0x9B, 0x84, 0x85, 0x84, 0x6F, 0x84, 0x5A, 0x84, 0x46, 0x84, 0x31, 0x84, 0x1D, 0x84, 0x0A, 0x84, 0xF6, 0x83, 0xE3, 0x83, 0xD1, 0x83, 0xBF, 0x83, 0xAD, 0x83, 0x9B,\n\t0x83, 0x89, 0x83, 0x78, 0x83, 0x68, 0x83, 0x57, 0x83, 0x47, 0x83, 0x37, 0x83, 0x27, 0x83, 0x18, 0x83, 0x09, 0x83, 0xFA, 0x82, 0xEC, 0x82, 0xDD, 0x82, 0xCF, 0x82, 0xC2, 0x82, 0xB4, 0x82, 0xA7,\n\t0x82, 0x9A, 0x82, 0x8D, 0x82, 0x80, 0x82, 0x74, 0x82, 0x68, 0x82, 0x5C, 0x82, 0x50, 0x82, 0x44, 0x82, 0x39, 0x82, 0x2E, 0x82, 0x23, 0x82, 0x18, 0x82, 0x0E, 0x82, 0x04, 0x82, 0xF9, 0x81, 0xF0,\n\t0x81, 0xE6, 0x81, 0xDC, 0x81, 0xD3, 0x81, 0xCA, 0x81, 0xC0, 0x81, 0xB8, 0x81, 0xAF, 0x81, 0xA6, 0x81, 0x9E, 0x81, 0x96, 0x81, 0x8E, 0x81, 0x86, 0x81, 0x7E, 0x81, 0x76, 0x81, 0x6F, 0x81, 0x67,\n\t0x81, 0x60, 0x81, 0x59, 0x81, 0x52, 0x81, 0x4B, 0x81, 0x44, 0x81, 0x3E, 0x81, 0x37, 0x81, 0x31, 0x81, 0x2B, 0x81, 0x25, 0x81, 0x1F, 0x81, 0x19, 0x81, 0x13, 0x81, 0x0D, 0x81, 0x08, 0x81, 0x03,\n\t0x81, 0xFD, 0x80, 0xF8, 0x80, 0xF3, 0x80, 0xEE, 0x80, 0xE9, 0x80, 0xE4, 0x80, 0xDF, 0x80, 0xDB, 0x80, 0xD6, 0x80, 0xD2, 0x80, 0xCD, 0x80, 0xC9, 0x80, 0xC5, 0x80, 0xC1, 0x80, 0xBD, 0x80, 0xB9,\n\t0x80, 0xB5, 0x80, 0xB1, 0x80, 0xAD, 0x80, 0xAA, 0x80, 0xA6, 0x80, 0xA2, 0x80, 0x9F, 0x80, 0x9C, 0x80, 0x98, 0x80, 0x95, 0x80, 0x92, 0x80, 0x8F, 0x80, 0x8C, 0x80, 0x89, 0x80, 0x86, 0x80, 0x83,\n\t0x80, 0x80, 0x80, 0x7D, 0x80, 0x7B, 0x80, 0x78, 0x80, 0x75, 0x80, 0x73, 0x80, 0x70, 0x80, 0x6E, 0x80, 0x6C, 0x80, 0x69, 0x80, 0x67, 0x80, 0x65, 0x80, 0x62, 0x80, 0x60, 0x80, 0x5E, 0x80, 0x5C,\n\t0x80, 0x5A, 0x80, 0x58, 0x80, 0x56, 0x80, 0x54, 0x80, 0x52, 0x80, 0x51, 0x80, 0x4F, 0x80, 0x4D, 0x80, 0x4B, 0x80, 0x4A, 0x80, 0x48, 0x80, 0x46, 0x80, 0x45, 0x80, 0x43, 0x80, 0x42, 0x80, 0x40,\n\t0x80, 0x3F, 0x80, 0x3D, 0x80, 0x3C, 0x80, 0x3B, 0x80, 0x39, 0x80, 0x38, 0x80, 0x37, 0x80, 0x36, 0x80, 0x34, 0x80, 0x33, 0x80, 0x32, 0x80, 0x31, 0x80, 0x30, 0x80, 0x2F, 0x80, 0x2E, 0x80, 0x2D,\n\t0x80, 0x2C, 0x80, 0x2B, 0x80, 0x2A, 0x80, 0x29, 0x80, 0x28, 0x80, 0x27, 0x80, 0x26, 0x80, 0x25, 0x80, 0x24, 0x80, 0x23, 0xC8, 0x88, 0x31, 0x21, 0x80, 0x20, 0xDC, 0x88, 0x11, 0x1E, 0xEA, 0x88,\n\t0x11, 0x1C, 0xF8, 0x88, 0x11, 0x1A, 0x06, 0x89, 0x00, 0x0E, 0x89, 0x11, 0x17, 0x1C, 0x89, 0x00, 0x24, 0x89, 0x00, 0x2C, 0x89, 0x11, 0x13, 0x02, 0x00, 0x00, 0x3E, 0x89, 0x00, 0x46, 0x89, 0x11,\n\t0x10, 0x02, 0x00, 0x00, 0x58, 0x89, 0x11, 0x0E, 0x02, 0x00, 0x11, 0x0D, 0x02, 0x00, 0x13, 0x0C, 0x02, 0x00, 0x11, 0x0B, 0x02, 0x00, 0x13, 0x0A, 0x02, 0x00, 0x15, 0x09, 0x02, 0x00, 0x15, 0x08,\n\t0x02, 0x00, 0x15, 0x07, 0x02, 0x00, 0x19, 0x06, 0x02, 0x00, 0x1B, 0x05, 0x02, 0x00, 0x1D, 0x04, 0x02, 0x00, 0x1F, 0x03, 0x02, 0x00, 0x06, 0x1F, 0x02, 0x02, 0x00, 0x10, 0x1F, 0x01, 0x02, 0x00,\n\t0x3A, 0x1F, 0x00, 0x02, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,\n\t0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,\n\t0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,\n\t0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC6, 0xFF, 0xFF, 0xFF, 0xFF, 0xF3, 0xFE, 0x01, 0xFE, 0x02, 0xFE, 0x03, 0xFE, 0x04,\n\t0xFE, 0x05, 0xFE, 0x06, 0xFE, 0x07, 0xFE, 0x08, 0xFE, 0x09, 0xFE, 0x0A, 0xFE, 0x0B, 0xFE, 0x0C, 0xFE, 0x0D, 0xFE, 0x0E, 0xFE, 0x0F, 0xFE, 0x10, 0xFE, 0x11, 0xFE, 0x12, 0xFE, 0x13, 0xFE, 0x14,\n\t0xFE, 0x15, 0xFE, 0x16, 0xFE, 0x17, 0xFE, 0x18, 0xFE, 0x19, 0xFE, 0x1A, 0xFE, 0x1B, 0xFE, 0x1C, 0xFE, 0x1D, 0xFE, 0x1E, 0xFE, 0x1F, 0xFE, 0x20, 0xFE, 0x21, 0xFE, 0x22, 0xFE, 0x23, 0xFE, 0x24,\n\t0xFE, 0x25, 0xFE, 0x26, 0xFE, 0x27, 0xFE, 0x28, 0xFE, 0x29, 0xFE, 0x2A, 0xFE, 0x2B, 0xFE, 0x2C, 0xFE, 0x2D, 0xFE, 0x2E, 0xFE, 0x2F, 0xFE, 0x30, 0xFE, 0x31, 0xFE, 0x32, 0xFE, 0x33, 0xFE, 0x34,\n\t0xFE, 0x35, 0xFE, 0x36, 0xFE, 0x37, 0xFE, 0x38, 0xFE, 0x39, 0xFE, 0x3A, 0xFE, 0x3B, 0xFE, 0x3C, 0xFE, 0x3D, 0xFE, 0x3E, 0xFE, 0x3F, 0xFE, 0x40, 0xFE, 0x41, 0xFE, 0x42, 0xFE, 0x43, 0xFE, 0x44,\n\t0xFE, 0x45, 0xFE, 0x46, 0xFE, 0x47, 0xFE, 0x48, 0xFE, 0x49, 0xFE, 0x4A, 0xFE, 0x4B, 0xFE, 0x4C, 0xFE, 0x4D, 0xFE, 0x4E, 0xFE, 0x4F, 0xFE, 0x50, 0xFE, 0x51, 0xFE, 0x52, 0xFE, 0x53, 0xFE, 0x54,\n\t0xFE, 0x55, 0xFE, 0x56, 0xFE, 0x57, 0xFE, 0x58, 0xFE, 0x59, 0xFE, 0x5A, 0xFE, 0x5B, 0xFE, 0x5C, 0xFE, 0x5D, 0xFE, 0x5E, 0xFE, 0x5F, 0xFE, 0x60, 0xFE, 0x61, 0xFE, 0x62, 0xFE, 0x63, 0xFE, 0x64,\n\t0xFE, 0x65, 0xFE, 0x66, 0xFE, 0x67, 0xFE, 0x68, 0xFE, 0x69, 0xFE, 0x6A, 0xFE, 0x6B, 0xFE, 0x6C, 0xFE, 0x6D, 0xFE, 0x6E, 0xFE, 0x6F, 0xFE, 0x70, 0xFE, 0x71, 0xFE, 0x72, 0xFE, 0x73, 0xFE, 0x74,\n\t0xFE, 0x75, 0xFE, 0x76, 0xFE, 0x77, 0xFE, 0x78, 0xFE, 0x79, 0xFE, 0x7A, 0xFE, 0x7B, 0xFE, 0x7C, 0xFE, 0x7D, 0xFE, 0x7E, 0xFE, 0x7F, 0xFE, 0x80, 0xFE, 0x81, 0xFE, 0x82, 0xFE, 0x83, 0xFE, 0x84,\n\t0xFE, 0x85, 0xFE, 0x86, 0xFE, 0x87, 0xFE, 0x88, 0xFE, 0x89, 0xFE, 0x8A, 0xFE, 0x8B, 0xFE, 0x8C, 0xFE, 0x8D, 0xFE, 0x8E, 0xFE, 0x8F, 0xFE, 0x90, 0xFE, 0x91, 0xFE, 0x92, 0xFE, 0x93, 0xFE, 0x94,\n\t0xFE, 0x95, 0xFE, 0x96, 0xFE, 0x97, 0xFE, 0x98, 0xFE, 0x99, 0xFE, 0x9A, 0xFE, 0x9B, 0xFE, 0x9C, 0xFE, 0x9D, 0xFE, 0x9E, 0xFE, 0x9F, 0xFE, 0xA0, 0xFE, 0xA1, 0xFE, 0xA2, 0xFE, 0xA3, 0xFE, 0xA4,\n\t0xFE, 0xA5, 0xFE, 0xA6, 0xFE, 0xA7, 0xFE, 0xA8, 0xFE, 0xA9, 0xFE, 0xAA, 0xFE, 0xAB, 0xFE, 0xAC, 0xFE, 0xAD, 0xFE, 0xAE, 0xFE, 0xAF, 0xFE, 0xB0, 0xFE, 0xB1, 0xFE, 0xB2, 0xFE, 0xB3, 0xFE, 0xB4,\n\t0xFE, 0xB5, 0xFE, 0xB6, 0xFE, 0xB7, 0xFE, 0xB8, 0xFE, 0xB9, 0xFE, 0xBA, 0xFE, 0xBB, 0xFE, 0xBC, 0xFE, 0xBD, 0xFE, 0xBE, 0xFE, 0xBF, 0xFE, 0xC0, 0xFE, 0xC1, 0xFE, 0xC2, 0xFE, 0xC3, 0xFE, 0xC4,\n\t0xFE, 0xC5, 0xFE, 0xC6, 0xFE, 0xC7, 0xFE, 0xC8, 0xFE, 0xC9, 0xFE, 0xCA, 0xFE, 0xCB, 0xFE, 0xCC, 0xFE, 0xCD, 0xFE, 0xCE, 0xFE, 0xCF, 0xFE, 0xD0, 0xFE, 0xD1, 0xFE, 0xD2, 0xFE, 0xD3, 0xFE, 0xD4,\n\t0xFE, 0xD5, 0xFE, 0xD6, 0xFE, 0xD7, 0xFE, 0xD8, 0xFE, 0xD9, 0xFE, 0xDA, 0xFE, 0xDB, 0xFE, 0xDC, 0xFE, 0xDD, 0xFE, 0xDE, 0xFE, 0xDF, 0xFE, 0xE0, 0xFE, 0xE1, 0xFE, 0xE2, 0xFE, 0xE3, 0xFE, 0xE4,\n\t0xFE, 0xE5, 0xFE, 0xE6, 0xFE, 0xE7, 0xFE, 0xE8, 0xFE, 0xE9, 0xFE, 0xEA, 0xFE, 0xEB, 0xFE, 0xEC, 0xFE, 0xED, 0xFE, 0xEE, 0xFE, 0xEF, 0xFE, 0xF0, 0xFE, 0xF1, 0xFE, 0xF2, 0xFE, 0xF3, 0xFE, 0xF4,\n\t0xFE, 0xF5, 0xFE, 0xF6, 0xFE, 0xF7, 0xFE, 0xF8, 0xFE, 0xF9, 0xFE, 0xFA, 0xFE, 0xFB, 0xFE, 0xFC, 0xFE, 0xFD, 0xFE, 0xFE, 0xFE, 0xFF, 0xFE, 0x00, 0xFF, 0x01, 0xFF, 0x02, 0xFF, 0x03, 0xFF, 0x04,\n\t0xFF, 0x05, 0xFF, 0x06, 0xFF, 0x07, 0xFF, 0x08, 0xFF, 0x09, 0xFF, 0x0A, 0xFF, 0x0B, 0xFF, 0x0C, 0xFF, 0x0D, 0xFF, 0x0E, 0xFF, 0x0F, 0xFF, 0x10, 0xFF, 0x11, 0xFF, 0x12, 0xFF, 0x13, 0xFF, 0x14,\n\t0xFF, 0x15, 0xFF, 0x16, 0xFF, 0x17, 0xFF, 0x18, 0xFF, 0x19, 0xFF, 0x1A, 0xFF, 0x1B, 0xFF, 0x1C, 0xFF, 0x1D, 0xFF, 0x1E, 0xFF, 0x1F, 0xFF, 0x20, 0xFF, 0x21, 0xFF, 0x22, 0xFF, 0x23, 0xFF, 0x24,\n\t0xFF, 0x25, 0xFF, 0x26, 0xFF, 0x27, 0xFF, 0x28, 0xFF, 0x29, 0xFF, 0x2A, 0xFF, 0x2B, 0xFF, 0x2C, 0xFF, 0x2D, 0xFF, 0x2E, 0xFF, 0x2F, 0xFF, 0x30, 0xFF, 0x31, 0xFF, 0x32, 0xFF, 0x33, 0xFF, 0x34,\n\t0xFF, 0x35, 0xFF, 0x36, 0xFF, 0x37, 0xFF, 0x38, 0xFF, 0x39, 0xFF, 0x3A, 0xFF, 0x3B, 0xFF, 0x3C, 0xFF, 0x3D, 0xFF, 0x3E, 0xFF, 0x3F, 0xFF, 0x40, 0xFF, 0x41, 0xFF, 0x42, 0xFF, 0x43, 0xFF, 0x44,\n\t0xFF, 0x45, 0xFF, 0x46, 0xFF, 0x47, 0xFF, 0x48, 0xFF, 0x49, 0xFF, 0x4A, 0xFF, 0x4B, 0xFF, 0x4C, 0xFF, 0x4D, 0xFF, 0x4E, 0xFF, 0x4F, 0xFF, 0x50, 0xFF, 0x51, 0xFF, 0x52, 0xFF, 0x53, 0xFF, 0x54,\n\t0xFF, 0x55, 0xFF, 0x56, 0xFF, 0x57, 0xFF, 0x58, 0xFF, 0x59, 0xFF, 0x5A, 0xFF, 0x5B, 0xFF, 0x5C, 0xFF, 0x5D, 0xFF, 0x5E, 0xFF, 0x5F, 0xFF, 0x60, 0xFF, 0x61, 0xFF, 0x62, 0xFF, 0x63, 0xFF, 0x64,\n\t0xFF, 0x65, 0xFF, 0x66, 0xFF, 0x67, 0xFF, 0x68, 0xFF, 0x69, 0xFF, 0x6A, 0xFF, 0x6B, 0xFF, 0x6C, 0xFF, 0x6D, 0xFF, 0x6E, 0xFF, 0x6F, 0xFF, 0x70, 0xFF, 0x71, 0xFF, 0x72, 0xFF, 0x73, 0xFF, 0x74,\n\t0xFF, 0x75, 0xFF, 0x76, 0xFF, 0x77, 0xFF, 0x78, 0xFF, 0x79, 0xFF, 0x7A, 0xFF, 0x7B, 0xFF, 0x7C, 0xFF, 0x7D, 0xFF, 0x7E, 0xFF, 0x7F, 0xFF, 0x80, 0xFF, 0x81, 0xFF, 0x82, 0xFF, 0x83, 0xFF, 0x84,\n\t0xFF, 0x85, 0xFF, 0x86, 0xFF, 0x87, 0xFF, 0x88, 0xFF, 0x89, 0xFF, 0x8A, 0xFF, 0x8B, 0xFF, 0x8C, 0xFF, 0x8D, 0xFF, 0x8E, 0xFF, 0x8F, 0xFF, 0x90, 0xFF, 0x91, 0xFF, 0x92, 0xFF, 0x93, 0xFF, 0x94,\n\t0xFF, 0x95, 0xFF, 0x96, 0xFF, 0x97, 0xFF, 0x98, 0xFF, 0x99, 0xFF, 0x9A, 0xFF, 0x9B, 0xFF, 0x9C, 0xFF, 0x9D, 0xFF, 0x9E, 0xFF, 0x9F, 0xFF, 0xA0, 0xFF, 0xA1, 0xFF, 0xA2, 0xFF, 0xA3, 0xFF, 0xA4,\n\t0xFF, 0xA5, 0xFF, 0xA6, 0xFF, 0xA7, 0xFF, 0xA8, 0xFF, 0xA9, 0xFF, 0xAA, 0xFF, 0xAB, 0xFF, 0xAC, 0xFF, 0xAD, 0xFF, 0xAE, 0xFF, 0xAF, 0xFF, 0xB0, 0xFF, 0xB1, 0xFF, 0xB2, 0xFF, 0xB3, 0xFF, 0xB4,\n\t0xFF, 0xB5, 0xFF, 0xB6, 0xFF, 0xB7, 0xFF, 0xB8, 0xFF, 0xB9, 0xFF, 0xBA, 0xFF, 0xBB, 0xFF, 0xBC, 0xFF, 0xBD, 0xFF, 0xBE, 0xFF, 0xBF, 0xFF, 0xC0, 0xFF, 0xC1, 0xFF, 0xC2, 0xFF, 0xC3, 0xFF, 0xC4,\n\t0xFF, 0xC5, 0xFF, 0xC6, 0xFF, 0xC7, 0xFF, 0xC8, 0xFF, 0xC9, 0xFF, 0xCA, 0xFF, 0xCB, 0xFF, 0xCC, 0xFF, 0xCD, 0xFF, 0xCE, 0xFF, 0xCF, 0xFF, 0xD0, 0xFF, 0xD1, 0xFF, 0xD2, 0xFF, 0xD3, 0xFF, 0xD4,\n\t0xFF, 0xD5, 0xFF, 0xD6, 0xFF, 0xD7, 0xFF, 0xD8, 0xFF, 0xD9, 0xFF, 0xDA, 0xFF, 0xDB, 0xFF, 0xDC, 0xFF, 0xDD, 0xFF, 0xDE, 0xFF, 0xDF, 0xFF, 0xE0, 0xFF, 0xE1, 0xFF, 0xE2, 0xFF, 0xE3, 0xFF, 0xE4,\n\t0xFF, 0xE5, 0xFF, 0xE6, 0xFF, 0xE7, 0xFF, 0xE8, 0xFF, 0xE9, 0xFF, 0xEA, 0xFF, 0xEB, 0xFF, 0xEC, 0xFF, 0xED, 0xFF, 0xEE, 0xFF, 0xEF, 0xFF, 0xF0, 0xFF, 0xF1, 0xFF, 0xF2, 0xFF, 0xF3, 0xFF, 0xF4,\n\t0xFF, 0xF5, 0xFF, 0xF6, 0xFF, 0xF7, 0xFF, 0xF8, 0xFF, 0xF9, 0xFF, 0xFA, 0xFF, 0xFB, 0xFF, 0xFC, 0xFF, 0xFD, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0x00, 0x04, 0xFF, 0xFF, 0xFF, 0xF1, 0x1F, 0x3C, 0x02,\n\t0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,\n\t0xFF, 0x0B, 0x1F, 0x01, 0x02, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF5, 0x1F, 0x02, 0x02, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF1, 0x1F, 0x03, 0x02, 0x00,\n\t0xFF, 0xFF, 0xFF, 0xEB, 0x1F, 0x04, 0x02, 0x00, 0xFF, 0xFF, 0xF0, 0x1F, 0x05, 0x02, 0x00, 0xFF, 0xEB, 0x1F, 0x06, 0x02, 0x00, 0xFF, 0xE9, 0x1F, 0x07, 0x02, 0x00, 0xFF, 0xE9, 0x1F, 0x08, 0x02,\n\t0x00, 0xFF, 0x73, 0x1F, 0x09, 0x02, 0x00, 0xEA, 0x1F, 0x0A, 0x02, 0x00, 0xEA, 0x1F, 0x0B, 0x02, 0x00, 0xE8, 0x1F, 0x0C, 0x02, 0x00, 0xEA, 0x1F, 0x0D, 0x02, 0x00, 0xE8, 0x1F, 0x0E, 0x02, 0x00,\n\t0xE8, 0x1F, 0x0F, 0x02, 0x00, 0xEA, 0x1F, 0x10, 0x02, 0x00, 0xB8, 0x1F, 0x11, 0x02, 0x00, 0x6A, 0x1F, 0x12, 0x02, 0x00, 0x6A, 0x1F, 0x13, 0x02, 0x00, 0x6A, 0x1F, 0x14, 0x02, 0x00, 0x6A, 0x1F,\n\t0x15, 0x02, 0x00, 0x68, 0x1F, 0x16, 0x02, 0x00, 0x6A, 0x1F, 0x17, 0x02, 0x00, 0x68, 0x1F, 0x18, 0x02, 0x00, 0x6A, 0x1F, 0x19, 0x02, 0x00, 0x6A, 0x1F, 0x1A, 0x02, 0x00, 0x68, 0x1F, 0x1B, 0x02,\n\t0x00, 0x68, 0x1F, 0x1C, 0x02, 0x00, 0x6A, 0x1F, 0x1D, 0x02, 0x00, 0x68, 0x1F, 0x1E, 0x02, 0x00, 0x68, 0x1F, 0x1F, 0x02, 0x00, 0x68, 0x1F, 0x20, 0x02, 0x00, 0x68, 0x1F, 0x21, 0x02, 0x00, 0x2A,\n\t0x1F, 0x22, 0x02, 0x00, 0x2A, 0x1F, 0x23, 0x02, 0x00, 0x2A, 0x1F, 0x24, 0x02, 0x00, 0x2A, 0x1F, 0x25, 0x02, 0x00, 0x2A, 0x1F, 0x26, 0x02, 0x00, 0x2A, 0x1F, 0x27, 0x02, 0x00, 0x2A, 0x1F, 0x28,\n\t0x02, 0x00, 0x2A, 0x1F, 0x29, 0x02, 0x00, 0x28, 0x1F, 0x2A, 0x02, 0x00, 0x2A, 0x1F, 0x2B, 0x02, 0x00, 0x2A, 0x1F, 0x2C, 0x02, 0x00, 0x28, 0x1F, 0x2D, 0x02, 0x00, 0x2A, 0x1F, 0x2E, 0x02, 0x00,\n\t0x2A, 0x1F, 0x2F, 0x02, 0x00, 0x28, 0x1F, 0x30, 0x02, 0x00, 0x2A, 0x1F, 0x31, 0x02, 0x00, 0x28, 0x1F, 0x32, 0x02, 0x00, 0x2A, 0x1F, 0x33, 0x02, 0x00, 0x28, 0x1F, 0x34, 0x02, 0x00, 0x2A, 0x1F,\n\t0x35, 0x02, 0x00, 0x28, 0x1F, 0x36, 0x02, 0x00, 0x2A, 0x1F, 0x37, 0x02, 0x00, 0x28, 0x1F, 0x38, 0x02, 0x00, 0x28, 0x1F, 0x39, 0x02, 0x00, 0x2A, 0x1F, 0x3A, 0x02, 0x00, 0x28, 0x1F, 0x3B, 0x02,\n\t0x00, 0x28, 0x0F, 0x01, 0x00, 0x29, 0x1F, 0x3D, 0x02, 0x00, 0x2A, 0x1F, 0x3E, 0x02, 0x00, 0x28, 0x1F, 0x3F, 0x02, 0x00, 0x28, 0x1F, 0x40, 0x02, 0x00, 0x28, 0x1F, 0x41, 0x02, 0x00, 0x28, 0x1F,\n\t0x42, 0x02, 0x00, 0x1A, 0x1F, 0x43, 0x02, 0x00, 0x0A, 0x1F, 0x44, 0x02, 0x00, 0x0A, 0x1F, 0x45, 0x02, 0x00, 0x0A, 0x1F, 0x46, 0x02, 0x00, 0x0A, 0x1F, 0x47, 0x02, 0x00, 0x0A, 0x1F, 0x48, 0x02,\n\t0x00, 0x0A, 0x1F, 0x49, 0x02, 0x00, 0x0A, 0x1F, 0x4A, 0x02, 0x00, 0x0A, 0x1F, 0x4B, 0x02, 0x00, 0x0A, 0x1F, 0x4C, 0x02, 0x00, 0x0A, 0x1F, 0x4D, 0x02, 0x00, 0x0A, 0x1F, 0x4E, 0x02, 0x00, 0x0A,\n\t0x1F, 0x4F, 0x02, 0x00, 0x0A, 0x1F, 0x50, 0x02, 0x00, 0x08, 0x1F, 0x51, 0x02, 0x00, 0x0A, 0x1F, 0x52, 0x02, 0x00, 0x0A, 0x1F, 0x53, 0x02, 0x00, 0x0A, 0x1F, 0x54, 0x02, 0x00, 0x0A, 0x1F, 0x55,\n\t0x02, 0x00, 0x08, 0x1F, 0x56, 0x02, 0x00, 0x0A, 0x1F, 0x57, 0x02, 0x00, 0x0A, 0x1F, 0x58, 0x02, 0x00, 0x0A, 0x1F, 0x59, 0x02, 0x00, 0x08, 0x1F, 0x5A, 0x02, 0x00, 0x0A, 0x1F, 0x5B, 0x02, 0x00,\n\t0x0A, 0x1F, 0x5C, 0x02, 0x00, 0x08, 0x1F, 0x5D, 0x02, 0x00, 0x0A, 0x1F, 0x5E, 0x02, 0x00, 0x0A, 0x1F, 0x5F, 0x02, 0x00, 0x08, 0x1F, 0x60, 0x02, 0x00, 0x0A, 0x1F, 0x61, 0x02, 0x00, 0x0A, 0x1F,\n\t0x62, 0x02, 0x00, 0x08, 0x1F, 0x63, 0x02, 0x00, 0x0A, 0x1F, 0x64, 0x02, 0x00, 0x08, 0x1F, 0x65, 0x02, 0x00, 0x0A, 0x1F, 0x66, 0x02, 0x00, 0x0A, 0x1F, 0x67, 0x02, 0x00, 0x08, 0x1F, 0x68, 0x02,\n\t0x00, 0x0A, 0x1F, 0x69, 0x02, 0x00, 0x08, 0x1F, 0x6A, 0x02, 0x00, 0x0A, 0x1F, 0x6B, 0x02, 0x00, 0x08, 0x1F, 0x6C, 0x02, 0x00, 0x0A, 0x1F, 0x6D, 0x02, 0x00, 0x08, 0x1F, 0x6E, 0x02, 0x00, 0x08,\n\t0x1F, 0x6F, 0x02, 0x00, 0x0A, 0x1F, 0x70, 0x02, 0x00, 0x08, 0x1F, 0x71, 0x02, 0x00, 0x0A, 0x1F, 0x72, 0x02, 0x00, 0x08, 0x1F, 0x73, 0x02, 0x00, 0x0A, 0x1F, 0x74, 0x02, 0x00, 0x08, 0x1F, 0x75,\n\t0x02, 0x00, 0x08, 0x1F, 0x76, 0x02, 0x00, 0x0A, 0x1F, 0x77, 0x02, 0x00, 0x08, 0x1F, 0x78, 0x02, 0x00, 0x08, 0x1F, 0x79, 0x02, 0x00, 0x0A, 0x1F, 0x7A, 0x02, 0x00, 0x08, 0x1F, 0x7B, 0x02, 0x00,\n\t0x08, 0x1F, 0x7C, 0x02, 0x00, 0x08, 0x1F, 0x7D, 0x02, 0x00, 0x0A, 0x1F, 0x7E, 0x02, 0x00, 0x08, 0x1F, 0x7F, 0x02, 0x00, 0x08, 0x1F, 0x80, 0x02, 0x00, 0x08, 0x1F, 0x81, 0x02, 0x00, 0x0A, 0x1F,\n\t0x82, 0x02, 0x00, 0x08, 0x1F, 0x83, 0x02, 0x00, 0x08, 0x1F, 0x84, 0x02, 0x00, 0x08, 0x1F, 0x85, 0x02, 0x00, 0x08, 0x1F, 0x86, 0x02, 0x00, 0x08, 0x1F, 0x87, 0x02, 0x00, 0x0A, 0x1F, 0x88, 0x02,\n\t0x00, 0x06, 0x19, 0x89, 0x02, 0x00, 0x19, 0x8A, 0x02, 0x00, 0x19, 0x8B, 0x02, 0x00, 0x19, 0x8C, 0x02, 0x00, 0x19, 0x8D, 0x02, 0x00, 0x19, 0x8E, 0x02, 0x00, 0x19, 0x8F, 0x02, 0x00, 0x19, 0x90,\n\t0x02, 0x00, 0x19, 0x91, 0x02, 0x00, 0x19, 0x92, 0x02, 0x00, 0x19, 0x93, 0x02, 0x00, 0x19, 0x94, 0x02, 0x00, 0x19, 0x95, 0x02, 0x00, 0x19, 0x96, 0x02, 0x00, 0x19, 0x97, 0x02, 0x00, 0x19, 0x98,\n\t0x02, 0x00, 0x19, 0x99, 0x02, 0x00, 0x19, 0x9A, 0x02, 0x00, 0x19, 0x9B, 0x02, 0x00, 0x19, 0x9C, 0x02, 0x00, 0x19, 0x9D, 0x02, 0x00, 0x17, 0x9E, 0x02, 0x00, 0x19, 0x9F, 0x02, 0x00, 0x19, 0xA0,\n\t0x02, 0x00, 0x19, 0xA1, 0x02, 0x00, 0x19, 0xA2, 0x02, 0x00, 0x19, 0xA3, 0x02, 0x00, 0x19, 0xA4, 0x02, 0x00, 0x19, 0xA5, 0x02, 0x00, 0x19, 0xA6, 0x02, 0x00, 0x19, 0xA7, 0x02, 0x00, 0x17, 0xA8,\n\t0x02, 0x00, 0x19, 0xA9, 0x02, 0x00, 0x19, 0xAA, 0x02, 0x00, 0x19, 0xAB, 0x02, 0x00, 0x19, 0xAC, 0x02, 0x00, 0x19, 0xAD, 0x02, 0x00, 0x19, 0xAE, 0x02, 0x00, 0x17, 0xAF, 0x02, 0x00, 0x19, 0xB0,\n\t0x02, 0x00, 0x19, 0xB1, 0x02, 0x00, 0x19, 0xB2, 0x02, 0x00, 0x19, 0xB3, 0x02, 0x00, 0x17, 0xB4, 0x02, 0x00, 0x19, 0xB5, 0x02, 0x00, 0x19, 0xB6, 0x02, 0x00, 0x19, 0xB7, 0x02, 0x00, 0x19, 0xB8,\n\t0x02, 0x00, 0x17, 0xB9, 0x02, 0x00, 0x19, 0xBA, 0x02, 0x00, 0x19, 0xBB, 0x02, 0x00, 0x19, 0xBC, 0x02, 0x00, 0x17, 0xBD, 0x02, 0x00, 0x19, 0xBE, 0x02, 0x00, 0x19, 0xBF, 0x02, 0x00, 0x19, 0xC0,\n\t0x02, 0x00, 0x17, 0xC1, 0x02, 0x00, 0x19, 0xC2, 0x02, 0x00, 0x19, 0xC3, 0x02, 0x00, 0x19, 0xC4, 0x02, 0x00, 0x17, 0xC5, 0x02, 0x00, 0x19, 0xC6, 0x02, 0x00, 0x19, 0xC7, 0x02, 0x00, 0x17, 0xC8,\n\t0x02, 0x00, 0x19, 0xC9, 0x02, 0x00, 0x19, 0xCA, 0x02, 0x00, 0x17, 0xCB, 0x02, 0x00, 0x19, 0xCC, 0x02, 0x00, 0x19, 0xCD, 0x02, 0x00, 0x17, 0xCE, 0x02, 0x00, 0x19, 0xCF, 0x02, 0x00, 0x19, 0xD0,\n\t0x02, 0x00, 0x17, 0xD1, 0x02, 0x00, 0x19, 0xD2, 0x02, 0x00, 0x19, 0xD3, 0x02, 0x00, 0x17, 0xD4, 0x02, 0x00, 0x19, 0xD5, 0x02, 0x00, 0x17, 0xD6, 0x02, 0x00, 0x19, 0xD7, 0x02, 0x00, 0x19, 0xD8,\n\t0x02, 0x00, 0x17, 0xD9, 0x02, 0x00, 0x19, 0xDA, 0x02, 0x00, 0x17, 0xDB, 0x02, 0x00, 0x19, 0xDC, 0x02, 0x00, 0x19, 0xDD, 0x02, 0x00, 0x17, 0xDE, 0x02, 0x00, 0x19, 0xDF, 0x02, 0x00, 0x17, 0xE0,\n\t0x02, 0x00, 0x19, 0xE1, 0x02, 0x00, 0x17, 0xE2, 0x02, 0x00, 0x19, 0xE3, 0x02, 0x00, 0x19, 0xE4, 0x02, 0x00, 0x17, 0xE5, 0x02, 0x00, 0x19, 0xE6, 0x02, 0x00, 0x17, 0xE7, 0x02, 0x00, 0x19, 0xE8,\n\t0x02, 0x00, 0x17, 0xE9, 0x02, 0x00, 0x19, 0xEA, 0x02, 0x00, 0x17, 0xEB, 0x02, 0x00, 0x19, 0xEC, 0x02, 0x00, 0x17, 0xED, 0x02, 0x00, 0x19, 0xEE, 0x02, 0x00, 0x17, 0xEF, 0x02, 0x00, 0x19, 0xF0,\n\t0x02, 0x00, 0x17, 0xF1, 0x02, 0x00, 0x19, 0xF2, 0x02, 0x00, 0x17, 0xF3, 0x02, 0x00, 0x19, 0xF4, 0x02, 0x00, 0x17, 0xF5, 0x02, 0x00, 0x17, 0xF6, 0x02, 0x00, 0x19, 0xF7, 0x02, 0x00, 0x17, 0xF8,\n\t0x02, 0x00, 0x19, 0xF9, 0x02, 0x00, 0x17, 0xFA, 0x02, 0x00, 0x19, 0xFB, 0x02, 0x00, 0x17, 0xFC, 0x02, 0x00, 0x17, 0xFD, 0x02, 0x00, 0x19, 0xFE, 0x02, 0x00, 0x17, 0xFF, 0x02, 0x00, 0x28, 0x00,\n\t0x3D, 0x02, 0x00, 0x17, 0x01, 0x02, 0x00, 0x17, 0x02, 0x02, 0x00, 0x19, 0x03, 0x02, 0x00, 0x17, 0x04, 0x02, 0x00, 0x19, 0x05, 0x02, 0x00, 0x17, 0x06, 0x02, 0x00, 0x17, 0x07, 0x02, 0x00, 0x19,\n\t0x08, 0x02, 0x00, 0x17, 0x09, 0x02, 0x00, 0x17, 0x0A, 0x02, 0x00, 0x19, 0x0B, 0x02, 0x00, 0x17, 0x0C, 0x02, 0x00, 0x17, 0x0D, 0x02, 0x00, 0x19, 0x0E, 0x02, 0x00, 0x17, 0x0F, 0x02, 0x00, 0x17,\n\t0x10, 0x02, 0x00, 0x19, 0x11, 0x02, 0x00, 0x17, 0x12, 0x02, 0x00, 0x17, 0x13, 0x02, 0x00, 0x19, 0x14, 0x02, 0x00, 0x17, 0x15, 0x02, 0x00, 0x17, 0x16, 0x02, 0x00, 0x19, 0x17, 0x02, 0x00, 0x17,\n\t0x18, 0x02, 0x00, 0x17, 0x19, 0x02, 0x00, 0x17, 0x1A, 0x02, 0x00, 0x19, 0x1B, 0x02, 0x00, 0x17, 0x1C, 0x02, 0x00, 0x17, 0x1D, 0x02, 0x00, 0x17, 0x1E, 0x02, 0x00, 0x19, 0x1F, 0x02, 0x00, 0x17,\n\t0x20, 0x02, 0x00, 0x17, 0x21, 0x02, 0x00, 0x17, 0x22, 0x02, 0x00, 0x15, 0x23, 0x02, 0x00, 0x11, 0x24, 0x02, 0x00, 0x11, 0x25, 0x02, 0x00, 0x11, 0x26, 0x02, 0x00, 0x11, 0x27, 0x02, 0x00, 0x11,\n\t0x28, 0x02, 0x00, 0x11, 0x29, 0x02, 0x00, 0x11, 0x2A, 0x02, 0x00, 0x11, 0x2B, 0x02, 0x00, 0x11, 0x2C, 0x02, 0x00, 0x13, 0x2D, 0x02, 0x00, 0x11, 0x2E, 0x02, 0x00, 0x11, 0x2F, 0x02, 0x00, 0x11,\n\t0x30, 0x02, 0x00, 0x11, 0x31, 0x02, 0x00, 0x11, 0x32, 0x02, 0x00, 0x11, 0x33, 0x02, 0x00, 0x11, 0x34, 0x02, 0x00, 0x11, 0x35, 0x02, 0x00, 0x11, 0x36, 0x02, 0x00, 0x11, 0x37, 0x02, 0x00, 0x11,\n\t0x38, 0x02, 0x00, 0x11, 0x39, 0x02, 0x00, 0x13, 0x3A, 0x02, 0x00, 0x11, 0x3B, 0x02, 0x00, 0x02, 0xB1, 0x11, 0x02, 0x01, 0x00, 0x11, 0x3E, 0x02, 0x00, 0x11, 0x3F, 0x02, 0x00, 0x11, 0x40, 0x02,\n\t0x00, 0x11, 0x41, 0x02, 0x00, 0x11, 0x42, 0x02, 0x00, 0x11, 0x43, 0x02, 0x00, 0x11, 0x44, 0x02, 0x00, 0x11, 0x45, 0x02, 0x00, 0x11, 0x46, 0x02, 0x00, 0x11, 0x47, 0x02, 0x00, 0x11, 0x48, 0x02,\n\t0x00, 0x11, 0x49, 0x02, 0x00, 0x11, 0x4A, 0x02, 0x00, 0x11, 0x4B, 0x02, 0x00, 0x11, 0x4C, 0x02, 0x00, 0x11, 0x4D, 0x02, 0x00, 0x11, 0x4E, 0x02, 0x00, 0x11, 0x4F, 0x02, 0x00, 0x11, 0x50, 0x02,\n\t0x00, 0x11, 0x51, 0x02, 0x00, 0x11, 0x52, 0x02, 0x00, 0x11, 0x53, 0x02, 0x00, 0x11, 0x54, 0x02, 0x00, 0x11, 0x55, 0x02, 0x00, 0x11, 0x56, 0x02, 0x00, 0x11, 0x57, 0x02, 0x00, 0x11, 0x58, 0x02,\n\t0x00, 0x11, 0x59, 0x02, 0x00, 0x11, 0x5A, 0x02, 0x00, 0x11, 0x5B, 0x02, 0x00, 0x11, 0x5C, 0x02, 0x00, 0x11, 0x5D, 0x02, 0x00, 0x11, 0x5E, 0x02, 0x00, 0x11, 0x5F, 0x02, 0x00, 0x11, 0x60, 0x02,\n\t0x00, 0x11, 0x61, 0x02, 0x00, 0x11, 0x62, 0x02, 0x00, 0x11, 0x63, 0x02, 0x00, 0x11, 0x64, 0x02, 0x00, 0x11, 0x65, 0x02, 0x00, 0x11, 0x66, 0x02, 0x00, 0x11, 0x67, 0x02, 0x00, 0x11, 0x68, 0x02,\n\t0x00, 0x11, 0x69, 0x02, 0x00, 0x11, 0x6A, 0x02, 0x00, 0x11, 0x6B, 0x02, 0x00, 0x11, 0x6C, 0x02, 0x00, 0x11, 0x6D, 0x02, 0x00, 0x11, 0x6E, 0x02, 0x00, 0x11, 0x6F, 0x02, 0x00, 0x11, 0x70, 0x02,\n\t0x00, 0x51, 0x71, 0x3D, 0x71, 0x3D, 0x72, 0x02, 0x00, 0x11, 0x73, 0x02, 0x00, 0x11, 0x74, 0x02, 0x00, 0x11, 0x75, 0x02, 0x00, 0x11, 0x76, 0x02, 0x00, 0x11, 0x77, 0x02, 0x00, 0x11, 0x78, 0x02,\n\t0x00, 0x11, 0x79, 0x02, 0x00, 0x11, 0x7A, 0x02, 0x00, 0x11, 0x7B, 0x02, 0x00, 0x11, 0x7C, 0x02, 0x00, 0x11, 0x7D, 0x02, 0x00, 0x11, 0x7E, 0x02, 0x00, 0x51, 0x7F, 0x3D, 0x7F, 0x3D, 0x80, 0x02,\n\t0x00, 0x11, 0x81, 0x02, 0x00, 0x11, 0x82, 0x02, 0x00, 0x11, 0x83, 0x02, 0x00, 0x11, 0x84, 0x02, 0x00, 0x11, 0x85, 0x02, 0x00, 0x11, 0x86, 0x02, 0x00, 0x11, 0x87, 0x02, 0x00, 0x11, 0x88, 0x02,\n\t0x00, 0x51, 0x89, 0x3D, 0x89, 0x3D, 0x8A, 0x02, 0x00, 0x11, 0x8B, 0x02, 0x00, 0x11, 0x8C, 0x02, 0x00, 0x11, 0x8D, 0x02, 0x00, 0x11, 0x8E, 0x02, 0x00, 0x11, 0x8F, 0x02, 0x00, 0x11, 0x90, 0x02,\n\t0x00, 0x51, 0x91, 0x3D, 0x91, 0x3D, 0x92, 0x02, 0x00, 0x11, 0x93, 0x02, 0x00, 0x11, 0x94, 0x02, 0x00, 0x11, 0x95, 0x02, 0x00, 0x11, 0x96, 0x02, 0x00, 0x11, 0x97, 0x02, 0x00, 0x11, 0x98, 0x02,\n\t0x00, 0x51, 0x99, 0x3D, 0x99, 0x3D, 0x9A, 0x02, 0x00, 0x11, 0x9B, 0x02, 0x00, 0x11, 0x9C, 0x02, 0x00, 0x11, 0x9D, 0x02, 0x00, 0x11, 0x9E, 0x02, 0x00, 0x11, 0x9F, 0x02, 0x00, 0x51, 0xA0, 0x3D,\n\t0xA0, 0x3D, 0xA1, 0x02, 0x00, 0x11, 0xA2, 0x02, 0x00, 0x11, 0xA3, 0x02, 0x00, 0x11, 0xA4, 0x02, 0x00, 0x11, 0xA5, 0x02, 0x00, 0x51, 0xA6, 0x3D, 0xA6, 0x3D, 0xA7, 0x02, 0x00, 0x11, 0xA8, 0x02,\n\t0x00, 0x11, 0xA9, 0x02, 0x00, 0x11, 0xAA, 0x02, 0x00, 0x11, 0xAB, 0x02, 0x00, 0x51, 0xAC, 0x3D, 0xAC, 0x3D, 0xAD, 0x02, 0x00, 0x11, 0xAE, 0x02, 0x00, 0x11, 0xAF, 0x02, 0x00, 0x01, 0x4D, 0xFF,\n\t0x01, 0x55, 0xF4, 0x11, 0xB2, 0x02, 0x00, 0x11, 0xB3, 0x02, 0x00, 0x11, 0xB4, 0x02, 0x00, 0x11, 0xB5, 0x02, 0x00, 0x51, 0xB6, 0x3D, 0xB6, 0x3D, 0xB7, 0x02, 0x00, 0x11, 0xB8, 0x02, 0x00, 0x11,\n\t0xB9, 0x02, 0x00, 0x11, 0xBA, 0x02, 0x00, 0x51, 0xBB, 0x3D, 0xBB, 0x3D, 0xBC, 0x02, 0x00, 0x11, 0xBD, 0x02, 0x00, 0x11, 0xBE, 0x02, 0x00, 0x11, 0xBF, 0x02, 0x00, 0x51, 0xC0, 0x3D, 0xC0, 0x3D,\n\t0xC1, 0x02, 0x00, 0x11, 0xC2, 0x02, 0x00, 0x11, 0xC3, 0x02, 0x00, 0x51, 0xC4, 0x3D, 0xC4, 0x3D, 0xC5, 0x02, 0x00, 0x11, 0xC6, 0x02, 0x00, 0x11, 0xC7, 0x02, 0x00, 0x11, 0xC8, 0x02, 0x00, 0x51,\n\t0xC9, 0x3D, 0xC9, 0x3D, 0xCA, 0x02, 0x00, 0x11, 0xCB, 0x02, 0x00, 0x11, 0xCC, 0x02, 0x00, 0x51, 0xCD, 0x3D, 0xCD, 0x3D, 0xCE, 0x02, 0x00, 0x11, 0xCF, 0x02, 0x00, 0x11, 0xD0, 0x02, 0x00, 0x51,\n\t0xD1, 0x3D, 0xD1, 0x3D, 0xD2, 0x02, 0x00, 0x11, 0xD3, 0x02, 0x00, 0x11, 0xD4, 0x02, 0x00, 0x51, 0xD5, 0x3D, 0xD5, 0x3D, 0xD6, 0x02, 0x00, 0x11, 0xD7, 0x02, 0x00, 0x11, 0xD8, 0x02, 0x00, 0x51,\n\t0xD9, 0x3D, 0xD9, 0x3D, 0xDA, 0x02, 0x00, 0x11, 0xDB, 0x02, 0x00, 0x11, 0xDC, 0x02, 0x00, 0x51, 0xDD, 0x3D, 0xDD, 0x3D, 0xDE, 0x02, 0x00, 0x11, 0xDF, 0x02, 0x00, 0x51, 0xE0, 0x3D, 0xE0, 0x3D,\n\t0xE1, 0x02, 0x00, 0x11, 0xE2, 0x02, 0x00, 0x11, 0xE3, 0x02, 0x00, 0x51, 0xE4, 0x3D, 0xE4, 0x3D, 0xE5, 0x02, 0x00, 0x11, 0xE6, 0x02, 0x00, 0x51, 0xE7, 0x3D, 0xE7, 0x3D, 0xE8, 0x02, 0x00, 0x11,\n\t0xE9, 0x02, 0x00, 0x11, 0xEA, 0x02, 0x00, 0x51, 0xEB, 0x3D, 0xEB, 0x3D, 0xEC, 0x02, 0x00, 0x11, 0xED, 0x02, 0x00, 0x51, 0xEE, 0x3D, 0xEE, 0x3D, 0xEF, 0x02, 0x00, 0x11, 0xF0, 0x02, 0x00, 0x51,\n\t0xF1, 0x3D, 0xF1, 0x3D, 0xF2, 0x02, 0x00, 0x11, 0xF3, 0x02, 0x00, 0x11, 0xF4, 0x02, 0x00, 0x51, 0xF5, 0x3D, 0xF5, 0x3D, 0xF6, 0x02, 0x00, 0x11, 0xF7, 0x02, 0x00, 0x51, 0xF8, 0x3D, 0xF8, 0x3D,\n\t0xF9, 0x02, 0x00, 0x11, 0xFA, 0x02, 0x00, 0x51, 0xFB, 0x3D, 0xFB, 0x3D, 0xFC, 0x02, 0x00, 0x11, 0xFD, 0x02, 0x00, 0x51, 0xFE, 0x3D, 0xFE, 0x3D, 0xFF, 0x02, 0x00, 0x20, 0x00, 0x3E, 0x02, 0x00,\n\t0x51, 0x01, 0x3E, 0x01, 0x3E, 0x02, 0x02, 0x00, 0x11, 0x03, 0x02, 0x00, 0x51, 0x04, 0x3E, 0x04, 0x3E, 0x05, 0x02, 0x00, 0x11, 0x06, 0x02, 0x00, 0x51, 0x07, 0x3E, 0x07, 0x3E, 0x08, 0x02, 0x00,\n\t0x11, 0x09, 0x02, 0x00, 0x51, 0x0A, 0x3E, 0x0A, 0x3E, 0x0B, 0x02, 0x00, 0x51, 0x0C, 0x3E, 0x0C, 0x3E, 0x0D, 0x02, 0x00, 0x11, 0x0E, 0x02, 0x00, 0x51, 0x0F, 0x3E, 0x0F, 0x3E, 0x10, 0x02, 0x00,\n\t0x11, 0x11, 0x02, 0x00, 0x51, 0x12, 0x3E, 0x12, 0x3E, 0x13, 0x02, 0x00, 0x11, 0x14, 0x02, 0x00, 0x51, 0x15, 0x3E, 0x15, 0x3E, 0x16, 0x02, 0x00, 0x51, 0x17, 0x3E, 0x17, 0x3E, 0x18, 0x02, 0x00,\n\t0x11, 0x19, 0x02, 0x00, 0x51, 0x1A, 0x3E, 0x1A, 0x3E, 0x1B, 0x02, 0x00, 0x11, 0x1C, 0x02, 0x00, 0x51, 0x1D, 0x3E, 0x1D, 0x3E, 0x1E, 0x02, 0x00, 0x51, 0x1F, 0x3E, 0x1F, 0x3E, 0x20, 0x02, 0x00,\n\t0x11, 0x21, 0x02, 0x00, 0x51, 0x22, 0x3E, 0x22, 0x3E, 0x23, 0x02, 0x00, 0x51, 0x24, 0x3E, 0x24, 0x3E, 0x25, 0x02, 0x00, 0x11, 0x26, 0x02, 0x00, 0x51, 0x27, 0x3E, 0x27, 0x3E, 0x28, 0x02, 0x00,\n\t0x51, 0x29, 0x3E, 0x29, 0x3E, 0x2A, 0x02, 0x00, 0x11, 0x2B, 0x02, 0x00, 0x51, 0x2C, 0x3E, 0x2C, 0x3E, 0x2D, 0x02, 0x00, 0x51, 0x2E, 0x3E, 0x2E, 0x3E, 0x2F, 0x02, 0x00, 0x11, 0x30, 0x02, 0x00,\n\t0x51, 0x31, 0x3E, 0x31, 0x3E, 0x32, 0x02, 0x00, 0x51, 0x33, 0x3E, 0x33, 0x3E, 0x34, 0x02, 0x00, 0x11, 0x35, 0x02, 0x00, 0x51, 0x36, 0x3E, 0x36, 0x3E, 0x37, 0x02, 0x00, 0x51, 0x38, 0x3E, 0x38,\n\t0x3E, 0x39, 0x02, 0x00, 0x51, 0x3A, 0x3E, 0x3A, 0x3E, 0x3B, 0x02, 0x00, 0x02, 0x0D, 0x17, 0x00, 0x91, 0x05, 0x02, 0x01, 0x00, 0x51, 0x3F, 0x3E, 0x3F, 0x3E, 0x40, 0x02, 0x00, 0x51, 0x41, 0x3E,\n\t0x41, 0x3E, 0x42, 0x02, 0x00, 0x51, 0x43, 0x3E, 0x43, 0x3E, 0x44, 0x02, 0x00, 0x11, 0x45, 0x02, 0x00, 0x51, 0x46, 0x3E, 0x46, 0x3E, 0x47, 0x02, 0x00, 0x51, 0x48, 0x3E, 0x48, 0x3E, 0x49, 0x02,\n\t0x00, 0x51, 0x4A, 0x3E, 0x4A, 0x3E, 0x4B, 0x02, 0x00, 0x51, 0x4C, 0x3E, 0x4C, 0x3E, 0x4D, 0x02, 0x00, 0x51, 0x4E, 0x3E, 0x4E, 0x3E, 0x4F, 0x02, 0x00, 0x11, 0x50, 0x02, 0x00, 0x51, 0x51, 0x3E,\n\t0x51, 0x3E, 0x52, 0x02, 0x00, 0x51, 0x53, 0x3E, 0x53, 0x3E, 0x54, 0x02, 0x00, 0x51, 0x55, 0x3E, 0x55, 0x3E, 0x56, 0x02, 0x00, 0x51, 0x57, 0x3E, 0x57, 0x3E, 0x58, 0x02, 0x00, 0x51, 0x59, 0x3E,\n\t0x59, 0x3E, 0x5A, 0x02, 0x00, 0x51, 0x5B, 0x3E, 0x5B, 0x3E, 0x5C, 0x02, 0x00, 0x51, 0x5D, 0x3E, 0x5D, 0x3E, 0x5E, 0x02, 0x00, 0x51, 0x5F, 0x3E, 0x5F, 0x3E, 0x60, 0x02, 0x00, 0x51, 0x61, 0x3E,\n\t0x61, 0x3E, 0x62, 0x02, 0x00, 0x51, 0x63, 0x3E, 0x63, 0x3E, 0x64, 0x02, 0x00, 0x51, 0x65, 0x3E, 0x65, 0x3E, 0x66, 0x02, 0x00, 0x51, 0x67, 0x3E, 0x67, 0x3E, 0x68, 0x02, 0x00, 0x51, 0x69, 0x3E,\n\t0x69, 0x3E, 0x6A, 0x02, 0x00, 0x51, 0x6B, 0x3E, 0x6B, 0x3E, 0x6C, 0x02, 0x00, 0x51, 0x6D, 0x3E, 0x6D, 0x3E, 0x6E, 0x02, 0x00, 0x51, 0x6F, 0x3E, 0x6F, 0x3E, 0x70, 0x02, 0x00, 0x51, 0x71, 0x3E,\n\t0x71, 0x3E, 0x72, 0x02, 0x00, 0x51, 0x73, 0x3E, 0x73, 0x3E, 0x74, 0x02, 0x00, 0x51, 0x75, 0x3E, 0x75, 0x3E, 0x76, 0x02, 0x00, 0x51, 0x77, 0x3E, 0x77, 0x3E, 0x78, 0x02, 0x00, 0x51, 0x79, 0x3E,\n\t0x79, 0x3E, 0x7A, 0x02, 0x00, 0x51, 0x7B, 0x3E, 0x7B, 0x3E, 0x7C, 0x02, 0x00, 0x91, 0x7D, 0x3E, 0x7D, 0x3E, 0x7E, 0x3E, 0x7E, 0x3E, 0x7F, 0x02, 0x00, 0x51, 0x80, 0x3E, 0x80, 0x3E, 0x81, 0x02,\n\t0x00, 0x51, 0x82, 0x3E, 0x82, 0x3E, 0x83, 0x02, 0x00, 0x51, 0x84, 0x3E, 0x84, 0x3E, 0x85, 0x02, 0x00, 0x51, 0x86, 0x3E, 0x86, 0x3E, 0x87, 0x02, 0x00, 0x91, 0x88, 0x3E, 0x88, 0x3E, 0x89, 0x3E,\n\t0x89, 0x3E, 0x8A, 0x02, 0x00, 0x51, 0x8B, 0x3E, 0x8B, 0x3E, 0x8C, 0x02, 0x00, 0x51, 0x8D, 0x3E, 0x8D, 0x3E, 0x8E, 0x02, 0x00, 0x51, 0x8F, 0x3E, 0x8F, 0x3E, 0x90, 0x02, 0x00, 0x91, 0x91, 0x3E,\n\t0x91, 0x3E, 0x92, 0x3E, 0x92, 0x3E, 0x93, 0x02, 0x00, 0x51, 0x94, 0x3E, 0x94, 0x3E, 0x95, 0x02, 0x00, 0x51, 0x96, 0x3E, 0x96, 0x3E, 0x97, 0x02, 0x00, 0xF1, 0xFF, 0x6D, 0x98, 0x3E, 0x98, 0x3E,\n\t0x99, 0x3E, 0x9A, 0x3E, 0x9B, 0x3E, 0x9C, 0x3E, 0x9C, 0x3E, 0x9D, 0x3E, 0x9E, 0x3E, 0x9F, 0x3E, 0xA0, 0x3E, 0xA1, 0x3E, 0xA1, 0x3E, 0xA2, 0x3E, 0xA3, 0x3E, 0xA4, 0x3E, 0xA5, 0x3E, 0xA6, 0x3E,\n\t0xA6, 0x3E, 0xA7, 0x3E, 0xA8, 0x3E, 0xA9, 0x3E, 0xAA, 0x3E, 0xAB, 0x3E, 0xAB, 0x3E, 0xAC, 0x3E, 0xAD, 0x3E, 0xAE, 0x3E, 0xAF, 0x3E, 0xB0, 0x3E, 0xB0, 0x3E, 0xB1, 0x3E, 0xB2, 0x3E, 0xB3, 0x3E,\n\t0xB4, 0x3E, 0xB5, 0x3E, 0xB5, 0x3E, 0xB6, 0x3E, 0xB7, 0x3E, 0xB8, 0x3E, 0xB9, 0x3E, 0xBA, 0x3E, 0xBA, 0x3E, 0xBB, 0x3E, 0xBC, 0x3E, 0xBD, 0x3E, 0xBE, 0x3E, 0xBF, 0x3E, 0xBF, 0x3E, 0xC0, 0x3E,\n\t0xC1, 0x3E, 0xC2, 0x3E, 0xC3, 0x3E, 0xC4, 0x3E, 0xC5, 0x3E, 0xC5, 0x3E, 0xC6, 0x3E, 0xC7, 0x3E, 0xC8, 0x3E, 0xC9, 0x3E, 0xCA, 0x3E, 0xCA, 0x3E, 0xCB, 0x3E, 0xCC, 0x3E, 0xCD, 0x3E, 0xCE, 0x3E,\n\t0xCF, 0x3E, 0xD0, 0x3E, 0xD0, 0x3E, 0xD1, 0x3E, 0xD2, 0x3E, 0xD3, 0x3E, 0xD4, 0x3E, 0xD5, 0x3E, 0xD6, 0x3E, 0xD6, 0x3E, 0xD7, 0x3E, 0xD8, 0x3E, 0xD9, 0x3E, 0xDA, 0x3E, 0xDB, 0x3E, 0xDC, 0x3E,\n\t0xDC, 0x3E, 0xDD, 0x3E, 0xDE, 0x3E, 0xDF, 0x3E, 0xE0, 0x3E, 0xE1, 0x3E, 0xE2, 0x3E, 0xE2, 0x3E, 0xE3, 0x3E, 0xE4, 0x3E, 0xE5, 0x3E, 0xE6, 0x3E, 0xE7, 0x3E, 0xE8, 0x3E, 0xE8, 0x3E, 0xE9, 0x3E,\n\t0xEA, 0x3E, 0xEB, 0x3E, 0xEC, 0x3E, 0xED, 0x3E, 0xEE, 0x3E, 0xEF, 0x3E, 0xEF, 0x3E, 0xF0, 0x3E, 0xF1, 0x3E, 0xF2, 0x3E, 0xF3, 0x3E, 0xF4, 0x3E, 0xF5, 0x3E, 0xF5, 0x3E, 0xF6, 0x3E, 0xF7, 0x3E,\n\t0xF8, 0x3E, 0xF9, 0x3E, 0xFA, 0x3E, 0xFB, 0x3E, 0xFC, 0x3E, 0xFC, 0x3E, 0xFD, 0x3E, 0xFE, 0x3E, 0xFF, 0x3E, 0x00, 0x3F, 0x01, 0x3F, 0x02, 0x3F, 0x03, 0x3F, 0x03, 0x3F, 0x04, 0x3F, 0x05, 0x3F,\n\t0x06, 0x3F, 0x07, 0x3F, 0x08, 0x3F, 0x09, 0x3F, 0x0A, 0x3F, 0x0A, 0x3F, 0x0B, 0x3F, 0x0C, 0x3F, 0x0D, 0x3F, 0x0E, 0x3F, 0x0F, 0x3F, 0x10, 0x3F, 0x11, 0x3F, 0x12, 0x3F, 0x12, 0x3F, 0x13, 0x3F,\n\t0x14, 0x3F, 0x15, 0x3F, 0x16, 0x3F, 0x17, 0x3F, 0x18, 0x3F, 0x19, 0x3F, 0x19, 0x3F, 0x1A, 0x3F, 0x1B, 0x3F, 0x1C, 0x3F, 0x1D, 0x3F, 0x1E, 0x3F, 0x1F, 0x3F, 0x20, 0x3F, 0x21, 0x3F, 0x21, 0x3F,\n\t0x22, 0x3F, 0x23, 0x3F, 0x24, 0x3F, 0x25, 0x3F, 0x26, 0x3F, 0x27, 0x3F, 0x28, 0x3F, 0x29, 0x3F, 0x2A, 0x3F, 0x2A, 0x3F, 0x2B, 0x3F, 0x2C, 0x3F, 0x2D, 0x3F, 0x2E, 0x3F, 0x2F, 0x3F, 0x30, 0x3F,\n\t0x31, 0x3F, 0x32, 0x3F, 0x32, 0x3F, 0x33, 0x3F, 0x34, 0x3F, 0x35, 0x3F, 0x36, 0x3F, 0x37, 0x3F, 0x38, 0x3F, 0x39, 0x3F, 0x3A, 0x3F, 0x3B, 0x17, 0x1A, 0xF1, 0xFF, 0xFF, 0x74, 0x3D, 0x3F, 0x3E,\n\t0x3F, 0x3F, 0x3F, 0x40, 0x3F, 0x41, 0x3F, 0x42, 0x3F, 0x43, 0x3F, 0x44, 0x3F, 0x45, 0x3F, 0x45, 0x3F, 0x46, 0x3F, 0x47, 0x3F, 0x48, 0x3F, 0x49, 0x3F, 0x4A, 0x3F, 0x4B, 0x3F, 0x4C, 0x3F, 0x4D,\n\t0x3F, 0x4E, 0x3F, 0x4F, 0x3F, 0x50, 0x3F, 0x50, 0x3F, 0x51, 0x3F, 0x52, 0x3F, 0x53, 0x3F, 0x54, 0x3F, 0x55, 0x3F, 0x56, 0x3F, 0x57, 0x3F, 0x58, 0x3F, 0x59, 0x3F, 0x5A, 0x3F, 0x5B, 0x3F, 0x5B,\n\t0x3F, 0x5C, 0x3F, 0x5D, 0x3F, 0x5E, 0x3F, 0x5F, 0x3F, 0x60, 0x3F, 0x61, 0x3F, 0x62, 0x3F, 0x63, 0x3F, 0x64, 0x3F, 0x65, 0x3F, 0x66, 0x3F, 0x66, 0x3F, 0x67, 0x3F, 0x68, 0x3F, 0x69, 0x3F, 0x6A,\n\t0x3F, 0x6B, 0x3F, 0x6C, 0x3F, 0x6D, 0x3F, 0x6E, 0x3F, 0x6F, 0x3F, 0x70, 0x3F, 0x71, 0x3F, 0x72, 0x3F, 0x73, 0x3F, 0x73, 0x3F, 0x74, 0x3F, 0x75, 0x3F, 0x76, 0x3F, 0x77, 0x3F, 0x78, 0x3F, 0x79,\n\t0x3F, 0x7A, 0x3F, 0x7B, 0x3F, 0x7C, 0x3F, 0x7D, 0x3F, 0x7E, 0x3F, 0x7F, 0x3F, 0x80, 0x3F, 0x81, 0x3F, 0x82, 0x3F, 0x82, 0x3F, 0x83, 0x3F, 0x84, 0x3F, 0x85, 0x3F, 0x86, 0x3F, 0x87, 0x3F, 0x88,\n\t0x3F, 0x89, 0x3F, 0x8A, 0x3F, 0x8B, 0x3F, 0x8C, 0x3F, 0x8D, 0x3F, 0x8E, 0x3F, 0x8F, 0x3F, 0x90, 0x3F, 0x91, 0x3F, 0x92, 0x3F, 0x92, 0x3F, 0x93, 0x3F, 0x94, 0x3F, 0x95, 0x3F, 0x96, 0x3F, 0x97,\n\t0x3F, 0x98, 0x3F, 0x99, 0x3F, 0x9A, 0x3F, 0x9B, 0x3F, 0x9C, 0x3F, 0x9D, 0x3F, 0x9E, 0x3F, 0x9F, 0x3F, 0xA0, 0x3F, 0xA1, 0x3F, 0xA2, 0x3F, 0xA3, 0x3F, 0xA4, 0x3F, 0xA5, 0x3F, 0xA5, 0x3F, 0xA6,\n\t0x3F, 0xA7, 0x3F, 0xA8, 0x3F, 0xA9, 0x3F, 0xAA, 0x3F, 0xAB, 0x3F, 0xAC, 0x3F, 0xAD, 0x3F, 0xAE, 0x3F, 0xAF, 0x3F, 0xB0, 0x3F, 0xB1, 0x3F, 0xB2, 0x3F, 0xB3, 0x3F, 0xB4, 0x3F, 0xB5, 0x3F, 0xB6,\n\t0x3F, 0xB7, 0x3F, 0xB8, 0x3F, 0xB9, 0x3F, 0xBA, 0x3F, 0xBB, 0x3F, 0xBC, 0x3F, 0xBD, 0x3F, 0xBE, 0x3F, 0xBF, 0x3F, 0xBF, 0x3F, 0xC0, 0x3F, 0xC1, 0x3F, 0xC2, 0x3F, 0xC3, 0x3F, 0xC4, 0x3F, 0xC5,\n\t0x3F, 0xC6, 0x3F, 0xC7, 0x3F, 0xC8, 0x3F, 0xC9, 0x3F, 0xCA, 0x3F, 0xCB, 0x3F, 0xCC, 0x3F, 0xCD, 0x3F, 0xCE, 0x3F, 0xCF, 0x3F, 0xD0, 0x3F, 0xD1, 0x3F, 0xD2, 0x3F, 0xD3, 0x3F, 0xD4, 0x3F, 0xD5,\n\t0x3F, 0xD6, 0x3F, 0xD7, 0x3F, 0xD8, 0x3F, 0xD9, 0x3F, 0xDA, 0x3F, 0xDB, 0x3F, 0xDC, 0x3F, 0xDD, 0x3F, 0xDE, 0x3F, 0xDF, 0x3F, 0xE0, 0x3F, 0xE1, 0x3F, 0xE2, 0x3F, 0xE3, 0x3F, 0xE4, 0x3F, 0xE5,\n\t0x3F, 0xE6, 0x3F, 0xE7, 0x3F, 0xE8, 0x3F, 0xE9, 0x3F, 0xEA, 0x3F, 0xEB, 0x3F, 0xEC, 0x3F, 0xED, 0x3F, 0xEE, 0x3F, 0xEF, 0x3F, 0xF0, 0x3F, 0xF0, 0x3F, 0xF1, 0x3F, 0xF2, 0x3F, 0xF3, 0x3F, 0xF4,\n\t0x3F, 0xF5, 0x3F, 0xF6, 0x3F, 0xF7, 0x3F, 0xF8, 0x3F, 0xF9, 0x3F, 0xFA, 0x3F, 0xFB, 0x3F, 0xFC, 0x3F, 0xFD, 0x3F, 0xFE, 0x3F, 0xFF, 0x3F, 0x00, 0x40, 0x01, 0x40, 0x01, 0x40, 0x02, 0x40, 0x02,\n\t0x40, 0x03, 0x40, 0x03, 0x40, 0x04, 0x40, 0x04, 0x40, 0x05, 0x40, 0x05, 0x40, 0x06, 0x40, 0x06, 0x40, 0x07, 0x40, 0x07, 0x40, 0x08, 0x40, 0x08, 0x40, 0x09, 0x40, 0x09, 0x40, 0x0A, 0x40, 0x0A,\n\t0x40, 0x0B, 0x40, 0x0B, 0x40, 0x0C, 0x40, 0x0C, 0x40, 0x0D, 0x40, 0x0D, 0x40, 0x0E, 0x40, 0x0E, 0x40, 0x0F, 0x40, 0x0F, 0x40, 0x10, 0x40, 0x10, 0x40, 0x11, 0x40, 0x11, 0x40, 0x12, 0x40, 0x12,\n\t0x40, 0x13, 0x40, 0x13, 0x40, 0x14, 0x40, 0x14, 0x40, 0x15, 0x40, 0x15, 0x40, 0x16, 0x40, 0x16, 0x40, 0x17, 0x40, 0x17, 0x40, 0x18, 0x40, 0x19, 0x40, 0x19, 0x40, 0x1A, 0x40, 0x1A, 0x40, 0x1B,\n\t0x40, 0x1B, 0x40, 0x1C, 0x40, 0x1C, 0x40, 0x1D, 0x40, 0x1D, 0x40, 0x1E, 0x40, 0x1E, 0x40, 0x1F, 0x40, 0x1F, 0x40, 0x20, 0x40, 0x20, 0x40, 0x21, 0x40, 0x21, 0x40, 0x22, 0x40, 0x22, 0x40, 0x23,\n\t0x40, 0x23, 0x40, 0x24, 0x40, 0x24, 0x40, 0x25, 0x40, 0x25, 0x40, 0x26, 0x40, 0x26, 0x40, 0x27, 0x40, 0x27, 0x40, 0x28, 0x40, 0x28, 0x40, 0x29, 0x40, 0x2A, 0x40, 0x2A, 0x40, 0x2B, 0x40, 0x2B,\n\t0x40, 0x2C, 0x40, 0x2C, 0x40, 0x2D, 0x40, 0x2D, 0x40, 0x2E, 0x40, 0x2E, 0x40, 0x2F, 0x40, 0x2F, 0x40, 0x30, 0x40, 0x30, 0x40, 0x31, 0x40, 0x31, 0x40, 0x32, 0x40, 0x32, 0x40, 0x33, 0x40, 0x33,\n\t0x40, 0x34, 0x40, 0x35, 0x40, 0x35, 0x40, 0x36, 0x40, 0x36, 0x40, 0x37, 0x40, 0x37, 0x40, 0x38, 0x40, 0x38, 0x40, 0x39, 0x40, 0x39, 0x40, 0x3A, 0x40, 0x3A, 0x40, 0x3B, 0x40, 0x3B, 0x61, 0x1C,\n\t0x00, 0x51, 0x0B, 0x60, 0x3E, 0x40, 0x3F, 0x40, 0x3F, 0x40, 0x01, 0x00, 0xF1, 0xFF, 0xFF, 0xFF, 0x45, 0x41, 0x40, 0x41, 0x40, 0x42, 0x40, 0x42, 0x40, 0x43, 0x40, 0x43, 0x40, 0x44, 0x40, 0x44,\n\t0x40, 0x45, 0x40, 0x45, 0x40, 0x46, 0x40, 0x47, 0x40, 0x47, 0x40, 0x48, 0x40, 0x48, 0x40, 0x49, 0x40, 0x49, 0x40, 0x4A, 0x40, 0x4A, 0x40, 0x4B, 0x40, 0x4B, 0x40, 0x4C, 0x40, 0x4C, 0x40, 0x4D,\n\t0x40, 0x4E, 0x40, 0x4E, 0x40, 0x4F, 0x40, 0x4F, 0x40, 0x50, 0x40, 0x50, 0x40, 0x51, 0x40, 0x51, 0x40, 0x52, 0x40, 0x52, 0x40, 0x53, 0x40, 0x53, 0x40, 0x54, 0x40, 0x55, 0x40, 0x55, 0x40, 0x56,\n\t0x40, 0x56, 0x40, 0x57, 0x40, 0x57, 0x40, 0x58, 0x40, 0x58, 0x40, 0x59, 0x40, 0x59, 0x40, 0x5A, 0x40, 0x5A, 0x40, 0x5B, 0x40, 0x5C, 0x40, 0x5C, 0x40, 0x5D, 0x40, 0x5D, 0x40, 0x5E, 0x40, 0x5E,\n\t0x40, 0x5F, 0x40, 0x5F, 0x40, 0x60, 0x40, 0x60, 0x40, 0x61, 0x40, 0x62, 0x40, 0x62, 0x40, 0x63, 0x40, 0x63, 0x40, 0x64, 0x40, 0x64, 0x40, 0x65, 0x40, 0x65, 0x40, 0x66, 0x40, 0x67, 0x40, 0x67,\n\t0x40, 0x68, 0x40, 0x68, 0x40, 0x69, 0x40, 0x69, 0x40, 0x6A, 0x40, 0x6A, 0x40, 0x6B, 0x40, 0x6B, 0x40, 0x6C, 0x40, 0x6D, 0x40, 0x6D, 0x40, 0x6E, 0x40, 0x6E, 0x40, 0x6F, 0x40, 0x6F, 0x40, 0x70,\n\t0x40, 0x70, 0x40, 0x71, 0x40, 0x72, 0x40, 0x72, 0x40, 0x73, 0x40, 0x73, 0x40, 0x74, 0x40, 0x74, 0x40, 0x75, 0x40, 0x75, 0x40, 0x76, 0x40, 0x77, 0x40, 0x77, 0x40, 0x78, 0x40, 0x78, 0x40, 0x79,\n\t0x40, 0x79, 0x40, 0x7A, 0x40, 0x7B, 0x40, 0x7B, 0x40, 0x7C, 0x40, 0x7C, 0x40, 0x7D, 0x40, 0x7D, 0x40, 0x7E, 0x40, 0x7E, 0x40, 0x7F, 0x40, 0x80, 0x40, 0x80, 0x40, 0x81, 0x40, 0x81, 0x40, 0x82,\n\t0x40, 0x82, 0x40, 0x83, 0x40, 0x84, 0x40, 0x84, 0x40, 0x85, 0x40, 0x85, 0x40, 0x86, 0x40, 0x86, 0x40, 0x87, 0x40, 0x87, 0x40, 0x88, 0x40, 0x89, 0x40, 0x89, 0x40, 0x8A, 0x40, 0x8A, 0x40, 0x8B,\n\t0x40, 0x8B, 0x40, 0x8C, 0x40, 0x8D, 0x40, 0x8D, 0x40, 0x8E, 0x40, 0x8E, 0x40, 0x8F, 0x40, 0x8F, 0x40, 0x90, 0x40, 0x91, 0x40, 0x91, 0x40, 0x92, 0x40, 0x92, 0x40, 0x93, 0x40, 0x93, 0x40, 0x94,\n\t0x40, 0x95, 0x40, 0x95, 0x40, 0x96, 0x40, 0x96, 0x40, 0x97, 0x40, 0x97, 0x40, 0x98, 0x40, 0x99, 0x40, 0x99, 0x40, 0x9A, 0x40, 0x9A, 0x40, 0x9B, 0x40, 0x9B, 0x40, 0x9C, 0x40, 0x9D, 0x40, 0x9D,\n\t0x40, 0x9E, 0x40, 0x9E, 0x40, 0x9F, 0x40, 0x9F, 0x40, 0xA0, 0x40, 0xA1, 0x40, 0xA1, 0x40, 0xA2, 0x40, 0xA2, 0x40, 0xA3, 0x40, 0xA4, 0x40, 0xA4, 0x40, 0xA5, 0x40, 0xA5, 0x40, 0xA6, 0x40, 0xA6,\n\t0x40, 0xA7, 0x40, 0xA8, 0x40, 0xA8, 0x40, 0xA9, 0x40, 0xA9, 0x40, 0xAA, 0x40, 0xAB, 0x40, 0xAB, 0x40, 0xAC, 0x40, 0xAC, 0x40, 0xAD, 0x40, 0xAD, 0x40, 0xAE, 0x40, 0xAF, 0x40, 0xAF, 0x40, 0xB0,\n\t0x40, 0xB0, 0x40, 0xB1, 0x40, 0xB2, 0x40, 0xB2, 0x40, 0xB3, 0x40, 0xB3, 0x40, 0xB4, 0x40, 0xB4, 0x40, 0xB5, 0x40, 0xB6, 0x40, 0xB6, 0x40, 0xB7, 0x40, 0xB7, 0x40, 0xB8, 0x40, 0xB9, 0x40, 0xB9,\n\t0x40, 0xBA, 0x40, 0xBA, 0x40, 0xBB, 0x40, 0xBC, 0x40, 0xBC, 0x40, 0xBD, 0x40, 0xBD, 0x40, 0xBE, 0x40, 0xBF, 0x40, 0xBF, 0x40, 0xC0, 0x40, 0xC0, 0x40, 0xC1, 0x40, 0xC1, 0x40, 0xC2, 0x40, 0xC3,\n\t0x40, 0xC3, 0x40, 0xC4, 0x40, 0xC4, 0x40, 0xC5, 0x40, 0xC6, 0x40, 0xC6, 0x40, 0xC7, 0x40, 0xC7, 0x40, 0xC8, 0x40, 0xC9, 0x40, 0xC9, 0x40, 0xCA, 0x40, 0xCA, 0x40, 0xCB, 0x40, 0xCC, 0x40, 0xCC,\n\t0x40, 0xCD, 0x40, 0xCD, 0x40, 0xCE, 0x40, 0xCF, 0x40, 0xCF, 0x40, 0xD0, 0x40, 0xD0, 0x40, 0xD1, 0x40, 0xD2, 0x40, 0xD2, 0x40, 0xD3, 0x40, 0xD3, 0x40, 0xD4, 0x40, 0xD5, 0x40, 0xD5, 0x40, 0xD6,\n\t0x40, 0xD6, 0x40, 0xD7, 0x40, 0xD8, 0x40, 0xD8, 0x40, 0xD9, 0x40, 0xD9, 0x40, 0xDA, 0x40, 0xDB, 0x40, 0xDB, 0x40, 0xDC, 0x40, 0xDD, 0x40, 0xDD, 0x40, 0xDE, 0x40, 0xDE, 0x40, 0xDF, 0x40, 0xE0,\n\t0x40, 0xE0, 0x40, 0xE1, 0x40, 0xE1, 0x40, 0xE2, 0x40, 0xE3, 0x40, 0xE3, 0x40, 0xE4, 0x40, 0xE4, 0x40, 0xE5, 0x40, 0xE6, 0x40, 0xE6, 0x40, 0xE7, 0x40, 0xE8, 0x40, 0xE8, 0x40, 0xE9, 0x40, 0xE9,\n\t0x40, 0xEA, 0x40, 0xEB, 0x40, 0xEB, 0x40, 0xEC, 0x40, 0xEC, 0x40, 0xED, 0x40, 0xEE, 0x40, 0xEE, 0x40, 0xEF, 0x40, 0xF0, 0x40, 0xF0, 0x40, 0xF1, 0x40, 0xF1, 0x40, 0xF2, 0x40, 0xF3, 0x40, 0xF3,\n\t0x40, 0xF4, 0x40, 0xF4, 0x40, 0xF5, 0x40, 0xF6, 0x40, 0xF6, 0x40, 0xF7, 0x40, 0xF8, 0x40, 0xF8, 0x40, 0xF9, 0x40, 0xF9, 0x40, 0xFA, 0x40, 0xFB, 0x40, 0xFB, 0x40, 0xFC, 0x40, 0xFD, 0x40, 0xFD,\n\t0x40, 0xFE, 0x40, 0xFE, 0x40, 0xFF, 0x40, 0x00, 0x41, 0x00, 0x41, 0x01, 0x41, 0x02, 0x41, 0x02, 0x41, 0x03, 0x41, 0x03, 0x41, 0x04, 0x41, 0x05, 0x41, 0x05, 0x41, 0x06, 0x41, 0x07, 0x41, 0x07,\n\t0x41, 0x08, 0x41, 0x08, 0x41, 0x09, 0x41, 0x0A, 0x41, 0x0A, 0x41, 0x0B, 0x41, 0x0C, 0x41, 0x0C, 0x41, 0x0D, 0x41, 0x0D, 0x41, 0x0E, 0x41, 0x0F, 0x41, 0x0F, 0x41, 0x10, 0x41, 0x11, 0x41, 0x11,\n\t0x41, 0x12, 0x41, 0x13, 0x41, 0x13, 0x41, 0x14, 0x41, 0x14, 0x41, 0x15, 0x41, 0x16, 0x41, 0x16, 0x41, 0x17, 0x41, 0x18, 0x41, 0x18, 0x41, 0x19, 0x41, 0x1A, 0x41, 0x1A, 0x41, 0x1B, 0x41, 0x1B,\n\t0x41, 0x1C, 0x41, 0x1D, 0x41, 0x1D, 0x41, 0x1E, 0x41, 0x1F, 0x41, 0x1F, 0x41, 0x20, 0x41, 0x21, 0x41, 0x21, 0x41, 0x22, 0x41, 0x22, 0x41, 0x23, 0x41, 0x24, 0x41, 0x24, 0x41, 0x25, 0x41, 0x26,\n\t0x41, 0x26, 0x41, 0x27, 0x41, 0x28, 0x41, 0x28, 0x41, 0x29, 0x41, 0x2A, 0x41, 0x2A, 0x41, 0x2B, 0x41, 0x2B, 0x41, 0x2C, 0x41, 0x2D, 0x41, 0x2D, 0x41, 0x2E, 0x41, 0x2F, 0x41, 0x2F, 0x41, 0x30,\n\t0x41, 0x31, 0x41, 0x31, 0x41, 0x32, 0x41, 0x33, 0x41, 0x33, 0x41, 0x34, 0x41, 0x35, 0x41, 0x35, 0x41, 0x36, 0x41, 0x37, 0x41, 0x37, 0x41, 0x38, 0x41, 0x38, 0x41, 0x39, 0x41, 0x3A, 0x41, 0x3A,\n\t0x41, 0x3B, 0x89, 0x1F, 0x20, 0x3D, 0x41, 0x1F, 0x09, 0x20, 0x3F, 0x41, 0x5F, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,\n\t0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xB2, 0x41, 0x41, 0x42, 0x41, 0x42, 0x41, 0x43, 0x41, 0x44, 0x41, 0x44, 0x41, 0x45, 0x41, 0x46, 0x41, 0x46, 0x41, 0x47, 0x41, 0x48,\n\t0x41, 0x48, 0x41, 0x49, 0x41, 0x4A, 0x41, 0x4A, 0x41, 0x4B, 0x41, 0x4C, 0x41, 0x4C, 0x41, 0x4D, 0x41, 0x4E, 0x41, 0x4E, 0x41, 0x4F, 0x41, 0x50, 0x41, 0x50, 0x41, 0x51, 0x41, 0x52, 0x41, 0x52,\n\t0x41, 0x53, 0x41, 0x54, 0x41, 0x54, 0x41, 0x55, 0x41, 0x56, 0x41, 0x56, 0x41, 0x57, 0x41, 0x58, 0x41, 0x58, 0x41, 0x59, 0x41, 0x5A, 0x41, 0x5A, 0x41, 0x5B, 0x41, 0x5C, 0x41, 0x5C, 0x41, 0x5D,\n\t0x41, 0x5E, 0x41, 0x5E, 0x41, 0x5F, 0x41, 0x60, 0x41, 0x60, 0x41, 0x61, 0x41, 0x62, 0x41, 0x62, 0x41, 0x63, 0x41, 0x64, 0x41, 0x64, 0x41, 0x65, 0x41, 0x66, 0x41, 0x66, 0x41, 0x67, 0x41, 0x68,\n\t0x41, 0x68, 0x41, 0x69, 0x41, 0x6A, 0x41, 0x6A, 0x41, 0x6B, 0x41, 0x6C, 0x41, 0x6C, 0x41, 0x6D, 0x41, 0x6E, 0x41, 0x6E, 0x41, 0x6F, 0x41, 0x70, 0x41, 0x71, 0x41, 0x72, 0x41, 0x74, 0x41, 0x75,\n\t0x41, 0x77, 0x41, 0x78, 0x41, 0x79, 0x41, 0x7B, 0x41, 0x7C, 0x41, 0x7D, 0x41, 0x7F, 0x41, 0x80, 0x41, 0x82, 0x41, 0x83, 0x41, 0x84, 0x41, 0x86, 0x41, 0x87, 0x41, 0x88, 0x41, 0x8A, 0x41, 0x8B,\n\t0x41, 0x8D, 0x41, 0x8E, 0x41, 0x8F, 0x41, 0x91, 0x41, 0x92, 0x41, 0x94, 0x41, 0x95, 0x41, 0x96, 0x41, 0x98, 0x41, 0x99, 0x41, 0x9B, 0x41, 0x9C, 0x41, 0x9D, 0x41, 0x9F, 0x41, 0xA0, 0x41, 0xA2,\n\t0x41, 0xA3, 0x41, 0xA4, 0x41, 0xA6, 0x41, 0xA7, 0x41, 0xA9, 0x41, 0xAA, 0x41, 0xAB, 0x41, 0xAD, 0x41, 0xAE, 0x41, 0xB0, 0x41, 0xB1, 0x41, 0xB3, 0x41, 0xB4, 0x41, 0xB5, 0x41, 0xB7, 0x41, 0xB8,\n\t0x41, 0xBA, 0x41, 0xBB, 0x41, 0xBD, 0x41, 0xBE, 0x41, 0xBF, 0x41, 0xC1, 0x41, 0xC2, 0x41, 0xC4, 0x41, 0xC5, 0x41, 0xC7, 0x41, 0xC8, 0x41, 0xCA, 0x41, 0xCB, 0x41, 0xCC, 0x41, 0xCE, 0x41, 0xCF,\n\t0x41, 0xD1, 0x41, 0xD2, 0x41, 0xD4, 0x41, 0xD5, 0x41, 0xD7, 0x41, 0xD8, 0x41, 0xDA, 0x41, 0xDB, 0x41, 0xDC, 0x41, 0xDE, 0x41, 0xDF, 0x41, 0xE1, 0x41, 0xE2, 0x41, 0xE4, 0x41, 0xE5, 0x41, 0xE7,\n\t0x41, 0xE8, 0x41, 0xEA, 0x41, 0xEB, 0x41, 0xED, 0x41, 0xEE, 0x41, 0xF0, 0x41, 0xF1, 0x41, 0xF3, 0x41, 0xF4, 0x41, 0xF6, 0x41, 0xF7, 0x41, 0xF9, 0x41, 0xFA, 0x41, 0xFC, 0x41, 0xFD, 0x41, 0xFF,\n\t0x41, 0x00, 0x42, 0x02, 0x42, 0x03, 0x42, 0x05, 0x42, 0x06, 0x42, 0x08, 0x42, 0x09, 0x42, 0x0B, 0x42, 0x0C, 0x42, 0x0E, 0x42, 0x0F, 0x42, 0x11, 0x42, 0x12, 0x42, 0x14, 0x42, 0x15, 0x42, 0x17,\n\t0x42, 0x18, 0x42, 0x1A, 0x42, 0x1B, 0x42, 0x1D, 0x42, 0x1E, 0x42, 0x20, 0x42, 0x21, 0x42, 0x23, 0x42, 0x24, 0x42, 0x26, 0x42, 0x28, 0x42, 0x29, 0x42, 0x2B, 0x42, 0x2C, 0x42, 0x2E, 0x42, 0x2F,\n\t0x42, 0x31, 0x42, 0x32, 0x42, 0x34, 0x42, 0x35, 0x42, 0x37, 0x42, 0x39, 0x42, 0x3A, 0x42, 0x3C, 0x42, 0x3D, 0x42, 0x3F, 0x42, 0x40, 0x42, 0x42, 0x42, 0x43, 0x42, 0x45, 0x42, 0x47, 0x42, 0x48,\n\t0x42, 0x4A, 0x42, 0x4B, 0x42, 0x4D, 0x42, 0x4E, 0x42, 0x50, 0x42, 0x52, 0x42, 0x53, 0x42, 0x55, 0x42, 0x56, 0x42, 0x58, 0x42, 0x5A, 0x42, 0x5B, 0x42, 0x5D, 0x42, 0x5E, 0x42, 0x60, 0x42, 0x62,\n\t0x42, 0x63, 0x42, 0x65, 0x42, 0x66, 0x42, 0x68, 0x42, 0x69, 0x42, 0x6B, 0x42, 0x6D, 0x42, 0x6E, 0x42, 0x70, 0x42, 0x72, 0x42, 0x73, 0x42, 0x75, 0x42, 0x76, 0x42, 0x78, 0x42, 0x7A, 0x42, 0x7B,\n\t0x42, 0x7D, 0x42, 0x7E, 0x42, 0x80, 0x42, 0x82, 0x42, 0x83, 0x42, 0x85, 0x42, 0x87, 0x42, 0x88, 0x42, 0x8A, 0x42, 0x8C, 0x42, 0x8D, 0x42, 0x8F, 0x42, 0x90, 0x42, 0x92, 0x42, 0x94, 0x42, 0x95,\n\t0x42, 0x97, 0x42, 0x99, 0x42, 0x9A, 0x42, 0x9C, 0x42, 0x9E, 0x42, 0x9F, 0x42, 0xA1, 0x42, 0xA3, 0x42, 0xA4, 0x42, 0xA6, 0x42, 0xA8, 0x42, 0xA9, 0x42, 0xAB, 0x42, 0xAD, 0x42, 0xAE, 0x42, 0xB0,\n\t0x42, 0xB2, 0x42, 0xB3, 0x42, 0xB5, 0x42, 0xB7, 0x42, 0xB8, 0x42, 0xBA, 0x42, 0xBC, 0x42, 0xBD, 0x42, 0xBF, 0x42, 0xC1, 0x42, 0xC2, 0x42, 0xC4, 0x42, 0xC6, 0x42, 0xC7, 0x42, 0xC9, 0x42, 0xCB,\n\t0x42, 0xCD, 0x42, 0xCE, 0x42, 0xD0, 0x42, 0xD2, 0x42, 0xD3, 0x42, 0xD5, 0x42, 0xD7, 0x42, 0xD8, 0x42, 0xDA, 0x42, 0xDC, 0x42, 0xDE, 0x42, 0xDF, 0x42, 0xE1, 0x42, 0xE3, 0x42, 0xE5, 0x42, 0xE6,\n\t0x42, 0xE8, 0x42, 0xEA, 0x42, 0xEB, 0x42, 0xED, 0x42, 0xEF, 0x42, 0xF1, 0x42, 0xF2, 0x42, 0xF4, 0x42, 0xF6, 0x42, 0xF8, 0x42, 0xF9, 0x42, 0xFB, 0x42, 0xFD, 0x42, 0xFF, 0x42, 0x00, 0x43, 0x02,\n\t0x43, 0x04, 0x43, 0x06, 0x43, 0x07, 0x43, 0x09, 0x43, 0x0B, 0x43, 0x0D, 0x43, 0x0E, 0x43, 0x10, 0x43, 0x12, 0x43, 0x14, 0x43, 0x15, 0x43, 0x17, 0x43, 0x19, 0x43, 0x1B, 0x43, 0x1D, 0x43, 0x1E,\n\t0x43, 0x20, 0x43, 0x22, 0x43, 0x24, 0x43, 0x25, 0x43, 0x27, 0x43, 0x29, 0x43, 0x2B, 0x43, 0x2D, 0x43, 0x2E, 0x43, 0x30, 0x43, 0x32, 0x43, 0x34, 0x43, 0x36, 0x43, 0x37, 0x43, 0x39, 0x43, 0x3B,\n\t0x43, 0x3D, 0x43, 0x3F, 0x43, 0x40, 0x43, 0x42, 0x43, 0x44, 0x43, 0x46, 0x43, 0x48, 0x43, 0x4A, 0x43, 0x4B, 0x43, 0x4D, 0x43, 0x4F, 0x43, 0x51, 0x43, 0x53, 0x43, 0x54, 0x43, 0x56, 0x43, 0x58,\n\t0x43, 0x5A, 0x43, 0x5C, 0x43, 0x5E, 0x43, 0x60, 0x43, 0x61, 0x43, 0x63, 0x43, 0x65, 0x43, 0x67, 0x43, 0x69, 0x43, 0x6B, 0x43, 0x6C, 0x43, 0x6E, 0x43, 0x70, 0x43, 0x72, 0x43, 0x74, 0x43, 0x76,\n\t0x43, 0x78, 0x43, 0x79, 0x43, 0x7B, 0x43, 0x7D, 0x43, 0x7F, 0x43, 0x81, 0x43, 0x83, 0x43, 0x85, 0x43, 0x87, 0x43, 0x88, 0x43, 0x8A, 0x43, 0x8C, 0x43, 0x8E, 0x43, 0x90, 0x43, 0x92, 0x43, 0x94,\n\t0x43, 0x96, 0x43, 0x98, 0x43, 0x9A, 0x43, 0x9B, 0x43, 0x9D, 0x43, 0x9F, 0x43, 0xA1, 0x43, 0xA3, 0x43, 0xA5, 0x43, 0xA7, 0x43, 0xA9, 0x43, 0xAB, 0x43, 0xAD, 0x43, 0xAF, 0x43, 0xB0, 0x43, 0xB2,\n\t0x43, 0xB4, 0x43, 0xB6, 0x43, 0xB8, 0x43, 0xBA, 0x43, 0xBC, 0x43, 0xBE, 0x43, 0xC0, 0x43, 0xC2, 0x43, 0xC4, 0x43, 0xC6, 0x43, 0xC8, 0x43, 0xCA, 0x43, 0xCC, 0x43, 0xCE, 0x43, 0xCF, 0x43, 0xD1,\n\t0x43, 0xD3, 0x43, 0xD5, 0x43, 0xD7, 0x43, 0xD9, 0x43, 0xDB, 0x43, 0xDD, 0x43, 0xDF, 0x43, 0xE1, 0x43, 0xE3, 0x43, 0xE5, 0x43, 0xE7, 0x43, 0xE9, 0x43, 0xEB, 0x43, 0xED, 0x43, 0xEF, 0x43, 0xF1,\n\t0x43, 0xF3, 0x43, 0xF5, 0x43, 0xF7, 0x43, 0xF9, 0x43, 0xFB, 0x43, 0xFD, 0x43, 0xFF, 0x43, 0x00, 0x44, 0x01, 0x44, 0x02, 0x44, 0x03, 0x44, 0x04, 0x44, 0x05, 0x44, 0x06, 0x44, 0x07, 0x44, 0x08,\n\t0x44, 0x09, 0x44, 0x0A, 0x44, 0x0B, 0x44, 0x0D, 0x44, 0x0E, 0x44, 0x0F, 0x44, 0x10, 0x44, 0x11, 0x44, 0x12, 0x44, 0x13, 0x44, 0x14, 0x44, 0x15, 0x44, 0x16, 0x44, 0x17, 0x44, 0x18, 0x44, 0x19,\n\t0x44, 0x1A, 0x44, 0x1B, 0x44, 0x1C, 0x44, 0x1D, 0x44, 0x1E, 0x44, 0x1F, 0x44, 0x20, 0x44, 0x21, 0x44, 0x22, 0x44, 0x23, 0x44, 0x24, 0x44, 0x25, 0x44, 0x26, 0x44, 0x27, 0x44, 0x28, 0x44, 0x29,\n\t0x44, 0x2A, 0x44, 0x2B, 0x44, 0x2C, 0x44, 0x2D, 0x44, 0x2E, 0x44, 0x30, 0x44, 0x31, 0x44, 0x32, 0x44, 0x33, 0x44, 0x34, 0x44, 0x35, 0x44, 0x36, 0x44, 0x37, 0x44, 0x38, 0x44, 0x39, 0x44, 0x3A,\n\t0x44, 0x3B, 0x44, 0x3C, 0x44, 0x3D, 0x44, 0x3E, 0x44, 0x3F, 0x44, 0x40, 0x44, 0x41, 0x44, 0x43, 0x44, 0x44, 0x44, 0x45, 0x44, 0x46, 0x44, 0x47, 0x44, 0x48, 0x44, 0x49, 0x44, 0x4A, 0x44, 0x4B,\n\t0x44, 0x4C, 0x44, 0x4D, 0x44, 0x4E, 0x44, 0x4F, 0x44, 0x50, 0x44, 0x52, 0x44, 0x53, 0x44, 0x54, 0x44, 0x55, 0x44, 0x56, 0x44, 0x57, 0x44, 0x58, 0x44, 0x59, 0x44, 0x5A, 0x44, 0x5B, 0x44, 0x5C,\n\t0x44, 0x5D, 0x44, 0x5F, 0x44, 0x60, 0x44, 0x61, 0x44, 0x62, 0x44, 0x63, 0x44, 0x64, 0x44, 0x65, 0x44, 0x66, 0x44, 0x67, 0x44, 0x68, 0x44, 0x6A, 0x44, 0x6B, 0x44, 0x6C, 0x44, 0x6D, 0x44, 0x6E,\n\t0x44, 0x6F, 0x44, 0x70, 0x44, 0x71, 0x44, 0x72, 0x44, 0x73, 0x44, 0x75, 0x44, 0x76, 0x44, 0x77, 0x44, 0x78, 0x44, 0x79, 0x44, 0x7A, 0x44, 0x7B, 0x44, 0x7C, 0x44, 0x7E, 0x44, 0x7F, 0x44, 0x80,\n\t0x44, 0x81, 0x44, 0x82, 0x44, 0x83, 0x44, 0x84, 0x44, 0x85, 0x44, 0x87, 0x44, 0x88, 0x44, 0x89, 0x44, 0x8A, 0x44, 0x8B, 0x44, 0x8C, 0x44, 0x8D, 0x44, 0x8F, 0x44, 0x90, 0x44, 0x91, 0x44, 0x92,\n\t0x44, 0x93, 0x44, 0x94, 0x44, 0x95, 0x44, 0x97, 0x44, 0x98, 0x44, 0x99, 0x44, 0x9A, 0x44, 0x9B, 0x44, 0x9C, 0x44, 0x9D, 0x44, 0x9F, 0x44, 0xA0, 0x44, 0xA1, 0x44, 0xA2, 0x44, 0xA3, 0x44, 0xA4,\n\t0x44, 0xA6, 0x44, 0xA7, 0x44, 0xA8, 0x44, 0xA9, 0x44, 0xAA, 0x44, 0xAB, 0x44, 0xAD, 0x44, 0xAE, 0x44, 0xAF, 0x44, 0xB0, 0x44, 0xB1, 0x44, 0xB2, 0x44, 0xB4, 0x44, 0xB5, 0x44, 0xB6, 0x44, 0xB7,\n\t0x44, 0xB8, 0x44, 0xB9, 0x44, 0xBB, 0x44, 0xBC, 0x44, 0xBD, 0x44, 0xBE, 0x44, 0xBF, 0x44, 0xC1, 0x44, 0xC2, 0x44, 0xC3, 0x44, 0xC4, 0x44, 0xC5, 0x44, 0xC7, 0x44, 0xC8, 0x44, 0xC9, 0x44, 0xCA,\n\t0x44, 0xCB, 0x44, 0xCC, 0x44, 0xCE, 0x44, 0xCF, 0x44, 0xD0, 0x44, 0xD1, 0x44, 0xD2, 0x44, 0xD4, 0x44, 0xD5, 0x44, 0xD6, 0x44, 0xD7, 0x44, 0xD9, 0x44, 0xDA, 0x44, 0xDB, 0x44, 0xDC, 0x44, 0xDD,\n\t0x44, 0xDF, 0x44, 0xE0, 0x44, 0xE1, 0x44, 0xE2, 0x44, 0xE3, 0x44, 0xE5, 0x44, 0xE6, 0x44, 0xE7, 0x44, 0xE8, 0x44, 0xEA, 0x44, 0xEB, 0x44, 0xEC, 0x44, 0xED, 0x44, 0xEF, 0x44, 0xF0, 0x44, 0xF1,\n\t0x44, 0xF2, 0x44, 0xF3, 0x44, 0xF5, 0x44, 0xF6, 0x44, 0xF7, 0x44, 0xF8, 0x44, 0xFA, 0x44, 0xFB, 0x44, 0xFC, 0x44, 0xFD, 0x44, 0xFF, 0x44, 0x00, 0x45, 0x01, 0x45, 0x02, 0x45, 0x04, 0x45, 0x05,\n\t0x45, 0x06, 0x45, 0x07, 0x45, 0x09, 0x45, 0x0A, 0x45, 0x0B, 0x45, 0x0C, 0x45, 0x0E, 0x45, 0x0F, 0x45, 0x10, 0x45, 0x12, 0x45, 0x13, 0x45, 0x14, 0x45, 0x15, 0x45, 0x17, 0x45, 0x18, 0x45, 0x19,\n\t0x45, 0x1A, 0x45, 0x1C, 0x45, 0x1D, 0x45, 0x1E, 0x45, 0x20, 0x45, 0x21, 0x45, 0x22, 0x45, 0x23, 0x45, 0x25, 0x45, 0x26, 0x45, 0x27, 0x45, 0x29, 0x45, 0x2A, 0x45, 0x2B, 0x45, 0x2C, 0x45, 0x2E,\n\t0x45, 0x2F, 0x45, 0x30, 0x45, 0x32, 0x45, 0x33, 0x45, 0x34, 0x45, 0x36, 0x45, 0x37, 0x45, 0x38, 0x45, 0x39, 0x45, 0x3B, 0x45, 0x3C, 0x45, 0x3D, 0x45, 0x3F, 0x45, 0x40, 0x45, 0x41, 0x45, 0x43,\n\t0x45, 0x44, 0x45, 0x45, 0x45, 0x47, 0x45, 0x48, 0x45, 0x49, 0x45, 0x4B, 0x45, 0x4C, 0x45, 0x4D, 0x45, 0x4E, 0x45, 0x50, 0x45, 0x51, 0x45, 0x52, 0x45, 0x54, 0x45, 0x55, 0x45, 0x56, 0x45, 0x58,\n\t0x45, 0x59, 0x45, 0x5A, 0x45, 0x5C, 0x45, 0x5D, 0x45, 0x5E, 0x45, 0x60, 0x45, 0x61, 0x45, 0x63, 0x45, 0x64, 0x45, 0x65, 0x45, 0x67, 0x45, 0x68, 0x45, 0x69, 0x45, 0x6B, 0x45, 0x6C, 0x45, 0x6D,\n\t0x45, 0x6F, 0x45, 0x70, 0x45, 0x71, 0x45, 0x73, 0x45, 0x74, 0x45, 0x76, 0x45, 0x77, 0x45, 0x78, 0x45, 0x7A, 0x45, 0x7B, 0x45, 0x7C, 0x45, 0x7E, 0x45, 0x7F, 0x45, 0x80, 0x45, 0x82, 0x45, 0x83,\n\t0x45, 0x85, 0x45, 0x86, 0x45, 0x87, 0x45, 0x89, 0x45, 0x8A, 0x45, 0x8C, 0x45, 0x8D, 0x45, 0x8E, 0x45, 0x90, 0x45, 0x91, 0x45, 0x92, 0x45, 0x94, 0x45, 0x95, 0x45, 0x97, 0x45, 0x98, 0x45, 0x99,\n\t0x45, 0x9B, 0x45, 0x9C, 0x45, 0x9E, 0x45, 0x9F, 0x45, 0xA0, 0x45, 0xA2, 0x45, 0xA3, 0x45, 0xA5, 0x45, 0xA6, 0x45, 0xA8, 0x45, 0xA9, 0x45, 0xAA, 0x45, 0xAC, 0x45, 0xAD, 0x45, 0xAF, 0x45, 0xB0,\n\t0x45, 0xB1, 0x45, 0xB3, 0x45, 0xB4, 0x45, 0xB6, 0x45, 0xB7, 0x45, 0xB9, 0x45, 0xBA, 0x45, 0xBB, 0x45, 0xBD, 0x45, 0xBE, 0x45, 0xC0, 0x45, 0xC1, 0x45, 0xC3, 0x45, 0xC4, 0x45, 0xC6, 0x45, 0xC7,\n\t0x45, 0xC8, 0x45, 0xCA, 0x45, 0xCB, 0x45, 0xCD, 0x45, 0xCE, 0x45, 0xD0, 0x45, 0xD1, 0x45, 0xD3, 0x45, 0xD4, 0x45, 0xD5, 0x45, 0xD7, 0x45, 0xD8, 0x45, 0xDA, 0x45, 0xDB, 0x45, 0xDD, 0x45, 0xDE,\n\t0x45, 0xE0, 0x45, 0xE1, 0x45, 0xE3, 0x45, 0xE4, 0x45, 0xE6, 0x45, 0xE7, 0x45, 0xE9, 0x45, 0xEA, 0x45, 0xEB, 0x45, 0xED, 0x45, 0xEE, 0x45, 0xF0, 0x45, 0xF1, 0x45, 0xF3, 0x45, 0xF4, 0x45, 0xF6,\n\t0x45, 0xF7, 0x45, 0xF9, 0x45, 0xFA, 0x45, 0xFC, 0x45, 0xFD, 0x45, 0xFF, 0x45, 0x00, 0x46, 0x02, 0x46, 0x03, 0x46, 0x05, 0x46, 0x06, 0x46, 0x08, 0x46, 0x09, 0x46, 0x0B, 0x46, 0x0C, 0x46, 0x0E,\n\t0x46, 0x0F, 0x46, 0x11, 0x46, 0x12, 0x46, 0x14, 0x46, 0x16, 0x46, 0x17, 0x46, 0x19, 0x46, 0x1A, 0x46, 0x1C, 0x46, 0x1D, 0x46, 0x1F, 0x46, 0x20, 0x46, 0x22, 0x46, 0x23, 0x46, 0x25, 0x46, 0x26,\n\t0x46, 0x28, 0x46, 0x29, 0x46, 0x2B, 0x46, 0x2C, 0x46, 0x2E, 0x46, 0x30, 0x46, 0x31, 0x46, 0x33, 0x46, 0x34, 0x46, 0x36, 0x46, 0x37, 0x46, 0x39, 0x46, 0x3A, 0x46, 0x3C, 0x46, 0x3E, 0x46, 0x3F,\n\t0x46, 0x41, 0x46, 0x42, 0x46, 0x44, 0x46, 0x45, 0x46, 0x47, 0x46, 0x49, 0x46, 0x4A, 0x46, 0x4C, 0x46, 0x4D, 0x46, 0x4F, 0x46, 0x50, 0x46, 0x52, 0x46, 0x54, 0x46, 0x55, 0x46, 0x57, 0x46, 0x58,\n\t0x46, 0x5A, 0x46, 0x5B, 0x46, 0x5D, 0x46, 0x5F, 0x46, 0x60, 0x46, 0x62, 0x46, 0x63, 0x46, 0x65, 0x46, 0x67, 0x46, 0x68, 0x46, 0x6A, 0x46, 0x6B, 0x46, 0x6D, 0x46, 0x6F, 0x46, 0x70, 0x46, 0x72,\n\t0x46, 0x73, 0x46, 0x75, 0x46, 0x77, 0x46, 0x78, 0x46, 0x7A, 0x46, 0x7C, 0x46, 0x7D, 0x46, 0x7F, 0x46, 0x80, 0x46, 0x82, 0x46, 0x84, 0x46, 0x85, 0x46, 0x87, 0x46, 0x89, 0x46, 0x8A, 0x46, 0x8C,\n\t0x46, 0x8E, 0x46, 0x8F, 0x46, 0x91, 0x46, 0x92, 0x46, 0x94, 0x46, 0x96, 0x46, 0x97, 0x46, 0x99, 0x46, 0x9B, 0x46, 0x9C, 0x46, 0x9E, 0x46, 0xA0, 0x46, 0xA1, 0x46, 0xA3, 0x46, 0xA5, 0x46, 0xA6,\n\t0x46, 0xA8, 0x46, 0xAA, 0x46, 0xAB, 0x46, 0xAD, 0x46, 0xAF, 0x46, 0xB0, 0x46, 0xB2, 0x46, 0xB4, 0x46, 0xB5, 0x46, 0xB7, 0x46, 0xB9, 0x46, 0xBA, 0x46, 0xBC, 0x46, 0xBE, 0x46, 0xBF, 0x46, 0xC1,\n\t0x46, 0xC3, 0x46, 0xC4, 0x46, 0xC6, 0x46, 0xC8, 0x46, 0xCA, 0x46, 0xCB, 0x46, 0xCD, 0x46, 0xCF, 0x46, 0xD0, 0x46, 0xD2, 0x46, 0xD4, 0x46, 0xD5, 0x46, 0xD7, 0x46, 0xD9, 0x46, 0xDB, 0x46, 0xDC,\n\t0x46, 0xDE, 0x46, 0xE0, 0x46, 0xE1, 0x46, 0xE3, 0x46, 0xE5, 0x46, 0xE7, 0x46, 0xE8, 0x46, 0xEA, 0x46, 0xEC, 0x46, 0xEE, 0x46, 0xEF, 0x46, 0xF1, 0x46, 0xF3, 0x46, 0xF4, 0x46, 0xF6, 0x46, 0xF8,\n\t0x46, 0xFA, 0x46, 0xFB, 0x46, 0xFD, 0x46, 0xFF, 0x46, 0x01, 0x47, 0x02, 0x47, 0x04, 0x47, 0x06, 0x47, 0x08, 0x47, 0x09, 0x47, 0x0B, 0x47, 0x0D, 0x47, 0x0F, 0x47, 0x11, 0x47, 0x12, 0x47, 0x14,\n\t0x47, 0x16, 0x47, 0x18, 0x47, 0x19, 0x47, 0x1B, 0x47, 0x1D, 0x47, 0x1F, 0x47, 0x20, 0x47, 0x22, 0x47, 0x24, 0x47, 0x26, 0x47, 0x28, 0x47, 0x29, 0x47, 0x2B, 0x47, 0x2D, 0x47, 0x2F, 0x47, 0x31,\n\t0x47, 0x32, 0x47, 0x34, 0x47, 0x36, 0x47, 0x38, 0x47, 0x3A, 0x47, 0x3B, 0x47, 0x3D, 0x47, 0x3F, 0x47, 0x41, 0x47, 0x43, 0x47, 0x44, 0x47, 0x46, 0x47, 0x48, 0x47, 0x4A, 0x47, 0x4C, 0x47, 0x4E,\n\t0x47, 0x4F, 0x47, 0x51, 0x47, 0x53, 0x47, 0x55, 0x47, 0x57, 0x47, 0x59, 0x47, 0x5A, 0x47, 0x5C, 0x47, 0x5E, 0x47, 0x60, 0x47, 0x62, 0x47, 0x64, 0x47, 0x67, 0x47, 0x6B, 0x47, 0x6F, 0x47, 0x72,\n\t0x47, 0x76, 0x47, 0x7A, 0x47, 0x7E, 0x47, 0x81, 0x47, 0x85, 0x47, 0x89, 0x47, 0x8D, 0x47, 0x90, 0x47, 0x94, 0x47, 0x98, 0x47, 0x9C, 0x47, 0xA0, 0x47, 0xA3, 0x47, 0xA7, 0x47, 0xAB, 0x47, 0xAF,\n\t0x47, 0xB3, 0x47, 0xB7, 0x47, 0xBB, 0x47, 0xBE, 0x47, 0xC2, 0x47, 0xC6, 0x47, 0xCA, 0x47, 0xCE, 0x47, 0xD2, 0x47, 0xD6, 0x47, 0xDA, 0x47, 0xDE, 0x47, 0xE2, 0x47, 0xE5, 0x47, 0xE9, 0x47, 0xED,\n\t0x47, 0xF1, 0x47, 0xF5, 0x47, 0xF9, 0x47, 0xFD, 0x47, 0x01, 0x48, 0x03, 0x48, 0x05, 0x48, 0x07, 0x48, 0x09, 0x48, 0x0B, 0x48, 0x0D, 0x48, 0x0F, 0x48, 0x11, 0x48, 0x13, 0x48, 0x15, 0x48, 0x17,\n\t0x48, 0x19, 0x48, 0x1B, 0x48, 0x1D, 0x48, 0x1F, 0x48, 0x21, 0x48, 0x23, 0x48, 0x25, 0x48, 0x27, 0x48, 0x29, 0x48, 0x2C, 0x48, 0x2E, 0x48, 0x30, 0x48, 0x32, 0x48, 0x34, 0x48, 0x36, 0x48, 0x38,\n\t0x48, 0x3A, 0x48, 0x3C, 0x48, 0x3E, 0x48, 0x41, 0x48, 0x43, 0x48, 0x45, 0x48, 0x47, 0x48, 0x49, 0x48, 0x4B, 0x48, 0x4D, 0x48, 0x50, 0x48, 0x52, 0x48, 0x54, 0x48, 0x56, 0x48, 0x58, 0x48, 0x5A,\n\t0x48, 0x5D, 0x48, 0x5F, 0x48, 0x61, 0x48, 0x63, 0x48, 0x65, 0x48, 0x68, 0x48, 0x6A, 0x48, 0x6C, 0x48, 0x6E, 0x48, 0x70, 0x48, 0x73, 0x48, 0x75, 0x48, 0x77, 0x48, 0x79, 0x48, 0x7C, 0x48, 0x7E,\n\t0x48, 0x80, 0x48, 0x82, 0x48, 0x85, 0x48, 0x87, 0x48, 0x89, 0x48, 0x8B, 0x48, 0x8E, 0x48, 0x90, 0x48, 0x92, 0x48, 0x94, 0x48, 0x97, 0x48, 0x99, 0x48, 0x9B, 0x48, 0x9E, 0x48, 0xA0, 0x48, 0xA2,\n\t0x48, 0xA5, 0x48, 0xA7, 0x48, 0xA9, 0x48, 0xAC, 0x48, 0xAE, 0x48, 0xB0, 0x48, 0xB3, 0x48, 0xB5, 0x48, 0xB7, 0x48, 0xBA, 0x48, 0xBC, 0x48, 0xBE, 0x48, 0xC1, 0x48, 0xC3, 0x48, 0xC6, 0x48, 0xC8,\n\t0x48, 0xCA, 0x48, 0xCD, 0x48, 0xCF, 0x48, 0xD2, 0x48, 0xD4, 0x48, 0xD6, 0x48, 0xD9, 0x48, 0xDB, 0x48, 0xDE, 0x48, 0xE0, 0x48, 0xE3, 0x48, 0xE5, 0x48, 0xE7, 0x48, 0xEA, 0x48, 0xEC, 0x48, 0xEF,\n\t0x48, 0xF1, 0x48, 0xF4, 0x48, 0xF6, 0x48, 0xF9, 0x48, 0xFB, 0x48, 0xFE, 0x48, 0x00, 0x49, 0x03, 0x49, 0x05, 0x49, 0x08, 0x49, 0x0A, 0x49, 0x0D, 0x49, 0x0F, 0x49, 0x12, 0x49, 0x14, 0x49, 0x17,\n\t0x49, 0x19, 0x49, 0x1C, 0x49, 0x1F, 0x49, 0x21, 0x49, 0x24, 0x49, 0x26, 0x49, 0x29, 0x49, 0x2B, 0x49, 0x2E, 0x49, 0x31, 0x49, 0x33, 0x49, 0x36, 0x49, 0x38, 0x49, 0x3B, 0x49, 0x3E, 0x49, 0x40,\n\t0x49, 0x43, 0x49, 0x46, 0x49, 0x48, 0x49, 0x4B, 0x49, 0x4D, 0x49, 0x50, 0x49, 0x53, 0x49, 0x55, 0x49, 0x58, 0x49, 0x5B, 0x49, 0x5D, 0x49, 0x60, 0x49, 0x63, 0x49, 0x66, 0x49, 0x68, 0x49, 0x6B,\n\t0x49, 0x6E, 0x49, 0x70, 0x49, 0x73, 0x49, 0x76, 0x49, 0x79, 0x49, 0x7B, 0x49, 0x7E, 0x49, 0x81, 0x49, 0x84, 0x49, 0x86, 0x49, 0x89, 0x49, 0x8C, 0x49, 0x8F, 0x49, 0x91, 0x49, 0x94, 0x49, 0x97,\n\t0x49, 0x9A, 0x49, 0x9D, 0x49, 0x9F, 0x49, 0xA2, 0x49, 0xA5, 0x49, 0xA8, 0x49, 0xAB, 0x49, 0xAD, 0x49, 0xB0, 0x49, 0xB3, 0x49, 0xB6, 0x49, 0xB9, 0x49, 0xBC, 0x49, 0xBF, 0x49, 0xC1, 0x49, 0xC4,\n\t0x49, 0xC7, 0x49, 0xCA, 0x49, 0xCD, 0x49, 0xD0, 0x49, 0xD3, 0x49, 0xD6, 0x49, 0xD9, 0x49, 0xDC, 0x49, 0xDF, 0x49, 0xE1, 0x49, 0xE4, 0x49, 0xE7, 0x49, 0xEA, 0x49, 0xED, 0x49, 0xF0, 0x49, 0xF3,\n\t0x49, 0xF6, 0x49, 0xF9, 0x49, 0xFC, 0x49, 0xFF, 0x49, 0x02, 0x4A, 0x05, 0x4A, 0x08, 0x4A, 0x0B, 0x4A, 0x0E, 0x4A, 0x11, 0x4A, 0x14, 0x4A, 0x17, 0x4A, 0x1A, 0x4A, 0x1D, 0x4A, 0x21, 0x4A, 0x24,\n\t0x4A, 0x27, 0x4A, 0x2A, 0x4A, 0x2D, 0x4A, 0x30, 0x4A, 0x33, 0x4A, 0x36, 0x4A, 0x39, 0x4A, 0x3C, 0x4A, 0x3F, 0x4A, 0x43, 0x4A, 0x46, 0x4A, 0x49, 0x4A, 0x4C, 0x4A, 0x4F, 0x4A, 0x52, 0x4A, 0x55,\n\t0x4A, 0x59, 0x4A, 0x5C, 0x4A, 0x5F, 0x4A, 0x62, 0x4A, 0x65, 0x4A, 0x69, 0x4A, 0x6C, 0x4A, 0x6F, 0x4A, 0x72, 0x4A, 0x75, 0x4A, 0x79, 0x4A, 0x7C, 0x4A, 0x7F, 0x4A, 0x82, 0x4A, 0x86, 0x4A, 0x89,\n\t0x4A, 0x8C, 0x4A, 0x8F, 0x4A, 0x93, 0x4A, 0x96, 0x4A, 0x99, 0x4A, 0x9D, 0x4A, 0xA0, 0x4A, 0xA3, 0x4A, 0xA7, 0x4A, 0xAA, 0x4A, 0xAD, 0x4A, 0xB1, 0x4A, 0xB4, 0x4A, 0xB7, 0x4A, 0xBB, 0x4A, 0xBE,\n\t0x4A, 0xC1, 0x4A, 0xC5, 0x4A, 0xC8, 0x4A, 0xCC, 0x4A, 0xCF, 0x4A, 0xD2, 0x4A, 0xD6, 0x4A, 0xD9, 0x4A, 0xDD, 0x4A, 0xE0, 0x4A, 0xE4, 0x4A, 0xE7, 0x4A, 0xEA, 0x4A, 0xEE, 0x4A, 0xF1, 0x4A, 0xF5,\n\t0x4A, 0xF8, 0x4A, 0xFC, 0x4A, 0xFF, 0x4A, 0x03, 0x4B, 0x06, 0x4B, 0x0A, 0x4B, 0x0D, 0x4B, 0x11, 0x4B, 0x14, 0x4B, 0x18, 0x4B, 0x1C, 0x4B, 0x1F, 0x4B, 0x23, 0x4B, 0x26, 0x4B, 0x2A, 0x4B, 0x2D,\n\t0x4B, 0x31, 0x4B, 0x35, 0x4B, 0x38, 0x4B, 0x3C, 0x4B, 0x3F, 0x4B, 0x43, 0x4B, 0x47, 0x4B, 0x4A, 0x4B, 0x4E, 0x4B, 0x52, 0x4B, 0x55, 0x4B, 0x59, 0x4B, 0x5D, 0x4B, 0x60, 0x4B, 0x64, 0x4B, 0x68,\n\t0x4B, 0x6B, 0x4B, 0x6F, 0x4B, 0x73, 0x4B, 0x77, 0x4B, 0x7A, 0x4B, 0x7E, 0x4B, 0x82, 0x4B, 0x86, 0x4B, 0x89, 0x4B, 0x8D, 0x4B, 0x91, 0x4B, 0x95, 0x4B, 0x98, 0x4B, 0x9C, 0x4B, 0xA0, 0x4B, 0xA4,\n\t0x4B, 0xA8, 0x4B, 0xAC, 0x4B, 0xAF, 0x4B, 0xB3, 0x4B, 0xB7, 0x4B, 0xBB, 0x4B, 0xBF, 0x4B, 0xC3, 0x4B, 0xC7, 0x4B, 0xCA, 0x4B, 0xCE, 0x4B, 0xD2, 0x4B, 0xD6, 0x4B, 0xDA, 0x4B, 0xDE, 0x4B, 0xE2,\n\t0x4B, 0xE6, 0x4B, 0xEA, 0x4B, 0xEE, 0x4B, 0xF2, 0x4B, 0xF6, 0x4B, 0xFA, 0x4B, 0xFE, 0x4B, 0x01, 0x4C, 0x03, 0x4C, 0x05, 0x4C, 0x07, 0x4C, 0x09, 0x4C, 0x0B, 0x4C, 0x0D, 0x4C, 0x0F, 0x4C, 0x11,\n\t0x4C, 0x13, 0x4C, 0x15, 0x4C, 0x17, 0x4C, 0x19, 0x4C, 0x1B, 0x4C, 0x1D, 0x4C, 0x1F, 0x4C, 0x21, 0x4C, 0x23, 0x4C, 0x26, 0x4C, 0x28, 0x4C, 0x2A, 0x4C, 0x2C, 0x4C, 0x2E, 0x4C, 0x30, 0x4C, 0x32,\n\t0x4C, 0x34, 0x4C, 0x36, 0x4C, 0x38, 0x4C, 0x3A, 0x4C, 0x3D, 0x4C, 0x3F, 0x4C, 0x41, 0x4C, 0x43, 0x4C, 0x45, 0x4C, 0x47, 0x4C, 0x49, 0x4C, 0x4C, 0x4C, 0x4E, 0x4C, 0x50, 0x4C, 0x52, 0x4C, 0x54,\n\t0x4C, 0x56, 0x4C, 0x58, 0x4C, 0x5B, 0x4C, 0x5D, 0x4C, 0x5F, 0x4C, 0x61, 0x4C, 0x63, 0x4C, 0x66, 0x4C, 0x68, 0x4C, 0x6A, 0x4C, 0x6C, 0x4C, 0x6E, 0x4C, 0x71, 0x4C, 0x73, 0x4C, 0x75, 0x4C, 0x77,\n\t0x4C, 0x7A, 0x4C, 0x7C, 0x4C, 0x7E, 0x4C, 0x80, 0x4C, 0x83, 0x4C, 0x85, 0x4C, 0x87, 0x4C, 0x89, 0x4C, 0x8C, 0x4C, 0x8E, 0x4C, 0x90, 0x4C, 0x92, 0x4C, 0x95, 0x4C, 0x97, 0x4C, 0x99, 0x4C, 0x9C,\n\t0x4C, 0x9E, 0x4C, 0xA0, 0x4C, 0xA3, 0x4C, 0xA5, 0x4C, 0xA7, 0x4C, 0xAA, 0x4C, 0xAC, 0x4C, 0xAE, 0x4C, 0xB1, 0x4C, 0xB3, 0x4C, 0xB5, 0x4C, 0xB8, 0x4C, 0xBA, 0x4C, 0xBC, 0x4C, 0xBF, 0x4C, 0xC1,\n\t0x4C, 0xC3, 0x4C, 0xC6, 0x4C, 0xC8, 0x4C, 0xCB, 0x4C, 0xCD, 0x4C, 0xCF, 0x4C, 0xD2, 0x4C, 0xD4, 0x4C, 0xD7, 0x4C, 0xD9, 0x4C, 0xDB, 0x4C, 0xDE, 0x4C, 0xE0, 0x4C, 0xE3, 0x4C, 0xE5, 0x4C, 0xE8,\n\t0x4C, 0xEA, 0x4C, 0xED, 0x4C, 0xEF, 0x4C, 0xF2, 0x4C, 0xF4, 0x4C, 0xF6, 0x4C, 0xF9, 0x4C, 0xFB, 0x4C, 0xFE, 0x4C, 0x00, 0x4D, 0x03, 0x4D, 0x05, 0x4D, 0x08, 0x4D, 0x0B, 0x4D, 0x0D, 0x4D, 0x10,\n\t0x4D, 0x12, 0x4D, 0x15, 0x4D, 0x17, 0x4D, 0x1A, 0x4D, 0x1C, 0x4D, 0x1F, 0x4D, 0x21, 0x4D, 0x24, 0x4D, 0x27, 0x4D, 0x29, 0x4D, 0x2C, 0x4D, 0x2E, 0x4D, 0x31, 0x4D, 0x33, 0x4D, 0x36, 0x4D, 0x39,\n\t0x4D, 0x3B, 0x4D, 0x3E, 0x4D, 0x41, 0x4D, 0x43, 0x4D, 0x46, 0x4D, 0x48, 0x4D, 0x4B, 0x4D, 0x4E, 0x4D, 0x50, 0x4D, 0x53, 0x4D, 0x56, 0x4D, 0x58, 0x4D, 0x5B, 0x4D, 0x5E, 0x4D, 0x60, 0x4D, 0x63,\n\t0x4D, 0x66, 0x4D, 0x69, 0x4D, 0x6B, 0x4D, 0x6E, 0x4D, 0x71, 0x4D, 0x73, 0x4D, 0x76, 0x4D, 0x79, 0x4D, 0x7C, 0x4D, 0x7E, 0x4D, 0x81, 0x4D, 0x84, 0x4D, 0x87, 0x4D, 0x89, 0x4D, 0x8C, 0x4D, 0x8F,\n\t0x4D, 0x92, 0x4D, 0x94, 0x4D, 0x97, 0x4D, 0x9A, 0x4D, 0x9D, 0x4D, 0xA0, 0x4D, 0xA2, 0x4D, 0xA5, 0x4D, 0xA8, 0x4D, 0xAB, 0x4D, 0xAE, 0x4D, 0xB1, 0x4D, 0xB3, 0x4D, 0xB6, 0x4D, 0xB9, 0x4D, 0xBC,\n\t0x4D, 0xBF, 0x4D, 0xC2, 0x4D, 0xC5, 0x4D, 0xC8, 0x4D, 0xCA, 0x4D, 0xCD, 0x4D, 0xD0, 0x4D, 0xD3, 0x4D, 0xD6, 0x4D, 0xD9, 0x4D, 0xDC, 0x4D, 0xDF, 0x4D, 0xE2, 0x4D, 0xE5, 0x4D, 0xE8, 0x4D, 0xEB,\n\t0x4D, 0xEE, 0x4D, 0xF1, 0x4D, 0xF4, 0x4D, 0xF7, 0x4D, 0xFA, 0x4D, 0xFD, 0x4D, 0x00, 0x4E, 0x03, 0x4E, 0x06, 0x4E, 0x09, 0x4E, 0x0C, 0x4E, 0x0F, 0x4E, 0x12, 0x4E, 0x15, 0x4E, 0x18, 0x4E, 0x1B,\n\t0x4E, 0x1E, 0x4E, 0x21, 0x4E, 0x24, 0x4E, 0x27, 0x4E, 0x2A, 0x4E, 0x2D, 0x4E, 0x30, 0x4E, 0x33, 0x4E, 0x36, 0x4E, 0x3A, 0x4E, 0x3D, 0x4E, 0x40, 0x4E, 0x43, 0x4E, 0x46, 0x4E, 0x49, 0x4E, 0x4C,\n\t0x4E, 0x4F, 0x4E, 0x53, 0x4E, 0x56, 0x4E, 0x59, 0x4E, 0x5C, 0x4E, 0x5F, 0x4E, 0x63, 0x4E, 0x66, 0x4E, 0x69, 0x4E, 0x6C, 0x4E, 0x6F, 0x4E, 0x73, 0x4E, 0x76, 0x4E, 0x79, 0x4E, 0x7C, 0x4E, 0x80,\n\t0x4E, 0x83, 0x4E, 0x86, 0x4E, 0x89, 0x4E, 0x8D, 0x4E, 0x90, 0x4E, 0x93, 0x4E, 0x96, 0x4E, 0x9A, 0x4E, 0x9D, 0x4E, 0xA0, 0x4E, 0xA4, 0x4E, 0xA7, 0x4E, 0xAA, 0x4E, 0xAE, 0x4E, 0xB1, 0x4E, 0xB4,\n\t0x4E, 0xB8, 0x4E, 0xBB, 0x4E, 0xBE, 0x4E, 0xC2, 0x4E, 0xC5, 0x4E, 0xC9, 0x4E, 0xCC, 0x4E, 0xCF, 0x4E, 0xD3, 0x4E, 0xD6, 0x4E, 0xDA, 0x4E, 0xDD, 0x4E, 0xE0, 0x4E, 0xE4, 0x4E, 0xE7, 0x4E, 0xEB,\n\t0x4E, 0xEE, 0x4E, 0xF2, 0x4E, 0xF5, 0x4E, 0xF9, 0x4E, 0xFC, 0x4E, 0x00, 0x4F, 0x03, 0x4F, 0x07, 0x4F, 0x0A, 0x4F, 0x0E, 0x4F, 0x11, 0x4F, 0x15, 0x4F, 0x18, 0x4F, 0x1C, 0x4F, 0x1F, 0x4F, 0x23,\n\t0x4F, 0x27, 0x4F, 0x2A, 0x4F, 0x2E, 0x4F, 0x31, 0x4F, 0x35, 0x4F, 0x39, 0x4F, 0x3C, 0x4F, 0x40, 0x4F, 0x43, 0x4F, 0x47, 0x4F, 0x4B, 0x4F, 0x4E, 0x4F, 0x52, 0x4F, 0x56, 0x4F, 0x59, 0x4F, 0x5D,\n\t0x4F, 0x61, 0x4F, 0x64, 0x4F, 0x68, 0x4F, 0x6C, 0x4F, 0x70, 0x4F, 0x73, 0x4F, 0x77, 0x4F, 0x7B, 0x4F, 0x7E, 0x4F, 0x82, 0x4F, 0x86, 0x4F, 0x8A, 0x4F, 0x8D, 0x4F, 0x91, 0x4F, 0x95, 0x4F, 0x99,\n\t0x4F, 0x9D, 0x4F, 0xA0, 0x4F, 0xA4, 0x4F, 0xA8, 0x4F, 0xAC, 0x4F, 0xB0, 0x4F, 0xB4, 0x4F, 0xB7, 0x4F, 0xBB, 0x4F, 0xBF, 0x4F, 0xC3, 0x4F, 0xC7, 0x4F, 0xCB, 0x4F, 0xCF, 0x4F, 0xD3, 0x4F, 0xD7,\n\t0x4F, 0xDB, 0x4F, 0xDE, 0x4F, 0xE2, 0x4F, 0xE6, 0x4F, 0xEA, 0x4F, 0xEE, 0x4F, 0xF2, 0x4F, 0xF6, 0x4F, 0xFA, 0x4F, 0xFE, 0x4F, 0x01, 0x50, 0x03, 0x50, 0x05, 0x50, 0x07, 0x50, 0x09, 0x50, 0x0B,\n\t0x50, 0x0D, 0x50, 0x0F, 0x50, 0x11, 0x50, 0x13, 0x50, 0x15, 0x50, 0x17, 0x50, 0x19, 0x50, 0x1B, 0x50, 0x1E, 0x50, 0x20, 0x50, 0x22, 0x50, 0x24, 0x50, 0x26, 0x50, 0x28, 0x50, 0x2A, 0x50, 0x2C,\n\t0x50, 0x2E, 0x50, 0x30, 0x50, 0x32, 0x50, 0x34, 0x50, 0x36, 0x50, 0x39, 0x50, 0x3B, 0x50, 0x3D, 0x50, 0x3F, 0x50, 0x41, 0x50, 0x43, 0x50, 0x45, 0x50, 0x47, 0x50, 0x4A, 0x50, 0x4C, 0x50, 0x4E,\n\t0x50, 0x50, 0x50, 0x52, 0x50, 0x54, 0x50, 0x57, 0x50, 0x59, 0x50, 0x5B, 0x50, 0x5D, 0x50, 0x5F, 0x50, 0x61, 0x50, 0x64, 0x50, 0x66, 0x50, 0x68, 0x50, 0x6A, 0x50, 0x6C, 0x50, 0x6F, 0x50, 0x71,\n\t0x50, 0x73, 0x50, 0x75, 0x50, 0x78, 0x50, 0x7A, 0x50, 0x7C, 0x50, 0x7E, 0x50, 0x81, 0x50, 0x83, 0x50, 0x85, 0x50, 0x87, 0x50, 0x8A, 0x50, 0x8C, 0x50, 0x8E, 0x50, 0x90, 0x50, 0x93, 0x50, 0x95,\n\t0x50, 0x97, 0x50, 0x9A, 0x50, 0x9C, 0x50, 0x9E, 0x50, 0xA0, 0x50, 0xA3, 0x50, 0xA5, 0x50, 0xA7, 0x50, 0xAA, 0x50, 0xAC, 0x50, 0xAE, 0x50, 0xB1, 0x50, 0xB3, 0x50, 0xB5, 0x50, 0xB8, 0x50, 0xBA,\n\t0x50, 0xBD, 0x50, 0xBF, 0x50, 0xC1, 0x50, 0xC4, 0x50, 0xC6, 0x50, 0xC8, 0x50, 0xCB, 0x50, 0xCD, 0x50, 0xD0, 0x50, 0xD2, 0x50, 0xD4, 0x50, 0xD7, 0x50, 0xD9, 0x50, 0xDC, 0x50, 0xDE, 0x50, 0xE1,\n\t0x50, 0xE3, 0x50, 0xE6, 0x50, 0xE8, 0x50, 0xEA, 0x50, 0xED, 0x50, 0xEF, 0x50, 0xF2, 0x50, 0xF4, 0x50, 0xF7, 0x50, 0xF9, 0x50, 0xFC, 0x50, 0xFE, 0x50, 0x01, 0x51, 0x03, 0x51, 0x06, 0x51, 0x08,\n\t0x51, 0x0B, 0x51, 0x0D, 0x51, 0x10, 0x51, 0x12, 0x51, 0x15, 0x51, 0x17, 0x51, 0x1A, 0x51, 0x1D, 0x51, 0x1F, 0x51, 0x22, 0x51, 0x24, 0x51, 0x27, 0x51, 0x29, 0x51, 0x2C, 0x51, 0x2F, 0x51, 0x31,\n\t0x51, 0x34, 0x51, 0x36, 0x51, 0x39, 0x51, 0x3C, 0x51, 0x3E, 0x51, 0x41, 0x51, 0x43, 0x51, 0x46, 0x51, 0x49, 0x51, 0x4B, 0x51, 0x4E, 0x51, 0x51, 0x51, 0x53, 0x51, 0x56, 0x51, 0x59, 0x51, 0x5B,\n\t0x51, 0x5E, 0x51, 0x61, 0x51, 0x63, 0x51, 0x66, 0x51, 0x69, 0x51, 0x6C, 0x51, 0x6E, 0x51, 0x71, 0x51, 0x74, 0x51, 0x76, 0x51, 0x79, 0x51, 0x7C, 0x51, 0x7F, 0x51, 0x81, 0x51, 0x84, 0x51, 0x87,\n\t0x51, 0x8A, 0x51, 0x8C, 0x51, 0x8F, 0x51, 0x92, 0x51, 0x95, 0x51, 0x98, 0x51, 0x9A, 0x51, 0x9D, 0x51, 0xA0, 0x51, 0xA3, 0x51, 0xA6, 0x51, 0xA8, 0x51, 0xAB, 0x51, 0xAE, 0x51, 0xB1, 0x51, 0xB4,\n\t0x51, 0xB7, 0x51, 0xBA, 0x51, 0xBC, 0x51, 0xBF, 0x51, 0xC2, 0x51, 0xC5, 0x51, 0xC8, 0x51, 0xCB, 0x51, 0xCE, 0x51, 0xD1, 0x51, 0xD3, 0x51, 0xD6, 0x51, 0xD9, 0x51, 0xDC, 0x51, 0xDF, 0x51, 0xE2,\n\t0x51, 0xE5, 0x51, 0xE8, 0x51, 0xEB, 0x51, 0xEE, 0x51, 0xF1, 0x51, 0xF4, 0x51, 0xF7, 0x51, 0xFA, 0x51, 0xFD, 0x51, 0x00, 0x52, 0x03, 0x52, 0x06, 0x52, 0x09, 0x52, 0x0C, 0x52, 0x0F, 0x52, 0x12,\n\t0x52, 0x15, 0x52, 0x18, 0x52, 0x1B, 0x52, 0x1E, 0x52, 0x21, 0x52, 0x24, 0x52, 0x27, 0x52, 0x2A, 0x52, 0x2D, 0x52, 0x31, 0x52, 0x34, 0x52, 0x37, 0x52, 0x3A, 0x52, 0x3D, 0x52, 0x40, 0x52, 0x43,\n\t0x52, 0x46, 0x52, 0x4A, 0x52, 0x4D, 0x52, 0x50, 0x52, 0x53, 0x52, 0x56, 0x52, 0x59, 0x52, 0x5D, 0x52, 0x60, 0x52, 0x63, 0x52, 0x66, 0x52, 0x69, 0x52, 0x6C, 0x52, 0x70, 0x52, 0x73, 0x52, 0x76,\n\t0x52, 0x79, 0x52, 0x7D, 0x52, 0x80, 0x52, 0x83, 0x52, 0x86, 0x52, 0x8A, 0x52, 0x8D, 0x52, 0x90, 0x52, 0x93, 0x52, 0x97, 0x52, 0x9A, 0x52, 0x9D, 0x52, 0xA1, 0x52, 0xA4, 0x52, 0xA7, 0x52, 0xAB,\n\t0x52, 0xAE, 0x52, 0xB1, 0x52, 0xB5, 0x52, 0xB8, 0x52, 0xBB, 0x52, 0xBF, 0x52, 0xC2, 0x52, 0xC6, 0x52, 0xC9, 0x52, 0xCC, 0x52, 0xD0, 0x52, 0xD3, 0x52, 0xDA, 0x52, 0xE1, 0x52, 0xE8, 0x52, 0xEF,\n\t0x52, 0xF6, 0x52, 0xFD, 0x52, 0x04, 0x53, 0x0B, 0x53, 0x12, 0x53, 0x19, 0x53, 0x20, 0x53, 0x27, 0x53, 0x2E, 0x53, 0x35, 0x53, 0x3D, 0x53, 0x44, 0x53, 0x4B, 0x53, 0x52, 0x53, 0x5A, 0x53, 0x61,\n\t0x53, 0x69, 0x53, 0x70, 0x53, 0x77, 0x53, 0x7F, 0x53, 0x86, 0x53, 0x8E, 0x53, 0x95, 0x53, 0x9D, 0x53, 0xA5, 0x53, 0xAC, 0x53, 0xB4, 0x53, 0xBC, 0x53, 0xC4, 0x53, 0xCB, 0x53, 0xD3, 0x53, 0xDB,\n\t0x53, 0xE3, 0x53, 0xEB, 0x53, 0xF3, 0x53, 0xFB, 0x53, 0x01, 0x54, 0x05, 0x54, 0x09, 0x54, 0x0D, 0x54, 0x11, 0x54, 0x16, 0x54, 0x1A, 0x54, 0x1E, 0x54, 0x22, 0x54, 0x26, 0x54, 0x2A, 0x54, 0x2E,\n\t0x54, 0x33, 0x54, 0x37, 0x54, 0x3B, 0x54, 0x3F, 0x54, 0x43, 0x54, 0x48, 0x54, 0x4C, 0x54, 0x50, 0x54, 0x55, 0x54, 0x59, 0x54, 0x5D, 0x54, 0x62, 0x54, 0x66, 0x54, 0x6A, 0x54, 0x6F, 0x54, 0x73,\n\t0x54, 0x78, 0x54, 0x7C, 0x54, 0x81, 0x54, 0x85, 0x54, 0x8A, 0x54, 0x8E, 0x54, 0x93, 0x54, 0x98, 0x54, 0x9C, 0x54, 0xA1, 0x54, 0xA5, 0x54, 0xAA, 0x54, 0xAF, 0x54, 0xB3, 0x54, 0xB8, 0x54, 0xBD,\n\t0x54, 0xC2, 0x54, 0xC6, 0x54, 0xCB, 0x54, 0xD0, 0x54, 0xD5, 0x54, 0xDA, 0x54, 0xDE, 0x54, 0xE3, 0x54, 0xE8, 0x54, 0xED, 0x54, 0xF2, 0x54, 0xF7, 0x54, 0xFC, 0x54, 0x01, 0x55, 0x06, 0x55, 0x0B,\n\t0x55, 0x10, 0x55, 0x15, 0x55, 0x1A, 0x55, 0x1F, 0x55, 0x25, 0x55, 0x2A, 0x55, 0x2F, 0x55, 0x34, 0x55, 0x39, 0x55, 0x3E, 0x55, 0x44, 0x55, 0x49, 0x55, 0x4E, 0x55, 0x54, 0x55, 0x59, 0x55, 0x5E,\n\t0x55, 0x64, 0x55, 0x69, 0x55, 0x6F, 0x55, 0x74, 0x55, 0x79, 0x55, 0x7F, 0x55, 0x84, 0x55, 0x8A, 0x55, 0x8F, 0x55, 0x95, 0x55, 0x9B, 0x55, 0xA0, 0x55, 0xA6, 0x55, 0xAC, 0x55, 0xB1, 0x55, 0xB7,\n\t0x55, 0xBD, 0x55, 0xC2, 0x55, 0xC8, 0x55, 0xCE, 0x55, 0xD4, 0x55, 0xDA, 0x55, 0xE0, 0x55, 0xE5, 0x55, 0xEB, 0x55, 0xF1, 0x55, 0xF7, 0x55, 0xFD, 0x55, 0x03, 0x56, 0x09, 0x56, 0x0F, 0x56, 0x15,\n\t0x56, 0x1B, 0x56, 0x22, 0x56, 0x28, 0x56, 0x2E, 0x56, 0x34, 0x56, 0x3A, 0x56, 0x40, 0x56, 0x47, 0x56, 0x4D, 0x56, 0x53, 0x56, 0x5A, 0x56, 0x60, 0x56, 0x66, 0x56, 0x6D, 0x56, 0x73, 0x56, 0x7A,\n\t0x56, 0x80, 0x56, 0x87, 0x56, 0x8D, 0x56, 0x94, 0x56, 0x9A, 0x56, 0xA1, 0x56, 0xA8, 0x56, 0xAE, 0x56, 0xB5, 0x56, 0xBC, 0x56, 0xC3, 0x56, 0xC9, 0x56, 0xD0, 0x56, 0xD7, 0x56, 0xDE, 0x56, 0xE5,\n\t0x56, 0xEC, 0x56, 0xF3, 0x56, 0xF9, 0x56, 0x00, 0x57, 0x07, 0x57, 0x0F, 0x57, 0x16, 0x57, 0x1D, 0x57, 0x24, 0x57, 0x2B, 0x57, 0x32, 0x57, 0x39, 0x57, 0x41, 0x57, 0x48, 0x57, 0x4F, 0x57, 0x56,\n\t0x57, 0x5E, 0x57, 0x65, 0x57, 0x6D, 0x57, 0x74, 0x57, 0x7C, 0x57, 0x83, 0x57, 0x8B, 0x57, 0x92, 0x57, 0x9A, 0x57, 0xA1, 0x57, 0xA9, 0x57, 0xB1, 0x57, 0xB8, 0x57, 0xC0, 0x57, 0xC8, 0x57, 0xD0,\n\t0x57, 0xD7, 0x57, 0xDF, 0x57, 0xE7, 0x57, 0xEF, 0x57, 0xF7, 0x57, 0xFF, 0x57, 0x04, 0x58, 0x08, 0x58, 0x0C, 0x58, 0x10, 0x58, 0x14, 0x58, 0x18, 0x58, 0x1C, 0x58, 0x20, 0x58, 0x24, 0x58, 0x28,\n\t0x58, 0x2C, 0x58, 0x31, 0x58, 0x35, 0x58, 0x39, 0x58, 0x3D, 0x58, 0x42, 0x58, 0x46, 0x58, 0x4A, 0x58, 0x4E, 0x58, 0x53, 0x58, 0x57, 0x58, 0x5B, 0x58, 0x60, 0x58, 0x64, 0x58, 0x69, 0x58, 0x6D,\n\t0x58, 0x71, 0x58, 0x76, 0x58, 0x7A, 0x58, 0x7F, 0x58, 0x83, 0x58, 0x88, 0x58, 0x8C, 0x58, 0x91, 0x58, 0x95, 0x58, 0x9A, 0x58, 0x9F, 0x58, 0xA3, 0x58, 0xA8, 0x58, 0xAD, 0x58, 0xB1, 0x58, 0xB6,\n\t0x58, 0xBB, 0x58, 0xBF, 0x58, 0xC4, 0x58, 0xC9, 0x58, 0xCE, 0x58, 0xD3, 0x58, 0xD7, 0x58, 0xDC, 0x58, 0xE1, 0x58, 0xE6, 0x58, 0xEB, 0x58, 0xF0, 0x58, 0xF5, 0x58, 0xFA, 0x58, 0xFF, 0x58, 0x04,\n\t0x59, 0x09, 0x59, 0x0E, 0x59, 0x13, 0x59, 0x18, 0x59, 0x1D, 0x59, 0x22, 0x59, 0x27, 0x59, 0x2D, 0x59, 0x32, 0x59, 0x37, 0x59, 0x3C, 0x59, 0x41, 0x59, 0x47, 0x59, 0x4C, 0x59, 0x51, 0x59, 0x57,\n\t0x59, 0x5C, 0x59, 0x61, 0x59, 0x67, 0x59, 0x6C, 0x59, 0x72, 0x59, 0x77, 0x59, 0x7C, 0x59, 0x82, 0x59, 0x87, 0x59, 0x8D, 0x59, 0x93, 0x59, 0x98, 0x59, 0x9E, 0x59, 0xA3, 0x59, 0xA9, 0x59, 0xAF,\n\t0x59, 0xB4, 0x59, 0xBA, 0x59, 0xC0, 0x59, 0xC6, 0x59, 0xCB, 0x59, 0xD1, 0x59, 0xD7, 0x59, 0xDD, 0x59, 0xE3, 0x59, 0xE9, 0x59, 0xEF, 0x59, 0xF5, 0x59, 0xFA, 0x59, 0x00, 0x5A, 0x07, 0x5A, 0x0D,\n\t0x5A, 0x13, 0x5A, 0x19, 0x5A, 0x1F, 0x5A, 0x25, 0x5A, 0x2B, 0x5A, 0x31, 0x5A, 0x37, 0x5A, 0x3E, 0x5A, 0x44, 0x5A, 0x4A, 0x5A, 0x51, 0x5A, 0x57, 0x5A, 0x5D, 0x5A, 0x64, 0x5A, 0x6A, 0x5A, 0x70,\n\t0x5A, 0x77, 0x5A, 0x7D, 0x5A, 0x84, 0x5A, 0x8A, 0x5A, 0x91, 0x5A, 0x98, 0x5A, 0x9E, 0x5A, 0xA5, 0x5A, 0xAB, 0x5A, 0xB2, 0x5A, 0xB9, 0x5A, 0xC0, 0x5A, 0xC6, 0x5A, 0xCD, 0x5A, 0xD4, 0x5A, 0xDB,\n\t0x5A, 0xE2, 0x5A, 0xE8, 0x5A, 0xEF, 0x5A, 0xF6, 0x5A, 0xFD, 0x5A, 0x04, 0x5B, 0x0B, 0x5B, 0x12, 0x5B, 0x1A, 0x5B, 0x21, 0x5B, 0x28, 0x5B, 0x2F, 0x5B, 0x36, 0x5B, 0x3D, 0x5B, 0x45, 0x5B, 0x4C,\n\t0x5B, 0x53, 0x5B, 0x5B, 0x5B, 0x62, 0x5B, 0x69, 0x5B, 0x71, 0x5B, 0x78, 0x5B, 0x80, 0x5B, 0x87, 0x5B, 0x8F, 0x5B, 0x96, 0x5B, 0x9E, 0x5B, 0xA6, 0x5B, 0xAD, 0x5B, 0xB5, 0x5B, 0xBD, 0x5B, 0xC4,\n\t0x5B, 0xCC, 0x5B, 0xD4, 0x5B, 0xDC, 0x5B, 0xE4, 0x5B, 0xEC, 0x5B, 0xF4, 0x5B, 0xFB, 0x5B, 0x02, 0x5C, 0x06, 0x5C, 0x0A, 0x5C, 0x0E, 0x5C, 0x12, 0x5C, 0x16, 0x5C, 0x1A, 0x5C, 0x1E, 0x5C, 0x22,\n\t0x5C, 0x26, 0x5C, 0x2B, 0x5C, 0x2F, 0x5C, 0x33, 0x5C, 0x37, 0x5C, 0x3B, 0x5C, 0x40, 0x5C, 0x44, 0x5C, 0x48, 0x5C, 0x4C, 0x5C, 0x51, 0x5C, 0x55, 0x5C, 0x59, 0x5C, 0x5E, 0x5C, 0x62, 0x5C, 0x67,\n\t0x5C, 0x6B, 0x5C, 0x6F, 0x5C, 0x74, 0x5C, 0x78, 0x5C, 0x7D, 0x5C, 0x81, 0x5C, 0x86, 0x5C, 0x8A, 0x5C, 0x8F, 0x5C, 0x93, 0x5C, 0x98, 0x5C, 0x9D, 0x5C, 0xA1, 0x5C, 0xA6, 0x5C, 0xAB, 0x5C, 0xAF,\n\t0x5C, 0xB4, 0x5C, 0xB9, 0x5C, 0xBD, 0x5C, 0xC2, 0x5C, 0xC7, 0x5C, 0xCC, 0x5C, 0xD0, 0x5C, 0xD5, 0x5C, 0xDA, 0x5C, 0xDF, 0x5C, 0xE4, 0x5C, 0xE9, 0x5C, 0xEE, 0x5C, 0xF3, 0x5C, 0xF8, 0x5C, 0xFD,\n\t0x5C, 0x02, 0x5D, 0x07, 0x5D, 0x0C, 0x5D, 0x11, 0x5D, 0x16, 0x5D, 0x1B, 0x5D, 0x20, 0x5D, 0x25, 0x5D, 0x2A, 0x5D, 0x2F, 0x5D, 0x35, 0x5D, 0x3A, 0x5D, 0x3F, 0x5D, 0x44, 0x5D, 0x4A, 0x5D, 0x4F,\n\t0x5D, 0x54, 0x5D, 0x5A, 0x5D, 0x5F, 0x5D, 0x64, 0x5D, 0x6A, 0x5D, 0x6F, 0x5D, 0x75, 0x5D, 0x7A, 0x5D, 0x80, 0x5D, 0x85, 0x5D, 0x8B, 0x5D, 0x90, 0x5D, 0x96, 0x5D, 0x9B, 0x5D, 0xA1, 0x5D, 0xA7,\n\t0x5D, 0xAC, 0x5D, 0xB2, 0x5D, 0xB8, 0x5D, 0xBD, 0x5D, 0xC3, 0x5D, 0xC9, 0x5D, 0xCF, 0x5D, 0xD4, 0x5D, 0xDA, 0x5D, 0xE0, 0x5D, 0xE6, 0x5D, 0xEC, 0x5D, 0xF2, 0x5D, 0xF8, 0x5D, 0xFE, 0x5D, 0x04,\n\t0x5E, 0x0A, 0x5E, 0x10, 0x5E, 0x16, 0x5E, 0x1C, 0x5E, 0x22, 0x5E, 0x28, 0x5E, 0x2F, 0x5E, 0x35, 0x5E, 0x3B, 0x5E, 0x41, 0x5E, 0x47, 0x5E, 0x4E, 0x5E, 0x54, 0x5E, 0x5A, 0x5E, 0x61, 0x5E, 0x67,\n\t0x5E, 0x6E, 0x5E, 0x74, 0x5E, 0x7A, 0x5E, 0x81, 0x5E, 0x87, 0x5E, 0x8E, 0x5E, 0x95, 0x5E, 0x9B, 0x5E, 0xA2, 0x5E, 0xA8, 0x5E, 0xAF, 0x5E, 0xB6, 0x5E, 0xBD, 0x5E, 0xC3, 0x5E, 0xCA, 0x5E, 0xD1,\n\t0x5E, 0xD8, 0x5E, 0xDF, 0x5E, 0xE5, 0x5E, 0xEC, 0x5E, 0xF3, 0x5E, 0xFA, 0x5E, 0x01, 0x5F, 0x08, 0x5F, 0x0F, 0x5F, 0x16, 0x5F, 0x1D, 0x5F, 0x25, 0x5F, 0x2C, 0x5F, 0x33, 0x5F, 0x3A, 0x5F, 0x41,\n\t0x5F, 0x49, 0x5F, 0x50, 0x5F, 0x57, 0x5F, 0x5F, 0x5F, 0x66, 0x5F, 0x6D, 0x5F, 0x75, 0x5F, 0x7C, 0x5F, 0x84, 0x5F, 0x8B, 0x5F, 0x93, 0x5F, 0x9B, 0x5F, 0xA2, 0x5F, 0xAA, 0x5F, 0xB1, 0x5F, 0xB9,\n\t0x5F, 0xC1, 0x5F, 0xC9, 0x5F, 0xD0, 0x5F, 0xD8, 0x5F, 0xE0, 0x5F, 0xE8, 0x5F, 0xF0, 0x5F, 0xF8, 0x5F, 0x00, 0x60, 0x04, 0x60, 0x08, 0x60, 0x0C, 0x60, 0x10, 0x60, 0x14, 0x60, 0x18, 0x60, 0x1C,\n\t0x60, 0x20, 0x60, 0x25, 0x60, 0x29, 0x60, 0x2D, 0x60, 0x31, 0x60, 0x35, 0x60, 0x3A, 0x60, 0x3E, 0x60, 0x42, 0x60, 0x46, 0x60, 0x4B, 0x60, 0x4F, 0x60, 0x53, 0x60, 0x57, 0x60, 0x5C, 0x60, 0x60,\n\t0x60, 0x65, 0x60, 0x69, 0x60, 0x6D, 0x60, 0x72, 0x60, 0x76, 0x60, 0x7B, 0x60, 0x7F, 0x60, 0x84, 0x60, 0x88, 0x60, 0x8D, 0x60, 0x91, 0x60, 0x96, 0x60, 0x9B, 0x60, 0x9F, 0x60, 0xA4, 0x60, 0xA8,\n\t0x60, 0xAD, 0x60, 0xB2, 0x60, 0xB7, 0x60, 0xBB, 0x60, 0xC0, 0x60, 0xC5, 0x60, 0xCA, 0x60, 0xCE, 0x60, 0xD3, 0x60, 0xD8, 0x60, 0xDD, 0x60, 0xE2, 0x60, 0xE7, 0x60, 0xEB, 0x60, 0xF0, 0x60, 0xF5,\n\t0x60, 0xFA, 0x60, 0xFF, 0x60, 0x04, 0x61, 0x09, 0x61, 0x0E, 0x61, 0x13, 0x61, 0x19, 0x61, 0x1E, 0x61, 0x23, 0x61, 0x28, 0x61, 0x2D, 0x61, 0x32, 0x61, 0x37, 0x61, 0x3D, 0x61, 0x42, 0x61, 0x47,\n\t0x61, 0x4D, 0x61, 0x52, 0x61, 0x57, 0x61, 0x5D, 0x61, 0x62, 0x61, 0x67, 0x61, 0x6D, 0x61, 0x72, 0x61, 0x78, 0x61, 0x7D, 0x61, 0x83, 0x61, 0x88, 0x61, 0x8E, 0x61, 0x93, 0x61, 0x99, 0x61, 0x9E,\n\t0x61, 0xA4, 0x61, 0xAA, 0x61, 0xAF, 0x61, 0xB5, 0x61, 0xBB, 0x61, 0xC0, 0x61, 0xC6, 0x61, 0xCC, 0x61, 0xD2, 0x61, 0xD8, 0x61, 0xDE, 0x61, 0xE3, 0x61, 0xE9, 0x61, 0xEF, 0x61, 0xF5, 0x61, 0xFB,\n\t0x61, 0x01, 0x62, 0x07, 0x62, 0x0D, 0x62, 0x13, 0x62, 0x19, 0x62, 0x1F, 0x62, 0x26, 0x62, 0x2C, 0x62, 0x32, 0x62, 0x38, 0x62, 0x3E, 0x62, 0x45, 0x62, 0x4B, 0x62, 0x51, 0x62, 0x58, 0x62, 0x5E,\n\t0x62, 0x64, 0x62, 0x6B, 0x62, 0x71, 0x62, 0x78, 0x62, 0x7E, 0x62, 0x85, 0x62, 0x8B, 0x62, 0x92, 0x62, 0x98, 0x62, 0x9F, 0x62, 0xA5, 0x62, 0xAC, 0x62, 0xB3, 0x62, 0xBA, 0x62, 0xC0, 0x62, 0xC7,\n\t0x62, 0xCE, 0x62, 0xD5, 0x62, 0xDB, 0x62, 0xE2, 0x62, 0xE9, 0x62, 0xF0, 0x62, 0xF7, 0x62, 0xFE, 0x62, 0x05, 0x63, 0x0C, 0x63, 0x13, 0x63, 0x1A, 0x63, 0x21, 0x63, 0x29, 0x63, 0x30, 0x63, 0x37,\n\t0x63, 0x3E, 0x63, 0x45, 0x63, 0x4D, 0x63, 0x54, 0x63, 0x5B, 0x63, 0x63, 0x63, 0x6A, 0x63, 0x72, 0x63, 0x79, 0x63, 0x80, 0x63, 0x88, 0x63, 0x90, 0x63, 0x97, 0x63, 0x9F, 0x63, 0xA6, 0x63, 0xAE,\n\t0x63, 0xB6, 0x63, 0xBD, 0x63, 0xC5, 0x63, 0xCD, 0x63, 0xD5, 0x63, 0xDD, 0x63, 0xE5, 0x63, 0xEC, 0x63, 0xF4, 0x63, 0xFC, 0x63, 0x02, 0x64, 0x06, 0x64, 0x0A, 0x64, 0x0E, 0x64, 0x12, 0x64, 0x16,\n\t0x64, 0x1B, 0x64, 0x1F, 0x64, 0x23, 0x64, 0x27, 0x64, 0x2B, 0x64, 0x2F, 0x64, 0x33, 0x64, 0x38, 0x64, 0x3C, 0x64, 0x40, 0x64, 0x44, 0x64, 0x49, 0x64, 0x4D, 0x64, 0x51, 0x64, 0x56, 0x64, 0x5A,\n\t0x64, 0x5E, 0x64, 0x63, 0x64, 0x67, 0x64, 0x6B, 0x64, 0x70, 0x64, 0x74, 0x64, 0x79, 0x64, 0x7D, 0x64, 0x82, 0x64, 0x86, 0x64, 0x8B, 0x64, 0x8F, 0x64, 0x94, 0x64, 0x99, 0x64, 0x9D, 0x64, 0xA2,\n\t0x64, 0xA6, 0x64, 0xAB, 0x64, 0xB0, 0x64, 0xB4, 0x64, 0xB9, 0x64, 0xBE, 0x64, 0xC3, 0x64, 0xC7, 0x64, 0xCC, 0x64, 0xD1, 0x64, 0xD6, 0x64, 0xDB, 0x64, 0xE0, 0x64, 0xE4, 0x64, 0xE9, 0x64, 0xEE,\n\t0x64, 0xF3, 0x64, 0xF8, 0x64, 0xFD, 0x64, 0x02, 0x65, 0x07, 0x65, 0x0C, 0x65, 0x11, 0x65, 0x16, 0x65, 0x1B, 0x65, 0x20, 0x65, 0x26, 0x65, 0x2B, 0x65, 0x30, 0x65, 0x35, 0x65, 0x3A, 0x65, 0x40,\n\t0x65, 0x45, 0x65, 0x4A, 0x65, 0x4F, 0x65, 0x55, 0x65, 0x5A, 0x65, 0x5F, 0x65, 0x65, 0x65, 0x6A, 0x65, 0x70, 0x65, 0x75, 0x65, 0x7B, 0x65, 0x80, 0x65, 0x86, 0x65, 0x8B, 0x65, 0x91, 0x65, 0x96,\n\t0x65, 0x9C, 0x65, 0xA1, 0x65, 0xA7, 0x65, 0xAD, 0x65, 0xB2, 0x65, 0xB8, 0x65, 0xBE, 0x65, 0xC4, 0x65, 0xC9, 0x65, 0xCF, 0x65, 0xD5, 0x65, 0xDB, 0x65, 0xE1, 0x65, 0xE7, 0x65, 0xED, 0x65, 0xF3,\n\t0x65, 0xF8, 0x65, 0xFE, 0x65, 0x04, 0x66, 0x0B, 0x66, 0x11, 0x66, 0x17, 0x66, 0x1D, 0x66, 0x23, 0x66, 0x29, 0x66, 0x2F, 0x66, 0x35, 0x66, 0x3C, 0x66, 0x42, 0x66, 0x48, 0x66, 0x4E, 0x66, 0x55,\n\t0x66, 0x5B, 0x66, 0x61, 0x66, 0x68, 0x66, 0x6E, 0x66, 0x75, 0x66, 0x7B, 0x66, 0x82, 0x66, 0x88, 0x66, 0x8F, 0x66, 0x95, 0x66, 0x9C, 0x66, 0xA2, 0x66, 0xA9, 0x66, 0xB0, 0x66, 0xB7, 0x66, 0xBD,\n\t0x66, 0xC4, 0x66, 0xCB, 0x66, 0xD2, 0x66, 0xD8, 0x66, 0xDF, 0x66, 0xE6, 0x66, 0xED, 0x66, 0xF4, 0x66, 0xFB, 0x66, 0x02, 0x67, 0x09, 0x67, 0x10, 0x67, 0x17, 0x67, 0x1E, 0x67, 0x25, 0x67, 0x2D,\n\t0x67, 0x34, 0x67, 0x3B, 0x67, 0x42, 0x67, 0x49, 0x67, 0x51, 0x67, 0x58, 0x67, 0x5F, 0x67, 0x67, 0x67, 0x6E, 0x67, 0x76, 0x67, 0x7D, 0x67, 0x85, 0x67, 0x8C, 0x67, 0x94, 0x67, 0x9B, 0x67, 0xA3,\n\t0x67, 0xAB, 0x67, 0xB2, 0x67, 0xBA, 0x67, 0xC2, 0x67, 0xCA, 0x67, 0xD1, 0x67, 0xD9, 0x67, 0xE1, 0x67, 0xE9, 0x67, 0xF1, 0x67, 0xF9, 0x67, 0x00, 0x68, 0x04, 0x68, 0x08, 0x68, 0x0C, 0x68, 0x11,\n\t0x68, 0x15, 0x68, 0x19, 0x68, 0x1D, 0x68, 0x21, 0x68, 0x25, 0x68, 0x29, 0x68, 0x2D, 0x68, 0x32, 0x68, 0x36, 0x68, 0x3A, 0x68, 0x3E, 0x68, 0x42, 0x68, 0x47, 0x68, 0x4B, 0x68, 0x4F, 0x68, 0x54,\n\t0x68, 0x58, 0x68, 0x5C, 0x68, 0x61, 0x68, 0x65, 0x68, 0x69, 0x68, 0x6E, 0x68, 0x72, 0x68, 0x77, 0x68, 0x7B, 0x68, 0x80, 0x68, 0x84, 0x68, 0x89, 0x68, 0x8D, 0x68, 0x92, 0x68, 0x96, 0x68, 0x9B,\n\t0x68, 0xA0, 0x68, 0xA4, 0x68, 0xA9, 0x68, 0xAE, 0x68, 0xB2, 0x68, 0xB7, 0x68, 0xBC, 0x68, 0xC0, 0x68, 0xC5, 0x68, 0xCA, 0x68, 0xCF, 0x68, 0xD4, 0x68, 0xD8, 0x68, 0xDD, 0x68, 0xE2, 0x68, 0xE7,\n\t0x68, 0xEC, 0x68, 0xF1, 0x68, 0xF6, 0x68, 0xFB, 0x68, 0x00, 0x69, 0x05, 0x69, 0x0A, 0x69, 0x0F, 0x69, 0x14, 0x69, 0x19, 0x69, 0x1E, 0x69, 0x23, 0x69, 0x28, 0x69, 0x2E, 0x69, 0x33, 0x69, 0x38,\n\t0x69, 0x3D, 0x69, 0x43, 0x69, 0x48, 0x69, 0x4D, 0x69, 0x52, 0x69, 0x58, 0x69, 0x5D, 0x69, 0x62, 0x69, 0x68, 0x69, 0x6D, 0x69, 0x73, 0x69, 0x78, 0x69, 0x7E, 0x69, 0x83, 0x69, 0x89, 0x69, 0x8E,\n\t0x69, 0x94, 0x69, 0x99, 0x69, 0x9F, 0x69, 0xA5, 0x69, 0xAA, 0x69, 0xB0, 0x69, 0xB6, 0x69, 0xBB, 0x69, 0xC1, 0x69, 0xC7, 0x69, 0xCD, 0x69, 0xD2, 0x69, 0xDE, 0x69, 0xEA, 0x69, 0xF6, 0x69, 0x02,\n\t0x6A, 0x0E, 0x6A, 0x1A, 0x6A, 0x26, 0x6A, 0x33, 0x6A, 0x3F, 0x6A, 0x4C, 0x6A, 0x58, 0x6A, 0x65, 0x6A, 0x72, 0x6A, 0x7F, 0x6A, 0x8C, 0x6A, 0x99, 0x6A, 0xA6, 0x6A, 0xB4, 0x6A, 0xC1, 0x6A, 0xCF,\n\t0x6A, 0xDC, 0x6A, 0xEA, 0x6A, 0xF8, 0x6A, 0x06, 0x6B, 0x14, 0x6B, 0x22, 0x6B, 0x30, 0x6B, 0x3F, 0x6B, 0x4D, 0x6B, 0x5C, 0x6B, 0x6B, 0x6B, 0x7A, 0x6B, 0x89, 0x6B, 0x98, 0x6B, 0xA7, 0x6B, 0xB7,\n\t0x6B, 0xC6, 0x6B, 0xD6, 0x6B, 0xE5, 0x6B, 0xF5, 0x6B, 0x03, 0x6C, 0x0B, 0x6C, 0x13, 0x6C, 0x1B, 0x6C, 0x23, 0x6C, 0x2C, 0x6C, 0x34, 0x6C, 0x3C, 0x6C, 0x45, 0x6C, 0x4D, 0x6C, 0x56, 0x6C, 0x5F,\n\t0x6C, 0x68, 0x6C, 0x70, 0x6C, 0x79, 0x6C, 0x82, 0x6C, 0x8B, 0x6C, 0x94, 0x6C, 0x9E, 0x6C, 0xA7, 0x6C, 0xB0, 0x6C, 0xBA, 0x6C, 0xC3, 0x6C, 0xCD, 0x6C, 0xD6, 0x6C, 0xE0, 0x6C, 0xEA, 0x6C, 0xF4,\n\t0x6C, 0xFE, 0x6C, 0x08, 0x6D, 0x12, 0x6D, 0x1C, 0x6D, 0x26, 0x6D, 0x31, 0x6D, 0x3B, 0x6D, 0x45, 0x6D, 0x50, 0x6D, 0x5B, 0x6D, 0x65, 0x6D, 0x70, 0x6D, 0x7B, 0x6D, 0x86, 0x6D, 0x91, 0x6D, 0x9C,\n\t0x6D, 0xA8, 0x6D, 0xB3, 0x6D, 0xBF, 0x6D, 0xCA, 0x6D, 0xD6, 0x6D, 0xE1, 0x6D, 0xED, 0x6D, 0xF9, 0x6D, 0x05, 0x6E, 0x11, 0x6E, 0x1D, 0x6E, 0x2A, 0x6E, 0x36, 0x6E, 0x43, 0x6E, 0x4F, 0x6E, 0x5C,\n\t0x6E, 0x69, 0x6E, 0x75, 0x6E, 0x82, 0x6E, 0x8F, 0x6E, 0x9D, 0x6E, 0xAA, 0x6E, 0xB7, 0x6E, 0xC5, 0x6E, 0xD2, 0x6E, 0xE0, 0x6E, 0xEE, 0x6E, 0xFC, 0x6E, 0x0A, 0x6F, 0x18, 0x6F, 0x26, 0x6F, 0x34,\n\t0x6F, 0x43, 0x6F, 0x52, 0x6F, 0x60, 0x6F, 0x6F, 0x6F, 0x7E, 0x6F, 0x8D, 0x6F, 0x9C, 0x6F, 0xAB, 0x6F, 0xBB, 0x6F, 0xCA, 0x6F, 0xDA, 0x6F, 0xEA, 0x6F, 0xFA, 0x6F, 0x05, 0x70, 0x0D, 0x70, 0x15,\n\t0x70, 0x1D, 0x70, 0x25, 0x70, 0x2E, 0x70, 0x36, 0x70, 0x3F, 0x70, 0x47, 0x70, 0x50, 0x70, 0x58, 0x70, 0x61, 0x70, 0x6A, 0x70, 0x73, 0x70, 0x7C, 0x70, 0x85, 0x70, 0x8E, 0x70, 0x97, 0x70, 0xA0,\n\t0x70, 0xA9, 0x70, 0xB3, 0x70, 0xBC, 0x70, 0xC6, 0x70, 0xCF, 0x70, 0xD9, 0x70, 0xE3, 0x70, 0xED, 0x70, 0xF6, 0x70, 0x00, 0x71, 0x0A, 0x71, 0x15, 0x71, 0x1F, 0x71, 0x29, 0x71, 0x33, 0x71, 0x3E,\n\t0x71, 0x48, 0x71, 0x53, 0x71, 0x5E, 0x71, 0x68, 0x71, 0x73, 0x71, 0x7E, 0x71, 0x89, 0x71, 0x94, 0x71, 0xA0, 0x71, 0xAB, 0x71, 0xB6, 0x71, 0xC2, 0x71, 0xCD, 0x71, 0xD9, 0x71, 0xE5, 0x71, 0xF1,\n\t0x71, 0xFC, 0x71, 0x08, 0x72, 0x15, 0x72, 0x21, 0x72, 0x2D, 0x72, 0x39, 0x72, 0x46, 0x72, 0x53, 0x72, 0x5F, 0x72, 0x6C, 0x72, 0x79, 0x72, 0x86, 0x72, 0x93, 0x72, 0xA0, 0x72, 0xAE, 0x72, 0xBB,\n\t0x72, 0xC8, 0x72, 0xD6, 0x72, 0xE4, 0x72, 0xF2, 0x72, 0x00, 0x73, 0x0E, 0x73, 0x1C, 0x73, 0x2A, 0x73, 0x38, 0x73, 0x47, 0x73, 0x56, 0x73, 0x64, 0x73, 0x73, 0x73, 0x82, 0x73, 0x91, 0x73, 0xA0,\n\t0x73, 0xB0, 0x73, 0xBF, 0x73, 0xCF, 0x73, 0xDE, 0x73, 0xEE, 0x73, 0xFE, 0x73, 0x07, 0x74, 0x0F, 0x74, 0x17, 0x74, 0x20, 0x74, 0x28, 0x74, 0x30, 0x74, 0x39, 0x74, 0x41, 0x74, 0x4A, 0x74, 0x52,\n\t0x74, 0x5B, 0x74, 0x64, 0x74, 0x6C, 0x74, 0x75, 0x74, 0x7E, 0x74, 0x87, 0x74, 0x90, 0x74, 0x9A, 0x74, 0xA3, 0x74, 0xAC, 0x74, 0xB5, 0x74, 0xBF, 0x74, 0xC8, 0x74, 0xD2, 0x74, 0xDC, 0x74, 0xE5,\n\t0x74, 0xEF, 0x74, 0xF9, 0x74, 0x03, 0x75, 0x0D, 0x75, 0x17, 0x75, 0x22, 0x75, 0x2C, 0x75, 0x36, 0x75, 0x41, 0x75, 0x4B, 0x75, 0x56, 0x75, 0x61, 0x75, 0x6B, 0x75, 0x76, 0x75, 0x81, 0x75, 0x8C,\n\t0x75, 0x97, 0x75, 0xA3, 0x75, 0xAE, 0x75, 0xB9, 0x75, 0xC5, 0x75, 0xD1, 0x75, 0xDC, 0x75, 0xE8, 0x75, 0xF4, 0x75, 0x00, 0x76, 0x0C, 0x76, 0x18, 0x76, 0x24, 0x76, 0x31, 0x76, 0x3D, 0x76, 0x49,\n\t0x76, 0x56, 0x76, 0x63, 0x76, 0x70, 0x76, 0x7D, 0x76, 0x8A, 0x76, 0x97, 0x76, 0xA4, 0x76, 0xB1, 0x76, 0xBF, 0x76, 0xCC, 0x76, 0xDA, 0x76, 0xE8, 0x76, 0xF6, 0x76, 0x03, 0x77, 0x12, 0x77, 0x20,\n\t0x77, 0x2E, 0x77, 0x3C, 0x77, 0x4B, 0x77, 0x5A, 0x77, 0x68, 0x77, 0x77, 0x77, 0x86, 0x77, 0x95, 0x77, 0xA5, 0x77, 0xB4, 0x77, 0xC3, 0x77, 0xD3, 0x77, 0xE3, 0x77, 0xF3, 0x77, 0x01, 0x78, 0x09,\n\t0x78, 0x11, 0x78, 0x1A, 0x78, 0x22, 0x78, 0x2A, 0x78, 0x32, 0x78, 0x3B, 0x78, 0x43, 0x78, 0x4C, 0x78, 0x55, 0x78, 0x5D, 0x78, 0x66, 0x78, 0x6F, 0x78, 0x78, 0x78, 0x81, 0x78, 0x8A, 0x78, 0x93,\n\t0x78, 0x9C, 0x78, 0xA5, 0x78, 0xAF, 0x78, 0xB8, 0x78, 0xC2, 0x78, 0xCB, 0x78, 0xD5, 0x78, 0xDE, 0x78, 0xE8, 0x78, 0xF2, 0x78, 0xFC, 0x78, 0x06, 0x79, 0x10, 0x79, 0x1A, 0x79, 0x24, 0x79, 0x2F,\n\t0x79, 0x39, 0x79, 0x44, 0x79, 0x4E, 0x79, 0x59, 0x79, 0x64, 0x79, 0x6E, 0x79, 0x79, 0x79, 0x84, 0x79, 0x8F, 0x79, 0x9B, 0x79, 0xA6, 0x79, 0xB1, 0x79, 0xBD, 0x79, 0xC8, 0x79, 0xD4, 0x79, 0xDF,\n\t0x79, 0xEB, 0x79, 0xF7, 0x79, 0x03, 0x7A, 0x0F, 0x7A, 0x1B, 0x7A, 0x28, 0x7A, 0x34, 0x7A, 0x40, 0x7A, 0x4D, 0x7A, 0x5A, 0x7A, 0x66, 0x7A, 0x73, 0x7A, 0x80, 0x7A, 0x8D, 0x7A, 0x9A, 0x7A, 0xA8,\n\t0x7A, 0xB5, 0x7A, 0xC2, 0x7A, 0xD0, 0x7A, 0xDE, 0x7A, 0xEB, 0x7A, 0xF9, 0x7A, 0x07, 0x7B, 0x16, 0x7B, 0x24, 0x7B, 0x32, 0x7B, 0x41, 0x7B, 0x4F, 0x7B, 0x5E, 0x7B, 0x6D, 0x7B, 0x7B, 0x7B, 0x8A,\n\t0x7B, 0x9A, 0x7B, 0xA9, 0x7B, 0xB8, 0x7B, 0xC8, 0x7B, 0xD7, 0x7B, 0xE7, 0x7B, 0xF7, 0x7B, 0x00, 0x7C, 0x02, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,\n\t0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,\n\t0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,\n\t0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3A, 0xFF, 0xFF, 0xFF, 0xFF, 0xF4, 0x01, 0x7E,\n\t0x02, 0x7E, 0x03, 0x7E, 0x04, 0x7E, 0x05, 0x7E, 0x06, 0x7E, 0x07, 0x7E, 0x08, 0x7E, 0x09, 0x7E, 0x0A, 0x7E, 0x0B, 0x7E, 0x0C, 0x7E, 0x0D, 0x7E, 0x0E, 0x7E, 0x0F, 0x7E, 0x10, 0x7E, 0x11, 0x7E,\n\t0x12, 0x7E, 0x13, 0x7E, 0x14, 0x7E, 0x15, 0x7E, 0x16, 0x7E, 0x17, 0x7E, 0x18, 0x7E, 0x19, 0x7E, 0x1A, 0x7E, 0x1B, 0x7E, 0x1C, 0x7E, 0x1D, 0x7E, 0x1E, 0x7E, 0x1F, 0x7E, 0x20, 0x7E, 0x21, 0x7E,\n\t0x22, 0x7E, 0x23, 0x7E, 0x24, 0x7E, 0x25, 0x7E, 0x26, 0x7E, 0x27, 0x7E, 0x28, 0x7E, 0x29, 0x7E, 0x2A, 0x7E, 0x2B, 0x7E, 0x2C, 0x7E, 0x2D, 0x7E, 0x2E, 0x7E, 0x2F, 0x7E, 0x30, 0x7E, 0x31, 0x7E,\n\t0x32, 0x7E, 0x33, 0x7E, 0x34, 0x7E, 0x35, 0x7E, 0x36, 0x7E, 0x37, 0x7E, 0x38, 0x7E, 0x39, 0x7E, 0x3A, 0x7E, 0x3B, 0x7E, 0x3C, 0x7E, 0x3D, 0x7E, 0x3E, 0x7E, 0x3F, 0x7E, 0x40, 0x7E, 0x41, 0x7E,\n\t0x42, 0x7E, 0x43, 0x7E, 0x44, 0x7E, 0x45, 0x7E, 0x46, 0x7E, 0x47, 0x7E, 0x48, 0x7E, 0x49, 0x7E, 0x4A, 0x7E, 0x4B, 0x7E, 0x4C, 0x7E, 0x4D, 0x7E, 0x4E, 0x7E, 0x4F, 0x7E, 0x50, 0x7E, 0x51, 0x7E,\n\t0x52, 0x7E, 0x53, 0x7E, 0x54, 0x7E, 0x55, 0x7E, 0x56, 0x7E, 0x57, 0x7E, 0x58, 0x7E, 0x59, 0x7E, 0x5A, 0x7E, 0x5B, 0x7E, 0x5C, 0x7E, 0x5D, 0x7E, 0x5E, 0x7E, 0x5F, 0x7E, 0x60, 0x7E, 0x61, 0x7E,\n\t0x62, 0x7E, 0x63, 0x7E, 0x64, 0x7E, 0x65, 0x7E, 0x66, 0x7E, 0x67, 0x7E, 0x68, 0x7E, 0x69, 0x7E, 0x6A, 0x7E, 0x6B, 0x7E, 0x6C, 0x7E, 0x6D, 0x7E, 0x6E, 0x7E, 0x6F, 0x7E, 0x70, 0x7E, 0x71, 0x7E,\n\t0x72, 0x7E, 0x73, 0x7E, 0x74, 0x7E, 0x75, 0x7E, 0x76, 0x7E, 0x77, 0x7E, 0x78, 0x7E, 0x79, 0x7E, 0x7A, 0x7E, 0x7B, 0x7E, 0x7C, 0x7E, 0x7D, 0x7E, 0x7E, 0x7E, 0x7F, 0x7E, 0x80, 0x7E, 0x81, 0x7E,\n\t0x82, 0x7E, 0x83, 0x7E, 0x84, 0x7E, 0x85, 0x7E, 0x86, 0x7E, 0x87, 0x7E, 0x88, 0x7E, 0x89, 0x7E, 0x8A, 0x7E, 0x8B, 0x7E, 0x8C, 0x7E, 0x8D, 0x7E, 0x8E, 0x7E, 0x8F, 0x7E, 0x90, 0x7E, 0x91, 0x7E,\n\t0x92, 0x7E, 0x93, 0x7E, 0x94, 0x7E, 0x95, 0x7E, 0x96, 0x7E, 0x97, 0x7E, 0x98, 0x7E, 0x99, 0x7E, 0x9A, 0x7E, 0x9B, 0x7E, 0x9C, 0x7E, 0x9D, 0x7E, 0x9E, 0x7E, 0x9F, 0x7E, 0xA0, 0x7E, 0xA1, 0x7E,\n\t0xA2, 0x7E, 0xA3, 0x7E, 0xA4, 0x7E, 0xA5, 0x7E, 0xA6, 0x7E, 0xA7, 0x7E, 0xA8, 0x7E, 0xA9, 0x7E, 0xAA, 0x7E, 0xAB, 0x7E, 0xAC, 0x7E, 0xAD, 0x7E, 0xAE, 0x7E, 0xAF, 0x7E, 0xB0, 0x7E, 0xB1, 0x7E,\n\t0xB2, 0x7E, 0xB3, 0x7E, 0xB4, 0x7E, 0xB5, 0x7E, 0xB6, 0x7E, 0xB7, 0x7E, 0xB8, 0x7E, 0xB9, 0x7E, 0xBA, 0x7E, 0xBB, 0x7E, 0xBC, 0x7E, 0xBD, 0x7E, 0xBE, 0x7E, 0xBF, 0x7E, 0xC0, 0x7E, 0xC1, 0x7E,\n\t0xC2, 0x7E, 0xC3, 0x7E, 0xC4, 0x7E, 0xC5, 0x7E, 0xC6, 0x7E, 0xC7, 0x7E, 0xC8, 0x7E, 0xC9, 0x7E, 0xCA, 0x7E, 0xCB, 0x7E, 0xCC, 0x7E, 0xCD, 0x7E, 0xCE, 0x7E, 0xCF, 0x7E, 0xD0, 0x7E, 0xD1, 0x7E,\n\t0xD2, 0x7E, 0xD3, 0x7E, 0xD4, 0x7E, 0xD5, 0x7E, 0xD6, 0x7E, 0xD7, 0x7E, 0xD8, 0x7E, 0xD9, 0x7E, 0xDA, 0x7E, 0xDB, 0x7E, 0xDC, 0x7E, 0xDD, 0x7E, 0xDE, 0x7E, 0xDF, 0x7E, 0xE0, 0x7E, 0xE1, 0x7E,\n\t0xE2, 0x7E, 0xE3, 0x7E, 0xE4, 0x7E, 0xE5, 0x7E, 0xE6, 0x7E, 0xE7, 0x7E, 0xE8, 0x7E, 0xE9, 0x7E, 0xEA, 0x7E, 0xEB, 0x7E, 0xEC, 0x7E, 0xED, 0x7E, 0xEE, 0x7E, 0xEF, 0x7E, 0xF0, 0x7E, 0xF1, 0x7E,\n\t0xF2, 0x7E, 0xF3, 0x7E, 0xF4, 0x7E, 0xF5, 0x7E, 0xF6, 0x7E, 0xF7, 0x7E, 0xF8, 0x7E, 0xF9, 0x7E, 0xFA, 0x7E, 0xFB, 0x7E, 0xFC, 0x7E, 0xFD, 0x7E, 0xFE, 0x7E, 0xFF, 0x7E, 0x00, 0x7F, 0x01, 0x7F,\n\t0x02, 0x7F, 0x03, 0x7F, 0x04, 0x7F, 0x05, 0x7F, 0x06, 0x7F, 0x07, 0x7F, 0x08, 0x7F, 0x09, 0x7F, 0x0A, 0x7F, 0x0B, 0x7F, 0x0C, 0x7F, 0x0D, 0x7F, 0x0E, 0x7F, 0x0F, 0x7F, 0x10, 0x7F, 0x11, 0x7F,\n\t0x12, 0x7F, 0x13, 0x7F, 0x14, 0x7F, 0x15, 0x7F, 0x16, 0x7F, 0x17, 0x7F, 0x18, 0x7F, 0x19, 0x7F, 0x1A, 0x7F, 0x1B, 0x7F, 0x1C, 0x7F, 0x1D, 0x7F, 0x1E, 0x7F, 0x1F, 0x7F, 0x20, 0x7F, 0x21, 0x7F,\n\t0x22, 0x7F, 0x23, 0x7F, 0x24, 0x7F, 0x25, 0x7F, 0x26, 0x7F, 0x27, 0x7F, 0x28, 0x7F, 0x29, 0x7F, 0x2A, 0x7F, 0x2B, 0x7F, 0x2C, 0x7F, 0x2D, 0x7F, 0x2E, 0x7F, 0x2F, 0x7F, 0x30, 0x7F, 0x31, 0x7F,\n\t0x32, 0x7F, 0x33, 0x7F, 0x34, 0x7F, 0x35, 0x7F, 0x36, 0x7F, 0x37, 0x7F, 0x38, 0x7F, 0x39, 0x7F, 0x3A, 0x7F, 0x3B, 0x7F, 0x3C, 0x7F, 0x3D, 0x7F, 0x3E, 0x7F, 0x3F, 0x7F, 0x40, 0x7F, 0x41, 0x7F,\n\t0x42, 0x7F, 0x43, 0x7F, 0x44, 0x7F, 0x45, 0x7F, 0x46, 0x7F, 0x47, 0x7F, 0x48, 0x7F, 0x49, 0x7F, 0x4A, 0x7F, 0x4B, 0x7F, 0x4C, 0x7F, 0x4D, 0x7F, 0x4E, 0x7F, 0x4F, 0x7F, 0x50, 0x7F, 0x51, 0x7F,\n\t0x52, 0x7F, 0x53, 0x7F, 0x54, 0x7F, 0x55, 0x7F, 0x56, 0x7F, 0x57, 0x7F, 0x58, 0x7F, 0x59, 0x7F, 0x5A, 0x7F, 0x5B, 0x7F, 0x5C, 0x7F, 0x5D, 0x7F, 0x5E, 0x7F, 0x5F, 0x7F, 0x60, 0x7F, 0x61, 0x7F,\n\t0x62, 0x7F, 0x63, 0x7F, 0x64, 0x7F, 0x65, 0x7F, 0x66, 0x7F, 0x67, 0x7F, 0x68, 0x7F, 0x69, 0x7F, 0x6A, 0x7F, 0x6B, 0x7F, 0x6C, 0x7F, 0x6D, 0x7F, 0x6E, 0x7F, 0x6F, 0x7F, 0x70, 0x7F, 0x71, 0x7F,\n\t0x72, 0x7F, 0x73, 0x7F, 0x74, 0x7F, 0x75, 0x7F, 0x76, 0x7F, 0x77, 0x7F, 0x78, 0x7F, 0x79, 0x7F, 0x7A, 0x7F, 0x7B, 0x7F, 0x7C, 0x7F, 0x7D, 0x7F, 0x7E, 0x7F, 0x7F, 0x7F, 0x80, 0x7F, 0x81, 0x7F,\n\t0x82, 0x7F, 0x83, 0x7F, 0x84, 0x7F, 0x85, 0x7F, 0x86, 0x7F, 0x87, 0x7F, 0x88, 0x7F, 0x89, 0x7F, 0x8A, 0x7F, 0x8B, 0x7F, 0x8C, 0x7F, 0x8D, 0x7F, 0x8E, 0x7F, 0x8F, 0x7F, 0x90, 0x7F, 0x91, 0x7F,\n\t0x92, 0x7F, 0x93, 0x7F, 0x94, 0x7F, 0x95, 0x7F, 0x96, 0x7F, 0x97, 0x7F, 0x98, 0x7F, 0x99, 0x7F, 0x9A, 0x7F, 0x9B, 0x7F, 0x9C, 0x7F, 0x9D, 0x7F, 0x9E, 0x7F, 0x9F, 0x7F, 0xA0, 0x7F, 0xA1, 0x7F,\n\t0xA2, 0x7F, 0xA3, 0x7F, 0xA4, 0x7F, 0xA5, 0x7F, 0xA6, 0x7F, 0xA7, 0x7F, 0xA8, 0x7F, 0xA9, 0x7F, 0xAA, 0x7F, 0xAB, 0x7F, 0xAC, 0x7F, 0xAD, 0x7F, 0xAE, 0x7F, 0xAF, 0x7F, 0xB0, 0x7F, 0xB1, 0x7F,\n\t0xB2, 0x7F, 0xB3, 0x7F, 0xB4, 0x7F, 0xB5, 0x7F, 0xB6, 0x7F, 0xB7, 0x7F, 0xB8, 0x7F, 0xB9, 0x7F, 0xBA, 0x7F, 0xBB, 0x7F, 0xBC, 0x7F, 0xBD, 0x7F, 0xBE, 0x7F, 0xBF, 0x7F, 0xC0, 0x7F, 0xC1, 0x7F,\n\t0xC2, 0x7F, 0xC3, 0x7F, 0xC4, 0x7F, 0xC5, 0x7F, 0xC6, 0x7F, 0xC7, 0x7F, 0xC8, 0x7F, 0xC9, 0x7F, 0xCA, 0x7F, 0xCB, 0x7F, 0xCC, 0x7F, 0xCD, 0x7F, 0xCE, 0x7F, 0xCF, 0x7F, 0xD0, 0x7F, 0xD1, 0x7F,\n\t0xD2, 0x7F, 0xD3, 0x7F, 0xD4, 0x7F, 0xD5, 0x7F, 0xD6, 0x7F, 0xD7, 0x7F, 0xD8, 0x7F, 0xD9, 0x7F, 0xDA, 0x7F, 0xDB, 0x7F, 0xDC, 0x7F, 0xDD, 0x7F, 0xDE, 0x7F, 0xDF, 0x7F, 0xE0, 0x7F, 0xE1, 0x7F,\n\t0xE2, 0x7F, 0xE3, 0x7F, 0xE4, 0x7F, 0xE5, 0x7F, 0xE6, 0x7F, 0xE7, 0x7F, 0xE8, 0x7F, 0xE9, 0x7F, 0xEA, 0x7F, 0xEB, 0x7F, 0xEC, 0x7F, 0xED, 0x7F, 0xEE, 0x7F, 0xEF, 0x7F, 0xF0, 0x7F, 0xF1, 0x7F,\n\t0xF2, 0x7F, 0xF3, 0x7F, 0xF4, 0x7F, 0xF5, 0x7F, 0xF6, 0x7F, 0xF7, 0x7F, 0xF8, 0x7F, 0xF9, 0x7F, 0xFA, 0x7F, 0xFB, 0x7F, 0xFC, 0x7F, 0xFD, 0x7F, 0xFE, 0x7F, 0xFF, 0x7F, 0x00, 0x7E, 0x00, 0x04,\n\t0xFF, 0xFF, 0xFF, 0xEE, 0x0F, 0x02, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x07,\n\t0x2F, 0xFF, 0x3B, 0x02, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF6, 0x1F, 0xFE, 0x02, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF1, 0x1F, 0xFD, 0x02, 0x00, 0xFF,\n\t0xFF, 0xFF, 0xF1, 0x1F, 0xFC, 0x02, 0x00, 0xFF, 0xFF, 0xEE, 0x1F, 0xFB, 0x02, 0x00, 0xFF, 0xED, 0x1F, 0xFA, 0x02, 0x00, 0xFF, 0xEF, 0x1F, 0xF9, 0x02, 0x00, 0xFF, 0xEF, 0x1F, 0xF8, 0x02, 0x00,\n\t0xFF, 0x6B, 0x1F, 0xF7, 0x02, 0x00, 0xEC, 0x1F, 0xF6, 0x02, 0x00, 0xEE, 0x1F, 0xF5, 0x02, 0x00, 0xEE, 0x1F, 0xF4, 0x02, 0x00, 0xEC, 0x1F, 0xF3, 0x02, 0x00, 0xEE, 0x1F, 0xF2, 0x02, 0x00, 0xEE,\n\t0x1F, 0xF1, 0x02, 0x00, 0xEE, 0x1F, 0xF0, 0x02, 0x00, 0xA6, 0x1F, 0xEF, 0x02, 0x00, 0x6C, 0x1F, 0xEE, 0x02, 0x00, 0x6E, 0x1F, 0xED, 0x02, 0x00, 0x6C, 0x1F, 0xEC, 0x02, 0x00, 0x6E, 0x1F, 0xEB,\n\t0x02, 0x00, 0x6E, 0x1F, 0xEA, 0x02, 0x00, 0x6C, 0x1F, 0xE9, 0x02, 0x00, 0x6E, 0x1F, 0xE8, 0x02, 0x00, 0x6E, 0x1F, 0xE7, 0x02, 0x00, 0x6E, 0x1F, 0xE6, 0x02, 0x00, 0x6E, 0x1F, 0xE5, 0x02, 0x00,\n\t0x6C, 0x1F, 0xE4, 0x02, 0x00, 0x6E, 0x1F, 0xE3, 0x02, 0x00, 0x6E, 0x1F, 0xE2, 0x02, 0x00, 0x6E, 0x1F, 0xE1, 0x02, 0x00, 0x6E, 0x1F, 0xE0, 0x02, 0x00, 0x3E, 0x1F, 0xDF, 0x02, 0x00, 0x2C, 0x1F,\n\t0xDE, 0x02, 0x00, 0x2E, 0x1F, 0xDD, 0x02, 0x00, 0x2C, 0x1F, 0xDC, 0x02, 0x00, 0x2E, 0x1F, 0xDB, 0x02, 0x00, 0x2E, 0x1F, 0xDA, 0x02, 0x00, 0x2C, 0x1F, 0xD9, 0x02, 0x00, 0x2E, 0x1F, 0xD8, 0x02,\n\t0x00, 0x2C, 0x1F, 0xD7, 0x02, 0x00, 0x2E, 0x1F, 0xD6, 0x02, 0x00, 0x2E, 0x1F, 0xD5, 0x02, 0x00, 0x2C, 0x1F, 0xD4, 0x02, 0x00, 0x2E, 0x1F, 0xD3, 0x02, 0x00, 0x2E, 0x1F, 0xD2, 0x02, 0x00, 0x2E,\n\t0x1F, 0xD1, 0x02, 0x00, 0x2C, 0x1F, 0xD0, 0x02, 0x00, 0x2E, 0x1F, 0xCF, 0x02, 0x00, 0x2E, 0x1F, 0xCE, 0x02, 0x00, 0x2E, 0x1F, 0xCD, 0x02, 0x00, 0x2E, 0x1F, 0xCC, 0x02, 0x00, 0x2C, 0x1F, 0xCB,\n\t0x02, 0x00, 0x2E, 0x1F, 0xCA, 0x02, 0x00, 0x2E, 0x1F, 0xC9, 0x02, 0x00, 0x2E, 0x1F, 0xC8, 0x02, 0x00, 0x2E, 0x1F, 0xC7, 0x02, 0x00, 0x2E, 0x1F, 0xC6, 0x02, 0x00, 0x2E, 0x1F, 0xC5, 0x02, 0x00,\n\t0x2E, 0x1F, 0xC4, 0x02, 0x00, 0x2E, 0x1F, 0xC3, 0x02, 0x00, 0x2E, 0x1F, 0xC2, 0x02, 0x00, 0x2E, 0x1F, 0xC1, 0x02, 0x00, 0x1E, 0x1F, 0xC0, 0x02, 0x00, 0x0C, 0x1F, 0xBF, 0x02, 0x00, 0x0E, 0x1F,\n\t0xBE, 0x02, 0x00, 0x0C, 0x1F, 0xBD, 0x02, 0x00, 0x0E, 0x1F, 0xBC, 0x02, 0x00, 0x0C, 0x1F, 0xBB, 0x02, 0x00, 0x0E, 0x1F, 0xBA, 0x02, 0x00, 0x0C, 0x1F, 0xB9, 0x02, 0x00, 0x0E, 0x1F, 0xB8, 0x02,\n\t0x00, 0x0E, 0x1F, 0xB7, 0x02, 0x00, 0x0C, 0x1F, 0xB6, 0x02, 0x00, 0x0E, 0x1F, 0xB5, 0x02, 0x00, 0x0C, 0x1F, 0xB4, 0x02, 0x00, 0x0E, 0x1F, 0xB3, 0x02, 0x00, 0x0E, 0x1F, 0xB2, 0x02, 0x00, 0x0C,\n\t0x1F, 0xB1, 0x02, 0x00, 0x0E, 0x1F, 0xB0, 0x02, 0x00, 0x0C, 0x1F, 0xAF, 0x02, 0x00, 0x0E, 0x1F, 0xAE, 0x02, 0x00, 0x0E, 0x1F, 0xAD, 0x02, 0x00, 0x0C, 0x1F, 0xAC, 0x02, 0x00, 0x0E, 0x1F, 0xAB,\n\t0x02, 0x00, 0x0E, 0x1F, 0xAA, 0x02, 0x00, 0x0E, 0x1F, 0xA9, 0x02, 0x00, 0x0C, 0x1F, 0xA8, 0x02, 0x00, 0x0E, 0x1F, 0xA7, 0x02, 0x00, 0x0E, 0x1F, 0xA6, 0x02, 0x00, 0x0C, 0x1F, 0xA5, 0x02, 0x00,\n\t0x0E, 0x1F, 0xA4, 0x02, 0x00, 0x0E, 0x1F, 0xA3, 0x02, 0x00, 0x0E, 0x1F, 0xA2, 0x02, 0x00, 0x0C, 0x1F, 0xA1, 0x02, 0x00, 0x0E, 0x1F, 0xA0, 0x02, 0x00, 0x0E, 0x1F, 0x9F, 0x02, 0x00, 0x0E, 0x1F,\n\t0x9E, 0x02, 0x00, 0x0E, 0x1F, 0x9D, 0x02, 0x00, 0x0C, 0x1F, 0x9C, 0x02, 0x00, 0x0E, 0x1F, 0x9B, 0x02, 0x00, 0x0E, 0x1F, 0x9A, 0x02, 0x00, 0x0E, 0x1F, 0x99, 0x02, 0x00, 0x0E, 0x1F, 0x98, 0x02,\n\t0x00, 0x0E, 0x1F, 0x97, 0x02, 0x00, 0x0E, 0x1F, 0x96, 0x02, 0x00, 0x0C, 0x1F, 0x95, 0x02, 0x00, 0x0E, 0x1F, 0x94, 0x02, 0x00, 0x0E, 0x1F, 0x93, 0x02, 0x00, 0x0E, 0x1F, 0x92, 0x02, 0x00, 0x0E,\n\t0x1F, 0x91, 0x02, 0x00, 0x0E, 0x1F, 0x90, 0x02, 0x00, 0x0E, 0x1F, 0x8F, 0x02, 0x00, 0x0E, 0x1F, 0x8E, 0x02, 0x00, 0x0E, 0x1F, 0x8D, 0x02, 0x00, 0x0E, 0x1F, 0x8C, 0x02, 0x00, 0x0E, 0x1F, 0x8B,\n\t0x02, 0x00, 0x0E, 0x1F, 0x8A, 0x02, 0x00, 0x0E, 0x1F, 0x89, 0x02, 0x00, 0x0E, 0x1F, 0x88, 0x02, 0x00, 0x0E, 0x1F, 0x87, 0x02, 0x00, 0x0E, 0x1F, 0x86, 0x02, 0x00, 0x0E, 0x1F, 0x85, 0x02, 0x00,\n\t0x0E, 0x1F, 0x84, 0x02, 0x00, 0x06, 0x1D, 0x83, 0x02, 0x00, 0x1B, 0x82, 0x02, 0x00, 0x1D, 0x81, 0x02, 0x00, 0x1B, 0x80, 0x02, 0x00, 0x1D, 0x7F, 0x02, 0x00, 0x1B, 0x7E, 0x02, 0x00, 0x1D, 0x7D,\n\t0x02, 0x00, 0x1B, 0x7C, 0x02, 0x00, 0x1D, 0x7B, 0x02, 0x00, 0x1B, 0x7A, 0x02, 0x00, 0x1D, 0x79, 0x02, 0x00, 0x1D, 0x78, 0x02, 0x00, 0x1B, 0x77, 0x02, 0x00, 0x1D, 0x76, 0x02, 0x00, 0x1B, 0x75,\n\t0x02, 0x00, 0x1D, 0x74, 0x02, 0x00, 0x1D, 0x73, 0x02, 0x00, 0x1B, 0x72, 0x02, 0x00, 0x1D, 0x71, 0x02, 0x00, 0x1B, 0x70, 0x02, 0x00, 0x1D, 0x6F, 0x02, 0x00, 0x1D, 0x6E, 0x02, 0x00, 0x1B, 0x6D,\n\t0x02, 0x00, 0x1D, 0x6C, 0x02, 0x00, 0x1B, 0x6B, 0x02, 0x00, 0x1D, 0x6A, 0x02, 0x00, 0x1D, 0x69, 0x02, 0x00, 0x1B, 0x68, 0x02, 0x00, 0x1D, 0x67, 0x02, 0x00, 0x1D, 0x66, 0x02, 0x00, 0x1B, 0x65,\n\t0x02, 0x00, 0x1D, 0x64, 0x02, 0x00, 0x1D, 0x63, 0x02, 0x00, 0x1B, 0x62, 0x02, 0x00, 0x1D, 0x61, 0x02, 0x00, 0x1D, 0x60, 0x02, 0x00, 0x1B, 0x5F, 0x02, 0x00, 0x1D, 0x5E, 0x02, 0x00, 0x1D, 0x5D,\n\t0x02, 0x00, 0x1B, 0x5C, 0x02, 0x00, 0x1D, 0x5B, 0x02, 0x00, 0x1D, 0x5A, 0x02, 0x00, 0x1B, 0x59, 0x02, 0x00, 0x1D, 0x58, 0x02, 0x00, 0x1D, 0x57, 0x02, 0x00, 0x1D, 0x56, 0x02, 0x00, 0x1B, 0x55,\n\t0x02, 0x00, 0x1D, 0x54, 0x02, 0x00, 0x1D, 0x53, 0x02, 0x00, 0x1D, 0x52, 0x02, 0x00, 0x1B, 0x51, 0x02, 0x00, 0x1D, 0x50, 0x02, 0x00, 0x1D, 0x4F, 0x02, 0x00, 0x1D, 0x4E, 0x02, 0x00, 0x1B, 0x4D,\n\t0x02, 0x00, 0x1D, 0x4C, 0x02, 0x00, 0x1D, 0x4B, 0x02, 0x00, 0x1D, 0x4A, 0x02, 0x00, 0x1B, 0x49, 0x02, 0x00, 0x1D, 0x48, 0x02, 0x00, 0x1D, 0x47, 0x02, 0x00, 0x1D, 0x46, 0x02, 0x00, 0x1D, 0x45,\n\t0x02, 0x00, 0x1B, 0x44, 0x02, 0x00, 0x1D, 0x43, 0x02, 0x00, 0x1D, 0x42, 0x02, 0x00, 0x1D, 0x41, 0x02, 0x00, 0x1D, 0x40, 0x02, 0x00, 0x1D, 0x3F, 0x02, 0x00, 0x1B, 0x3E, 0x02, 0x00, 0x1D, 0x3D,\n\t0x02, 0x00, 0x1D, 0x3C, 0x02, 0x00, 0x0E, 0x01, 0x00, 0x1D, 0x3A, 0x02, 0x00, 0x1D, 0x39, 0x02, 0x00, 0x1D, 0x38, 0x02, 0x00, 0x1B, 0x37, 0x02, 0x00, 0x1D, 0x36, 0x02, 0x00, 0x1D, 0x35, 0x02,\n\t0x00, 0x1D, 0x34, 0x02, 0x00, 0x1D, 0x33, 0x02, 0x00, 0x1D, 0x32, 0x02, 0x00, 0x1D, 0x31, 0x02, 0x00, 0x1D, 0x30, 0x02, 0x00, 0x1D, 0x2F, 0x02, 0x00, 0x1B, 0x2E, 0x02, 0x00, 0x1D, 0x2D, 0x02,\n\t0x00, 0x1D, 0x2C, 0x02, 0x00, 0x1D, 0x2B, 0x02, 0x00, 0x1D, 0x2A, 0x02, 0x00, 0x1D, 0x29, 0x02, 0x00, 0x1D, 0x28, 0x02, 0x00, 0x1D, 0x27, 0x02, 0x00, 0x1D, 0x26, 0x02, 0x00, 0x1D, 0x25, 0x02,\n\t0x00, 0x1D, 0x24, 0x02, 0x00, 0x1D, 0x23, 0x02, 0x00, 0x1D, 0x22, 0x02, 0x00, 0x1D, 0x21, 0x02, 0x00, 0x1D, 0x20, 0x02, 0x00, 0x1D, 0x1F, 0x02, 0x00, 0x1D, 0x1E, 0x02, 0x00, 0x1D, 0x1D, 0x02,\n\t0x00, 0x1D, 0x1C, 0x02, 0x00, 0x1D, 0x1B, 0x02, 0x00, 0x1D, 0x1A, 0x02, 0x00, 0x1D, 0x19, 0x02, 0x00, 0x1D, 0x18, 0x02, 0x00, 0x1D, 0x17, 0x02, 0x00, 0x1D, 0x16, 0x02, 0x00, 0x1D, 0x15, 0x02,\n\t0x00, 0x1D, 0x14, 0x02, 0x00, 0x1D, 0x13, 0x02, 0x00, 0x1D, 0x12, 0x02, 0x00, 0x1D, 0x11, 0x02, 0x00, 0x1D, 0x10, 0x02, 0x00, 0x15, 0x0F, 0x02, 0x00, 0x15, 0x0E, 0x02, 0x00, 0x13, 0x0D, 0x02,\n\t0x00, 0x15, 0x0C, 0x02, 0x00, 0x15, 0x0B, 0x02, 0x00, 0x13, 0x0A, 0x02, 0x00, 0x15, 0x09, 0x02, 0x00, 0x13, 0x08, 0x02, 0x00, 0x15, 0x07, 0x02, 0x00, 0x13, 0x06, 0x02, 0x00, 0x15, 0x05, 0x02,\n\t0x00, 0x13, 0x04, 0x02, 0x00, 0x15, 0x03, 0x02, 0x00, 0x15, 0x02, 0x02, 0x00, 0x13, 0x01, 0x02, 0x00, 0x15, 0x00, 0x02, 0x00, 0x22, 0xFF, 0x3A, 0x02, 0x00, 0x15, 0xFE, 0x02, 0x00, 0x13, 0xFD,\n\t0x02, 0x00, 0x15, 0xFC, 0x02, 0x00, 0x15, 0xFB, 0x02, 0x00, 0x13, 0xFA, 0x02, 0x00, 0x15, 0xF9, 0x02, 0x00, 0x13, 0xF8, 0x02, 0x00, 0x15, 0xF7, 0x02, 0x00, 0x15, 0xF6, 0x02, 0x00, 0x13, 0xF5,\n\t0x02, 0x00, 0x15, 0xF4, 0x02, 0x00, 0x13, 0xF3, 0x02, 0x00, 0x15, 0xF2, 0x02, 0x00, 0x15, 0xF1, 0x02, 0x00, 0x13, 0xF0, 0x02, 0x00, 0x15, 0xEF, 0x02, 0x00, 0x13, 0xEE, 0x02, 0x00, 0x15, 0xED,\n\t0x02, 0x00, 0x15, 0xEC, 0x02, 0x00, 0x13, 0xEB, 0x02, 0x00, 0x15, 0xEA, 0x02, 0x00, 0x15, 0xE9, 0x02, 0x00, 0x13, 0xE8, 0x02, 0x00, 0x15, 0xE7, 0x02, 0x00, 0x13, 0xE6, 0x02, 0x00, 0x15, 0xE5,\n\t0x02, 0x00, 0x15, 0xE4, 0x02, 0x00, 0x13, 0xE3, 0x02, 0x00, 0x15, 0xE2, 0x02, 0x00, 0x15, 0xE1, 0x02, 0x00, 0x13, 0xE0, 0x02, 0x00, 0x15, 0xDF, 0x02, 0x00, 0x15, 0xDE, 0x02, 0x00, 0x13, 0xDD,\n\t0x02, 0x00, 0x15, 0xDC, 0x02, 0x00, 0x15, 0xDB, 0x02, 0x00, 0x13, 0xDA, 0x02, 0x00, 0x15, 0xD9, 0x02, 0x00, 0x15, 0xD8, 0x02, 0x00, 0x13, 0xD7, 0x02, 0x00, 0x15, 0xD6, 0x02, 0x00, 0x15, 0xD5,\n\t0x02, 0x00, 0x13, 0xD4, 0x02, 0x00, 0x15, 0xD3, 0x02, 0x00, 0x15, 0xD2, 0x02, 0x00, 0x13, 0xD1, 0x02, 0x00, 0x15, 0xD0, 0x02, 0x00, 0x15, 0xCF, 0x02, 0x00, 0x15, 0xCE, 0x02, 0x00, 0x13, 0xCD,\n\t0x02, 0x00, 0x15, 0xCC, 0x02, 0x00, 0x15, 0xCB, 0x02, 0x00, 0x13, 0xCA, 0x02, 0x00, 0x15, 0xC9, 0x02, 0x00, 0x15, 0xC8, 0x02, 0x00, 0x15, 0xC7, 0x02, 0x00, 0x13, 0xC6, 0x02, 0x00, 0x15, 0xC5,\n\t0x02, 0x00, 0x15, 0xC4, 0x02, 0x00, 0x13, 0xC3, 0x02, 0x00, 0x15, 0xC2, 0x02, 0x00, 0x15, 0xC1, 0x02, 0x00, 0x15, 0xC0, 0x02, 0x00, 0x13, 0xBF, 0x02, 0x00, 0x15, 0xBE, 0x02, 0x00, 0x15, 0xBD,\n\t0x02, 0x00, 0x15, 0xBC, 0x02, 0x00, 0x13, 0xBB, 0x02, 0x00, 0x15, 0xBA, 0x02, 0x00, 0x15, 0xB9, 0x02, 0x00, 0x15, 0xB8, 0x02, 0x00, 0x13, 0xB7, 0x02, 0x00, 0x15, 0xB6, 0x02, 0x00, 0x15, 0xB5,\n\t0x02, 0x00, 0x15, 0xB4, 0x02, 0x00, 0x15, 0xB3, 0x02, 0x00, 0x13, 0xB2, 0x02, 0x00, 0x15, 0xB1, 0x02, 0x00, 0x15, 0xB0, 0x02, 0x00, 0x15, 0xAF, 0x02, 0x00, 0x13, 0xAE, 0x02, 0x00, 0x15, 0xAD,\n\t0x02, 0x00, 0x15, 0xAC, 0x02, 0x00, 0x15, 0xAB, 0x02, 0x00, 0x15, 0xAA, 0x02, 0x00, 0x13, 0xA9, 0x02, 0x00, 0x15, 0xA8, 0x02, 0x00, 0x15, 0xA7, 0x02, 0x00, 0x15, 0xA6, 0x02, 0x00, 0x15, 0xA5,\n\t0x02, 0x00, 0x15, 0xA4, 0x02, 0x00, 0x13, 0xA3, 0x02, 0x00, 0x15, 0xA2, 0x02, 0x00, 0x15, 0xA1, 0x02, 0x00, 0x15, 0xA0, 0x02, 0x00, 0x15, 0x9F, 0x02, 0x00, 0x15, 0x9E, 0x02, 0x00, 0x13, 0x9D,\n\t0x02, 0x00, 0x15, 0x9C, 0x02, 0x00, 0x15, 0x9B, 0x02, 0x00, 0x15, 0x9A, 0x02, 0x00, 0x15, 0x99, 0x02, 0x00, 0x15, 0x98, 0x02, 0x00, 0x13, 0x97, 0x02, 0x00, 0x15, 0x96, 0x02, 0x00, 0x15, 0x95,\n\t0x02, 0x00, 0x15, 0x94, 0x02, 0x00, 0x15, 0x93, 0x02, 0x00, 0x15, 0x92, 0x02, 0x00, 0x15, 0x91, 0x02, 0x00, 0x15, 0x90, 0x02, 0x00, 0x13, 0x8F, 0x02, 0x00, 0x15, 0x8E, 0x02, 0x00, 0x15, 0x8D,\n\t0x02, 0x00, 0x15, 0x8C, 0x02, 0x00, 0x15, 0x8B, 0x02, 0x00, 0x15, 0x8A, 0x02, 0x00, 0x15, 0x89, 0x02, 0x00, 0x15, 0x88, 0x02, 0x00, 0x15, 0x87, 0x02, 0x00, 0x13, 0x86, 0x02, 0x00, 0x15, 0x85,\n\t0x02, 0x00, 0x15, 0x84, 0x02, 0x00, 0x15, 0x83, 0x02, 0x00, 0x15, 0x82, 0x02, 0x00, 0x15, 0x81, 0x02, 0x00, 0x15, 0x80, 0x02, 0x00, 0x15, 0x7F, 0x02, 0x00, 0x15, 0x7E, 0x02, 0x00, 0x15, 0x7D,\n\t0x02, 0x00, 0x15, 0x7C, 0x02, 0x00, 0x15, 0x7B, 0x02, 0x00, 0x15, 0x7A, 0x02, 0x00, 0x13, 0x79, 0x02, 0x00, 0x15, 0x78, 0x02, 0x00, 0x15, 0x77, 0x02, 0x00, 0x15, 0x76, 0x02, 0x00, 0x15, 0x75,\n\t0x02, 0x00, 0x15, 0x74, 0x02, 0x00, 0x15, 0x73, 0x02, 0x00, 0x15, 0x72, 0x02, 0x00, 0x15, 0x71, 0x02, 0x00, 0x15, 0x70, 0x02, 0x00, 0x15, 0x6F, 0x02, 0x00, 0x15, 0x6E, 0x02, 0x00, 0x15, 0x6D,\n\t0x02, 0x00, 0x15, 0x6C, 0x02, 0x00, 0x15, 0x6B, 0x02, 0x00, 0x15, 0x6A, 0x02, 0x00, 0x15, 0x69, 0x02, 0x00, 0x15, 0x68, 0x02, 0x00, 0x15, 0x67, 0x02, 0x00, 0x15, 0x66, 0x02, 0x00, 0x15, 0x65,\n\t0x02, 0x00, 0x15, 0x64, 0x02, 0x00, 0x15, 0x63, 0x02, 0x00, 0x15, 0x62, 0x02, 0x00, 0x15, 0x61, 0x02, 0x00, 0x15, 0x60, 0x02, 0x00, 0x15, 0x5F, 0x02, 0x00, 0x15, 0x5E, 0x02, 0x00, 0x15, 0x5D,\n\t0x02, 0x00, 0x15, 0x5C, 0x02, 0x00, 0x15, 0x5B, 0x02, 0x00, 0x15, 0x5A, 0x02, 0x00, 0x15, 0x59, 0x02, 0x00, 0x15, 0x58, 0x02, 0x00, 0x15, 0x57, 0x02, 0x00, 0x15, 0x56, 0x02, 0x00, 0x15, 0x55,\n\t0x02, 0x00, 0x17, 0x54, 0x02, 0x00, 0x15, 0x53, 0x02, 0x00, 0x15, 0x52, 0x02, 0x00, 0x15, 0x51, 0x02, 0x00, 0x15, 0x50, 0x02, 0x00, 0x15, 0x4F, 0x02, 0x00, 0x15, 0x4E, 0x02, 0x00, 0x15, 0x4D,\n\t0x02, 0x00, 0x15, 0x4C, 0x02, 0x00, 0x15, 0x4B, 0x02, 0x00, 0x15, 0x4A, 0x02, 0x00, 0x15, 0x49, 0x02, 0x00, 0x15, 0x48, 0x02, 0x00, 0x17, 0x47, 0x02, 0x00, 0x15, 0x46, 0x02, 0x00, 0x15, 0x45,\n\t0x02, 0x00, 0x15, 0x44, 0x02, 0x00, 0x15, 0x43, 0x02, 0x00, 0x15, 0x42, 0x02, 0x00, 0x15, 0x41, 0x02, 0x00, 0x15, 0x40, 0x02, 0x00, 0x15, 0x3F, 0x02, 0x00, 0x17, 0x3E, 0x02, 0x00, 0x15, 0x3D,\n\t0x02, 0x00, 0x15, 0x3C, 0x02, 0x00, 0x04, 0xF7, 0x0A, 0x00, 0x01, 0x00, 0x11, 0x39, 0x02, 0x00, 0x51, 0x38, 0x3A, 0x38, 0x3A, 0x37, 0x02, 0x00, 0x11, 0x36, 0x02, 0x00, 0x51, 0x35, 0x3A, 0x35,\n\t0x3A, 0x34, 0x02, 0x00, 0x51, 0x33, 0x3A, 0x33, 0x3A, 0x32, 0x02, 0x00, 0x11, 0x31, 0x02, 0x00, 0x51, 0x30, 0x3A, 0x30, 0x3A, 0x2F, 0x02, 0x00, 0x51, 0x2E, 0x3A, 0x2E, 0x3A, 0x2D, 0x02, 0x00,\n\t0x51, 0x2C, 0x3A, 0x2C, 0x3A, 0x2B, 0x02, 0x00, 0x11, 0x2A, 0x02, 0x00, 0x51, 0x29, 0x3A, 0x29, 0x3A, 0x28, 0x02, 0x00, 0x51, 0x27, 0x3A, 0x27, 0x3A, 0x26, 0x02, 0x00, 0x11, 0x25, 0x02, 0x00,\n\t0x51, 0x24, 0x3A, 0x24, 0x3A, 0x23, 0x02, 0x00, 0x51, 0x22, 0x3A, 0x22, 0x3A, 0x21, 0x02, 0x00, 0x11, 0x20, 0x02, 0x00, 0x51, 0x1F, 0x3A, 0x1F, 0x3A, 0x1E, 0x02, 0x00, 0x11, 0x1D, 0x02, 0x00,\n\t0x51, 0x1C, 0x3A, 0x1C, 0x3A, 0x1B, 0x02, 0x00, 0x51, 0x1A, 0x3A, 0x1A, 0x3A, 0x19, 0x02, 0x00, 0x11, 0x18, 0x02, 0x00, 0x51, 0x17, 0x3A, 0x17, 0x3A, 0x16, 0x02, 0x00, 0x11, 0x15, 0x02, 0x00,\n\t0x51, 0x14, 0x3A, 0x14, 0x3A, 0x13, 0x02, 0x00, 0x51, 0x12, 0x3A, 0x12, 0x3A, 0x11, 0x02, 0x00, 0x11, 0x10, 0x02, 0x00, 0x51, 0x0F, 0x3A, 0x0F, 0x3A, 0x0E, 0x02, 0x00, 0x11, 0x0D, 0x02, 0x00,\n\t0x51, 0x0C, 0x3A, 0x0C, 0x3A, 0x0B, 0x02, 0x00, 0x11, 0x0A, 0x02, 0x00, 0x51, 0x09, 0x3A, 0x09, 0x3A, 0x08, 0x02, 0x00, 0x11, 0x07, 0x02, 0x00, 0x51, 0x06, 0x3A, 0x06, 0x3A, 0x05, 0x02, 0x00,\n\t0x11, 0x04, 0x02, 0x00, 0x51, 0x03, 0x3A, 0x03, 0x3A, 0x02, 0x02, 0x00, 0x11, 0x01, 0x02, 0x00, 0x60, 0x00, 0x3A, 0x00, 0x3A, 0xFF, 0x39, 0x02, 0x00, 0x11, 0xFE, 0x02, 0x00, 0x51, 0xFD, 0x39,\n\t0xFD, 0x39, 0xFC, 0x02, 0x00, 0x11, 0xFB, 0x02, 0x00, 0x51, 0xFA, 0x39, 0xFA, 0x39, 0xF9, 0x02, 0x00, 0x11, 0xF8, 0x02, 0x00, 0x51, 0xF7, 0x39, 0xF7, 0x39, 0xF6, 0x02, 0x00, 0x11, 0xF5, 0x02,\n\t0x00, 0x51, 0xF4, 0x39, 0xF4, 0x39, 0xF3, 0x02, 0x00, 0x11, 0xF2, 0x02, 0x00, 0x51, 0xF1, 0x39, 0xF1, 0x39, 0xF0, 0x02, 0x00, 0x11, 0xEF, 0x02, 0x00, 0x51, 0xEE, 0x39, 0xEE, 0x39, 0xED, 0x02,\n\t0x00, 0x11, 0xEC, 0x02, 0x00, 0x11, 0xEB, 0x02, 0x00, 0x51, 0xEA, 0x39, 0xEA, 0x39, 0xE9, 0x02, 0x00, 0x11, 0xE8, 0x02, 0x00, 0x51, 0xE7, 0x39, 0xE7, 0x39, 0xE6, 0x02, 0x00, 0x11, 0xE5, 0x02,\n\t0x00, 0x11, 0xE4, 0x02, 0x00, 0x51, 0xE3, 0x39, 0xE3, 0x39, 0xE2, 0x02, 0x00, 0x11, 0xE1, 0x02, 0x00, 0x51, 0xE0, 0x39, 0xE0, 0x39, 0xDF, 0x02, 0x00, 0x11, 0xDE, 0x02, 0x00, 0x11, 0xDD, 0x02,\n\t0x00, 0x51, 0xDC, 0x39, 0xDC, 0x39, 0xDB, 0x02, 0x00, 0x11, 0xDA, 0x02, 0x00, 0x11, 0xD9, 0x02, 0x00, 0x51, 0xD8, 0x39, 0xD8, 0x39, 0xD7, 0x02, 0x00, 0x11, 0xD6, 0x02, 0x00, 0x11, 0xD5, 0x02,\n\t0x00, 0x51, 0xD4, 0x39, 0xD4, 0x39, 0xD3, 0x02, 0x00, 0x11, 0xD2, 0x02, 0x00, 0x11, 0xD1, 0x02, 0x00, 0x51, 0xD0, 0x39, 0xD0, 0x39, 0xCF, 0x02, 0x00, 0x11, 0xCE, 0x02, 0x00, 0x11, 0xCD, 0x02,\n\t0x00, 0x51, 0xCC, 0x39, 0xCC, 0x39, 0xCB, 0x02, 0x00, 0x11, 0xCA, 0x02, 0x00, 0x11, 0xC9, 0x02, 0x00, 0x51, 0xC8, 0x39, 0xC8, 0x39, 0xC7, 0x02, 0x00, 0x11, 0xC6, 0x02, 0x00, 0x11, 0xC5, 0x02,\n\t0x00, 0x51, 0xC4, 0x39, 0xC4, 0x39, 0xC3, 0x02, 0x00, 0x11, 0xC2, 0x02, 0x00, 0x11, 0xC1, 0x02, 0x00, 0x11, 0xC0, 0x02, 0x00, 0x51, 0xBF, 0x39, 0xBF, 0x39, 0xBE, 0x02, 0x00, 0x11, 0xBD, 0x02,\n\t0x00, 0x11, 0xBC, 0x02, 0x00, 0x11, 0xBB, 0x02, 0x00, 0x51, 0xBA, 0x39, 0xBA, 0x39, 0xB9, 0x02, 0x00, 0x11, 0xB8, 0x02, 0x00, 0x11, 0xB7, 0x02, 0x00, 0x51, 0xB6, 0x39, 0xB6, 0x39, 0xB5, 0x02,\n\t0x00, 0x11, 0xB4, 0x02, 0x00, 0x11, 0xB3, 0x02, 0x00, 0x11, 0xB2, 0x02, 0x00, 0x11, 0xB1, 0x02, 0x00, 0x51, 0xB0, 0x39, 0xB0, 0x39, 0xAF, 0x02, 0x00, 0x11, 0xAE, 0x02, 0x00, 0x11, 0xAD, 0x02,\n\t0x00, 0x11, 0xAC, 0x02, 0x00, 0x51, 0xAB, 0x39, 0xAB, 0x39, 0xAA, 0x02, 0x00, 0x11, 0xA9, 0x02, 0x00, 0x11, 0xA8, 0x02, 0x00, 0x11, 0xA7, 0x02, 0x00, 0x11, 0xA6, 0x02, 0x00, 0x51, 0xA5, 0x39,\n\t0xA5, 0x39, 0xA4, 0x02, 0x00, 0x11, 0xA3, 0x02, 0x00, 0x11, 0xA2, 0x02, 0x00, 0x11, 0xA1, 0x02, 0x00, 0x11, 0xA0, 0x02, 0x00, 0x51, 0x9F, 0x39, 0x9F, 0x39, 0x9E, 0x02, 0x00, 0x11, 0x9D, 0x02,\n\t0x00, 0x11, 0x9C, 0x02, 0x00, 0x11, 0x9B, 0x02, 0x00, 0x11, 0x9A, 0x02, 0x00, 0x11, 0x99, 0x02, 0x00, 0x51, 0x98, 0x39, 0x98, 0x39, 0x97, 0x02, 0x00, 0x11, 0x96, 0x02, 0x00, 0x11, 0x95, 0x02,\n\t0x00, 0x11, 0x94, 0x02, 0x00, 0x11, 0x93, 0x02, 0x00, 0x11, 0x92, 0x02, 0x00, 0x11, 0x91, 0x02, 0x00, 0x51, 0x90, 0x39, 0x90, 0x39, 0x8F, 0x02, 0x00, 0x11, 0x8E, 0x02, 0x00, 0x11, 0x8D, 0x02,\n\t0x00, 0x11, 0x8C, 0x02, 0x00, 0x11, 0x8B, 0x02, 0x00, 0x11, 0x8A, 0x02, 0x00, 0x11, 0x89, 0x02, 0x00, 0x51, 0x88, 0x39, 0x88, 0x39, 0x87, 0x02, 0x00, 0x11, 0x86, 0x02, 0x00, 0x11, 0x85, 0x02,\n\t0x00, 0x11, 0x84, 0x02, 0x00, 0x11, 0x83, 0x02, 0x00, 0x11, 0x82, 0x02, 0x00, 0x11, 0x81, 0x02, 0x00, 0x11, 0x80, 0x02, 0x00, 0x11, 0x7F, 0x02, 0x00, 0x11, 0x7E, 0x02, 0x00, 0x51, 0x7D, 0x39,\n\t0x7D, 0x39, 0x7C, 0x02, 0x00, 0x11, 0x7B, 0x02, 0x00, 0x11, 0x7A, 0x02, 0x00, 0x11, 0x79, 0x02, 0x00, 0x11, 0x78, 0x02, 0x00, 0x11, 0x77, 0x02, 0x00, 0x11, 0x76, 0x02, 0x00, 0x11, 0x75, 0x02,\n\t0x00, 0x11, 0x74, 0x02, 0x00, 0x11, 0x73, 0x02, 0x00, 0x11, 0x72, 0x02, 0x00, 0x11, 0x71, 0x02, 0x00, 0x11, 0x70, 0x02, 0x00, 0x51, 0x6F, 0x39, 0x6F, 0x39, 0x6E, 0x02, 0x00, 0x11, 0x6D, 0x02,\n\t0x00, 0x11, 0x6C, 0x02, 0x00, 0x11, 0x6B, 0x02, 0x00, 0x11, 0x6A, 0x02, 0x00, 0x11, 0x69, 0x02, 0x00, 0x11, 0x68, 0x02, 0x00, 0x11, 0x67, 0x02, 0x00, 0x11, 0x66, 0x02, 0x00, 0x11, 0x65, 0x02,\n\t0x00, 0x11, 0x64, 0x02, 0x00, 0x11, 0x63, 0x02, 0x00, 0x11, 0x62, 0x02, 0x00, 0x11, 0x61, 0x02, 0x00, 0x11, 0x60, 0x02, 0x00, 0x11, 0x5F, 0x02, 0x00, 0x11, 0x5E, 0x02, 0x00, 0x11, 0x5D, 0x02,\n\t0x00, 0x11, 0x5C, 0x02, 0x00, 0x11, 0x5B, 0x02, 0x00, 0x11, 0x5A, 0x02, 0x00, 0x11, 0x59, 0x02, 0x00, 0x11, 0x58, 0x02, 0x00, 0x11, 0x57, 0x02, 0x00, 0x11, 0x56, 0x02, 0x00, 0x11, 0x55, 0x02,\n\t0x00, 0x11, 0x54, 0x02, 0x00, 0x11, 0x53, 0x02, 0x00, 0x11, 0x52, 0x02, 0x00, 0x11, 0x51, 0x02, 0x00, 0x11, 0x50, 0x02, 0x00, 0x11, 0x4F, 0x02, 0x00, 0x11, 0x4E, 0x02, 0x00, 0x11, 0x4D, 0x02,\n\t0x00, 0x11, 0x4C, 0x02, 0x00, 0x11, 0x4B, 0x02, 0x00, 0x11, 0x4A, 0x02, 0x00, 0x11, 0x49, 0x02, 0x00, 0x11, 0x48, 0x02, 0x00, 0x11, 0x47, 0x02, 0x00, 0x11, 0x46, 0x02, 0x00, 0x11, 0x45, 0x02,\n\t0x00, 0x11, 0x44, 0x02, 0x00, 0x11, 0x43, 0x02, 0x00, 0x11, 0x42, 0x02, 0x00, 0x11, 0x41, 0x02, 0x00, 0x11, 0x40, 0x02, 0x00, 0x11, 0x3F, 0x02, 0x00, 0x02, 0x63, 0xFF, 0x11, 0x3D, 0x02, 0x00,\n\t0x13, 0x3C, 0x02, 0x00, 0x02, 0x7F, 0x10, 0x02, 0x97, 0x05, 0x02, 0x01, 0x00, 0x11, 0x38, 0x02, 0x00, 0x11, 0x37, 0x02, 0x00, 0x11, 0x36, 0x02, 0x00, 0x11, 0x35, 0x02, 0x00, 0x11, 0x34, 0x02,\n\t0x00, 0x11, 0x33, 0x02, 0x00, 0x11, 0x32, 0x02, 0x00, 0x11, 0x31, 0x02, 0x00, 0x11, 0x30, 0x02, 0x00, 0x11, 0x2F, 0x02, 0x00, 0x13, 0x2E, 0x02, 0x00, 0x11, 0x2D, 0x02, 0x00, 0x11, 0x2C, 0x02,\n\t0x00, 0x11, 0x2B, 0x02, 0x00, 0x11, 0x2A, 0x02, 0x00, 0x11, 0x29, 0x02, 0x00, 0x11, 0x28, 0x02, 0x00, 0x11, 0x27, 0x02, 0x00, 0x11, 0x26, 0x02, 0x00, 0x11, 0x25, 0x02, 0x00, 0x13, 0x24, 0x02,\n\t0x00, 0x11, 0x23, 0x02, 0x00, 0x11, 0x22, 0x02, 0x00, 0x11, 0x21, 0x02, 0x00, 0x11, 0x20, 0x02, 0x00, 0x11, 0x1F, 0x02, 0x00, 0x11, 0x1E, 0x02, 0x00, 0x11, 0x1D, 0x02, 0x00, 0x13, 0x1C, 0x02,\n\t0x00, 0x11, 0x1B, 0x02, 0x00, 0x11, 0x1A, 0x02, 0x00, 0x11, 0x19, 0x02, 0x00, 0x11, 0x18, 0x02, 0x00, 0x11, 0x17, 0x02, 0x00, 0x11, 0x16, 0x02, 0x00, 0x13, 0x15, 0x02, 0x00, 0x11, 0x14, 0x02,\n\t0x00, 0x11, 0x13, 0x02, 0x00, 0x11, 0x12, 0x02, 0x00, 0x11, 0x11, 0x02, 0x00, 0x11, 0x10, 0x02, 0x00, 0x11, 0x0F, 0x02, 0x00, 0x13, 0x0E, 0x02, 0x00, 0x11, 0x0D, 0x02, 0x00, 0x11, 0x0C, 0x02,\n\t0x00, 0x11, 0x0B, 0x02, 0x00, 0x11, 0x0A, 0x02, 0x00, 0x13, 0x09, 0x02, 0x00, 0x11, 0x08, 0x02, 0x00, 0x11, 0x07, 0x02, 0x00, 0x11, 0x06, 0x02, 0x00, 0x11, 0x05, 0x02, 0x00, 0x11, 0x04, 0x02,\n\t0x00, 0x13, 0x03, 0x02, 0x00, 0x11, 0x02, 0x02, 0x00, 0x11, 0x01, 0x02, 0x00, 0x11, 0x00, 0x02, 0x00, 0x20, 0xFF, 0x38, 0x02, 0x00, 0x13, 0xFE, 0x02, 0x00, 0x11, 0xFD, 0x02, 0x00, 0x11, 0xFC,\n\t0x02, 0x00, 0x11, 0xFB, 0x02, 0x00, 0x11, 0xFA, 0x02, 0x00, 0x13, 0xF9, 0x02, 0x00, 0x11, 0xF8, 0x02, 0x00, 0x11, 0xF7, 0x02, 0x00, 0x11, 0xF6, 0x02, 0x00, 0x13, 0xF5, 0x02, 0x00, 0x11, 0xF4,\n\t0x02, 0x00, 0x11, 0xF3, 0x02, 0x00, 0x11, 0xF2, 0x02, 0x00, 0x13, 0xF1, 0x02, 0x00, 0x11, 0xF0, 0x02, 0x00, 0x11, 0xEF, 0x02, 0x00, 0x11, 0xEE, 0x02, 0x00, 0x13, 0xED, 0x02, 0x00, 0x11, 0xEC,\n\t0x02, 0x00, 0x11, 0xEB, 0x02, 0x00, 0x11, 0xEA, 0x02, 0x00, 0x13, 0xE9, 0x02, 0x00, 0x11, 0xE8, 0x02, 0x00, 0x11, 0xE7, 0x02, 0x00, 0x11, 0xE6, 0x02, 0x00, 0x13, 0xE5, 0x02, 0x00, 0x11, 0xE4,\n\t0x02, 0x00, 0x11, 0xE3, 0x02, 0x00, 0x11, 0xE2, 0x02, 0x00, 0x13, 0xE1, 0x02, 0x00, 0x11, 0xE0, 0x02, 0x00, 0x11, 0xDF, 0x02, 0x00, 0x13, 0xDE, 0x02, 0x00, 0x11, 0xDD, 0x02, 0x00, 0x11, 0xDC,\n\t0x02, 0x00, 0x11, 0xDB, 0x02, 0x00, 0x11, 0xDA, 0x02, 0x00, 0xF0, 0xFF, 0xFF, 0x09, 0xD9, 0x38, 0xD8, 0x38, 0xD8, 0x38, 0xD7, 0x38, 0xD7, 0x38, 0xD6, 0x38, 0xD5, 0x38, 0xD5, 0x38, 0xD4, 0x38,\n\t0xD4, 0x38, 0xD3, 0x38, 0xD2, 0x38, 0xD2, 0x38, 0xD1, 0x38, 0xD1, 0x38, 0xD0, 0x38, 0xCF, 0x38, 0xCF, 0x38, 0xCE, 0x38, 0xCE, 0x38, 0xCD, 0x38, 0xCC, 0x38, 0xCC, 0x38, 0xCB, 0x38, 0xCB, 0x38,\n\t0xCA, 0x38, 0xC9, 0x38, 0xC9, 0x38, 0xC8, 0x38, 0xC8, 0x38, 0xC7, 0x38, 0xC6, 0x38, 0xC6, 0x38, 0xC5, 0x38, 0xC5, 0x38, 0xC4, 0x38, 0xC3, 0x38, 0xC3, 0x38, 0xC2, 0x38, 0xC2, 0x38, 0xC1, 0x38,\n\t0xC0, 0x38, 0xC0, 0x38, 0xBF, 0x38, 0xBF, 0x38, 0xBE, 0x38, 0xBD, 0x38, 0xBD, 0x38, 0xBC, 0x38, 0xBC, 0x38, 0xBB, 0x38, 0xBA, 0x38, 0xBA, 0x38, 0xB9, 0x38, 0xB9, 0x38, 0xB8, 0x38, 0xB7, 0x38,\n\t0xB7, 0x38, 0xB6, 0x38, 0xB6, 0x38, 0xB5, 0x38, 0xB5, 0x38, 0xB4, 0x38, 0xB3, 0x38, 0xB3, 0x38, 0xB2, 0x38, 0xB2, 0x38, 0xB1, 0x38, 0xB0, 0x38, 0xB0, 0x38, 0xAF, 0x38, 0xAF, 0x38, 0xAE, 0x38,\n\t0xAE, 0x38, 0xAD, 0x38, 0xAC, 0x38, 0xAC, 0x38, 0xAB, 0x38, 0xAB, 0x38, 0xAA, 0x38, 0xA9, 0x38, 0xA9, 0x38, 0xA8, 0x38, 0xA8, 0x38, 0xA7, 0x38, 0xA7, 0x38, 0xA6, 0x38, 0xA5, 0x38, 0xA5, 0x38,\n\t0xA4, 0x38, 0xA4, 0x38, 0xA3, 0x38, 0xA2, 0x38, 0xA2, 0x38, 0xA1, 0x38, 0xA1, 0x38, 0xA0, 0x38, 0xA0, 0x38, 0x9F, 0x38, 0x9E, 0x38, 0x9E, 0x38, 0x9D, 0x38, 0x9D, 0x38, 0x9C, 0x38, 0x9C, 0x38,\n\t0x9B, 0x38, 0x9A, 0x38, 0x9A, 0x38, 0x99, 0x38, 0x99, 0x38, 0x98, 0x38, 0x97, 0x38, 0x97, 0x38, 0x96, 0x38, 0x96, 0x38, 0x95, 0x38, 0x95, 0x38, 0x94, 0x38, 0x93, 0x38, 0x93, 0x38, 0x92, 0x38,\n\t0x92, 0x38, 0x91, 0x38, 0x91, 0x38, 0x90, 0x38, 0x8F, 0x38, 0x8F, 0x38, 0x8E, 0x38, 0x8E, 0x38, 0x8D, 0x38, 0x8D, 0x38, 0x8C, 0x38, 0x8C, 0x38, 0x8B, 0x38, 0x8A, 0x38, 0x8A, 0x38, 0x89, 0x38,\n\t0x89, 0x38, 0x88, 0x38, 0x88, 0x38, 0x87, 0x38, 0x86, 0x38, 0x86, 0x38, 0x85, 0x38, 0x85, 0x38, 0x84, 0x38, 0x84, 0x38, 0x83, 0x38, 0x82, 0x38, 0x82, 0x38, 0x81, 0x38, 0x81, 0x38, 0x80, 0x38,\n\t0x80, 0x38, 0x7F, 0x38, 0x7F, 0x38, 0x7E, 0x38, 0x7D, 0x38, 0x7D, 0x38, 0x7C, 0x38, 0x7C, 0x38, 0x7B, 0x38, 0x7B, 0x38, 0x7A, 0x38, 0x79, 0x38, 0x79, 0x38, 0x78, 0x38, 0x78, 0x38, 0x77, 0x38,\n\t0x77, 0x38, 0x76, 0x38, 0x76, 0x38, 0x75, 0x38, 0x74, 0x38, 0x74, 0x38, 0x73, 0x38, 0x73, 0x38, 0x72, 0x38, 0x72, 0x38, 0x71, 0x38, 0x71, 0x38, 0x70, 0x38, 0x6F, 0x38, 0x6F, 0x38, 0x6E, 0x38,\n\t0x6E, 0x38, 0x6D, 0x38, 0x6D, 0x38, 0x6C, 0x38, 0x6C, 0x38, 0x6B, 0x38, 0x6A, 0x38, 0x6A, 0x38, 0x69, 0x38, 0x69, 0x38, 0x68, 0x38, 0x68, 0x38, 0x67, 0x38, 0x67, 0x38, 0x66, 0x38, 0x66, 0x38,\n\t0x65, 0x38, 0x64, 0x38, 0x64, 0x38, 0x63, 0x38, 0x63, 0x38, 0x62, 0x38, 0x62, 0x38, 0x61, 0x38, 0x61, 0x38, 0x60, 0x38, 0x5F, 0x38, 0x5F, 0x38, 0x5E, 0x38, 0x5E, 0x38, 0x5D, 0x38, 0x5D, 0x38,\n\t0x5C, 0x38, 0x5C, 0x38, 0x5B, 0x38, 0x5B, 0x38, 0x5A, 0x38, 0x59, 0x38, 0x59, 0x38, 0x58, 0x38, 0x58, 0x38, 0x57, 0x38, 0x57, 0x38, 0x56, 0x38, 0x56, 0x38, 0x55, 0x38, 0x55, 0x38, 0x54, 0x38,\n\t0x54, 0x38, 0x53, 0x38, 0x52, 0x38, 0x52, 0x38, 0x51, 0x38, 0x51, 0x38, 0x50, 0x38, 0x50, 0x38, 0x4F, 0x38, 0x4F, 0x38, 0x4E, 0x38, 0x4E, 0x38, 0x4D, 0x38, 0x4D, 0x38, 0x4C, 0x38, 0x4B, 0x38,\n\t0x4B, 0x38, 0x4A, 0x38, 0x4A, 0x38, 0x49, 0x38, 0x49, 0x38, 0x48, 0x38, 0x48, 0x38, 0x47, 0x38, 0x47, 0x38, 0x46, 0x38, 0x46, 0x38, 0x45, 0x38, 0x44, 0x38, 0x44, 0x38, 0x43, 0x38, 0x43, 0x38,\n\t0x42, 0x38, 0x42, 0x38, 0xC3, 0xFA, 0x00, 0x2F, 0xFE, 0xD1, 0x3F, 0x38, 0x3F, 0x38, 0x3E, 0x38, 0x3E, 0x38, 0x3D, 0x38, 0x3D, 0x38, 0x3C, 0x03, 0x15, 0x00, 0x27, 0x0A, 0x00, 0x8D, 0x04, 0x00,\n\t0x01, 0x00, 0xF1, 0xFF, 0xFF, 0xFF, 0xFF, 0xCA, 0x37, 0x38, 0x37, 0x38, 0x36, 0x38, 0x36, 0x38, 0x35, 0x38, 0x35, 0x38, 0x34, 0x38, 0x34, 0x38, 0x33, 0x38, 0x32, 0x38, 0x32, 0x38, 0x31, 0x38,\n\t0x31, 0x38, 0x30, 0x38, 0x30, 0x38, 0x2F, 0x38, 0x2F, 0x38, 0x2E, 0x38, 0x2E, 0x38, 0x2D, 0x38, 0x2D, 0x38, 0x2C, 0x38, 0x2C, 0x38, 0x2B, 0x38, 0x2B, 0x38, 0x2A, 0x38, 0x2A, 0x38, 0x29, 0x38,\n\t0x29, 0x38, 0x28, 0x38, 0x28, 0x38, 0x27, 0x38, 0x26, 0x38, 0x26, 0x38, 0x25, 0x38, 0x25, 0x38, 0x24, 0x38, 0x24, 0x38, 0x23, 0x38, 0x23, 0x38, 0x22, 0x38, 0x22, 0x38, 0x21, 0x38, 0x21, 0x38,\n\t0x20, 0x38, 0x20, 0x38, 0x1F, 0x38, 0x1F, 0x38, 0x1E, 0x38, 0x1E, 0x38, 0x1D, 0x38, 0x1D, 0x38, 0x1C, 0x38, 0x1C, 0x38, 0x1B, 0x38, 0x1B, 0x38, 0x1A, 0x38, 0x1A, 0x38, 0x19, 0x38, 0x19, 0x38,\n\t0x18, 0x38, 0x18, 0x38, 0x17, 0x38, 0x17, 0x38, 0x16, 0x38, 0x16, 0x38, 0x15, 0x38, 0x14, 0x38, 0x14, 0x38, 0x13, 0x38, 0x13, 0x38, 0x12, 0x38, 0x12, 0x38, 0x11, 0x38, 0x11, 0x38, 0x10, 0x38,\n\t0x10, 0x38, 0x0F, 0x38, 0x0F, 0x38, 0x0E, 0x38, 0x0E, 0x38, 0x0D, 0x38, 0x0D, 0x38, 0x0C, 0x38, 0x0C, 0x38, 0x0B, 0x38, 0x0B, 0x38, 0x0A, 0x38, 0x0A, 0x38, 0x09, 0x38, 0x09, 0x38, 0x08, 0x38,\n\t0x08, 0x38, 0x07, 0x38, 0x07, 0x38, 0x06, 0x38, 0x06, 0x38, 0x05, 0x38, 0x05, 0x38, 0x04, 0x38, 0x04, 0x38, 0x03, 0x38, 0x03, 0x38, 0x02, 0x38, 0x02, 0x38, 0x01, 0x38, 0x01, 0x38, 0x00, 0x38,\n\t0x00, 0x38, 0xFF, 0x37, 0xFE, 0x37, 0xFD, 0x37, 0xFC, 0x37, 0xFB, 0x37, 0xFA, 0x37, 0xF9, 0x37, 0xF8, 0x37, 0xF7, 0x37, 0xF6, 0x37, 0xF5, 0x37, 0xF4, 0x37, 0xF3, 0x37, 0xF2, 0x37, 0xF1, 0x37,\n\t0xF0, 0x37, 0xEF, 0x37, 0xEE, 0x37, 0xED, 0x37, 0xEC, 0x37, 0xEB, 0x37, 0xEA, 0x37, 0xE9, 0x37, 0xE8, 0x37, 0xE7, 0x37, 0xE6, 0x37, 0xE5, 0x37, 0xE4, 0x37, 0xE3, 0x37, 0xE2, 0x37, 0xE1, 0x37,\n\t0xE0, 0x37, 0xDF, 0x37, 0xDE, 0x37, 0xDD, 0x37, 0xDC, 0x37, 0xDB, 0x37, 0xDA, 0x37, 0xD9, 0x37, 0xD8, 0x37, 0xD7, 0x37, 0xD6, 0x37, 0xD5, 0x37, 0xD4, 0x37, 0xD3, 0x37, 0xD2, 0x37, 0xD1, 0x37,\n\t0xD0, 0x37, 0xCF, 0x37, 0xCE, 0x37, 0xCD, 0x37, 0xCC, 0x37, 0xCB, 0x37, 0xCA, 0x37, 0xC9, 0x37, 0xC8, 0x37, 0xC7, 0x37, 0xC6, 0x37, 0xC5, 0x37, 0xC4, 0x37, 0xC3, 0x37, 0xC3, 0x37, 0xC2, 0x37,\n\t0xC1, 0x37, 0xC0, 0x37, 0xBF, 0x37, 0xBE, 0x37, 0xBD, 0x37, 0xBC, 0x37, 0xBB, 0x37, 0xBA, 0x37, 0xB9, 0x37, 0xB8, 0x37, 0xB7, 0x37, 0xB6, 0x37, 0xB5, 0x37, 0xB4, 0x37, 0xB3, 0x37, 0xB2, 0x37,\n\t0xB1, 0x37, 0xB0, 0x37, 0xAF, 0x37, 0xAE, 0x37, 0xAD, 0x37, 0xAC, 0x37, 0xAB, 0x37, 0xAA, 0x37, 0xA9, 0x37, 0xA8, 0x37, 0xA8, 0x37, 0xA7, 0x37, 0xA6, 0x37, 0xA5, 0x37, 0xA4, 0x37, 0xA3, 0x37,\n\t0xA2, 0x37, 0xA1, 0x37, 0xA0, 0x37, 0x9F, 0x37, 0x9E, 0x37, 0x9D, 0x37, 0x9C, 0x37, 0x9B, 0x37, 0x9A, 0x37, 0x99, 0x37, 0x98, 0x37, 0x97, 0x37, 0x96, 0x37, 0x95, 0x37, 0x94, 0x37, 0x94, 0x37,\n\t0x93, 0x37, 0x92, 0x37, 0x91, 0x37, 0x90, 0x37, 0x8F, 0x37, 0x8E, 0x37, 0x8D, 0x37, 0x8C, 0x37, 0x8B, 0x37, 0x8A, 0x37, 0x89, 0x37, 0x88, 0x37, 0x87, 0x37, 0x86, 0x37, 0x85, 0x37, 0x84, 0x37,\n\t0x84, 0x37, 0x83, 0x37, 0x82, 0x37, 0x81, 0x37, 0x80, 0x37, 0x7F, 0x37, 0x7E, 0x37, 0x7D, 0x37, 0x7C, 0x37, 0x7B, 0x37, 0x7A, 0x37, 0x79, 0x37, 0x78, 0x37, 0x77, 0x37, 0x76, 0x37, 0x75, 0x37,\n\t0x75, 0x37, 0x74, 0x37, 0x73, 0x37, 0x72, 0x37, 0x71, 0x37, 0x70, 0x37, 0x6F, 0x37, 0x6E, 0x37, 0x6D, 0x37, 0x6C, 0x37, 0x6B, 0x37, 0x6A, 0x37, 0x69, 0x37, 0x68, 0x37, 0x68, 0x37, 0x67, 0x37,\n\t0x66, 0x37, 0x65, 0x37, 0x64, 0x37, 0x63, 0x37, 0x62, 0x37, 0x61, 0x37, 0x60, 0x37, 0x5F, 0x37, 0x5E, 0x37, 0x5D, 0x37, 0x5C, 0x37, 0x5C, 0x37, 0x5B, 0x37, 0x5A, 0x37, 0x59, 0x37, 0x58, 0x37,\n\t0x57, 0x37, 0x56, 0x37, 0x55, 0x37, 0x54, 0x37, 0x53, 0x37, 0x52, 0x37, 0x51, 0x37, 0x51, 0x37, 0x50, 0x37, 0x4F, 0x37, 0x4E, 0x37, 0x4D, 0x37, 0x4C, 0x37, 0x4B, 0x37, 0x4A, 0x37, 0x49, 0x37,\n\t0x48, 0x37, 0x47, 0x37, 0x47, 0x37, 0x46, 0x37, 0x45, 0x37, 0x44, 0x37, 0x43, 0x37, 0x42, 0x37, 0x41, 0x37, 0x40, 0x37, 0x3F, 0x37, 0x3E, 0x37, 0x3D, 0x37, 0x3D, 0x37, 0x3C, 0x37, 0x3B, 0x37,\n\t0x3A, 0x37, 0x39, 0x37, 0x38, 0x37, 0x37, 0x37, 0x36, 0x37, 0x35, 0x37, 0x34, 0x37, 0x34, 0x37, 0x33, 0x37, 0x32, 0x37, 0x31, 0x37, 0x30, 0x37, 0x2F, 0x37, 0x2E, 0x37, 0x2D, 0x37, 0x2C, 0x37,\n\t0x2B, 0x37, 0x2B, 0x37, 0x2A, 0x37, 0x29, 0x37, 0x28, 0x37, 0x27, 0x37, 0x26, 0x37, 0x25, 0x37, 0x24, 0x37, 0x23, 0x37, 0x22, 0x37, 0x22, 0x37, 0x21, 0x37, 0x20, 0x37, 0x1F, 0x37, 0x1E, 0x37,\n\t0x1D, 0x37, 0x1C, 0x37, 0x1B, 0x37, 0x1A, 0x37, 0x1A, 0x37, 0x19, 0x37, 0x18, 0x37, 0x17, 0x37, 0x16, 0x37, 0x15, 0x37, 0x14, 0x37, 0x13, 0x37, 0x13, 0x37, 0x12, 0x37, 0x11, 0x37, 0x10, 0x37,\n\t0x0F, 0x37, 0x0E, 0x37, 0x0D, 0x37, 0x0C, 0x37, 0x0B, 0x37, 0x0B, 0x37, 0x0A, 0x37, 0x09, 0x37, 0x08, 0x37, 0x07, 0x37, 0x06, 0x37, 0x05, 0x37, 0x04, 0x37, 0x04, 0x37, 0x03, 0x37, 0x02, 0x37,\n\t0x01, 0x37, 0x00, 0x37, 0xFF, 0x36, 0xFE, 0x36, 0xFD, 0x36, 0xFD, 0x36, 0xFC, 0x36, 0xFB, 0x36, 0xFA, 0x36, 0xF9, 0x36, 0xF8, 0x36, 0xF7, 0x36, 0xF6, 0x36, 0xF6, 0x36, 0xF5, 0x36, 0xF4, 0x36,\n\t0xF3, 0x36, 0xF2, 0x36, 0xF1, 0x36, 0xF0, 0x36, 0xEF, 0x36, 0xEF, 0x36, 0xEE, 0x36, 0xED, 0x36, 0xEC, 0x36, 0xEB, 0x36, 0xEA, 0x36, 0xE9, 0x36, 0xE9, 0x36, 0xE8, 0x36, 0xE7, 0x36, 0xE6, 0x36,\n\t0xE5, 0x36, 0xE4, 0x36, 0xE3, 0x36, 0xE3, 0x36, 0xE2, 0x36, 0xE1, 0x36, 0xE0, 0x36, 0xDF, 0x36, 0xDE, 0x36, 0xDD, 0x36, 0xDD, 0x36, 0xDC, 0x36, 0xDB, 0x36, 0xDA, 0x36, 0xD9, 0x36, 0xD8, 0x36,\n\t0xD7, 0x36, 0xD7, 0x36, 0xD6, 0x36, 0xD5, 0x36, 0xD4, 0x36, 0xD3, 0x36, 0xD2, 0x36, 0xD1, 0x36, 0xD1, 0x36, 0xD0, 0x36, 0xCF, 0x36, 0xCE, 0x36, 0xCD, 0x36, 0xCC, 0x36, 0xCB, 0x36, 0xCB, 0x36,\n\t0xCA, 0x36, 0xC9, 0x36, 0xC8, 0x36, 0xC7, 0x36, 0xC6, 0x36, 0xC6, 0x36, 0xC5, 0x36, 0xC4, 0x36, 0xC3, 0x36, 0xC2, 0x36, 0xC1, 0x36, 0xC0, 0x36, 0xC0, 0x36, 0xBF, 0x36, 0xBE, 0x36, 0xBD, 0x36,\n\t0xBC, 0x36, 0xBB, 0x36, 0xBB, 0x36, 0xBA, 0x36, 0xB9, 0x36, 0xB8, 0x36, 0xB7, 0x36, 0xB6, 0x36, 0xB6, 0x36, 0xB5, 0x36, 0xB4, 0x36, 0xB3, 0x36, 0xB2, 0x36, 0xB1, 0x36, 0xB0, 0x36, 0xB0, 0x36,\n\t0xAF, 0x36, 0xAE, 0x36, 0xAD, 0x36, 0xAC, 0x36, 0xAB, 0x36, 0xAB, 0x36, 0xAA, 0x36, 0xA9, 0x36, 0xA8, 0x36, 0xA7, 0x36, 0xA6, 0x36, 0xA6, 0x36, 0xA5, 0x36, 0xA4, 0x36, 0xA3, 0x36, 0xA2, 0x36,\n\t0xA1, 0x36, 0xA1, 0x36, 0xA0, 0x36, 0x9F, 0x36, 0x9E, 0x36, 0x9D, 0x36, 0x9D, 0x36, 0x9C, 0x36, 0x9B, 0x36, 0x9A, 0x36, 0x99, 0x36, 0x98, 0x36, 0x98, 0x36, 0x97, 0x36, 0x96, 0x36, 0x95, 0x36,\n\t0x94, 0x36, 0x93, 0x36, 0x93, 0x36, 0x92, 0x36, 0x91, 0x36, 0x90, 0x36, 0x8F, 0x36, 0x8F, 0x36, 0x8E, 0x36, 0x8D, 0x36, 0x8C, 0x36, 0x8B, 0x36, 0x8A, 0x36, 0x8A, 0x36, 0x89, 0x36, 0x88, 0x36,\n\t0x87, 0x36, 0x86, 0x36, 0x86, 0x36, 0x85, 0x36, 0x84, 0x36, 0x83, 0x36, 0x82, 0x36, 0x81, 0x36, 0x81, 0x36, 0x80, 0x36, 0x7F, 0x36, 0x7E, 0x36, 0x7D, 0x36, 0x7D, 0x36, 0x7C, 0x36, 0x7B, 0x36,\n\t0x7A, 0x36, 0x79, 0x36, 0x79, 0x36, 0x78, 0x36, 0x77, 0x36, 0x76, 0x36, 0x75, 0x36, 0x75, 0x36, 0x74, 0x36, 0x73, 0x36, 0x72, 0x36, 0x71, 0x36, 0x70, 0x36, 0x70, 0x36, 0x6F, 0x36, 0x6E, 0x36,\n\t0x6D, 0x36, 0x6C, 0x36, 0x6C, 0x36, 0x6B, 0x36, 0x6A, 0x36, 0x69, 0x36, 0x68, 0x36, 0x68, 0x36, 0x67, 0x36, 0x66, 0x36, 0x65, 0x36, 0x64, 0x36, 0x64, 0x36, 0x63, 0x36, 0x62, 0x36, 0x61, 0x36,\n\t0x60, 0x36, 0x60, 0x36, 0x5F, 0x36, 0x5E, 0x36, 0x5D, 0x36, 0x5C, 0x36, 0x5C, 0x36, 0x5B, 0x36, 0x5A, 0x36, 0x59, 0x36, 0x59, 0x36, 0x58, 0x36, 0x57, 0x36, 0x56, 0x36, 0x55, 0x36, 0x55, 0x36,\n\t0x54, 0x36, 0x53, 0x36, 0x52, 0x36, 0x51, 0x36, 0x51, 0x36, 0x50, 0x36, 0x4F, 0x36, 0x4E, 0x36, 0x4D, 0x36, 0x4D, 0x36, 0x4C, 0x36, 0x4B, 0x36, 0x4A, 0x36, 0x4A, 0x36, 0x49, 0x36, 0x48, 0x36,\n\t0x47, 0x36, 0x46, 0x36, 0x46, 0x36, 0x45, 0x36, 0x44, 0x36, 0x43, 0x36, 0x42, 0x36, 0x42, 0x36, 0x41, 0x36, 0x40, 0x36, 0x3F, 0x36, 0x3F, 0x36, 0x3E, 0x36, 0x3D, 0x36, 0x3C, 0xC7, 0x19, 0x40,\n\t0x3A, 0x36, 0x39, 0x36, 0xDB, 0x04, 0xF1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,\n\t0xFF, 0xFF, 0xFF, 0xFF, 0x75, 0x37, 0x36, 0x36, 0x36, 0x35, 0x36, 0x34, 0x36, 0x34, 0x36, 0x33, 0x36, 0x32, 0x36, 0x31, 0x36, 0x31, 0x36, 0x30, 0x36, 0x2F, 0x36, 0x2E, 0x36, 0x2D, 0x36, 0x2D,\n\t0x36, 0x2C, 0x36, 0x2B, 0x36, 0x2A, 0x36, 0x2A, 0x36, 0x29, 0x36, 0x28, 0x36, 0x27, 0x36, 0x27, 0x36, 0x26, 0x36, 0x25, 0x36, 0x24, 0x36, 0x23, 0x36, 0x23, 0x36, 0x22, 0x36, 0x21, 0x36, 0x20,\n\t0x36, 0x20, 0x36, 0x1F, 0x36, 0x1E, 0x36, 0x1D, 0x36, 0x1D, 0x36, 0x1C, 0x36, 0x1B, 0x36, 0x1A, 0x36, 0x1A, 0x36, 0x19, 0x36, 0x18, 0x36, 0x17, 0x36, 0x16, 0x36, 0x16, 0x36, 0x15, 0x36, 0x14,\n\t0x36, 0x13, 0x36, 0x13, 0x36, 0x12, 0x36, 0x11, 0x36, 0x10, 0x36, 0x10, 0x36, 0x0F, 0x36, 0x0E, 0x36, 0x0D, 0x36, 0x0D, 0x36, 0x0C, 0x36, 0x0B, 0x36, 0x0A, 0x36, 0x0A, 0x36, 0x09, 0x36, 0x08,\n\t0x36, 0x07, 0x36, 0x07, 0x36, 0x06, 0x36, 0x05, 0x36, 0x04, 0x36, 0x04, 0x36, 0x03, 0x36, 0x02, 0x36, 0x01, 0x36, 0x01, 0x36, 0x00, 0x36, 0xFF, 0x35, 0xFE, 0x35, 0xFE, 0x35, 0xFD, 0x35, 0xFC,\n\t0x35, 0xFB, 0x35, 0xFB, 0x35, 0xFA, 0x35, 0xF9, 0x35, 0xF8, 0x35, 0xF8, 0x35, 0xF7, 0x35, 0xF6, 0x35, 0xF5, 0x35, 0xF5, 0x35, 0xF4, 0x35, 0xF3, 0x35, 0xF2, 0x35, 0xF2, 0x35, 0xF1, 0x35, 0xF0,\n\t0x35, 0xEF, 0x35, 0xEF, 0x35, 0xEE, 0x35, 0xED, 0x35, 0xEC, 0x35, 0xEC, 0x35, 0xEB, 0x35, 0xEA, 0x35, 0xE9, 0x35, 0xE9, 0x35, 0xE8, 0x35, 0xE7, 0x35, 0xE7, 0x35, 0xE6, 0x35, 0xE5, 0x35, 0xE4,\n\t0x35, 0xE4, 0x35, 0xE3, 0x35, 0xE1, 0x35, 0xE0, 0x35, 0xDE, 0x35, 0xDD, 0x35, 0xDB, 0x35, 0xDA, 0x35, 0xD9, 0x35, 0xD7, 0x35, 0xD6, 0x35, 0xD4, 0x35, 0xD3, 0x35, 0xD1, 0x35, 0xD0, 0x35, 0xCE,\n\t0x35, 0xCD, 0x35, 0xCB, 0x35, 0xCA, 0x35, 0xC9, 0x35, 0xC7, 0x35, 0xC6, 0x35, 0xC4, 0x35, 0xC3, 0x35, 0xC1, 0x35, 0xC0, 0x35, 0xBE, 0x35, 0xBD, 0x35, 0xBC, 0x35, 0xBA, 0x35, 0xB9, 0x35, 0xB7,\n\t0x35, 0xB6, 0x35, 0xB4, 0x35, 0xB3, 0x35, 0xB2, 0x35, 0xB0, 0x35, 0xAF, 0x35, 0xAD, 0x35, 0xAC, 0x35, 0xAB, 0x35, 0xA9, 0x35, 0xA8, 0x35, 0xA6, 0x35, 0xA5, 0x35, 0xA3, 0x35, 0xA2, 0x35, 0xA1,\n\t0x35, 0x9F, 0x35, 0x9E, 0x35, 0x9C, 0x35, 0x9B, 0x35, 0x9A, 0x35, 0x98, 0x35, 0x97, 0x35, 0x95, 0x35, 0x94, 0x35, 0x93, 0x35, 0x91, 0x35, 0x90, 0x35, 0x8E, 0x35, 0x8D, 0x35, 0x8C, 0x35, 0x8A,\n\t0x35, 0x89, 0x35, 0x88, 0x35, 0x86, 0x35, 0x85, 0x35, 0x83, 0x35, 0x82, 0x35, 0x81, 0x35, 0x7F, 0x35, 0x7E, 0x35, 0x7D, 0x35, 0x7B, 0x35, 0x7A, 0x35, 0x78, 0x35, 0x77, 0x35, 0x76, 0x35, 0x74,\n\t0x35, 0x73, 0x35, 0x72, 0x35, 0x70, 0x35, 0x6F, 0x35, 0x6E, 0x35, 0x6C, 0x35, 0x6B, 0x35, 0x69, 0x35, 0x68, 0x35, 0x67, 0x35, 0x65, 0x35, 0x64, 0x35, 0x63, 0x35, 0x61, 0x35, 0x60, 0x35, 0x5F,\n\t0x35, 0x5D, 0x35, 0x5C, 0x35, 0x5B, 0x35, 0x59, 0x35, 0x58, 0x35, 0x57, 0x35, 0x55, 0x35, 0x54, 0x35, 0x53, 0x35, 0x51, 0x35, 0x50, 0x35, 0x4F, 0x35, 0x4D, 0x35, 0x4C, 0x35, 0x4B, 0x35, 0x49,\n\t0x35, 0x48, 0x35, 0x47, 0x35, 0x45, 0x35, 0x44, 0x35, 0x43, 0x35, 0x41, 0x35, 0x40, 0x35, 0x3F, 0x35, 0x3E, 0x35, 0x3C, 0x35, 0x3B, 0x35, 0x3A, 0x35, 0x38, 0x35, 0x37, 0x35, 0x36, 0x35, 0x34,\n\t0x35, 0x33, 0x35, 0x32, 0x35, 0x30, 0x35, 0x2F, 0x35, 0x2E, 0x35, 0x2D, 0x35, 0x2B, 0x35, 0x2A, 0x35, 0x29, 0x35, 0x27, 0x35, 0x26, 0x35, 0x25, 0x35, 0x24, 0x35, 0x22, 0x35, 0x21, 0x35, 0x20,\n\t0x35, 0x1E, 0x35, 0x1D, 0x35, 0x1C, 0x35, 0x1B, 0x35, 0x19, 0x35, 0x18, 0x35, 0x17, 0x35, 0x16, 0x35, 0x14, 0x35, 0x13, 0x35, 0x12, 0x35, 0x10, 0x35, 0x0F, 0x35, 0x0E, 0x35, 0x0D, 0x35, 0x0B,\n\t0x35, 0x0A, 0x35, 0x09, 0x35, 0x08, 0x35, 0x06, 0x35, 0x05, 0x35, 0x04, 0x35, 0x03, 0x35, 0x01, 0x35, 0x00, 0x35, 0xFF, 0x34, 0xFE, 0x34, 0xFC, 0x34, 0xFB, 0x34, 0xFA, 0x34, 0xF9, 0x34, 0xF7,\n\t0x34, 0xF6, 0x34, 0xF5, 0x34, 0xF4, 0x34, 0xF2, 0x34, 0xF1, 0x34, 0xF0, 0x34, 0xEF, 0x34, 0xED, 0x34, 0xEC, 0x34, 0xEB, 0x34, 0xEA, 0x34, 0xE9, 0x34, 0xE7, 0x34, 0xE6, 0x34, 0xE5, 0x34, 0xE4,\n\t0x34, 0xE2, 0x34, 0xE1, 0x34, 0xE0, 0x34, 0xDF, 0x34, 0xDE, 0x34, 0xDC, 0x34, 0xDB, 0x34, 0xDA, 0x34, 0xD9, 0x34, 0xD7, 0x34, 0xD6, 0x34, 0xD5, 0x34, 0xD4, 0x34, 0xD3, 0x34, 0xD1, 0x34, 0xD0,\n\t0x34, 0xCF, 0x34, 0xCE, 0x34, 0xCD, 0x34, 0xCB, 0x34, 0xCA, 0x34, 0xC9, 0x34, 0xC8, 0x34, 0xC7, 0x34, 0xC5, 0x34, 0xC4, 0x34, 0xC3, 0x34, 0xC2, 0x34, 0xC1, 0x34, 0xC0, 0x34, 0xBE, 0x34, 0xBD,\n\t0x34, 0xBC, 0x34, 0xBB, 0x34, 0xBA, 0x34, 0xB8, 0x34, 0xB7, 0x34, 0xB6, 0x34, 0xB5, 0x34, 0xB4, 0x34, 0xB3, 0x34, 0xB1, 0x34, 0xB0, 0x34, 0xAF, 0x34, 0xAE, 0x34, 0xAD, 0x34, 0xAC, 0x34, 0xAA,\n\t0x34, 0xA9, 0x34, 0xA8, 0x34, 0xA7, 0x34, 0xA6, 0x34, 0xA5, 0x34, 0xA3, 0x34, 0xA2, 0x34, 0xA1, 0x34, 0xA0, 0x34, 0x9F, 0x34, 0x9E, 0x34, 0x9C, 0x34, 0x9B, 0x34, 0x9A, 0x34, 0x99, 0x34, 0x98,\n\t0x34, 0x97, 0x34, 0x96, 0x34, 0x94, 0x34, 0x93, 0x34, 0x92, 0x34, 0x91, 0x34, 0x90, 0x34, 0x8F, 0x34, 0x8E, 0x34, 0x8C, 0x34, 0x8B, 0x34, 0x8A, 0x34, 0x89, 0x34, 0x88, 0x34, 0x87, 0x34, 0x86,\n\t0x34, 0x84, 0x34, 0x83, 0x34, 0x82, 0x34, 0x81, 0x34, 0x80, 0x34, 0x7F, 0x34, 0x7E, 0x34, 0x7D, 0x34, 0x7B, 0x34, 0x7A, 0x34, 0x79, 0x34, 0x78, 0x34, 0x77, 0x34, 0x76, 0x34, 0x75, 0x34, 0x74,\n\t0x34, 0x73, 0x34, 0x71, 0x34, 0x70, 0x34, 0x6F, 0x34, 0x6E, 0x34, 0x6D, 0x34, 0x6C, 0x34, 0x6B, 0x34, 0x6A, 0x34, 0x69, 0x34, 0x67, 0x34, 0x66, 0x34, 0x65, 0x34, 0x64, 0x34, 0x63, 0x34, 0x62,\n\t0x34, 0x61, 0x34, 0x60, 0x34, 0x5F, 0x34, 0x5E, 0x34, 0x5D, 0x34, 0x5B, 0x34, 0x5A, 0x34, 0x59, 0x34, 0x58, 0x34, 0x57, 0x34, 0x56, 0x34, 0x55, 0x34, 0x54, 0x34, 0x53, 0x34, 0x52, 0x34, 0x51,\n\t0x34, 0x50, 0x34, 0x4E, 0x34, 0x4D, 0x34, 0x4C, 0x34, 0x4B, 0x34, 0x4A, 0x34, 0x49, 0x34, 0x48, 0x34, 0x47, 0x34, 0x46, 0x34, 0x45, 0x34, 0x44, 0x34, 0x43, 0x34, 0x42, 0x34, 0x41, 0x34, 0x3F,\n\t0x34, 0x3E, 0x34, 0x3D, 0x34, 0x3C, 0x34, 0x3B, 0x34, 0x3A, 0x34, 0x39, 0x34, 0x38, 0x34, 0x37, 0x34, 0x36, 0x34, 0x35, 0x34, 0x34, 0x34, 0x33, 0x34, 0x32, 0x34, 0x31, 0x34, 0x30, 0x34, 0x2F,\n\t0x34, 0x2E, 0x34, 0x2D, 0x34, 0x2B, 0x34, 0x2A, 0x34, 0x29, 0x34, 0x28, 0x34, 0x27, 0x34, 0x26, 0x34, 0x25, 0x34, 0x24, 0x34, 0x23, 0x34, 0x22, 0x34, 0x21, 0x34, 0x20, 0x34, 0x1F, 0x34, 0x1E,\n\t0x34, 0x1D, 0x34, 0x1C, 0x34, 0x1B, 0x34, 0x1A, 0x34, 0x19, 0x34, 0x18, 0x34, 0x17, 0x34, 0x16, 0x34, 0x15, 0x34, 0x14, 0x34, 0x13, 0x34, 0x12, 0x34, 0x11, 0x34, 0x10, 0x34, 0x0F, 0x34, 0x0E,\n\t0x34, 0x0D, 0x34, 0x0C, 0x34, 0x0B, 0x34, 0x0A, 0x34, 0x09, 0x34, 0x08, 0x34, 0x07, 0x34, 0x06, 0x34, 0x05, 0x34, 0x04, 0x34, 0x03, 0x34, 0x02, 0x34, 0x01, 0x34, 0xFF, 0x33, 0xFD, 0x33, 0xFB,\n\t0x33, 0xF9, 0x33, 0xF7, 0x33, 0xF5, 0x33, 0xF3, 0x33, 0xF1, 0x33, 0xEF, 0x33, 0xED, 0x33, 0xEB, 0x33, 0xE9, 0x33, 0xE7, 0x33, 0xE5, 0x33, 0xE3, 0x33, 0xE1, 0x33, 0xDF, 0x33, 0xDD, 0x33, 0xDB,\n\t0x33, 0xD9, 0x33, 0xD8, 0x33, 0xD6, 0x33, 0xD4, 0x33, 0xD2, 0x33, 0xD0, 0x33, 0xCE, 0x33, 0xCC, 0x33, 0xCA, 0x33, 0xC8, 0x33, 0xC6, 0x33, 0xC4, 0x33, 0xC2, 0x33, 0xC0, 0x33, 0xBE, 0x33, 0xBC,\n\t0x33, 0xBA, 0x33, 0xB8, 0x33, 0xB6, 0x33, 0xB5, 0x33, 0xB3, 0x33, 0xB1, 0x33, 0xAF, 0x33, 0xAD, 0x33, 0xAB, 0x33, 0xA9, 0x33, 0xA7, 0x33, 0xA5, 0x33, 0xA3, 0x33, 0xA1, 0x33, 0x9F, 0x33, 0x9E,\n\t0x33, 0x9C, 0x33, 0x9A, 0x33, 0x98, 0x33, 0x96, 0x33, 0x94, 0x33, 0x92, 0x33, 0x90, 0x33, 0x8E, 0x33, 0x8D, 0x33, 0x8B, 0x33, 0x89, 0x33, 0x87, 0x33, 0x85, 0x33, 0x83, 0x33, 0x81, 0x33, 0x7F,\n\t0x33, 0x7D, 0x33, 0x7C, 0x33, 0x7A, 0x33, 0x78, 0x33, 0x76, 0x33, 0x74, 0x33, 0x72, 0x33, 0x70, 0x33, 0x6F, 0x33, 0x6D, 0x33, 0x6B, 0x33, 0x69, 0x33, 0x67, 0x33, 0x65, 0x33, 0x63, 0x33, 0x62,\n\t0x33, 0x60, 0x33, 0x5E, 0x33, 0x5C, 0x33, 0x5A, 0x33, 0x58, 0x33, 0x57, 0x33, 0x55, 0x33, 0x53, 0x33, 0x51, 0x33, 0x4F, 0x33, 0x4D, 0x33, 0x4C, 0x33, 0x4A, 0x33, 0x48, 0x33, 0x46, 0x33, 0x44,\n\t0x33, 0x42, 0x33, 0x41, 0x33, 0x3F, 0x33, 0x3D, 0x33, 0x3B, 0x33, 0x39, 0x33, 0x38, 0x33, 0x36, 0x33, 0x34, 0x33, 0x32, 0x33, 0x30, 0x33, 0x2F, 0x33, 0x2D, 0x33, 0x2B, 0x33, 0x29, 0x33, 0x27,\n\t0x33, 0x26, 0x33, 0x24, 0x33, 0x22, 0x33, 0x20, 0x33, 0x1F, 0x33, 0x1D, 0x33, 0x1B, 0x33, 0x19, 0x33, 0x17, 0x33, 0x16, 0x33, 0x14, 0x33, 0x12, 0x33, 0x10, 0x33, 0x0F, 0x33, 0x0D, 0x33, 0x0B,\n\t0x33, 0x09, 0x33, 0x08, 0x33, 0x06, 0x33, 0x04, 0x33, 0x02, 0x33, 0x01, 0x33, 0xFF, 0x32, 0xFD, 0x32, 0xFB, 0x32, 0xFA, 0x32, 0xF8, 0x32, 0xF6, 0x32, 0xF4, 0x32, 0xF3, 0x32, 0xF1, 0x32, 0xEF,\n\t0x32, 0xED, 0x32, 0xEC, 0x32, 0xEA, 0x32, 0xE8, 0x32, 0xE6, 0x32, 0xE5, 0x32, 0xE3, 0x32, 0xE1, 0x32, 0xE0, 0x32, 0xDE, 0x32, 0xDC, 0x32, 0xDA, 0x32, 0xD9, 0x32, 0xD7, 0x32, 0xD5, 0x32, 0xD4,\n\t0x32, 0xD2, 0x32, 0xD0, 0x32, 0xCE, 0x32, 0xCD, 0x32, 0xCB, 0x32, 0xC9, 0x32, 0xC8, 0x32, 0xC6, 0x32, 0xC4, 0x32, 0xC3, 0x32, 0xC1, 0x32, 0xBF, 0x32, 0xBE, 0x32, 0xBC, 0x32, 0xBA, 0x32, 0xB8,\n\t0x32, 0xB7, 0x32, 0xB5, 0x32, 0xB3, 0x32, 0xB2, 0x32, 0xB0, 0x32, 0xAE, 0x32, 0xAD, 0x32, 0xAB, 0x32, 0xA9, 0x32, 0xA8, 0x32, 0xA6, 0x32, 0xA4, 0x32, 0xA3, 0x32, 0xA1, 0x32, 0x9F, 0x32, 0x9E,\n\t0x32, 0x9C, 0x32, 0x9B, 0x32, 0x99, 0x32, 0x97, 0x32, 0x96, 0x32, 0x94, 0x32, 0x92, 0x32, 0x91, 0x32, 0x8F, 0x32, 0x8D, 0x32, 0x8C, 0x32, 0x8A, 0x32, 0x88, 0x32, 0x87, 0x32, 0x85, 0x32, 0x84,\n\t0x32, 0x82, 0x32, 0x80, 0x32, 0x7F, 0x32, 0x7D, 0x32, 0x7B, 0x32, 0x7A, 0x32, 0x78, 0x32, 0x77, 0x32, 0x75, 0x32, 0x73, 0x32, 0x72, 0x32, 0x70, 0x32, 0x6F, 0x32, 0x6D, 0x32, 0x6B, 0x32, 0x6A,\n\t0x32, 0x68, 0x32, 0x67, 0x32, 0x65, 0x32, 0x63, 0x32, 0x62, 0x32, 0x60, 0x32, 0x5F, 0x32, 0x5D, 0x32, 0x5B, 0x32, 0x5A, 0x32, 0x58, 0x32, 0x57, 0x32, 0x55, 0x32, 0x53, 0x32, 0x52, 0x32, 0x50,\n\t0x32, 0x4F, 0x32, 0x4D, 0x32, 0x4C, 0x32, 0x4A, 0x32, 0x48, 0x32, 0x47, 0x32, 0x45, 0x32, 0x44, 0x32, 0x42, 0x32, 0x41, 0x32, 0x3F, 0x32, 0x3D, 0x32, 0x3C, 0x32, 0x3A, 0x32, 0x39, 0x32, 0x37,\n\t0x32, 0x36, 0x32, 0x34, 0x32, 0x33, 0x32, 0x31, 0x32, 0x2F, 0x32, 0x2E, 0x32, 0x2C, 0x32, 0x2B, 0x32, 0x29, 0x32, 0x28, 0x32, 0x26, 0x32, 0x25, 0x32, 0x23, 0x32, 0x22, 0x32, 0x20, 0x32, 0x1F,\n\t0x32, 0x1D, 0x32, 0x1B, 0x32, 0x1A, 0x32, 0x18, 0x32, 0x17, 0x32, 0x15, 0x32, 0x14, 0x32, 0x12, 0x32, 0x11, 0x32, 0x0F, 0x32, 0x0E, 0x32, 0x0C, 0x32, 0x0B, 0x32, 0x09, 0x32, 0x08, 0x32, 0x06,\n\t0x32, 0x05, 0x32, 0x03, 0x32, 0x02, 0x32, 0x00, 0x32, 0xFF, 0x31, 0xFD, 0x31, 0xFC, 0x31, 0xFA, 0x31, 0xF9, 0x31, 0xF7, 0x31, 0xF6, 0x31, 0xF4, 0x31, 0xF3, 0x31, 0xF1, 0x31, 0xF0, 0x31, 0xEE,\n\t0x31, 0xED, 0x31, 0xEB, 0x31, 0xEA, 0x31, 0xE8, 0x31, 0xE7, 0x31, 0xE5, 0x31, 0xE4, 0x31, 0xE3, 0x31, 0xE1, 0x31, 0xE0, 0x31, 0xDE, 0x31, 0xDD, 0x31, 0xDB, 0x31, 0xDA, 0x31, 0xD8, 0x31, 0xD7,\n\t0x31, 0xD5, 0x31, 0xD4, 0x31, 0xD2, 0x31, 0xD1, 0x31, 0xD0, 0x31, 0xCE, 0x31, 0xCD, 0x31, 0xCB, 0x31, 0xCA, 0x31, 0xC8, 0x31, 0xC7, 0x31, 0xC5, 0x31, 0xC4, 0x31, 0xC2, 0x31, 0xC1, 0x31, 0xC0,\n\t0x31, 0xBE, 0x31, 0xBD, 0x31, 0xBB, 0x31, 0xBA, 0x31, 0xB8, 0x31, 0xB7, 0x31, 0xB6, 0x31, 0xB4, 0x31, 0xB3, 0x31, 0xB1, 0x31, 0xB0, 0x31, 0xAE, 0x31, 0xAD, 0x31, 0xAC, 0x31, 0xAA, 0x31, 0xA9,\n\t0x31, 0xA7, 0x31, 0xA6, 0x31, 0xA5, 0x31, 0xA3, 0x31, 0xA2, 0x31, 0xA0, 0x31, 0x9F, 0x31, 0x9E, 0x31, 0x9C, 0x31, 0x9B, 0x31, 0x99, 0x31, 0x98, 0x31, 0x97, 0x31, 0x95, 0x31, 0x94, 0x31, 0x92,\n\t0x31, 0x91, 0x31, 0x90, 0x31, 0x8E, 0x31, 0x8D, 0x31, 0x8B, 0x31, 0x8A, 0x31, 0x89, 0x31, 0x87, 0x31, 0x86, 0x31, 0x84, 0x31, 0x83, 0x31, 0x82, 0x31, 0x80, 0x31, 0x7F, 0x31, 0x7E, 0x31, 0x7C,\n\t0x31, 0x7B, 0x31, 0x79, 0x31, 0x78, 0x31, 0x77, 0x31, 0x75, 0x31, 0x74, 0x31, 0x73, 0x31, 0x71, 0x31, 0x70, 0x31, 0x6F, 0x31, 0x6D, 0x31, 0x6C, 0x31, 0x6B, 0x31, 0x69, 0x31, 0x68, 0x31, 0x66,\n\t0x31, 0x65, 0x31, 0x64, 0x31, 0x62, 0x31, 0x61, 0x31, 0x60, 0x31, 0x5E, 0x31, 0x5D, 0x31, 0x5C, 0x31, 0x5A, 0x31, 0x59, 0x31, 0x58, 0x31, 0x56, 0x31, 0x55, 0x31, 0x54, 0x31, 0x52, 0x31, 0x51,\n\t0x31, 0x50, 0x31, 0x4E, 0x31, 0x4D, 0x31, 0x4C, 0x31, 0x4A, 0x31, 0x49, 0x31, 0x48, 0x31, 0x46, 0x31, 0x45, 0x31, 0x44, 0x31, 0x42, 0x31, 0x41, 0x31, 0x40, 0x31, 0x3F, 0x31, 0x3D, 0x31, 0x3C,\n\t0x31, 0x3B, 0x31, 0x39, 0x31, 0x38, 0x31, 0x37, 0x31, 0x35, 0x31, 0x34, 0x31, 0x33, 0x31, 0x31, 0x31, 0x30, 0x31, 0x2F, 0x31, 0x2E, 0x31, 0x2C, 0x31, 0x2B, 0x31, 0x2A, 0x31, 0x28, 0x31, 0x27,\n\t0x31, 0x26, 0x31, 0x25, 0x31, 0x23, 0x31, 0x22, 0x31, 0x21, 0x31, 0x1F, 0x31, 0x1E, 0x31, 0x1D, 0x31, 0x1C, 0x31, 0x1A, 0x31, 0x19, 0x31, 0x18, 0x31, 0x17, 0x31, 0x15, 0x31, 0x14, 0x31, 0x13,\n\t0x31, 0x11, 0x31, 0x10, 0x31, 0x0F, 0x31, 0x0E, 0x31, 0x0C, 0x31, 0x0B, 0x31, 0x0A, 0x31, 0x09, 0x31, 0x07, 0x31, 0x06, 0x31, 0x05, 0x31, 0x04, 0x31, 0x02, 0x31, 0x01, 0x31, 0x00, 0x31, 0xFF,\n\t0x30, 0xFD, 0x30, 0xFC, 0x30, 0xFB, 0x30, 0xFA, 0x30, 0xF8, 0x30, 0xF7, 0x30, 0xF6, 0x30, 0xF5, 0x30, 0xF3, 0x30, 0xF2, 0x30, 0xF1, 0x30, 0xF0, 0x30, 0xEE, 0x30, 0xED, 0x30, 0xEC, 0x30, 0xEB,\n\t0x30, 0xEA, 0x30, 0xE8, 0x30, 0xE7, 0x30, 0xE6, 0x30, 0xE5, 0x30, 0xE3, 0x30, 0xE2, 0x30, 0xE1, 0x30, 0xE0, 0x30, 0xDF, 0x30, 0xDD, 0x30, 0xDC, 0x30, 0xDB, 0x30, 0xDA, 0x30, 0xD8, 0x30, 0xD7,\n\t0x30, 0xD6, 0x30, 0xD5, 0x30, 0xD4, 0x30, 0xD2, 0x30, 0xD1, 0x30, 0xD0, 0x30, 0xCF, 0x30, 0xCE, 0x30, 0xCC, 0x30, 0xCB, 0x30, 0xCA, 0x30, 0xC9, 0x30, 0xC8, 0x30, 0xC6, 0x30, 0xC5, 0x30, 0xC4,\n\t0x30, 0xC3, 0x30, 0xC2, 0x30, 0xC0, 0x30, 0xBF, 0x30, 0xBE, 0x30, 0xBD, 0x30, 0xBC, 0x30, 0xBB, 0x30, 0xB9, 0x30, 0xB8, 0x30, 0xB7, 0x30, 0xB6, 0x30, 0xB5, 0x30, 0xB3, 0x30, 0xB2, 0x30, 0xB1,\n\t0x30, 0xB0, 0x30, 0xAF, 0x30, 0xAE, 0x30, 0xAC, 0x30, 0xAB, 0x30, 0xAA, 0x30, 0xA9, 0x30, 0xA8, 0x30, 0xA7, 0x30, 0xA5, 0x30, 0xA4, 0x30, 0xA3, 0x30, 0xA2, 0x30, 0xA1, 0x30, 0xA0, 0x30, 0x9E,\n\t0x30, 0x9D, 0x30, 0x9C, 0x30, 0x9B, 0x30, 0x9A, 0x30, 0x99, 0x30, 0x98, 0x30, 0x96, 0x30, 0x95, 0x30, 0x94, 0x30, 0x93, 0x30, 0x92, 0x30, 0x91, 0x30, 0x90, 0x30, 0x8E, 0x30, 0x8D, 0x30, 0x8C,\n\t0x30, 0x8B, 0x30, 0x8A, 0x30, 0x89, 0x30, 0x88, 0x30, 0x86, 0x30, 0x85, 0x30, 0x84, 0x30, 0x83, 0x30, 0x82, 0x30, 0x81, 0x30, 0x80, 0x30, 0x7F, 0x30, 0x7D, 0x30, 0x7C, 0x30, 0x7B, 0x30, 0x7A,\n\t0x30, 0x79, 0x30, 0x78, 0x30, 0x77, 0x30, 0x76, 0x30, 0x75, 0x30, 0x73, 0x30, 0x72, 0x30, 0x71, 0x30, 0x70, 0x30, 0x6F, 0x30, 0x6E, 0x30, 0x6D, 0x30, 0x6C, 0x30, 0x6B, 0x30, 0x69, 0x30, 0x68,\n\t0x30, 0x67, 0x30, 0x66, 0x30, 0x65, 0x30, 0x64, 0x30, 0x63, 0x30, 0x62, 0x30, 0x61, 0x30, 0x60, 0x30, 0x5E, 0x30, 0x5D, 0x30, 0x5C, 0x30, 0x5B, 0x30, 0x5A, 0x30, 0x59, 0x30, 0x58, 0x30, 0x57,\n\t0x30, 0x56, 0x30, 0x55, 0x30, 0x53, 0x30, 0x50, 0x30, 0x4E, 0x30, 0x4C, 0x30, 0x4A, 0x30, 0x48, 0x30, 0x46, 0x30, 0x43, 0x30, 0x41, 0x30, 0x3F, 0x30, 0x3D, 0x30, 0x3B, 0x30, 0x39, 0x30, 0x37,\n\t0x30, 0x35, 0x30, 0x33, 0x30, 0x30, 0x30, 0x2E, 0x30, 0x2C, 0x30, 0x2A, 0x30, 0x28, 0x30, 0x26, 0x30, 0x24, 0x30, 0x22, 0x30, 0x20, 0x30, 0x1E, 0x30, 0x1C, 0x30, 0x1A, 0x30, 0x18, 0x30, 0x16,\n\t0x30, 0x14, 0x30, 0x11, 0x30, 0x0F, 0x30, 0x0D, 0x30, 0x0B, 0x30, 0x09, 0x30, 0x07, 0x30, 0x05, 0x30, 0x03, 0x30, 0x01, 0x30, 0xFF, 0x2F, 0xFB, 0x2F, 0xF7, 0x2F, 0xF3, 0x2F, 0xEF, 0x2F, 0xEB,\n\t0x2F, 0xE7, 0x2F, 0xE3, 0x2F, 0xDF, 0x2F, 0xDB, 0x2F, 0xD7, 0x2F, 0xD3, 0x2F, 0xCF, 0x2F, 0xCB, 0x2F, 0xC7, 0x2F, 0xC4, 0x2F, 0xC0, 0x2F, 0xBC, 0x2F, 0xB8, 0x2F, 0xB4, 0x2F, 0xB0, 0x2F, 0xAC,\n\t0x2F, 0xA9, 0x2F, 0xA5, 0x2F, 0xA1, 0x2F, 0x9D, 0x2F, 0x99, 0x2F, 0x96, 0x2F, 0x92, 0x2F, 0x8E, 0x2F, 0x8A, 0x2F, 0x86, 0x2F, 0x83, 0x2F, 0x7F, 0x2F, 0x7B, 0x2F, 0x77, 0x2F, 0x74, 0x2F, 0x70,\n\t0x2F, 0x6C, 0x2F, 0x69, 0x2F, 0x65, 0x2F, 0x61, 0x2F, 0x5E, 0x2F, 0x5A, 0x2F, 0x56, 0x2F, 0x52, 0x2F, 0x4F, 0x2F, 0x4B, 0x2F, 0x48, 0x2F, 0x44, 0x2F, 0x40, 0x2F, 0x3D, 0x2F, 0x39, 0x2F, 0x35,\n\t0x2F, 0x32, 0x2F, 0x2E, 0x2F, 0x2B, 0x2F, 0x27, 0x2F, 0x23, 0x2F, 0x20, 0x2F, 0x1C, 0x2F, 0x19, 0x2F, 0x15, 0x2F, 0x12, 0x2F, 0x0E, 0x2F, 0x0B, 0x2F, 0x07, 0x2F, 0x04, 0x2F, 0x00, 0x2F, 0xFD,\n\t0x2E, 0xF9, 0x2E, 0xF6, 0x2E, 0xF2, 0x2E, 0xEF, 0x2E, 0xEB, 0x2E, 0xE8, 0x2E, 0xE4, 0x2E, 0xE1, 0x2E, 0xDD, 0x2E, 0xDA, 0x2E, 0xD7, 0x2E, 0xD3, 0x2E, 0xD0, 0x2E, 0xCC, 0x2E, 0xC9, 0x2E, 0xC6,\n\t0x2E, 0xC2, 0x2E, 0xBF, 0x2E, 0xBB, 0x2E, 0xB8, 0x2E, 0xB5, 0x2E, 0xB1, 0x2E, 0xAE, 0x2E, 0xAB, 0x2E, 0xA7, 0x2E, 0xA4, 0x2E, 0xA1, 0x2E, 0x9D, 0x2E, 0x9A, 0x2E, 0x97, 0x2E, 0x94, 0x2E, 0x90,\n\t0x2E, 0x8D, 0x2E, 0x8A, 0x2E, 0x86, 0x2E, 0x83, 0x2E, 0x80, 0x2E, 0x7D, 0x2E, 0x79, 0x2E, 0x76, 0x2E, 0x73, 0x2E, 0x70, 0x2E, 0x6D, 0x2E, 0x69, 0x2E, 0x66, 0x2E, 0x63, 0x2E, 0x60, 0x2E, 0x5D,\n\t0x2E, 0x59, 0x2E, 0x56, 0x2E, 0x53, 0x2E, 0x50, 0x2E, 0x4D, 0x2E, 0x4A, 0x2E, 0x46, 0x2E, 0x43, 0x2E, 0x40, 0x2E, 0x3D, 0x2E, 0x3A, 0x2E, 0x37, 0x2E, 0x34, 0x2E, 0x31, 0x2E, 0x2E, 0x2E, 0x2A,\n\t0x2E, 0x27, 0x2E, 0x24, 0x2E, 0x21, 0x2E, 0x1E, 0x2E, 0x1B, 0x2E, 0x18, 0x2E, 0x15, 0x2E, 0x12, 0x2E, 0x0F, 0x2E, 0x0C, 0x2E, 0x09, 0x2E, 0x06, 0x2E, 0x03, 0x2E, 0x00, 0x2E, 0xFD, 0x2D, 0xFA,\n\t0x2D, 0xF7, 0x2D, 0xF4, 0x2D, 0xF1, 0x2D, 0xEE, 0x2D, 0xEB, 0x2D, 0xE8, 0x2D, 0xE5, 0x2D, 0xE2, 0x2D, 0xDF, 0x2D, 0xDC, 0x2D, 0xD9, 0x2D, 0xD6, 0x2D, 0xD4, 0x2D, 0xD1, 0x2D, 0xCE, 0x2D, 0xCB,\n\t0x2D, 0xC8, 0x2D, 0xC5, 0x2D, 0xC2, 0x2D, 0xBF, 0x2D, 0xBC, 0x2D, 0xBA, 0x2D, 0xB7, 0x2D, 0xB4, 0x2D, 0xB1, 0x2D, 0xAE, 0x2D, 0xAB, 0x2D, 0xA8, 0x2D, 0xA6, 0x2D, 0xA3, 0x2D, 0xA0, 0x2D, 0x9D,\n\t0x2D, 0x9A, 0x2D, 0x98, 0x2D, 0x95, 0x2D, 0x92, 0x2D, 0x8F, 0x2D, 0x8C, 0x2D, 0x8A, 0x2D, 0x87, 0x2D, 0x84, 0x2D, 0x81, 0x2D, 0x7F, 0x2D, 0x7C, 0x2D, 0x79, 0x2D, 0x76, 0x2D, 0x74, 0x2D, 0x71,\n\t0x2D, 0x6E, 0x2D, 0x6C, 0x2D, 0x69, 0x2D, 0x66, 0x2D, 0x63, 0x2D, 0x61, 0x2D, 0x5E, 0x2D, 0x5B, 0x2D, 0x59, 0x2D, 0x56, 0x2D, 0x53, 0x2D, 0x51, 0x2D, 0x4E, 0x2D, 0x4B, 0x2D, 0x49, 0x2D, 0x46,\n\t0x2D, 0x44, 0x2D, 0x41, 0x2D, 0x3E, 0x2D, 0x3C, 0x2D, 0x39, 0x2D, 0x36, 0x2D, 0x34, 0x2D, 0x31, 0x2D, 0x2F, 0x2D, 0x2C, 0x2D, 0x29, 0x2D, 0x27, 0x2D, 0x24, 0x2D, 0x22, 0x2D, 0x1F, 0x2D, 0x1D,\n\t0x2D, 0x1A, 0x2D, 0x18, 0x2D, 0x15, 0x2D, 0x12, 0x2D, 0x10, 0x2D, 0x0D, 0x2D, 0x0B, 0x2D, 0x08, 0x2D, 0x06, 0x2D, 0x03, 0x2D, 0x01, 0x2D, 0xFE, 0x2C, 0xFC, 0x2C, 0xF9, 0x2C, 0xF7, 0x2C, 0xF4,\n\t0x2C, 0xF2, 0x2C, 0xEF, 0x2C, 0xED, 0x2C, 0xEA, 0x2C, 0xE8, 0x2C, 0xE6, 0x2C, 0xE3, 0x2C, 0xE1, 0x2C, 0xDE, 0x2C, 0xDC, 0x2C, 0xD9, 0x2C, 0xD7, 0x2C, 0xD5, 0x2C, 0xD2, 0x2C, 0xD0, 0x2C, 0xCD,\n\t0x2C, 0xCB, 0x2C, 0xC9, 0x2C, 0xC6, 0x2C, 0xC4, 0x2C, 0xC1, 0x2C, 0xBF, 0x2C, 0xBD, 0x2C, 0xBA, 0x2C, 0xB8, 0x2C, 0xB6, 0x2C, 0xB3, 0x2C, 0xB1, 0x2C, 0xAF, 0x2C, 0xAC, 0x2C, 0xAA, 0x2C, 0xA7,\n\t0x2C, 0xA5, 0x2C, 0xA3, 0x2C, 0xA1, 0x2C, 0x9E, 0x2C, 0x9C, 0x2C, 0x9A, 0x2C, 0x97, 0x2C, 0x95, 0x2C, 0x93, 0x2C, 0x90, 0x2C, 0x8E, 0x2C, 0x8C, 0x2C, 0x8A, 0x2C, 0x87, 0x2C, 0x85, 0x2C, 0x83,\n\t0x2C, 0x81, 0x2C, 0x7E, 0x2C, 0x7C, 0x2C, 0x7A, 0x2C, 0x78, 0x2C, 0x75, 0x2C, 0x73, 0x2C, 0x71, 0x2C, 0x6F, 0x2C, 0x6D, 0x2C, 0x6A, 0x2C, 0x68, 0x2C, 0x66, 0x2C, 0x64, 0x2C, 0x61, 0x2C, 0x5F,\n\t0x2C, 0x5D, 0x2C, 0x5B, 0x2C, 0x59, 0x2C, 0x57, 0x2C, 0x54, 0x2C, 0x52, 0x2C, 0x50, 0x2C, 0x4E, 0x2C, 0x4C, 0x2C, 0x4A, 0x2C, 0x48, 0x2C, 0x45, 0x2C, 0x43, 0x2C, 0x41, 0x2C, 0x3F, 0x2C, 0x3D,\n\t0x2C, 0x3B, 0x2C, 0x39, 0x2C, 0x37, 0x2C, 0x34, 0x2C, 0x32, 0x2C, 0x30, 0x2C, 0x2E, 0x2C, 0x2C, 0x2C, 0x2A, 0x2C, 0x28, 0x2C, 0x26, 0x2C, 0x24, 0x2C, 0x22, 0x2C, 0x20, 0x2C, 0x1E, 0x2C, 0x1B,\n\t0x2C, 0x19, 0x2C, 0x17, 0x2C, 0x15, 0x2C, 0x13, 0x2C, 0x11, 0x2C, 0x0F, 0x2C, 0x0D, 0x2C, 0x0B, 0x2C, 0x09, 0x2C, 0x07, 0x2C, 0x05, 0x2C, 0x03, 0x2C, 0x01, 0x2C, 0xFE, 0x2B, 0xFA, 0x2B, 0xF6,\n\t0x2B, 0xF2, 0x2B, 0xEE, 0x2B, 0xEA, 0x2B, 0xE6, 0x2B, 0xE2, 0x2B, 0xDF, 0x2B, 0xDB, 0x2B, 0xD7, 0x2B, 0xD3, 0x2B, 0xCF, 0x2B, 0xCB, 0x2B, 0xC7, 0x2B, 0xC3, 0x2B, 0xBF, 0x2B, 0xBB, 0x2B, 0xB8,\n\t0x2B, 0xB4, 0x2B, 0xB0, 0x2B, 0xAC, 0x2B, 0xA8, 0x2B, 0xA4, 0x2B, 0xA1, 0x2B, 0x9D, 0x2B, 0x99, 0x2B, 0x95, 0x2B, 0x91, 0x2B, 0x8E, 0x2B, 0x8A, 0x2B, 0x86, 0x2B, 0x82, 0x2B, 0x7F, 0x2B, 0x7B,\n\t0x2B, 0x77, 0x2B, 0x73, 0x2B, 0x70, 0x2B, 0x6C, 0x2B, 0x68, 0x2B, 0x64, 0x2B, 0x61, 0x2B, 0x5D, 0x2B, 0x59, 0x2B, 0x56, 0x2B, 0x52, 0x2B, 0x4E, 0x2B, 0x4B, 0x2B, 0x47, 0x2B, 0x44, 0x2B, 0x40,\n\t0x2B, 0x3C, 0x2B, 0x39, 0x2B, 0x35, 0x2B, 0x31, 0x2B, 0x2E, 0x2B, 0x2A, 0x2B, 0x27, 0x2B, 0x23, 0x2B, 0x20, 0x2B, 0x1C, 0x2B, 0x18, 0x2B, 0x15, 0x2B, 0x11, 0x2B, 0x0E, 0x2B, 0x0A, 0x2B, 0x07,\n\t0x2B, 0x03, 0x2B, 0x00, 0x2B, 0xFC, 0x2A, 0xF9, 0x2A, 0xF5, 0x2A, 0xF2, 0x2A, 0xEE, 0x2A, 0xEB, 0x2A, 0xE7, 0x2A, 0xE4, 0x2A, 0xE1, 0x2A, 0xDD, 0x2A, 0xDA, 0x2A, 0xD6, 0x2A, 0xD3, 0x2A, 0xCF,\n\t0x2A, 0xCC, 0x2A, 0xC9, 0x2A, 0xC5, 0x2A, 0xC2, 0x2A, 0xBE, 0x2A, 0xBB, 0x2A, 0xB8, 0x2A, 0xB4, 0x2A, 0xB1, 0x2A, 0xAE, 0x2A, 0xAA, 0x2A, 0xA7, 0x2A, 0xA4, 0x2A, 0xA0, 0x2A, 0x9D, 0x2A, 0x9A,\n\t0x2A, 0x97, 0x2A, 0x93, 0x2A, 0x90, 0x2A, 0x8D, 0x2A, 0x89, 0x2A, 0x86, 0x2A, 0x83, 0x2A, 0x80, 0x2A, 0x7C, 0x2A, 0x79, 0x2A, 0x76, 0x2A, 0x73, 0x2A, 0x6F, 0x2A, 0x6C, 0x2A, 0x69, 0x2A, 0x66,\n\t0x2A, 0x63, 0x2A, 0x5F, 0x2A, 0x5C, 0x2A, 0x59, 0x2A, 0x56, 0x2A, 0x53, 0x2A, 0x50, 0x2A, 0x4C, 0x2A, 0x49, 0x2A, 0x46, 0x2A, 0x43, 0x2A, 0x40, 0x2A, 0x3D, 0x2A, 0x3A, 0x2A, 0x37, 0x2A, 0x33,\n\t0x2A, 0x30, 0x2A, 0x2D, 0x2A, 0x2A, 0x2A, 0x27, 0x2A, 0x24, 0x2A, 0x21, 0x2A, 0x1E, 0x2A, 0x1B, 0x2A, 0x18, 0x2A, 0x15, 0x2A, 0x12, 0x2A, 0x0F, 0x2A, 0x0C, 0x2A, 0x09, 0x2A, 0x06, 0x2A, 0x03,\n\t0x2A, 0x00, 0x2A, 0xFD, 0x29, 0xFA, 0x29, 0xF7, 0x29, 0xF4, 0x29, 0xF1, 0x29, 0xEE, 0x29, 0xEB, 0x29, 0xE8, 0x29, 0xE5, 0x29, 0xE2, 0x29, 0xDF, 0x29, 0xDC, 0x29, 0xD9, 0x29, 0xD6, 0x29, 0xD3,\n\t0x29, 0xD0, 0x29, 0xCD, 0x29, 0xCB, 0x29, 0xC8, 0x29, 0xC5, 0x29, 0xC2, 0x29, 0xBF, 0x29, 0xBC, 0x29, 0xB9, 0x29, 0xB6, 0x29, 0xB4, 0x29, 0xB1, 0x29, 0xAE, 0x29, 0xAB, 0x29, 0xA8, 0x29, 0xA5,\n\t0x29, 0xA3, 0x29, 0xA0, 0x29, 0x9D, 0x29, 0x9A, 0x29, 0x97, 0x29, 0x95, 0x29, 0x92, 0x29, 0x8F, 0x29, 0x8C, 0x29, 0x89, 0x29, 0x87, 0x29, 0x84, 0x29, 0x81, 0x29, 0x7E, 0x29, 0x7C, 0x29, 0x79,\n\t0x29, 0x76, 0x29, 0x73, 0x29, 0x71, 0x29, 0x6E, 0x29, 0x6B, 0x29, 0x69, 0x29, 0x66, 0x29, 0x63, 0x29, 0x60, 0x29, 0x5E, 0x29, 0x5B, 0x29, 0x58, 0x29, 0x56, 0x29, 0x53, 0x29, 0x50, 0x29, 0x4E,\n\t0x29, 0x4B, 0x29, 0x48, 0x29, 0x46, 0x29, 0x43, 0x29, 0x41, 0x29, 0x3E, 0x29, 0x3B, 0x29, 0x39, 0x29, 0x36, 0x29, 0x34, 0x29, 0x31, 0x29, 0x2E, 0x29, 0x2C, 0x29, 0x29, 0x29, 0x27, 0x29, 0x24,\n\t0x29, 0x21, 0x29, 0x1F, 0x29, 0x1C, 0x29, 0x1A, 0x29, 0x17, 0x29, 0x15, 0x29, 0x12, 0x29, 0x10, 0x29, 0x0D, 0x29, 0x0B, 0x29, 0x08, 0x29, 0x06, 0x29, 0x03, 0x29, 0x01, 0x29, 0xFE, 0x28, 0xFC,\n\t0x28, 0xF9, 0x28, 0xF7, 0x28, 0xF4, 0x28, 0xF2, 0x28, 0xEF, 0x28, 0xED, 0x28, 0xEA, 0x28, 0xE8, 0x28, 0xE5, 0x28, 0xE3, 0x28, 0xE0, 0x28, 0xDE, 0x28, 0xDC, 0x28, 0xD9, 0x28, 0xD7, 0x28, 0xD4,\n\t0x28, 0xD2, 0x28, 0xCF, 0x28, 0xCD, 0x28, 0xCB, 0x28, 0xC8, 0x28, 0xC6, 0x28, 0xC3, 0x28, 0xC1, 0x28, 0xBF, 0x28, 0xBC, 0x28, 0xBA, 0x28, 0xB8, 0x28, 0xB5, 0x28, 0xB3, 0x28, 0xB1, 0x28, 0xAE,\n\t0x28, 0xAC, 0x28, 0xAA, 0x28, 0xA7, 0x28, 0xA5, 0x28, 0xA3, 0x28, 0xA0, 0x28, 0x9E, 0x28, 0x9C, 0x28, 0x99, 0x28, 0x97, 0x28, 0x95, 0x28, 0x92, 0x28, 0x90, 0x28, 0x8E, 0x28, 0x8C, 0x28, 0x89,\n\t0x28, 0x87, 0x28, 0x85, 0x28, 0x83, 0x28, 0x80, 0x28, 0x7E, 0x28, 0x7C, 0x28, 0x7A, 0x28, 0x77, 0x28, 0x75, 0x28, 0x73, 0x28, 0x71, 0x28, 0x6E, 0x28, 0x6C, 0x28, 0x6A, 0x28, 0x68, 0x28, 0x66,\n\t0x28, 0x63, 0x28, 0x61, 0x28, 0x5F, 0x28, 0x5D, 0x28, 0x5B, 0x28, 0x59, 0x28, 0x56, 0x28, 0x54, 0x28, 0x52, 0x28, 0x50, 0x28, 0x4E, 0x28, 0x4C, 0x28, 0x49, 0x28, 0x47, 0x28, 0x45, 0x28, 0x43,\n\t0x28, 0x41, 0x28, 0x3F, 0x28, 0x3D, 0x28, 0x3B, 0x28, 0x38, 0x28, 0x36, 0x28, 0x34, 0x28, 0x32, 0x28, 0x30, 0x28, 0x2E, 0x28, 0x2C, 0x28, 0x2A, 0x28, 0x28, 0x28, 0x26, 0x28, 0x24, 0x28, 0x21,\n\t0x28, 0x1F, 0x28, 0x1D, 0x28, 0x1B, 0x28, 0x19, 0x28, 0x17, 0x28, 0x15, 0x28, 0x13, 0x28, 0x11, 0x28, 0x0F, 0x28, 0x0D, 0x28, 0x0B, 0x28, 0x09, 0x28, 0x07, 0x28, 0x05, 0x28, 0x03, 0x28, 0x01,\n\t0x28, 0xFE, 0x27, 0xFA, 0x27, 0xF6, 0x27, 0xF2, 0x27, 0xEE, 0x27, 0xEA, 0x27, 0xE6, 0x27, 0xE2, 0x27, 0xDE, 0x27, 0xDA, 0x27, 0xD6, 0x27, 0xD2, 0x27, 0xCE, 0x27, 0xCB, 0x27, 0xC7, 0x27, 0xC3,\n\t0x27, 0xBF, 0x27, 0xBB, 0x27, 0xB7, 0x27, 0xB3, 0x27, 0xAF, 0x27, 0xAC, 0x27, 0xA8, 0x27, 0xA4, 0x27, 0xA0, 0x27, 0x9C, 0x27, 0x99, 0x27, 0x95, 0x27, 0x91, 0x27, 0x8D, 0x27, 0x89, 0x27, 0x86,\n\t0x27, 0x82, 0x27, 0x7E, 0x27, 0x7A, 0x27, 0x77, 0x27, 0x73, 0x27, 0x6F, 0x27, 0x6B, 0x27, 0x68, 0x27, 0x64, 0x27, 0x60, 0x27, 0x5D, 0x27, 0x59, 0x27, 0x55, 0x27, 0x52, 0x27, 0x4E, 0x27, 0x4A,\n\t0x27, 0x47, 0x27, 0x43, 0x27, 0x3F, 0x27, 0x3C, 0x27, 0x38, 0x27, 0x35, 0x27, 0x31, 0x27, 0x2D, 0x27, 0x2A, 0x27, 0x26, 0x27, 0x23, 0x27, 0x1F, 0x27, 0x1C, 0x27, 0x18, 0x27, 0x15, 0x27, 0x11,\n\t0x27, 0x0D, 0x27, 0x0A, 0x27, 0x06, 0x27, 0x03, 0x27, 0xFF, 0x26, 0xFC, 0x26, 0xF8, 0x26, 0xF5, 0x26, 0xF1, 0x26, 0xEE, 0x26, 0xEB, 0x26, 0xE7, 0x26, 0xE4, 0x26, 0xE0, 0x26, 0xDD, 0x26, 0xD9,\n\t0x26, 0xD6, 0x26, 0xD2, 0x26, 0xCF, 0x26, 0xCC, 0x26, 0xC8, 0x26, 0xC5, 0x26, 0xC2, 0x26, 0xBE, 0x26, 0xBB, 0x26, 0xB7, 0x26, 0xB4, 0x26, 0xB1, 0x26, 0xAD, 0x26, 0xAA, 0x26, 0xA7, 0x26, 0xA3,\n\t0x26, 0xA0, 0x26, 0x9D, 0x26, 0x99, 0x26, 0x96, 0x26, 0x93, 0x26, 0x90, 0x26, 0x8C, 0x26, 0x89, 0x26, 0x86, 0x26, 0x82, 0x26, 0x7F, 0x26, 0x7C, 0x26, 0x79, 0x26, 0x76, 0x26, 0x72, 0x26, 0x6F,\n\t0x26, 0x6C, 0x26, 0x69, 0x26, 0x65, 0x26, 0x62, 0x26, 0x5F, 0x26, 0x5C, 0x26, 0x59, 0x26, 0x56, 0x26, 0x52, 0x26, 0x4F, 0x26, 0x4C, 0x26, 0x49, 0x26, 0x46, 0x26, 0x43, 0x26, 0x40, 0x26, 0x3C,\n\t0x26, 0x39, 0x26, 0x36, 0x26, 0x33, 0x26, 0x30, 0x26, 0x2D, 0x26, 0x2A, 0x26, 0x27, 0x26, 0x24, 0x26, 0x21, 0x26, 0x1E, 0x26, 0x1A, 0x26, 0x17, 0x26, 0x14, 0x26, 0x11, 0x26, 0x0E, 0x26, 0x0B,\n\t0x26, 0x08, 0x26, 0x05, 0x26, 0x02, 0x26, 0xFF, 0x25, 0xFC, 0x25, 0xF9, 0x25, 0xF6, 0x25, 0xF3, 0x25, 0xF0, 0x25, 0xED, 0x25, 0xEA, 0x25, 0xE7, 0x25, 0xE4, 0x25, 0xE2, 0x25, 0xDF, 0x25, 0xDC,\n\t0x25, 0xD9, 0x25, 0xD6, 0x25, 0xD3, 0x25, 0xD0, 0x25, 0xCD, 0x25, 0xCA, 0x25, 0xC7, 0x25, 0xC4, 0x25, 0xC2, 0x25, 0xBF, 0x25, 0xBC, 0x25, 0xB9, 0x25, 0xB6, 0x25, 0xB3, 0x25, 0xB0, 0x25, 0xAE,\n\t0x25, 0xAB, 0x25, 0xA8, 0x25, 0xA5, 0x25, 0xA2, 0x25, 0x9F, 0x25, 0x9D, 0x25, 0x9A, 0x25, 0x97, 0x25, 0x94, 0x25, 0x91, 0x25, 0x8F, 0x25, 0x8C, 0x25, 0x89, 0x25, 0x86, 0x25, 0x84, 0x25, 0x81,\n\t0x25, 0x7E, 0x25, 0x7B, 0x25, 0x79, 0x25, 0x76, 0x25, 0x73, 0x25, 0x70, 0x25, 0x6E, 0x25, 0x6B, 0x25, 0x68, 0x25, 0x66, 0x25, 0x63, 0x25, 0x60, 0x25, 0x5E, 0x25, 0x5B, 0x25, 0x58, 0x25, 0x55,\n\t0x25, 0x53, 0x25, 0x50, 0x25, 0x4E, 0x25, 0x4B, 0x25, 0x48, 0x25, 0x46, 0x25, 0x43, 0x25, 0x40, 0x25, 0x3E, 0x25, 0x3B, 0x25, 0x38, 0x25, 0x36, 0x25, 0x33, 0x25, 0x31, 0x25, 0x2E, 0x25, 0x2B,\n\t0x25, 0x29, 0x25, 0x26, 0x25, 0x24, 0x25, 0x21, 0x25, 0x1F, 0x25, 0x1C, 0x25, 0x19, 0x25, 0x17, 0x25, 0x14, 0x25, 0x12, 0x25, 0x0F, 0x25, 0x0D, 0x25, 0x0A, 0x25, 0x08, 0x25, 0x05, 0x25, 0x03,\n\t0x25, 0x00, 0x25, 0xFE, 0x24, 0xFB, 0x24, 0xF9, 0x24, 0xF6, 0x24, 0xF4, 0x24, 0xF1, 0x24, 0xEF, 0x24, 0xEC, 0x24, 0xEA, 0x24, 0xE7, 0x24, 0xE5, 0x24, 0xE3, 0x24, 0xE0, 0x24, 0xDE, 0x24, 0xDB,\n\t0x24, 0xD9, 0x24, 0xD6, 0x24, 0xD4, 0x24, 0xD2, 0x24, 0xCF, 0x24, 0xCD, 0x24, 0xCA, 0x24, 0xC8, 0x24, 0xC6, 0x24, 0xC3, 0x24, 0xC1, 0x24, 0xBE, 0x24, 0xBC, 0x24, 0xBA, 0x24, 0xB7, 0x24, 0xB5,\n\t0x24, 0xB3, 0x24, 0xB0, 0x24, 0xAC, 0x24, 0xA7, 0x24, 0xA2, 0x24, 0x9E, 0x24, 0x99, 0x24, 0x95, 0x24, 0x90, 0x24, 0x8B, 0x24, 0x87, 0x24, 0x82, 0x24, 0x7E, 0x24, 0x79, 0x24, 0x75, 0x24, 0x70,\n\t0x24, 0x6C, 0x24, 0x68, 0x24, 0x63, 0x24, 0x5F, 0x24, 0x5A, 0x24, 0x56, 0x24, 0x52, 0x24, 0x4D, 0x24, 0x49, 0x24, 0x45, 0x24, 0x41, 0x24, 0x3C, 0x24, 0x38, 0x24, 0x34, 0x24, 0x30, 0x24, 0x2C,\n\t0x24, 0x27, 0x24, 0x23, 0x24, 0x1F, 0x24, 0x1B, 0x24, 0x17, 0x24, 0x13, 0x24, 0x0F, 0x24, 0x0B, 0x24, 0x07, 0x24, 0x03, 0x24, 0xFD, 0x23, 0xF5, 0x23, 0xED, 0x23, 0xE6, 0x23, 0xDE, 0x23, 0xD6,\n\t0x23, 0xCE, 0x23, 0xC6, 0x23, 0xBE, 0x23, 0xB7, 0x23, 0xAF, 0x23, 0xA7, 0x23, 0xA0, 0x23, 0x98, 0x23, 0x91, 0x23, 0x89, 0x23, 0x81, 0x23, 0x7A, 0x23, 0x73, 0x23, 0x6B, 0x23, 0x64, 0x23, 0x5C,\n\t0x23, 0x55, 0x23, 0x4E, 0x23, 0x46, 0x23, 0x3F, 0x23, 0x38, 0x23, 0x31, 0x23, 0x29, 0x23, 0x22, 0x23, 0x1B, 0x23, 0x14, 0x23, 0x0D, 0x23, 0x06, 0x23, 0xFF, 0x22, 0xF8, 0x22, 0xF1, 0x22, 0xEA,\n\t0x22, 0xE3, 0x22, 0xDC, 0x22, 0xD6, 0x22, 0xCF, 0x22, 0xC8, 0x22, 0xC1, 0x22, 0xBA, 0x22, 0xB4, 0x22, 0xAD, 0x22, 0xA6, 0x22, 0xA0, 0x22, 0x99, 0x22, 0x92, 0x22, 0x8C, 0x22, 0x85, 0x22, 0x7F,\n\t0x22, 0x78, 0x22, 0x72, 0x22, 0x6C, 0x22, 0x65, 0x22, 0x5F, 0x22, 0x58, 0x22, 0x52, 0x22, 0x4C, 0x22, 0x45, 0x22, 0x3F, 0x22, 0x39, 0x22, 0x33, 0x22, 0x2D, 0x22, 0x26, 0x22, 0x20, 0x22, 0x1A,\n\t0x22, 0x14, 0x22, 0x0E, 0x22, 0x08, 0x22, 0x02, 0x22, 0xFC, 0x21, 0xF6, 0x21, 0xF0, 0x21, 0xEA, 0x21, 0xE4, 0x21, 0xDE, 0x21, 0xD8, 0x21, 0xD3, 0x21, 0xCD, 0x21, 0xC7, 0x21, 0xC1, 0x21, 0xBB,\n\t0x21, 0xB6, 0x21, 0xB0, 0x21, 0xAA, 0x21, 0xA5, 0x21, 0x9F, 0x21, 0x9A, 0x21, 0x94, 0x21, 0x8E, 0x21, 0x89, 0x21, 0x83, 0x21, 0x7E, 0x21, 0x78, 0x21, 0x73, 0x21, 0x6D, 0x21, 0x68, 0x21, 0x63,\n\t0x21, 0x5D, 0x21, 0x58, 0x21, 0x53, 0x21, 0x4D, 0x21, 0x48, 0x21, 0x43, 0x21, 0x3D, 0x21, 0x38, 0x21, 0x33, 0x21, 0x2E, 0x21, 0x29, 0x21, 0x23, 0x21, 0x1E, 0x21, 0x19, 0x21, 0x14, 0x21, 0x0F,\n\t0x21, 0x0A, 0x21, 0x05, 0x21, 0x00, 0x21, 0xFB, 0x20, 0xF6, 0x20, 0xF1, 0x20, 0xEC, 0x20, 0xE7, 0x20, 0xE2, 0x20, 0xDD, 0x20, 0xD9, 0x20, 0xD4, 0x20, 0xCF, 0x20, 0xCA, 0x20, 0xC5, 0x20, 0xC1,\n\t0x20, 0xBC, 0x20, 0xB7, 0x20, 0xB2, 0x20, 0xAE, 0x20, 0xA9, 0x20, 0xA4, 0x20, 0xA0, 0x20, 0x9B, 0x20, 0x97, 0x20, 0x92, 0x20, 0x8D, 0x20, 0x89, 0x20, 0x84, 0x20, 0x80, 0x20, 0x7B, 0x20, 0x77,\n\t0x20, 0x72, 0x20, 0x6E, 0x20, 0x6A, 0x20, 0x65, 0x20, 0x61, 0x20, 0x5C, 0x20, 0x58, 0x20, 0x54, 0x20, 0x4F, 0x20, 0x4B, 0x20, 0x47, 0x20, 0x43, 0x20, 0x3E, 0x20, 0x3A, 0x20, 0x36, 0x20, 0x32,\n\t0x20, 0x2D, 0x20, 0x29, 0x20, 0x25, 0x20, 0x21, 0x20, 0x1D, 0x20, 0x19, 0x20, 0x15, 0x20, 0x11, 0x20, 0x0D, 0x20, 0x09, 0x20, 0x04, 0x20, 0x00, 0x20, 0xF9, 0x1F, 0xF1, 0x1F, 0xE9, 0x1F, 0xE1,\n\t0x1F, 0xD9, 0x1F, 0xD1, 0x1F, 0xCA, 0x1F, 0xC2, 0x1F, 0xBA, 0x1F, 0xB2, 0x1F, 0xAB, 0x1F, 0xA3, 0x1F, 0x9C, 0x1F, 0x94, 0x1F, 0x8C, 0x1F, 0x85, 0x1F, 0x7D, 0x1F, 0x76, 0x1F, 0x6E, 0x1F, 0x67,\n\t0x1F, 0x60, 0x1F, 0x58, 0x1F, 0x51, 0x1F, 0x4A, 0x1F, 0x42, 0x1F, 0x3B, 0x1F, 0x34, 0x1F, 0x2D, 0x1F, 0x26, 0x1F, 0x1E, 0x1F, 0x17, 0x1F, 0x10, 0x1F, 0x09, 0x1F, 0x02, 0x1F, 0xFB, 0x1E, 0xF4,\n\t0x1E, 0xED, 0x1E, 0xE6, 0x1E, 0xDF, 0x1E, 0xD9, 0x1E, 0xD2, 0x1E, 0xCB, 0x1E, 0xC4, 0x1E, 0xBD, 0x1E, 0xB7, 0x1E, 0xB0, 0x1E, 0xA9, 0x1E, 0xA3, 0x1E, 0x9C, 0x1E, 0x95, 0x1E, 0x8F, 0x1E, 0x88,\n\t0x1E, 0x82, 0x1E, 0x7B, 0x1E, 0x75, 0x1E, 0x6E, 0x1E, 0x68, 0x1E, 0x62, 0x1E, 0x5B, 0x1E, 0x55, 0x1E, 0x4F, 0x1E, 0x48, 0x1E, 0x42, 0x1E, 0x3C, 0x1E, 0x36, 0x1E, 0x2F, 0x1E, 0x29, 0x1E, 0x23,\n\t0x1E, 0x1D, 0x1E, 0x17, 0x1E, 0x11, 0x1E, 0x0B, 0x1E, 0x05, 0x1E, 0xFF, 0x1D, 0xF9, 0x1D, 0xF3, 0x1D, 0xED, 0x1D, 0xE7, 0x1D, 0xE1, 0x1D, 0xDB, 0x1D, 0xD5, 0x1D, 0xCF, 0x1D, 0xCA, 0x1D, 0xC4,\n\t0x1D, 0xBE, 0x1D, 0xB8, 0x1D, 0xB3, 0x1D, 0xAD, 0x1D, 0xA7, 0x1D, 0xA2, 0x1D, 0x9C, 0x1D, 0x96, 0x1D, 0x91, 0x1D, 0x8B, 0x1D, 0x86, 0x1D, 0x80, 0x1D, 0x7B, 0x1D, 0x75, 0x1D, 0x70, 0x1D, 0x6A,\n\t0x1D, 0x65, 0x1D, 0x60, 0x1D, 0x5A, 0x1D, 0x55, 0x1D, 0x50, 0x1D, 0x4A, 0x1D, 0x45, 0x1D, 0x40, 0x1D, 0x3B, 0x1D, 0x35, 0x1D, 0x30, 0x1D, 0x2B, 0x1D, 0x26, 0x1D, 0x21, 0x1D, 0x1B, 0x1D, 0x16,\n\t0x1D, 0x11, 0x1D, 0x0C, 0x1D, 0x07, 0x1D, 0x02, 0x1D, 0xFD, 0x1C, 0xF8, 0x1C, 0xF3, 0x1C, 0xEE, 0x1C, 0xE9, 0x1C, 0xE5, 0x1C, 0xE0, 0x1C, 0xDB, 0x1C, 0xD6, 0x1C, 0xD1, 0x1C, 0xCC, 0x1C, 0xC7,\n\t0x1C, 0xC3, 0x1C, 0xBE, 0x1C, 0xB9, 0x1C, 0xB5, 0x1C, 0xB0, 0x1C, 0xAB, 0x1C, 0xA6, 0x1C, 0xA2, 0x1C, 0x9D, 0x1C, 0x99, 0x1C, 0x94, 0x1C, 0x8F, 0x1C, 0x8B, 0x1C, 0x86, 0x1C, 0x82, 0x1C, 0x7D,\n\t0x1C, 0x79, 0x1C, 0x74, 0x1C, 0x70, 0x1C, 0x6C, 0x1C, 0x67, 0x1C, 0x63, 0x1C, 0x5E, 0x1C, 0x5A, 0x1C, 0x56, 0x1C, 0x51, 0x1C, 0x4D, 0x1C, 0x49, 0x1C, 0x44, 0x1C, 0x40, 0x1C, 0x3C, 0x1C, 0x38,\n\t0x1C, 0x34, 0x1C, 0x2F, 0x1C, 0x2B, 0x1C, 0x27, 0x1C, 0x23, 0x1C, 0x1F, 0x1C, 0x1B, 0x1C, 0x17, 0x1C, 0x12, 0x1C, 0x0E, 0x1C, 0x0A, 0x1C, 0x06, 0x1C, 0x02, 0x1C, 0xFD, 0x1B, 0xF5, 0x1B, 0xED,\n\t0x1B, 0xE5, 0x1B, 0xDD, 0x1B, 0xD5, 0x1B, 0xCD, 0x1B, 0xC5, 0x1B, 0xBE, 0x1B, 0xB6, 0x1B, 0xAE, 0x1B, 0xA7, 0x1B, 0x9F, 0x1B, 0x97, 0x1B, 0x90, 0x1B, 0x88, 0x1B, 0x81, 0x1B, 0x79, 0x1B, 0x72,\n\t0x1B, 0x6A, 0x1B, 0x63, 0x1B, 0x5C, 0x1B, 0x54, 0x1B, 0x4D, 0x1B, 0x46, 0x1B, 0x3E, 0x1B, 0x37, 0x1B, 0x30, 0x1B, 0x29, 0x1B, 0x22, 0x1B, 0x1A, 0x1B, 0x13, 0x1B, 0x0C, 0x1B, 0x05, 0x1B, 0xFE,\n\t0x1A, 0xF7, 0x1A, 0xF0, 0x1A, 0xE9, 0x1A, 0xE2, 0x1A, 0xDC, 0x1A, 0xD5, 0x1A, 0xCE, 0x1A, 0xC7, 0x1A, 0xC0, 0x1A, 0xBA, 0x1A, 0xB3, 0x1A, 0xAC, 0x1A, 0xA6, 0x1A, 0x9F, 0x1A, 0x98, 0x1A, 0x92,\n\t0x1A, 0x8B, 0x1A, 0x85, 0x1A, 0x7E, 0x1A, 0x78, 0x1A, 0x71, 0x1A, 0x6B, 0x1A, 0x64, 0x1A, 0x5E, 0x1A, 0x58, 0x1A, 0x51, 0x1A, 0x4B, 0x1A, 0x45, 0x1A, 0x3F, 0x1A, 0x38, 0x1A, 0x32, 0x1A, 0x2C,\n\t0x1A, 0x26, 0x1A, 0x20, 0x1A, 0x19, 0x1A, 0x13, 0x1A, 0x0D, 0x1A, 0x07, 0x1A, 0x01, 0x1A, 0xFB, 0x19, 0xF5, 0x19, 0xEF, 0x19, 0xE9, 0x19, 0xE4, 0x19, 0xDE, 0x19, 0xD8, 0x19, 0xD2, 0x19, 0xCC,\n\t0x19, 0xC6, 0x19, 0xC1, 0x19, 0xBB, 0x19, 0xB5, 0x19, 0xAF, 0x19, 0xAA, 0x19, 0xA4, 0x19, 0x9F, 0x19, 0x99, 0x19, 0x93, 0x19, 0x8E, 0x19, 0x88, 0x19, 0x83, 0x19, 0x7D, 0x19, 0x78, 0x19, 0x72,\n\t0x19, 0x6D, 0x19, 0x67, 0x19, 0x62, 0x19, 0x5D, 0x19, 0x57, 0x19, 0x52, 0x19, 0x4D, 0x19, 0x47, 0x19, 0x42, 0x19, 0x3D, 0x19, 0x38, 0x19, 0x32, 0x19, 0x2D, 0x19, 0x28, 0x19, 0x23, 0x19, 0x1E,\n\t0x19, 0x19, 0x19, 0x14, 0x19, 0x0F, 0x19, 0x09, 0x19, 0x04, 0x19, 0xFF, 0x18, 0xFA, 0x18, 0xF5, 0x18, 0xF1, 0x18, 0xEC, 0x18, 0xE7, 0x18, 0xE2, 0x18, 0xDD, 0x18, 0xD8, 0x18, 0xD3, 0x18, 0xCE,\n\t0x18, 0xCA, 0x18, 0xC5, 0x18, 0xC0, 0x18, 0xBB, 0x18, 0xB7, 0x18, 0xB2, 0x18, 0xAD, 0x18, 0xA9, 0x18, 0xA4, 0x18, 0x9F, 0x18, 0x9B, 0x18, 0x96, 0x18, 0x91, 0x18, 0x8D, 0x18, 0x88, 0x18, 0x84,\n\t0x18, 0x7F, 0x18, 0x7B, 0x18, 0x76, 0x18, 0x72, 0x18, 0x6E, 0x18, 0x69, 0x18, 0x65, 0x18, 0x60, 0x18, 0x5C, 0x18, 0x58, 0x18, 0x53, 0x18, 0x4F, 0x18, 0x4B, 0x18, 0x46, 0x18, 0x42, 0x18, 0x3E,\n\t0x18, 0x3A, 0x18, 0x35, 0x18, 0x31, 0x18, 0x2D, 0x18, 0x29, 0x18, 0x25, 0x18, 0x21, 0x18, 0x1C, 0x18, 0x18, 0x18, 0x14, 0x18, 0x10, 0x18, 0x0C, 0x18, 0x08, 0x18, 0x04, 0x18, 0x00, 0x18, 0xF8,\n\t0x17, 0xF0, 0x17, 0xE8, 0x17, 0xE0, 0x17, 0xD8, 0x17, 0xD1, 0x17, 0xC9, 0x17, 0xC1, 0x17, 0xB9, 0x17, 0xB2, 0x17, 0xAA, 0x17, 0xA2, 0x17, 0x9B, 0x17, 0x93, 0x17, 0x8C, 0x17, 0x84, 0x17, 0x7D,\n\t0x17, 0x75, 0x17, 0x6E, 0x17, 0x66, 0x17, 0x5F, 0x17, 0x57, 0x17, 0x50, 0x17, 0x49, 0x17, 0x42, 0x17, 0x3A, 0x17, 0x33, 0x17, 0x2C, 0x17, 0x25, 0x17, 0x1E, 0x17, 0x17, 0x17, 0x0F, 0x17, 0x08,\n\t0x17, 0x01, 0x17, 0xFA, 0x16, 0xF3, 0x16, 0xEC, 0x16, 0xE6, 0x16, 0xDF, 0x16, 0xD8, 0x16, 0xD1, 0x16, 0xCA, 0x16, 0xC3, 0x16, 0xBD, 0x16, 0xB6, 0x16, 0xAF, 0x16, 0xA9, 0x16, 0xA2, 0x16, 0x9B,\n\t0x16, 0x95, 0x16, 0x8E, 0x16, 0x88, 0x16, 0x81, 0x16, 0x7B, 0x16, 0x74, 0x16, 0x6E, 0x16, 0x67, 0x16, 0x61, 0x16, 0x5B, 0x16, 0x54, 0x16, 0x4E, 0x16, 0x48, 0x16, 0x41, 0x16, 0x3B, 0x16, 0x35,\n\t0x16, 0x2F, 0x16, 0x28, 0x16, 0x22, 0x16, 0x1C, 0x16, 0x16, 0x16, 0x10, 0x16, 0x0A, 0x16, 0x04, 0x16, 0xFE, 0x15, 0xF8, 0x15, 0xF2, 0x15, 0xEC, 0x15, 0xE6, 0x15, 0xE0, 0x15, 0xDA, 0x15, 0xD5,\n\t0x15, 0xCF, 0x15, 0xC9, 0x15, 0xC3, 0x15, 0xBD, 0x15, 0xB8, 0x15, 0xB2, 0x15, 0xAC, 0x15, 0xA7, 0x15, 0xA1, 0x15, 0x9B, 0x15, 0x96, 0x15, 0x90, 0x15, 0x8B, 0x15, 0x85, 0x15, 0x80, 0x15, 0x7A,\n\t0x15, 0x75, 0x15, 0x6F, 0x15, 0x6A, 0x15, 0x64, 0x15, 0x5F, 0x15, 0x5A, 0x15, 0x54, 0x15, 0x4F, 0x15, 0x4A, 0x15, 0x44, 0x15, 0x3F, 0x15, 0x3A, 0x15, 0x35, 0x15, 0x30, 0x15, 0x2A, 0x15, 0x25,\n\t0x15, 0x20, 0x15, 0x1B, 0x15, 0x16, 0x15, 0x11, 0x15, 0x0C, 0x15, 0x07, 0x15, 0x02, 0x15, 0xFD, 0x14, 0xF8, 0x14, 0xF3, 0x14, 0xEE, 0x14, 0xE9, 0x14, 0xE4, 0x14, 0xDF, 0x14, 0xDA, 0x14, 0xD5,\n\t0x14, 0xD1, 0x14, 0xCC, 0x14, 0xC7, 0x14, 0xC2, 0x14, 0xBD, 0x14, 0xB9, 0x14, 0xB4, 0x14, 0xAF, 0x14, 0xAB, 0x14, 0xA6, 0x14, 0xA1, 0x14, 0x9D, 0x14, 0x98, 0x14, 0x94, 0x14, 0x8F, 0x14, 0x8A,\n\t0x14, 0x86, 0x14, 0x81, 0x14, 0x7D, 0x14, 0x78, 0x14, 0x74, 0x14, 0x6F, 0x14, 0x6B, 0x14, 0x67, 0x14, 0x62, 0x14, 0x5E, 0x14, 0x5A, 0x14, 0x55, 0x14, 0x51, 0x14, 0x4D, 0x14, 0x48, 0x14, 0x44,\n\t0x14, 0x40, 0x14, 0x3B, 0x14, 0x37, 0x14, 0x33, 0x14, 0x2F, 0x14, 0x2B, 0x14, 0x27, 0x14, 0x22, 0x14, 0x1E, 0x14, 0x1A, 0x14, 0x16, 0x14, 0x12, 0x14, 0x0E, 0x14, 0x0A, 0x14, 0x06, 0x14, 0x02,\n\t0x14, 0xFC, 0x13, 0xF4, 0x13, 0xEC, 0x13, 0xE4, 0x13, 0xDC, 0x13, 0xD4, 0x13, 0xCC, 0x13, 0xC5, 0x13, 0xBD, 0x13, 0xB5, 0x13, 0xAD, 0x13, 0xA6, 0x13, 0x9E, 0x13, 0x96, 0x13, 0x8F, 0x13, 0x87,\n\t0x13, 0x80, 0x13, 0x78, 0x13, 0x71, 0x13, 0x69, 0x13, 0x62, 0x13, 0x5B, 0x13, 0x53, 0x13, 0x4C, 0x13, 0x45, 0x13, 0x3E, 0x13, 0x36, 0x13, 0x2F, 0x13, 0x28, 0x13, 0x21, 0x13, 0x1A, 0x13, 0x13,\n\t0x13, 0x0C, 0x13, 0x04, 0x13, 0xFD, 0x12, 0xF7, 0x12, 0xF0, 0x12, 0xE9, 0x12, 0xE2, 0x12, 0xDB, 0x12, 0xD4, 0x12, 0xCD, 0x12, 0xC6, 0x12, 0xC0, 0x12, 0xB9, 0x12, 0xB2, 0x12, 0xAC, 0x12, 0xA5,\n\t0x12, 0x9E, 0x12, 0x98, 0x12, 0x91, 0x12, 0x8B, 0x12, 0x84, 0x12, 0x7D, 0x12, 0x77, 0x12, 0x71, 0x12, 0x6A, 0x12, 0x64, 0x12, 0x5D, 0x12, 0x57, 0x12, 0x51, 0x12, 0x4A, 0x12, 0x44, 0x12, 0x3E,\n\t0x12, 0x38, 0x12, 0x31, 0x12, 0x2B, 0x12, 0x25, 0x12, 0x1F, 0x12, 0x19, 0x12, 0x13, 0x12, 0x0D, 0x12, 0x07, 0x12, 0x01, 0x12, 0xFB, 0x11, 0xF5, 0x11, 0xEF, 0x11, 0xE9, 0x11, 0xE3, 0x11, 0xDD,\n\t0x11, 0xD7, 0x11, 0xD1, 0x11, 0xCC, 0x11, 0xC6, 0x11, 0xC0, 0x11, 0xBA, 0x11, 0xB5, 0x11, 0xAF, 0x11, 0xA9, 0x11, 0xA4, 0x11, 0x9E, 0x11, 0x98, 0x11, 0x93, 0x11, 0x8D, 0x11, 0x88, 0x11, 0x82,\n\t0x11, 0x7D, 0x11, 0x77, 0x11, 0x72, 0x11, 0x6C, 0x11, 0x67, 0x11, 0x61, 0x11, 0x5C, 0x11, 0x57, 0x11, 0x51, 0x11, 0x4C, 0x11, 0x47, 0x11, 0x42, 0x11, 0x3C, 0x11, 0x37, 0x11, 0x32, 0x11, 0x2D,\n\t0x11, 0x27, 0x11, 0x22, 0x11, 0x1D, 0x11, 0x18, 0x11, 0x13, 0x11, 0x0E, 0x11, 0x09, 0x11, 0x04, 0x11, 0xFF, 0x10, 0xFA, 0x10, 0xF5, 0x10, 0xF0, 0x10, 0xEB, 0x10, 0xE6, 0x10, 0xE1, 0x10, 0xDC,\n\t0x10, 0xD8, 0x10, 0xD3, 0x10, 0xCE, 0x10, 0xC9, 0x10, 0xC4, 0x10, 0xC0, 0x10, 0xBB, 0x10, 0xB6, 0x10, 0xB1, 0x10, 0xAD, 0x10, 0xA8, 0x10, 0xA3, 0x10, 0x9F, 0x10, 0x9A, 0x10, 0x96, 0x10, 0x91,\n\t0x10, 0x8C, 0x10, 0x88, 0x10, 0x83, 0x10, 0x7F, 0x10, 0x7A, 0x10, 0x76, 0x10, 0x71, 0x10, 0x6D, 0x10, 0x69, 0x10, 0x64, 0x10, 0x60, 0x10, 0x5B, 0x10, 0x57, 0x10, 0x53, 0x10, 0x4E, 0x10, 0x4A,\n\t0x10, 0x46, 0x10, 0x42, 0x10, 0x3D, 0x10, 0x39, 0x10, 0x35, 0x10, 0x31, 0x10, 0x2D, 0x10, 0x28, 0x10, 0x24, 0x10, 0x20, 0x10, 0x1C, 0x10, 0x18, 0x10, 0x14, 0x10, 0x10, 0x10, 0x0C, 0x10, 0x08,\n\t0x10, 0x04, 0x10, 0xFF, 0x0F, 0xF7, 0x0F, 0xEF, 0x0F, 0xE7, 0x0F, 0xDF, 0x0F, 0xD8, 0x0F, 0xD0, 0x0F, 0xC8, 0x0F, 0xC0, 0x0F, 0xB8, 0x0F, 0xB1, 0x0F, 0xA9, 0x0F, 0xA1, 0x0F, 0x9A, 0x0F, 0x92,\n\t0x0F, 0x8B, 0x0F, 0x83, 0x0F, 0x7C, 0x0F, 0x74, 0x0F, 0x6D, 0x0F, 0x65, 0x0F, 0x5E, 0x0F, 0x57, 0x0F, 0x4F, 0x0F, 0x48, 0x0F, 0x41, 0x0F, 0x3A, 0x0F, 0x32, 0x0F, 0x2B, 0x0F, 0x24, 0x0F, 0x1D,\n\t0x0F, 0x16, 0x0F, 0x0F, 0x0F, 0x08, 0x0F, 0x01, 0x0F, 0xFA, 0x0E, 0xF3, 0x0E, 0xEC, 0x0E, 0xE5, 0x0E, 0xDE, 0x0E, 0xD7, 0x0E, 0xD0, 0x0E, 0xC9, 0x0E, 0xC3, 0x0E, 0xBC, 0x0E, 0xB5, 0x0E, 0xAF,\n\t0x0E, 0xA8, 0x0E, 0xA1, 0x0E, 0x9B, 0x0E, 0x94, 0x0E, 0x8D, 0x0E, 0x87, 0x0E, 0x80, 0x0E, 0x7A, 0x0E, 0x73, 0x0E, 0x6D, 0x0E, 0x67, 0x0E, 0x60, 0x0E, 0x5A, 0x0E, 0x53, 0x0E, 0x4D, 0x0E, 0x47,\n\t0x0E, 0x41, 0x0E, 0x3A, 0x0E, 0x34, 0x0E, 0x2E, 0x0E, 0x28, 0x0E, 0x22, 0x0E, 0x1C, 0x0E, 0x15, 0x0E, 0x0F, 0x0E, 0x09, 0x0E, 0x03, 0x0E, 0xFD, 0x0D, 0xF7, 0x0D, 0xF1, 0x0D, 0xEB, 0x0D, 0xE6,\n\t0x0D, 0xE0, 0x0D, 0xDA, 0x0D, 0xD4, 0x0D, 0xCE, 0x0D, 0xC8, 0x0D, 0xC3, 0x0D, 0xBD, 0x0D, 0xB7, 0x0D, 0xB1, 0x0D, 0xAC, 0x0D, 0xA6, 0x0D, 0xA0, 0x0D, 0x9B, 0x0D, 0x95, 0x0D, 0x90, 0x0D, 0x8A,\n\t0x0D, 0x85, 0x0D, 0x7F, 0x0D, 0x74, 0x0D, 0x69, 0x0D, 0x5E, 0x0D, 0x54, 0x0D, 0x49, 0x0D, 0x3F, 0x0D, 0x34, 0x0D, 0x2A, 0x0D, 0x1F, 0x0D, 0x15, 0x0D, 0x0B, 0x0D, 0x01, 0x0D, 0xF7, 0x0C, 0xED,\n\t0x0C, 0xE3, 0x0C, 0xDA, 0x0C, 0xD0, 0x0C, 0xC6, 0x0C, 0xBD, 0x0C, 0xB3, 0x0C, 0xAA, 0x0C, 0xA1, 0x0C, 0x98, 0x0C, 0x8E, 0x0C, 0x85, 0x0C, 0x7C, 0x0C, 0x73, 0x0C, 0x6B, 0x0C, 0x62, 0x0C, 0x59,\n\t0x0C, 0x50, 0x0C, 0x48, 0x0C, 0x3F, 0x0C, 0x37, 0x0C, 0x2E, 0x0C, 0x26, 0x0C, 0x1E, 0x0C, 0x16, 0x0C, 0x0D, 0x0C, 0x05, 0x0C, 0xFB, 0x0B, 0xEB, 0x0B, 0xDB, 0x0B, 0xCB, 0x0B, 0xBC, 0x0B, 0xAD,\n\t0x0B, 0x9D, 0x0B, 0x8E, 0x0B, 0x7F, 0x0B, 0x70, 0x0B, 0x61, 0x0B, 0x53, 0x0B, 0x44, 0x0B, 0x36, 0x0B, 0x27, 0x0B, 0x19, 0x0B, 0x0B, 0x0B, 0xFD, 0x0A, 0xEF, 0x0A, 0xE1, 0x0A, 0xD3, 0x0A, 0xC6,\n\t0x0A, 0xB8, 0x0A, 0xAB, 0x0A, 0x9E, 0x0A, 0x90, 0x0A, 0x83, 0x0A, 0x76, 0x0A, 0x69, 0x0A, 0x5D, 0x0A, 0x50, 0x0A, 0x43, 0x0A, 0x37, 0x0A, 0x2B, 0x0A, 0x1E, 0x0A, 0x12, 0x0A, 0x06, 0x0A, 0xFA,\n\t0x09, 0xEE, 0x09, 0xE2, 0x09, 0xD7, 0x09, 0xCB, 0x09, 0xBF, 0x09, 0xB4, 0x09, 0xA9, 0x09, 0x9D, 0x09, 0x92, 0x09, 0x87, 0x09, 0x7C, 0x09, 0x71, 0x09, 0x66, 0x09, 0x5B, 0x09, 0x51, 0x09, 0x46,\n\t0x09, 0x3C, 0x09, 0x31, 0x09, 0x27, 0x09, 0x1D, 0x09, 0x12, 0x09, 0x08, 0x09, 0xFE, 0x08, 0xF4, 0x08, 0xEB, 0x08, 0xE1, 0x08, 0xD7, 0x08, 0xCD, 0x08, 0xC4, 0x08, 0xBA, 0x08, 0xB1, 0x08, 0xA8,\n\t0x08, 0x9E, 0x08, 0x95, 0x08, 0x8C, 0x08, 0x83, 0x08, 0x7A, 0x08, 0x71, 0x08, 0x68, 0x08, 0x5F, 0x08, 0x57, 0x08, 0x4E, 0x08, 0x45, 0x08, 0x3D, 0x08, 0x34, 0x08, 0x2C, 0x08, 0x24, 0x08, 0x1C,\n\t0x08, 0x13, 0x08, 0x0B, 0x08, 0x03, 0x08, 0xF6, 0x07, 0xE7, 0x07, 0xD7, 0x07, 0xC7, 0x07, 0xB8, 0x07, 0xA8, 0x07, 0x99, 0x07, 0x8A, 0x07, 0x7B, 0x07, 0x6C, 0x07, 0x5D, 0x07, 0x4F, 0x07, 0x40,\n\t0x07, 0x32, 0x07, 0x23, 0x07, 0x15, 0x07, 0x07, 0x07, 0xF9, 0x06, 0xEB, 0x06, 0xDD, 0x06, 0xD0, 0x06, 0xC2, 0x06, 0xB4, 0x06, 0xA7, 0x06, 0x9A, 0x06, 0x8D, 0x06, 0x80, 0x06, 0x73, 0x06, 0x66,\n\t0x06, 0x59, 0x06, 0x4C, 0x06, 0x40, 0x06, 0x33, 0x06, 0x27, 0x06, 0x1B, 0x06, 0x0F, 0x06, 0x03, 0x06, 0xF7, 0x05, 0xEB, 0x05, 0xDF, 0x05, 0xD3, 0x05, 0xC8, 0x05, 0xBC, 0x05, 0xB1, 0x05, 0xA5,\n\t0x05, 0x9A, 0x05, 0x8F, 0x05, 0x84, 0x05, 0x79, 0x05, 0x6E, 0x05, 0x63, 0x05, 0x58, 0x05, 0x4E, 0x05, 0x43, 0x05, 0x39, 0x05, 0x2E, 0x05, 0x24, 0x05, 0x1A, 0x05, 0x10, 0x05, 0x06, 0x05, 0xFC,\n\t0x04, 0xF2, 0x04, 0xE8, 0x04, 0xDE, 0x04, 0xD4, 0x04, 0xCB, 0x04, 0xC1, 0x04, 0xB8, 0x04, 0xAE, 0x04, 0xA5, 0x04, 0x9C, 0x04, 0x93, 0x04, 0x89, 0x04, 0x80, 0x04, 0x77, 0x04, 0x6F, 0x04, 0x66,\n\t0x04, 0x5D, 0x04, 0x54, 0x04, 0x4C, 0x04, 0x43, 0x04, 0x3B, 0x04, 0x32, 0x04, 0x2A, 0x04, 0x21, 0x04, 0x19, 0x04, 0x11, 0x04, 0x09, 0x04, 0x01, 0x04, 0xF9, 0x03, 0xF1, 0x03, 0xE9, 0x03, 0xE1,\n\t0x03, 0xDA, 0x03, 0xD2, 0x03, 0xCA, 0x03, 0xC3, 0x03, 0xBB, 0x03, 0xB4, 0x03, 0xAD, 0x03, 0xA5, 0x03, 0x9E, 0x03, 0x97, 0x03, 0x90, 0x03, 0x89, 0x03, 0x81, 0x03, 0x7A, 0x03, 0x74, 0x03, 0x6D,\n\t0x03, 0x66, 0x03, 0x5F, 0x03, 0x58, 0x03, 0x52, 0x03, 0x4B, 0x03, 0x45, 0x03, 0x3E, 0x03, 0x38, 0x03, 0x31, 0x03, 0x2B, 0x03, 0x24, 0x03, 0x1E, 0x03, 0x18, 0x03, 0x12, 0x03, 0x0C, 0x03, 0x06,\n\t0x03, 0x00, 0x03, 0xFA, 0x02, 0xF4, 0x02, 0xEE, 0x02, 0xE8, 0x02, 0xE2, 0x02, 0xDD, 0x02, 0xD7, 0x02, 0xD1, 0x02, 0xCC, 0x02, 0xC6, 0x02, 0xC0, 0x02, 0xBB, 0x02, 0xB6, 0x02, 0xB0, 0x02, 0xAB,\n\t0x02, 0xA5, 0x02, 0xA0, 0x02, 0x9B, 0x02, 0x96, 0x02, 0x91, 0x02, 0x8C, 0x02, 0x86, 0x02, 0x81, 0x02, 0x7C, 0x02, 0x77, 0x02, 0x73, 0x02, 0x6E, 0x02, 0x69, 0x02, 0x64, 0x02, 0x5F, 0x02, 0x5B,\n\t0x02, 0x56, 0x02, 0x51, 0x02, 0x4D, 0x02, 0x48, 0x02, 0x43, 0x02, 0x3F, 0x02, 0x3A, 0x02, 0x36, 0x02, 0x32, 0x02, 0x2D, 0x02, 0x29, 0x02, 0x25, 0x02, 0x20, 0x02, 0x1C, 0x02, 0x18, 0x02, 0x14,\n\t0x02, 0x10, 0x02, 0x0B, 0x02, 0x07, 0x02, 0x03, 0x02, 0xFF, 0x01, 0xFB, 0x01, 0xF7, 0x01, 0xF4, 0x01, 0xF0, 0x01, 0xEC, 0x01, 0xE8, 0x01, 0xE4, 0x01, 0xE0, 0x01, 0xDD, 0x01, 0xD9, 0x01, 0xD5,\n\t0x01, 0xD2, 0x01, 0xCE, 0x01, 0xCA, 0x01, 0xC7, 0x01, 0xC3, 0x01, 0xC0, 0x01, 0xBC, 0x01, 0xB9, 0x01, 0xB5, 0x01, 0xB2, 0x01, 0xAF, 0x01, 0xAB, 0x01, 0xA8, 0x01, 0xA5, 0x01, 0xA1, 0x01, 0x9E,\n\t0x01, 0x9B, 0x01, 0x98, 0x01, 0x95, 0x01, 0x91, 0x01, 0x8E, 0x01, 0x8B, 0x01, 0x88, 0x01, 0x85, 0x01, 0x82, 0x01, 0x7F, 0x01, 0x7C, 0x01, 0x79, 0x01, 0x76, 0x01, 0x73, 0x01, 0x70, 0x01, 0x6D,\n\t0x01, 0x6B, 0x01, 0x68, 0x01, 0x65, 0x01, 0x62, 0x01, 0x5F, 0x01, 0x5D, 0x01, 0x5A, 0x01, 0x57, 0x01, 0x55, 0x01, 0x52, 0x01, 0x4F, 0x01, 0x4D, 0x01, 0x4A, 0x01, 0x48, 0x01, 0x45, 0x01, 0x43,\n\t0x01, 0x40, 0x01, 0x3E, 0x01, 0x3B, 0x01, 0x39, 0x01, 0x36, 0x01, 0x34, 0x01, 0x31, 0x01, 0x2F, 0x01, 0x2D, 0x01, 0x2A, 0x01, 0x28, 0x01, 0x26, 0x01, 0x23, 0x01, 0x21, 0x01, 0x1F, 0x01, 0x1D,\n\t0x01, 0x1A, 0x01, 0x18, 0x01, 0x16, 0x01, 0x14, 0x01, 0x12, 0x01, 0x10, 0x01, 0x0D, 0x01, 0x0B, 0x01, 0x09, 0x01, 0x07, 0x01, 0x05, 0x01, 0x03, 0x01, 0x01, 0x01, 0xFF, 0x00, 0xFD, 0x00, 0xFB,\n\t0x00, 0xF9, 0x00, 0xF7, 0x00, 0xF5, 0x00, 0xF3, 0x00, 0xF2, 0x00, 0xF0, 0x00, 0xEE, 0x00, 0xEC, 0x00, 0xEA, 0x00, 0xE8, 0x00, 0xE6, 0x00, 0xE5, 0x00, 0xE3, 0x00, 0xE1, 0x00, 0xDF, 0x00, 0xDE,\n\t0x00, 0xDC, 0x00, 0xDA, 0x00, 0xD9, 0x00, 0xD7, 0x00, 0xD5, 0x00, 0xD4, 0x00, 0xD2, 0x00, 0xD0, 0x00, 0xCF, 0x00, 0xCD, 0x00, 0xCB, 0x00, 0xCA, 0x00, 0xC8, 0x00, 0xC7, 0x00, 0xC5, 0x00, 0xC4,\n\t0x00, 0xC2, 0x00, 0xC1, 0x00, 0xBF, 0x00, 0xBE, 0x00, 0xBC, 0x00, 0xBB, 0x00, 0xB9, 0x00, 0xB8, 0x00, 0xB6, 0x00, 0xB5, 0x00, 0xB4, 0x00, 0xB2, 0x00, 0xB1, 0x00, 0xAF, 0x00, 0xAE, 0x00, 0xAD,\n\t0x00, 0xAB, 0x00, 0xAA, 0x00, 0xA9, 0x00, 0xA7, 0x00, 0xA6, 0x00, 0xA5, 0x00, 0xA3, 0x00, 0xA2, 0x00, 0xA1, 0x00, 0xA0, 0x00, 0x9E, 0x00, 0x9D, 0x00, 0x9C, 0x00, 0x9B, 0x00, 0x9A, 0x00, 0x98,\n\t0x00, 0x97, 0x00, 0x96, 0x00, 0x95, 0x00, 0x94, 0x00, 0x93, 0x00, 0x91, 0x00, 0x90, 0x00, 0x8F, 0x00, 0x8E, 0x00, 0x8D, 0x00, 0x8C, 0x00, 0x8B, 0x00, 0x8A, 0x00, 0x89, 0x00, 0x87, 0x00, 0x86,\n\t0x00, 0x85, 0x00, 0x84, 0x00, 0x83, 0x00, 0x82, 0x00, 0x81, 0x00, 0x80, 0x00, 0x7F, 0x00, 0x7E, 0x00, 0x7D, 0x00, 0x7C, 0x00, 0x7B, 0x00, 0x7A, 0x00, 0x79, 0x00, 0x79, 0x00, 0x78, 0x00, 0x77,\n\t0x00, 0x76, 0x00, 0x75, 0x00, 0x74, 0x00, 0x73, 0x00, 0x72, 0x00, 0x71, 0x00, 0x70, 0x00, 0x6F, 0x00, 0x6F, 0x00, 0x6E, 0x00, 0x6D, 0x00, 0x6C, 0x00, 0x6B, 0x00, 0x6A, 0x00, 0x6A, 0x00, 0x69,\n\t0x00, 0x68, 0x00, 0x67, 0x00, 0x66, 0x00, 0x65, 0x00, 0x65, 0x00, 0x64, 0x00, 0x63, 0x00, 0x62, 0x00, 0x62, 0x00, 0x61, 0x00, 0x60, 0x00, 0x5F, 0x00, 0x5F, 0x00, 0x5E, 0x00, 0x5D, 0x00, 0x5C,\n\t0x00, 0x5C, 0x00, 0x5B, 0x00, 0x5A, 0x00, 0x5A, 0x00, 0x59, 0x00, 0x58, 0x00, 0x57, 0x00, 0x57, 0x00, 0x56, 0x00, 0x55, 0x00, 0x55, 0x00, 0x54, 0x00, 0x53, 0x00, 0x53, 0x00, 0x52, 0x00, 0x52,\n\t0x00, 0x51, 0x00, 0x50, 0x00, 0x50, 0x00, 0x4F, 0x00, 0x4E, 0x00, 0x4E, 0x00, 0x4D, 0x00, 0x4D, 0x00, 0x4C, 0x00, 0x4B, 0x00, 0x4B, 0x00, 0x4A, 0x00, 0x4A, 0x00, 0x49, 0x00, 0x49, 0x00, 0x48,\n\t0x00, 0x47, 0x00, 0x47, 0x00, 0x46, 0x00, 0x46, 0x00, 0x45, 0x00, 0x45, 0x00, 0x44, 0x00, 0x44, 0x00, 0x43, 0x00, 0x43, 0x00, 0x42, 0x00, 0x42, 0x00, 0x41, 0x00, 0x41, 0x00, 0x40, 0x00, 0x40,\n\t0x00, 0x3F, 0x00, 0x3F, 0x00, 0x3E, 0x00, 0x3E, 0x00, 0x3D, 0x00, 0x3D, 0x8D, 0x7C, 0x00, 0x01, 0x34, 0x00, 0x5F, 0x2B, 0x00, 0x8D, 0x25, 0x11, 0x38, 0x02, 0x00, 0x91, 0x37, 0x00, 0x37, 0x00,\n\t0x36, 0x00, 0x36, 0x00, 0x35, 0x02, 0x00, 0x51, 0x34, 0x00, 0x34, 0x00, 0x33, 0x02, 0x00, 0x51, 0x32, 0x00, 0x32, 0x00, 0x31, 0x02, 0x00, 0x11, 0x30, 0x02, 0x00, 0x51, 0x2F, 0x00, 0x2F, 0x00,\n\t0x2E, 0x02, 0x00, 0x11, 0x2D, 0x02, 0x00, 0x11, 0x2C, 0x02, 0x00, 0x11, 0x2B, 0x02, 0x00, 0x11, 0x2A, 0x02, 0x00, 0x11, 0x29, 0x02, 0x00, 0x11, 0x28, 0x02, 0x00, 0x13, 0x27, 0x02, 0x00, 0x11,\n\t0x26, 0x02, 0x00, 0x11, 0x25, 0x02, 0x00, 0x13, 0x24, 0x02, 0x00, 0x13, 0x23, 0x02, 0x00, 0x11, 0x22, 0x02, 0x00, 0x13, 0x21, 0x02, 0x00, 0x13, 0x20, 0x02, 0x00, 0x13, 0x1F, 0x02, 0x00, 0x15,\n\t0x1E, 0x02, 0x00, 0x13, 0x1D, 0x02, 0x00, 0x15, 0x1C, 0x02, 0x00, 0x13, 0x1B, 0x02, 0x00, 0x15, 0x1A, 0x02, 0x00, 0x15, 0x19, 0x02, 0x00, 0x17, 0x18, 0x02, 0x00, 0x15, 0x17, 0x02, 0x00, 0x17,\n\t0x16, 0x02, 0x00, 0x17, 0x15, 0x02, 0x00, 0x19, 0x14, 0x02, 0x00, 0x17, 0x13, 0x02, 0x00, 0x19, 0x12, 0x02, 0x00, 0x1B, 0x11, 0x02, 0x00, 0x1B, 0x10, 0x02, 0x00, 0x1D, 0x0F, 0x02, 0x00, 0x1D,\n\t0x0E, 0x02, 0x00, 0x2E, 0x0D, 0x00, 0x02, 0x00, 0x2E, 0x0C, 0x00, 0x02, 0x00, 0x1F, 0x0B, 0x02, 0x00, 0x04, 0x1F, 0x0A, 0x02, 0x00, 0x06, 0x1F, 0x09, 0x02, 0x00, 0x08, 0x1F, 0x08, 0x02, 0x00,\n\t0x0C, 0x1F, 0x07, 0x02, 0x00, 0x10, 0x1F, 0x06, 0x02, 0x00, 0x18, 0x1F, 0x05, 0x02, 0x00, 0x1E, 0x1F, 0x04, 0x02, 0x00, 0x2C, 0x1F, 0x03, 0x02, 0x00, 0x44, 0x1F, 0x02, 0x02, 0x00, 0x50, 0x1F,\n\t0x01, 0x02, 0x00, 0x7A, 0x0F, 0x01, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,\n\t0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,\n\t0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,\n\t0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xA2, 0xFF, 0xFF, 0xFF, 0xFF, 0xF4, 0x01, 0xFE, 0x02, 0xFE, 0x03, 0xFE, 0x04, 0xFE, 0x05, 0xFE, 0x06, 0xFE, 0x07, 0xFE, 0x08, 0xFE, 0x09, 0xFE, 0x0A, 0xFE,\n\t0x0B, 0xFE, 0x0C, 0xFE, 0x0D, 0xFE, 0x0E, 0xFE, 0x0F, 0xFE, 0x10, 0xFE, 0x11, 0xFE, 0x12, 0xFE, 0x13, 0xFE, 0x14, 0xFE, 0x15, 0xFE, 0x16, 0xFE, 0x17, 0xFE, 0x18, 0xFE, 0x19, 0xFE, 0x1A, 0xFE,\n\t0x1B, 0xFE, 0x1C, 0xFE, 0x1D, 0xFE, 0x1E, 0xFE, 0x1F, 0xFE, 0x20, 0xFE, 0x21, 0xFE, 0x22, 0xFE, 0x23, 0xFE, 0x24, 0xFE, 0x25, 0xFE, 0x26, 0xFE, 0x27, 0xFE, 0x28, 0xFE, 0x29, 0xFE, 0x2A, 0xFE,\n\t0x2B, 0xFE, 0x2C, 0xFE, 0x2D, 0xFE, 0x2E, 0xFE, 0x2F, 0xFE, 0x30, 0xFE, 0x31, 0xFE, 0x32, 0xFE, 0x33, 0xFE, 0x34, 0xFE, 0x35, 0xFE, 0x36, 0xFE, 0x37, 0xFE, 0x38, 0xFE, 0x39, 0xFE, 0x3A, 0xFE,\n\t0x3B, 0xFE, 0x3C, 0xFE, 0x3D, 0xFE, 0x3E, 0xFE, 0x3F, 0xFE, 0x40, 0xFE, 0x41, 0xFE, 0x42, 0xFE, 0x43, 0xFE, 0x44, 0xFE, 0x45, 0xFE, 0x46, 0xFE, 0x47, 0xFE, 0x48, 0xFE, 0x49, 0xFE, 0x4A, 0xFE,\n\t0x4B, 0xFE, 0x4C, 0xFE, 0x4D, 0xFE, 0x4E, 0xFE, 0x4F, 0xFE, 0x50, 0xFE, 0x51, 0xFE, 0x52, 0xFE, 0x53, 0xFE, 0x54, 0xFE, 0x55, 0xFE, 0x56, 0xFE, 0x57, 0xFE, 0x58, 0xFE, 0x59, 0xFE, 0x5A, 0xFE,\n\t0x5B, 0xFE, 0x5C, 0xFE, 0x5D, 0xFE, 0x5E, 0xFE, 0x5F, 0xFE, 0x60, 0xFE, 0x61, 0xFE, 0x62, 0xFE, 0x63, 0xFE, 0x64, 0xFE, 0x65, 0xFE, 0x66, 0xFE, 0x67, 0xFE, 0x68, 0xFE, 0x69, 0xFE, 0x6A, 0xFE,\n\t0x6B, 0xFE, 0x6C, 0xFE, 0x6D, 0xFE, 0x6E, 0xFE, 0x6F, 0xFE, 0x70, 0xFE, 0x71, 0xFE, 0x72, 0xFE, 0x73, 0xFE, 0x74, 0xFE, 0x75, 0xFE, 0x76, 0xFE, 0x77, 0xFE, 0x78, 0xFE, 0x79, 0xFE, 0x7A, 0xFE,\n\t0x7B, 0xFE, 0x7C, 0xFE, 0x7D, 0xFE, 0x7E, 0xFE, 0x7F, 0xFE, 0x80, 0xFE, 0x81, 0xFE, 0x82, 0xFE, 0x83, 0xFE, 0x84, 0xFE, 0x85, 0xFE, 0x86, 0xFE, 0x87, 0xFE, 0x88, 0xFE, 0x89, 0xFE, 0x8A, 0xFE,\n\t0x8B, 0xFE, 0x8C, 0xFE, 0x8D, 0xFE, 0x8E, 0xFE, 0x8F, 0xFE, 0x90, 0xFE, 0x91, 0xFE, 0x92, 0xFE, 0x93, 0xFE, 0x94, 0xFE, 0x95, 0xFE, 0x96, 0xFE, 0x97, 0xFE, 0x98, 0xFE, 0x99, 0xFE, 0x9A, 0xFE,\n\t0x9B, 0xFE, 0x9C, 0xFE, 0x9D, 0xFE, 0x9E, 0xFE, 0x9F, 0xFE, 0xA0, 0xFE, 0xA1, 0xFE, 0xA2, 0xFE, 0xA3, 0xFE, 0xA4, 0xFE, 0xA5, 0xFE, 0xA6, 0xFE, 0xA7, 0xFE, 0xA8, 0xFE, 0xA9, 0xFE, 0xAA, 0xFE,\n\t0xAB, 0xFE, 0xAC, 0xFE, 0xAD, 0xFE, 0xAE, 0xFE, 0xAF, 0xFE, 0xB0, 0xFE, 0xB1, 0xFE, 0xB2, 0xFE, 0xB3, 0xFE, 0xB4, 0xFE, 0xB5, 0xFE, 0xB6, 0xFE, 0xB7, 0xFE, 0xB8, 0xFE, 0xB9, 0xFE, 0xBA, 0xFE,\n\t0xBB, 0xFE, 0xBC, 0xFE, 0xBD, 0xFE, 0xBE, 0xFE, 0xBF, 0xFE, 0xC0, 0xFE, 0xC1, 0xFE, 0xC2, 0xFE, 0xC3, 0xFE, 0xC4, 0xFE, 0xC5, 0xFE, 0xC6, 0xFE, 0xC7, 0xFE, 0xC8, 0xFE, 0xC9, 0xFE, 0xCA, 0xFE,\n\t0xCB, 0xFE, 0xCC, 0xFE, 0xCD, 0xFE, 0xCE, 0xFE, 0xCF, 0xFE, 0xD0, 0xFE, 0xD1, 0xFE, 0xD2, 0xFE, 0xD3, 0xFE, 0xD4, 0xFE, 0xD5, 0xFE, 0xD6, 0xFE, 0xD7, 0xFE, 0xD8, 0xFE, 0xD9, 0xFE, 0xDA, 0xFE,\n\t0xDB, 0xFE, 0xDC, 0xFE, 0xDD, 0xFE, 0xDE, 0xFE, 0xDF, 0xFE, 0xE0, 0xFE, 0xE1, 0xFE, 0xE2, 0xFE, 0xE3, 0xFE, 0xE4, 0xFE, 0xE5, 0xFE, 0xE6, 0xFE, 0xE7, 0xFE, 0xE8, 0xFE, 0xE9, 0xFE, 0xEA, 0xFE,\n\t0xEB, 0xFE, 0xEC, 0xFE, 0xED, 0xFE, 0xEE, 0xFE, 0xEF, 0xFE, 0xF0, 0xFE, 0xF1, 0xFE, 0xF2, 0xFE, 0xF3, 0xFE, 0xF4, 0xFE, 0xF5, 0xFE, 0xF6, 0xFE, 0xF7, 0xFE, 0xF8, 0xFE, 0xF9, 0xFE, 0xFA, 0xFE,\n\t0xFB, 0xFE, 0xFC, 0xFE, 0xFD, 0xFE, 0xFE, 0xFE, 0xFF, 0xFE, 0x00, 0xFF, 0x01, 0xFF, 0x02, 0xFF, 0x03, 0xFF, 0x04, 0xFF, 0x05, 0xFF, 0x06, 0xFF, 0x07, 0xFF, 0x08, 0xFF, 0x09, 0xFF, 0x0A, 0xFF,\n\t0x0B, 0xFF, 0x0C, 0xFF, 0x0D, 0xFF, 0x0E, 0xFF, 0x0F, 0xFF, 0x10, 0xFF, 0x11, 0xFF, 0x12, 0xFF, 0x13, 0xFF, 0x14, 0xFF, 0x15, 0xFF, 0x16, 0xFF, 0x17, 0xFF, 0x18, 0xFF, 0x19, 0xFF, 0x1A, 0xFF,\n\t0x1B, 0xFF, 0x1C, 0xFF, 0x1D, 0xFF, 0x1E, 0xFF, 0x1F, 0xFF, 0x20, 0xFF, 0x21, 0xFF, 0x22, 0xFF, 0x23, 0xFF, 0x24, 0xFF, 0x25, 0xFF, 0x26, 0xFF, 0x27, 0xFF, 0x28, 0xFF, 0x29, 0xFF, 0x2A, 0xFF,\n\t0x2B, 0xFF, 0x2C, 0xFF, 0x2D, 0xFF, 0x2E, 0xFF, 0x2F, 0xFF, 0x30, 0xFF, 0x31, 0xFF, 0x32, 0xFF, 0x33, 0xFF, 0x34, 0xFF, 0x35, 0xFF, 0x36, 0xFF, 0x37, 0xFF, 0x38, 0xFF, 0x39, 0xFF, 0x3A, 0xFF,\n\t0x3B, 0xFF, 0x3C, 0xFF, 0x3D, 0xFF, 0x3E, 0xFF, 0x3F, 0xFF, 0x40, 0xFF, 0x41, 0xFF, 0x42, 0xFF, 0x43, 0xFF, 0x44, 0xFF, 0x45, 0xFF, 0x46, 0xFF, 0x47, 0xFF, 0x48, 0xFF, 0x49, 0xFF, 0x4A, 0xFF,\n\t0x4B, 0xFF, 0x4C, 0xFF, 0x4D, 0xFF, 0x4E, 0xFF, 0x4F, 0xFF, 0x50, 0xFF, 0x51, 0xFF, 0x52, 0xFF, 0x53, 0xFF, 0x54, 0xFF, 0x55, 0xFF, 0x56, 0xFF, 0x57, 0xFF, 0x58, 0xFF, 0x59, 0xFF, 0x5A, 0xFF,\n\t0x5B, 0xFF, 0x5C, 0xFF, 0x5D, 0xFF, 0x5E, 0xFF, 0x5F, 0xFF, 0x60, 0xFF, 0x61, 0xFF, 0x62, 0xFF, 0x63, 0xFF, 0x64, 0xFF, 0x65, 0xFF, 0x66, 0xFF, 0x67, 0xFF, 0x68, 0xFF, 0x69, 0xFF, 0x6A, 0xFF,\n\t0x6B, 0xFF, 0x6C, 0xFF, 0x6D, 0xFF, 0x6E, 0xFF, 0x6F, 0xFF, 0x70, 0xFF, 0x71, 0xFF, 0x72, 0xFF, 0x73, 0xFF, 0x74, 0xFF, 0x75, 0xFF, 0x76, 0xFF, 0x77, 0xFF, 0x78, 0xFF, 0x79, 0xFF, 0x7A, 0xFF,\n\t0x7B, 0xFF, 0x7C, 0xFF, 0x7D, 0xFF, 0x7E, 0xFF, 0x7F, 0xFF, 0x80, 0xFF, 0x81, 0xFF, 0x82, 0xFF, 0x83, 0xFF, 0x84, 0xFF, 0x85, 0xFF, 0x86, 0xFF, 0x87, 0xFF, 0x88, 0xFF, 0x89, 0xFF, 0x8A, 0xFF,\n\t0x8B, 0xFF, 0x8C, 0xFF, 0x8D, 0xFF, 0x8E, 0xFF, 0x8F, 0xFF, 0x90, 0xFF, 0x91, 0xFF, 0x92, 0xFF, 0x93, 0xFF, 0x94, 0xFF, 0x95, 0xFF, 0x96, 0xFF, 0x97, 0xFF, 0x98, 0xFF, 0x99, 0xFF, 0x9A, 0xFF,\n\t0x9B, 0xFF, 0x9C, 0xFF, 0x9D, 0xFF, 0x9E, 0xFF, 0x9F, 0xFF, 0xA0, 0xFF, 0xA1, 0xFF, 0xA2, 0xFF, 0xA3, 0xFF, 0xA4, 0xFF, 0xA5, 0xFF, 0xA6, 0xFF, 0xA7, 0xFF, 0xA8, 0xFF, 0xA9, 0xFF, 0xAA, 0xFF,\n\t0xAB, 0xFF, 0xAC, 0xFF, 0xAD, 0xFF, 0xAE, 0xFF, 0xAF, 0xFF, 0xB0, 0xFF, 0xB1, 0xFF, 0xB2, 0xFF, 0xB3, 0xFF, 0xB4, 0xFF, 0xB5, 0xFF, 0xB6, 0xFF, 0xB7, 0xFF, 0xB8, 0xFF, 0xB9, 0xFF, 0xBA, 0xFF,\n\t0xBB, 0xFF, 0xBC, 0xFF, 0xBD, 0xFF, 0xBE, 0xFF, 0xBF, 0xFF, 0xC0, 0xFF, 0xC1, 0xFF, 0xC2, 0xFF, 0xC3, 0xFF, 0xC4, 0xFF, 0xC5, 0xFF, 0xC6, 0xFF, 0xC7, 0xFF, 0xC8, 0xFF, 0xC9, 0xFF, 0xCA, 0xFF,\n\t0xCB, 0xFF, 0xCC, 0xFF, 0xCD, 0xFF, 0xCE, 0xFF, 0xCF, 0xFF, 0xD0, 0xFF, 0xD1, 0xFF, 0xD2, 0xFF, 0xD3, 0xFF, 0xD4, 0xFF, 0xD5, 0xFF, 0xD6, 0xFF, 0xD7, 0xFF, 0xD8, 0xFF, 0xD9, 0xFF, 0xDA, 0xFF,\n\t0xDB, 0xFF, 0xDC, 0xFF, 0xDD, 0xFF, 0xDE, 0xFF, 0xDF, 0xFF, 0xE0, 0xFF, 0xE1, 0xFF, 0xE2, 0xFF, 0xE3, 0xFF, 0xE4, 0xFF, 0xE5, 0xFF, 0xE6, 0xFF, 0xE7, 0xFF, 0xE8, 0xFF, 0xE9, 0xFF, 0xEA, 0xFF,\n\t0xEB, 0xFF, 0xEC, 0xFF, 0xED, 0xFF, 0xEE, 0xFF, 0xEF, 0xFF, 0xF0, 0xFF, 0xF1, 0xFF, 0xF2, 0xFF, 0xF3, 0xFF, 0xF4, 0xFF, 0xF5, 0xFF, 0xF6, 0xFF, 0xF7, 0xFF, 0xF8, 0xFF, 0xF9, 0xFF, 0xFA, 0xFF,\n\t0xFB, 0xFF, 0xFC, 0xFF, 0xFD, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0x00, 0xFE, 0x00, 0x04, 0xFF, 0xFF, 0xFF, 0xE9, 0x50, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF,\n};"
  },
  {
    "path": "Whisper/ML/MlContext.cpp",
    "content": "#include \"stdafx.h\"\n#include \"MlContext.h\"\n#include \"../D3D/shaderNames.h\"\n#include \"LookupTables.h\"\n#include \"../D3D/shaders.h\"\n#include \"../D3D/Binder.h\"\n#include \"../D3D/MappedResource.h\"\n#include \"../D3D/downloadBuffer.h\"\n#include \"testUtils.h\"\n#include \"reshapedMultiply.h\"\nusing namespace DirectCompute;\n\n// TODO: change this to a field, and set to false when the GPU doesn't support FP64 math\n// Most notably, Intel has dropped the support recently:\n// https://www.intel.com/content/www/us/en/developer/articles/guide/lp-api-developer-optimization-guide.html#inpage-nav-3-8-undefined\n// \"To improve power and performance\", LOL\nconstexpr bool usePreciseComputeShaders = true;\n\nMlContext::MlContext( Whisper::ProfileCollection& profileColl ) :\n\tprofiler( profileColl )\n{\n\tcheck( cb.create() );\n\tcheck( profiler.create() );\n}\n\nvoid MlContext::bindShader( eComputeShader cs )\n{\n\tDirectCompute::bindShader( cs );\n\tprofiler.computeShader( cs );\n}\n\nvoid MlContext::mulMatDot( const Tensor& src0, const Tensor& src1, Tensor& res )\n{\n\tconst auto& size1 = src1.ne;\n\tif( 1 != size1[ 3 ] )\n\t\tthrow E_UNEXPECTED;\n\n\tconst size_t tempLength = size1[ 0 ] * size1[ 1 ] * size1[ 2 ] * size1[ 3 ];\n\tconst TensorGpuViews& tempBuffer = temp.fp16( tempLength );\n\tcb.bind();\n\n\tbindShader( eComputeShader::mulMatDotReshape );\n\tcb.update( src1 );\n\tBinder bind;\n\tbind.bind( src1, tempBuffer );\n\tcontext()->Dispatch( size1[ 1 ], size1[ 2 ], 1 );\n\n\tbindShader( eComputeShader::mulMatDotMain );\n\tcb.update( src0, src1, res );\n\tbind.bind( src0, tempBuffer, res );\n\n\tconst auto& size0 = src0.ne;\n\t// total rows in src0\n\tconst uint32_t nr = size0[ 1 ] * size0[ 2 ] * size0[ 3 ];\n\tcontext()->Dispatch( size1[ 1 ], nr, 1 );\n}\n\nvoid MlContext::mulMatMad( const Tensor& a, const Tensor& b, Tensor& res )\n{\n\t// CaptureRaii renderDoc;\n\tconst uint32_t resultElts = res.countElements();\n\tconstexpr uint32_t nth = 4;\n\n\tuint32_t fp16;\n\tTensorGpuViews tempBuffer;\n\n\tconst eDataType dataType = a.getType();\n\tif( dataType == eDataType::FP16 )\n\t{\n\t\tfp16 = TRUE;\n\t\ttempBuffer = temp.fp16( resultElts * nth );\n\t}\n\telse if( dataType == eDataType::FP32 )\n\t{\n\t\tfp16 = FALSE;\n\t\ttempBuffer = temp.fp32( resultElts * nth );\n\t}\n\telse\n\t\tthrow E_INVALIDARG;\n\n\tTensorShape resultShape = res;\n\tresultShape.nb = { fp16, resultElts, 0, 0 };\n\n\tcb.update( a, b, resultShape );\n\tbindShader( eComputeShader::mulMatMadMain );\n\tcb.bind();\n\n\tBinder bind;\n\tbind.bind( { a, b }, { res, tempBuffer } );\n\tcontext()->Dispatch( b.ne[ 1 ], b.ne[ 2 ], b.ne[ 3 ] );\n}\n\nvoid MlContext::mulMatTiled( const Tensor& a, const Tensor& b, Tensor& res )\n{\n\tcb.update( a, b, res );\n\tcb.bind();\n\n\tBinder bind;\n\tbind.bind( a, b, res );\n\n\tif( b.ne[ 1 ] == 1 )\n\t{\n\t\tif( b.ne[ 0 ] != 1 )\n\t\t{\n#if 0\n\t\t\tstatic PrintUniqueTensorSizes printSize( \"mulMatByRow\" );\n\t\t\tprintSize.print( a, b );\n#endif\n\t\t\t// Tensor B is a single row, we have optimized compute shaders for that use case\n\t\t\t// Even 2 of them, tiled and sequential. Select between these two shaders.\n\t\t\tconstexpr uint32_t minHeightToTile = 2;\n\t\t\tif( a.ne[ 1 ] < minHeightToTile )\n\t\t\t{\n\t\t\t\tbindShader( eComputeShader::mulMatByRow );\n\t\t\t\tcontext()->Dispatch( a.ne[ 1 ], a.ne[ 2 ], a.ne[ 3 ] );\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tbindShader( eComputeShader::mulMatByRowTiled );\n\t\t\t\tconstexpr uint32_t TILE_Y = 64;\n\t\t\t\tconst uint32_t groupsX = ( a.ne[ 1 ] + TILE_Y - 1 ) / TILE_Y;\n\t\t\t\tcontext()->Dispatch( groupsX, a.ne[ 2 ], a.ne[ 3 ] );\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// Tensor B is a single element: we have an optimized shader for that as well\n\t\t\tbindShader( eComputeShader::mulMatByScalar );\n\t\t\tcontext()->Dispatch( a.ne[ 2 ], a.ne[ 3 ], 1 );\n\t\t}\n\t}\n\telse\n\t{\n\t\t// According to visual studio debugger, when the second argument of this method is a 2D matrix, the first argument is 2D as well.\n\t\t// Assuming both arguments are 2D matrices.\n\t\t// For optimal VRAM bandwidth utilization, we compute such matrix products in square tiles, a tile is 32x32 elements.\n\t\t// Dispatching one thread group for each tile of the output matrix.\n\t\tbindShader( eComputeShader::mulMatTiled );\n\n\t\t// These compute shaders correctly handle partial tiles on the right and bottom edges of the output matrix, that's why rounding up\n\t\tconstexpr uint32_t TILE_SIZE = 32;\n\t\tconst uint32_t x = ( res.ne[ 0 ] + TILE_SIZE - 1 ) / TILE_SIZE;\n\t\tconst uint32_t y = ( res.ne[ 1 ] + TILE_SIZE - 1 ) / TILE_SIZE;\n\n\t\tconst uint32_t z = res.ne[ 2 ] * res.ne[ 3 ];\n\t\tcontext()->Dispatch( x, y, z );\n\t}\n}\n\nvoid MlContext::mulMat( const Tensor& src0, const Tensor& src1, Tensor& res )\n{\n\tconst uint32_t nb00 = src0.nb[ 0 ];\n\tconst uint32_t nb01 = src0.nb[ 1 ];\n\tif( nb01 >= nb00 )\n\t\tmulMatDot( src0, src1, res );\n\telse\n\t\tmulMatMad( src0, src1, res );\n}\n\nnamespace\n{\n\t// Must match the HLSL in flashAttention.hlsl\n\tstruct sFlashAttentionConstants\n\t{\n\t\tTensorShape q, k, v, res;\n\t\tBOOL masked;\n\t\tfloat scale;\n\t\tuint32_t tempBufferStride;\n\t\tuint32_t zzPadding;\n\t};\n\n\tstruct sFlashAttnDispatchInfo\n\t{\n\t\tuint32_t tempStride;\n\t\tuint32_t groupsCount;\n\t};\n\n\tsFlashAttnDispatchInfo makeFlashAttentionConstants( CComPtr<ID3D11Buffer>& buffer, const Tensor& q, const Tensor& k, const Tensor& v, Tensor& res, bool masked )\n\t{\n\t\tif( nullptr == buffer )\n\t\t{\n\t\t\tCD3D11_BUFFER_DESC desc{ sizeof( sFlashAttentionConstants ), D3D11_BIND_CONSTANT_BUFFER, D3D11_USAGE_DYNAMIC, D3D11_CPU_ACCESS_WRITE };\n\t\t\tcheck( device()->CreateBuffer( &desc, nullptr, &buffer ) );\n\t\t}\n\n\t\tsFlashAttnDispatchInfo result;\n\n\t\tsFlashAttentionConstants cb;\n\t\tcb.q = q;\n\t\tcb.k = k;\n\t\tcb.v = v;\n\t\tcb.res = res;\n\t\tcb.masked = masked ? TRUE : FALSE;\n\n\t\tconst int neq0 = (int)cb.q.ne[ 0 ];\n\t\tconst int D = neq0;\n\t\tcb.scale = (float)( 1.0 / sqrt( (double)(int)D ) );\n\n\t\tconst uint32_t nek1 = cb.k.ne[ 1 ];\n\t\tconstexpr uint32_t align = 32 / 4;\n\t\tresult.tempStride = ( ( nek1 + align - 1 ) / align ) * align;\n\t\tcb.tempBufferStride = result.tempStride;\n\t\tcb.zzPadding = 0;\n\t\tresult.groupsCount = cb.q.ne[ 1 ] * cb.q.ne[ 2 ] * cb.q.ne[ 3 ];\n\n\t\tMappedResource mapped;\n\t\tcheck( mapped.map( buffer, false ) );\n\t\tmemcpy( mapped.data(), &cb, sizeof( cb ) );\n\t\treturn result;\n\t}\n}\n\nvoid MlContext::flashAttention( const Tensor& q, const Tensor& k, const Tensor& v, Tensor& res, bool masked )\n{\n\tsFlashAttnDispatchInfo di = makeFlashAttentionConstants( flashAttentionConstants, q, k, v, res, masked );\n\n\tconst uint32_t tempLength = di.tempStride * di.groupsCount;\n\tconst TensorGpuViews& tb = temp.fp32( tempLength );\n\n\tcsSetCB( flashAttentionConstants );\n\tID3D11DeviceContext* const ctx = context();\n\n\tBinder bind;\n\tbind.bind( { q, k, v, lookupTables().exponent()}, {res, tb});\n\n\tif constexpr( usePreciseComputeShaders && !enableInexactOptimizations )\n\t{\n\t\tbindShader( eComputeShader::flashAttentionCompat1 );\n\t\tctx->Dispatch( di.groupsCount, 1, 1 );\n\n\t\tbindShader( eComputeShader::flashAttentionCompat2 );\n\t\tctx->Dispatch( ( di.groupsCount + 31 ) / 32, 1, 1 );\n\n\t\tbindShader( eComputeShader::flashAttentionCompat3 );\n\t\tctx->Dispatch( di.groupsCount, 1, 1 );\n\t}\n\telse\n\t{\n\t\t// This version is not too bad, e.g. maxAbsDiff = 2.7895e-05, avgDiffSquared = 1.61783e-14\n\t\t// And probably much faster.\n\t\t// But still, it does not deliver bitwise equality with the reference CPU version\n\t\tbindShader( eComputeShader::flashAttention );\n\t\tctx->Dispatch( di.groupsCount, 1, 1 );\n\t}\n}\n\nnamespace\n{\n\t// Round up the number to be a multiple of 32\n\tinline uint32_t roundUp32( uint32_t x )\n\t{\n\t\treturn ( x + 31 ) & ( ~31u );\n\t}\n}\n\nvoid MlContext::convolutionImpl( const Tensor& a, const Tensor& b, Tensor& res, bool is2 )\n{\n\tconst uint32_t ne00 = a.ne[ 0 ];\n\tconst uint32_t ne01 = a.ne[ 1 ];\n\tconst uint32_t ne02 = a.ne[ 2 ];\n\n\tconst uint32_t ne10 = b.ne[ 0 ];\n\tconst uint32_t ne11 = b.ne[ 1 ];\n\n\tconst uint32_t nb00 = a.nb[ 0 ];\n\tconst uint32_t nb01 = a.nb[ 1 ];\n\tconst uint32_t nb02 = a.nb[ 2 ];\n\n\tconst uint32_t nb10 = b.nb[ 0 ];\n\tconst uint32_t nb11 = b.nb[ 1 ];\n\n\tconst uint32_t nb1 = res.nb[ 1 ];\n\n\tconst uint32_t ew0 = roundUp32( ne01 );\n\n\tconst uint32_t nk = ne00;\n\tconst uint32_t nh = nk / 2;\n\n\tconst uint32_t lenTemp1 = ne02 * ew0 * ne00;\n\tconst uint32_t lenTemp2 = ( ne10 + ne00 ) * ew0;\n\n\tconst TensorGpuViews& temp1 = temp.fp16( lenTemp1, true );\n\tconst TensorGpuViews& temp2 = temp.fp16_2( lenTemp2, true );\n\n\tcb.bind();\n\n\tbindShader( eComputeShader::convolutionPrep1 );\n\tcb.update( a );\n\tBinder bind;\n\tbind.bind( a, temp1 );\n\tcontext()->Dispatch( ne01, ne02, 1 );\n\n\tbindShader( eComputeShader::convolutionPrep2 );\n\tcb.update( a, b );\n\tbind.bind( b, temp2 );\n\tcontext()->Dispatch( ne11, 1, 1 );\n\n\tcb.update( a, b, res );\n\tbind.bind( temp1, temp2, res );\n\tif( is2 )\n\t{\n\t\tif constexpr( enableInexactOptimizations )\n\t\t{\n\t\t\tconstexpr uint32_t KERNEL = 3;\n\t\t\tconstexpr uint32_t TILE_Y = 8;\n\t\t\tif( a.ne[ 0 ] == KERNEL )\n\t\t\t{\n\t\t\t\tconst uint32_t x = ( ( ne10 / 2 ) + TILE_Y - 1 ) / TILE_Y;\n\t\t\t\tbindShader( eComputeShader::convolutionMain2Fixed );\n\t\t\t\tcontext()->Dispatch( x, ne02, 1 );\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tbindShader( eComputeShader::convolutionMain2 );\n\t\tcontext()->Dispatch( ne10 / 2, ne02, 1 );\n\t}\n\telse\n\t{\n\t\tbindShader( eComputeShader::convolutionMain );\n\t\tcontext()->Dispatch( ne10, ne02, 1 );\n\t}\n#if 0\n\tstd::vector<uint16_t> tmp;\n\tdownloadBuffer( temp1, tmp );\n\tdbgWriteBinaryFile( L\"conv-gpu-arg1.bin\", tmp.data(), lenTemp1 * 2 );\n\tdownloadBuffer( temp2, tmp );\n\tdbgWriteBinaryFile( L\"conv-gpu-arg2.bin\", tmp.data(), lenTemp1 * 2 );\n\tres.download( tempVector );\n\tdbgWriteBinaryFile( L\"conv-gpu-result.bin\", tempVector.data(), tempVector.size() * 4 );\n#endif\n}\n\nvoid MlContext::norm( const Tensor& a, Tensor& res )\n{\n\tconst uint32_t ne01 = a.ne[ 1 ];\n\tconst uint32_t ne02 = a.ne[ 2 ];\n\tconst uint32_t ne03 = a.ne[ 3 ];\n\n\tcb.bind();\n\tcb.update( a, res );\n\tBinder bind;\n\tbind.bind( a, res );\n\n\tif constexpr( usePreciseComputeShaders && !enableInexactOptimizations )\n\t{\n\t\tbindShader( eComputeShader::normCompat );\n\t\tcontext()->Dispatch( ( ne01 + 31 ) / 32, ne02, ne03 );\n\t}\n\telse\n\t{\n\t\tconstexpr uint32_t FIXED_ROW_SIZE = 1024;\n\t\teComputeShader cs = ( a.ne[ 0 ] == FIXED_ROW_SIZE ) ? eComputeShader::normFixed : eComputeShader::norm;\n\t\tbindShader( cs );\n\t\tcontext()->Dispatch( ne01, ne02, ne03 );\n\t}\n}\n\nvoid MlContext::cwiseBinary( const Tensor& a, const Tensor& b, Tensor& res, eComputeShader cs )\n{\n\tassert( isSameShape( a, b ) );\n\tassert( isSameShape( a, res ) );\n\n\tbindShader( cs );\n\tcb.bind();\n\tcheck( cb.update( a, b, res ) );\n\tBinder bind;\n\tbind.bind( a, b, res );\n\n\tuint32_t rows = a.countRows();\n\tcontext()->Dispatch( rows, 1, 1 );\n}\n\nTensor MlContext::add( const Tensor& a, const Tensor& b )\n{\n\treturn cwiseBinary( a, b, eComputeShader::add );\n}\n\nvoid MlContext::addInPlace( Tensor& a, const Tensor& b )\n{\n\tif( !isSameShape( a, b ) )\n\t\tthrow E_INVALIDARG;\n\tassert( a.getType() == eDataType::FP32 );\n\n\tcheck( cb.update( a, b ) );\n\tbindShader( eComputeShader::addInPlace );\n\tcb.bind();\n\n\tBinder bind;\n\tbind.bind( b, a );\n\tcontext()->Dispatch( a.ne[ 1 ], a.ne[ 2 ], a.ne[ 3 ] );\n}\n\nvoid MlContext::copyImpl( const Tensor& a, Tensor& res, bool downcastFp32 )\n{\n\tassert( res.isContinuous() );\n\tconst eComputeShader cs = a.isContinuous() ? eComputeShader::copyConvert : eComputeShader::copyTranspose;\n\tbindShader( cs );\n\n\tcb.bind();\n\t// These two shaders don't need shape of the destination because dense, but they wants a boolean flag whether to implement rounding while downcasting\n\tTensorShape dummyShape;\n\tdummyShape.setZero();\n\tdummyShape.ne[ 0 ] = downcastFp32 ? TRUE : FALSE;\n\tcheck( cb.update( a, dummyShape ) );\n\n\tBinder bind;\n\tbind.bind( a, res );\n\tcontext()->Dispatch( a.ne[ 1 ], a.ne[ 2 ], a.ne[ 3 ] );\n}\n\nnamespace\n{\n\tuint32_t bitcast( float val )\n\t{\n\t\t__m128 f = _mm_set_ss( val );\n\t\t__m128i i = _mm_castps_si128( f );\n\t\treturn (uint32_t)_mm_cvtsi128_si32( i );\n\t}\n}\n\nvoid MlContext::scale( Tensor& a, float mul )\n{\n\tif( !a.isContinuous() )\n\t\tthrow E_INVALIDARG;\n\n\tbindShader( eComputeShader::scaleInPlace );\n\tcb.bind();\n\tTensorShape dummyShape;\n\tdummyShape.setZero();\n\tdummyShape.ne[ 0 ] = bitcast( mul );\n\tcheck( cb.update( a, dummyShape ) );\n\n\tBinder bind;\n\tbind.bind( a );\n\tcontext()->Dispatch( a.countRows(), 1, 1 );\n}\n\nvoid MlContext::addRepeat( Tensor& a, const Tensor& b )\n{\n\tcheck( cb.update( a, b ) );\n\tbindShader( eComputeShader::addRepeat );\n\tcb.bind();\n\n\tBinder bind;\n\tbind.bind( b, a );\n\tcontext()->Dispatch( a.ne[ 1 ], a.ne[ 2 ], a.ne[ 3 ] );\n}\n\nvoid MlContext::addRepeatScale( Tensor& a, const Tensor& b, float scale )\n{\n#if 0\n\taddRepeat( a, b );\n\tthis->scale( a, scale );\n\treturn;\n#endif\n\n\tTensorShape dummyShape;\n\tdummyShape.setZero();\n\tdummyShape.ne[ 0 ] = bitcast( scale );\n\tcheck( cb.update( a, b, dummyShape ) );\n\tbindShader( eComputeShader::addRepeatScale );\n\tcb.bind();\n\n\tBinder bind;\n\tbind.bind( b, a );\n\tcontext()->Dispatch( a.ne[ 1 ], a.ne[ 2 ], a.ne[ 3 ] );\n}\n\nvoid MlContext::fmaRepeat( Tensor& a, const Tensor& mul, const Tensor& add )\n{\n\teComputeShader cs;\n\tif( isSameShapeAndLayout( mul, add ) )\n\t{\n\t\tcs = eComputeShader::fmaRepeat1;\n\t\tcheck( cb.update( a, mul ) );\n\t}\n\telse\n\t{\n\t\tcs = eComputeShader::fmaRepeat2;\n\t\tcheck( cb.update( a, mul, add ) );\n\t}\n\n\tbindShader( cs );\n\tcb.bind();\n\tBinder bind;\n\tbind.bind( mul, add, a );\n\tcontext()->Dispatch( a.ne[ 1 ], a.ne[ 2 ], a.ne[ 3 ] );\n}\n\nvoid MlContext::diagMaskInf( Tensor& a, uint32_t n_past )\n{\n\tif( !a.isContinuous() )\n\t\tthrow E_INVALIDARG;\n\n\tbindShader( eComputeShader::diagMaskInf );\n\tTensorShape dummyShape;\n\tdummyShape.setZero();\n\tdummyShape.ne[ 0 ] = n_past;\n\n\tcb.bind();\n\tcheck( cb.update( a, dummyShape ) );\n\n\tBinder bind;\n\tbind.bind( a );\n\n\tconst uint32_t n = a.countRows();\n\tconst uint32_t nr = a.ne[ 1 ];\n\tconst uint32_t nz = n / nr;\n\tcontext()->Dispatch( nr, nz, 1 );\n}\n\nvoid MlContext::softMax( Tensor& a, float inputScale )\n{\n\tif( !a.isContinuous() )\n\t\tthrow E_INVALIDARG;\n\n\tif constexpr( usePreciseComputeShaders && !enableInexactOptimizations )\n\t{\n\t\tassert( inputScale == 1.0f );\n\t\tbindShader( eComputeShader::softMaxCompat );\n\t\tconst uint32_t nr = a.countRows();\n\t\tTensorShape dummyShape;\n\t\tdummyShape.setZero();\n\t\tdummyShape.ne[ 0 ] = nr;\n\n\t\tcb.bind();\n\t\tcheck( cb.update( a, dummyShape ) );\n\n\t\tBinder bind;\n\t\tbind.bind( lookupTables().exponent(), a);\n\t\tcontext()->Dispatch( ( nr + 31 ) / 32, 1, 1 );\n\t}\n\telse\n\t{\n#if 0\n\t\tstatic PrintUniqueTensorSizes printSizes( \"softMax\" );\n\t\tprintSizes.print( a );\n#endif\n\t\tconstexpr uint32_t FIXED_ROW_SIZE = 1500;\n\n\t\teComputeShader cs;\n\t\tif( a.ne[ 0 ] == FIXED_ROW_SIZE )\n\t\t\tcs = eComputeShader::softMaxFixed;\n\t\telse if( a.ne[ 0 ] >= ( 1024 * 4 ) )\n\t\t\tcs = eComputeShader::softMaxLong;\n\t\telse\n\t\t\tcs = eComputeShader::softMax;\n\n\t\tbindShader( cs );\n\t\tconst uint32_t nr = a.countRows();\n\t\tTensorShape dummyShape;\n\t\tdummyShape.setZero();\n\t\tdummyShape.ne[ 0 ] = nr;\n\t\tdummyShape.ne[ 1 ] = bitcast( inputScale );\n\n\t\tcb.bind();\n\t\tcheck( cb.update( a, dummyShape ) );\n\n\t\tBinder bind;\n\t\tbind.bind( lookupTables().exponent(), a);\n\t\tcontext()->Dispatch( nr, 1, 1 );\n\t}\n}\n\nvoid MlContext::addRepeatGelu( Tensor& a, const Tensor& b )\n{\n\tcheck( cb.update( a, b ) );\n\tbindShader( eComputeShader::addRepeatGelu );\n\tcb.bind();\n\n\tBinder bind;\n\tbind.bind( b, lookupTables().gelu(), a);\n\tcontext()->Dispatch( a.ne[ 1 ], a.ne[ 2 ], a.ne[ 3 ] );\n}\n\nnamespace\n{\n\tinline bool canAddRows( const Tensor& tokenEmbedding, const Tensor& positionalEmbedding, const Tensor& embd, uint32_t pastTokensCount )\n\t{\n\t\tif( tokenEmbedding.ne[ 0 ] != positionalEmbedding.ne[ 0 ] )\n\t\t\treturn false;\t// Different row lengths\n\t\tif( embd.ne[ 0 ] + pastTokensCount > positionalEmbedding.ne[ 1 ] )\n\t\t\treturn false;\t// Too many rows requested, positionalEmbedding matrix doesn't have that many \n\t\treturn true;\n\t}\n}\n\nTensor MlContext::addRows( const Tensor& tokenEmbedding, const Tensor& positionalEmbedding, const Tensor& embd, uint32_t pastTokensCount )\n{\n\tif( !canAddRows( tokenEmbedding, positionalEmbedding, embd, pastTokensCount ) )\n\t\tthrow E_INVALIDARG;\n\n\tconst uint32_t rowLength = tokenEmbedding.ne[ 0 ];\n\tconst uint32_t rows = embd.ne[ 0 ];\n\tTensor result = createTensor( eDataType::FP32, { rowLength, rows } );\n\n\tTensorShape constants;\n\t// rowLength\n\tconstants.ne[ 0 ] = rowLength;\n\t// pastTokensCount\n\tconstants.ne[ 1 ] = pastTokensCount;\n\t// outputRowStride\n\tconstants.ne[ 2 ] = result.nb[ 1 ];\n\t// embStrides\n\tconstants.nb[ 0 ] = tokenEmbedding.nb[ 0 ];\n\tconstants.nb[ 1 ] = tokenEmbedding.nb[ 1 ];\n\t// posStrides\n\tconstants.nb[ 2 ] = positionalEmbedding.nb[ 0 ];\n\tconstants.nb[ 3 ] = positionalEmbedding.nb[ 1 ];\n\tcheck( cb.update( constants ) );\n\n\tbindShader( eComputeShader::addRows );\n\tcb.bind();\n\tBinder bind;\n\tbind.bind( { tokenEmbedding, positionalEmbedding, embd }, { result } );\n\tcontext()->Dispatch( rows, 1, 1 );\n\treturn result;\n}\n\nTensor MlContext::reshapePanels( const Tensor& a )\n{\n\tconstexpr uint32_t TILE_SIZE = ReshapedMultiply::TILE_SIZE;\n\n\tconst eDataType dataType = a.getType();\n\t// Reshaping into column major horizontal panels, height = TILE_SIZE, width = width of the source matrix\n\n\t// Round height to multiple of tile size\n\tstd::array<uint32_t, 4> ne = a.ne;\n\t// Dispatch a group of threads thread per panel\n\tconst uint32_t groupsX = ( ne[ 1 ] + TILE_SIZE - 1 ) / TILE_SIZE;\n\tne[ 1 ] = groupsX * TILE_SIZE;;\n\t// Each panel has [ size.x, TILE_SIZE ] elements\n\tconst uint32_t panelSize = ne[ 0 ] * TILE_SIZE;\n\n\tTensor result = createTensor( dataType, ne );\n\n\tTensorShape constants;\n\tconstants.setZero();\n\t// uint panelSize : packoffset( c2.y );\n\tconstants.ne[ 1 ] = panelSize;\n\t// uint2 layerStrides: packoffset( c2.z );\n\tconstants.ne[ 2 ] = result.nb[ 2 ];\n\tconstants.ne[ 3 ] = result.nb[ 3 ];\n\n\tcheck( cb.update( a, constants ) );\n\tbindShader( eComputeShader::matReshapePanels );\n\tcb.bind();\n\n\tBinder bind;\n\tbind.bind( a, result );\n\tcontext()->Dispatch( groupsX, a.ne[ 2 ], a.ne[ 3 ] );\n\n#if 0\n\tif( dataType == eDataType::FP32 )\n\t{\n\t\tstd::vector<float> v1, v2;\n\t\ta.download( v1 );\n\t\tresult.download( v2 );\n\t\t__debugbreak();\n\t}\n\telse if( dataType == eDataType::FP16 )\n\t{\n\t\tstd::vector<uint16_t> v1, v2;\n\t\ta.download( v1 );\n\t\tresult.download( v2 );\n\t\t__debugbreak();\n\t}\n#endif\n\n\t// Set up size and stride expected by the mulMatTiledEx compute shader\n\tresult.ne = a.ne;\n\tresult.nb[ 0 ] = 0;\n\tresult.nb[ 1 ] = panelSize;\n\treturn result;\n}\n\nTensor MlContext::mulMatTiledEx( const Tensor& a, const Tensor& b )\n{\n\tconstexpr uint32_t TILE_SIZE = ReshapedMultiply::TILE_SIZE;\n\n\tif( !canMulMat( a, b ) )\n\t\tthrow E_INVALIDARG;\t// Wrong size\n\tif( 0 != ( a.nb[ 0 ] | b.nb[ 0 ] ) )\n\t\tthrow E_INVALIDARG;\t// Both tensors are expected to be pre-transposed into these panels\n\n\tTensor res = createTensor( eDataType::FP32, { a.ne[ 1 ], b.ne[ 1 ], a.ne[ 2 ], b.ne[ 3 ] } );\n\n\tcheck( cb.update( a, b, res ) );\n\tbindShader( eComputeShader::mulMatTiledEx );\n\tcb.bind();\n\n\tBinder bind;\n\tbind.bind( a, b, res );\n\n\tconst uint32_t x = ( res.ne[ 0 ] + TILE_SIZE - 1 ) / TILE_SIZE;\n\tconst uint32_t y = ( res.ne[ 1 ] + TILE_SIZE - 1 ) / TILE_SIZE;\n\tconst uint32_t z = res.ne[ 2 ] * res.ne[ 3 ];\n\tcontext()->Dispatch( x, y, z );\n\n\treturn res;\n}\n\nTensor MlContext::mulMatByRowTiledEx( const Tensor& a, const Tensor& b )\n{\n\tconstexpr uint32_t TILE_SIZE = ReshapedMultiply::TILE_SIZE;\n\tassert( canMulMat( a, b ) );\n\tassert( b.ne[ 1 ] == 1 );\n\n\tTensor res = createTensor( eDataType::FP32, { a.ne[ 1 ], 1, a.ne[ 2 ], b.ne[ 3 ] } );\n\n\tcheck( cb.update( a, b, res ) );\n\tbindShader( eComputeShader::mulMatByRowTiledEx );\n\tcb.bind();\n\n\tBinder bind;\n\tbind.bind( a, b, res );\n\n\tconst uint32_t x = ( res.ne[ 0 ] + TILE_SIZE - 1 ) / TILE_SIZE;\n\tconst uint32_t y = res.ne[ 2 ];\n\tconst uint32_t z = res.ne[ 3 ];\n\tcontext()->Dispatch( x, y, z );\n\n\treturn res;\n}\n\nvoid MlContext::addRepeatEx( Tensor& dest, const Tensor& pattern, const Tensor& finalAdd )\n{\n\tif( !isSameShape( dest, finalAdd ) )\n\t\tthrow E_INVALIDARG;\n\tassert( dest.getType() == eDataType::FP32 );\n\n\tcheck( cb.update( dest, pattern, finalAdd ) );\n\tbindShader( eComputeShader::addRepeatEx );\n\tcb.bind();\n\n\tBinder bind;\n\tbind.bind( pattern, finalAdd, dest );\n\tcontext()->Dispatch( dest.ne[ 1 ], dest.ne[ 2 ], dest.ne[ 3 ] );\n}\n\n__m128i MlContext::getMemoryUse() const\n{\n\t__m128i v = cb.getMemoryUse();\n\tv = _mm_add_epi64( v, temp.getMemoryUse() );\n\tv = _mm_add_epi64( v, bufferMemoryUsage( flashAttentionConstants ) );\n\tv = _mm_add_epi64( v, lookupTables().getMemoryUsage());\n\treturn v;\n}"
  },
  {
    "path": "Whisper/ML/MlContext.dbg.cpp",
    "content": "#include \"stdafx.h\"\n#include \"MlContext.h\"\n#include \"../source/ggml.h\"\n#include \"testUtils.h\"\nusing namespace DirectCompute;\n\n#define E_TYPE HRESULT_FROM_WIN32( ERROR_DATATYPE_MISMATCH )\n\nstatic void dbgPrintSizeDiff( const char* what, __m128i ref, __m128i gpu )\n{\n\tstd::array<int, 8> a;\n\t_mm_storeu_si128( ( __m128i* ) & a[ 0 ], ref );\n\t_mm_storeu_si128( ( __m128i* ) & a[ 4 ], gpu );\n\tprintf( \"%s; reference [ %i, %i, %i, %i ], GPGPU [ %i, %i, %i, %i ]\\n\",\n\t\twhat,\n\t\ta[ 0 ], a[ 1 ], a[ 2 ], a[ 3 ],\n\t\ta[ 4 ], a[ 5 ], a[ 6 ], a[ 7 ] );\n}\n\nvoid MlContext::dbgPrintDifference( const ggml_tensor* reference, const Tensor& gpu, const char* what, bool trapToDebugger )\n{\n\tsTensorDiff diff;\n\tconst __m128i gpuSize = gpu.sizeVec();\n\tconst __m128i gpuStrides = gpu.stridesVec();\n\t__m128i expectedStrides;\n\tif( reference->type == GGML_TYPE_F32 )\n\t{\n\t\tif( gpu.getType() != eDataType::FP32 )\n\t\t\tthrow E_TYPE;\n\t\texpectedStrides = _mm_slli_epi32( gpuStrides, 2 );\n\n\t\tstd::vector<float> v;\n\t\tgpu.download( v );\n\t\tdiff = computeDiff( v.data(), (const float*)reference->data, v.size() );\n\t}\n\telse if( reference->type == GGML_TYPE_F16 )\n\t{\n\t\tif( gpu.getType() != eDataType::FP16 )\n\t\t\tthrow E_TYPE;\n\t\texpectedStrides = _mm_slli_epi32( gpuStrides, 1 );\n\n\t\tstd::vector<uint16_t> v;\n\t\tgpu.download( v );\n\t\tdiff = computeDiff( v.data(), (const uint16_t*)reference->data, v.size() );\n\t}\n\telse\n\t\tthrow E_NOTIMPL;\n\n\tconst __m128i ggmlSize = _mm_loadu_si128( ( const __m128i* ) & reference->ne[ 0 ] );\n\tconst __m128i ggmlStrides = _mm_loadu_si128( ( const __m128i* ) & reference->nb[ 0 ] );\n\tif( !vectorEqual( gpuSize, ggmlSize ) )\n\t\tdbgPrintSizeDiff( \"dbgPrintDifference - size is different\", ggmlSize, gpuSize );\n\t// if( !vectorEqual( expectedStrides, ggmlStrides ) ) dbgPrintSizeDiff( \"dbgPrintDifference - stride is different\", ggmlStrides, expectedStrides );\n\n\tdiff.print( what );\n\n\tif( trapToDebugger )\n\t\t__debugbreak();\n}"
  },
  {
    "path": "Whisper/ML/MlContext.h",
    "content": "#pragma once\n#include <vector>\n#include \"TempBuffers.h\"\n#include \"ConstantBuffer.h\"\n#include \"Tensor.h\"\n#include \"../Utils/GpuProfiler.h\"\n#include \"../Utils/ProfileCollection.h\"\n\nnamespace DirectCompute\n{\n\tenum struct eComputeShader : uint16_t;\n\n\tclass MlContext\n\t{\n\t\t// When false, the implementation is 100% compatible with the CPU-running code written by Georgi Gerganov\n\t\t// When true, the implementation is much faster, and doesn't require FP64 support in the compute shaders.\n\t\t// FP64 is an optional feature, not all GPUs support that.\n\t\tstatic constexpr bool enableInexactOptimizations = true;\n\n\t\tConstantBuffer cb;\n\t\tTempBuffers temp;\n\t\tCComPtr<ID3D11Buffer> flashAttentionConstants;\n\n\t\tvoid convolutionImpl( const Tensor& a, const Tensor& b, Tensor& res, bool is2 );\n\n\t\tvoid cwiseBinary( const Tensor& a, const Tensor& b, Tensor& res, eComputeShader cs );\n\t\tTensor cwiseBinary( const Tensor& a, const Tensor& b, eComputeShader cs );\n\n\t\tvoid mulMatDot( const Tensor& a, const Tensor& b, Tensor& res );\n\t\tvoid mulMatMad( const Tensor& a, const Tensor& b, Tensor& res );\n\t\tvoid mulMatTiled( const Tensor& a, const Tensor& b, Tensor& res );\n\n\t\tvoid bindShader( eComputeShader cs );\n\n\tprotected:\n\t\tvoid copyImpl( const Tensor& a, Tensor& res, bool downcastFp32 );\n\n\t\t// Create a dense output tensor for the results of a computation\n\t\t// Override this method to implement a pool of these tensors\n\t\tvirtual Tensor createTensor( eDataType type, const std::array<uint32_t, 4>& ne );\n\n\t\tTensor createTensor( eDataType type, std::initializer_list<uint32_t> ne );\n\n\t\tGpuProfiler profiler;\n\n\tpublic:\n\t\tMlContext( Whisper::ProfileCollection& profileColl );\n\t\tMlContext( const MlContext& ) = delete;\n\n\t\t// res = a * b\n\t\tvoid mulMat( const Tensor& a, const Tensor& b, Tensor& res );\n\n\t\tvoid flashAttention( const Tensor& q, const Tensor& k, const Tensor& v, Tensor& res, bool masked );\n\n\t\tinline void convolution( const Tensor& a, const Tensor& b, Tensor& res )\n\t\t{\n\t\t\tconvolutionImpl( a, b, res, false );\n\t\t}\n\t\tvoid convolution2( const Tensor& a, const Tensor& b, Tensor& res )\n\t\t{\n\t\t\tconvolutionImpl( a, b, res, true );\n\t\t}\n\n\t\tvoid norm( const Tensor& a, Tensor& res );\n\n\t\tTensor conv_1d_1s( const Tensor& a, const Tensor& b );\n\t\tTensor conv_1d_2s( const Tensor& a, const Tensor& b );\n\n\t\tTensor add( const Tensor& a, const Tensor& b );\n\t\tvoid addInPlace( Tensor& a, const Tensor& b );\n\n\t\tTensor view2d( const Tensor& a, uint32_t ne0, uint32_t ne1, uint32_t nb1, uint32_t offset );\n\t\tTensor transpose( const Tensor& a );\n\n\t\tTensor norm( const Tensor& a );\n\t\tTensor mulMat( const Tensor& a, const Tensor& b );\n\t\tTensor mulMatEx( const Tensor& a, const Tensor& b, const char* tagName );\n\t\tTensor permute( const Tensor& a, uint8_t axis0, uint8_t axis1, uint8_t axis2, uint8_t axis3 );\n\t\tTensor flashAttention( const Tensor& q, const Tensor& k, const Tensor& v, bool masked );\n\n\t\tTensor copy( const Tensor& a, eDataType type, std::initializer_list<uint32_t> size );\n\t\tvoid copyInPlace( Tensor& dest, const Tensor& a, eDataType type, std::initializer_list<uint32_t> size );\n\n\t\tvoid dbgPrintDifference( const ggml_tensor* reference, const Tensor& gpu, const char * what, bool trapToDebugger = true );\n\n\t\tvoid scale( Tensor& a, float mul );\n\n\t\tvoid addRepeat( Tensor& a, const Tensor& b );\n\t\tvoid addRepeatScale( Tensor& a, const Tensor& b, float scale );\n\t\tvoid fmaRepeat( Tensor& a, const Tensor& mul, const Tensor& add );\n\n\t\t// ggml_diag_mask_inf\n\t\tvoid diagMaskInf( Tensor& a, uint32_t n_past );\n\t\t// ggml_soft_max\n\t\tvoid softMax( Tensor& a, float inputScale = 1.0f );\n\n\t\tvoid addRepeatGelu( Tensor& a, const Tensor& b );\n\n\t\t// Extract rows from tokenEmbedding matrix, row indices are taken from the `embd` R32_UINT row vector\n\t\t// Extract same count of rows from positionalEmbedding matrix, starting at the `pastTokensCount` row\n\t\t// Return a new FP32 matrix with the sum of these rows\n\t\tTensor addRows( const Tensor& tokenEmbedding, const Tensor& positionalEmbedding, const Tensor& embd, uint32_t pastTokensCount );\n\n\t\tTensor reshapePanels( const Tensor& a );\n\n\t\tTensor mulMatTiledEx( const Tensor& a, const Tensor& b );\n\t\tTensor mulMatByRowTiledEx( const Tensor& a, const Tensor& b );\n\n\t\t// An equivalent of addRepeat( dest, pattern ) followed by addInPlace( dest, finalAdd )\n\t\tvoid addRepeatEx( Tensor& dest, const Tensor& pattern, const Tensor& finalAdd );\n\n\t\t__m128i getMemoryUse() const;\n\t};\n}"
  },
  {
    "path": "Whisper/ML/Reshaper.cpp",
    "content": "#include \"stdafx.h\"\n#include \"Reshaper.h\"\n#include \"../D3D/MappedResource.h\"\n#include \"../D3D/Binder.h\"\n#include \"../D3D/shaders.h\"\n#include \"reshapedMultiply.h\"\n\nnamespace\n{\n\tusing namespace DirectCompute;\n\tstruct Constants\n\t{\n\t\t// Size and strides of the source tensor\n\t\tTensorShape arg0;\n\t\tuint32_t zzPadding;\n\t\t// Count of elements per panel\n\t\tuint32_t panelSize;\n\t\t// Layer strides of the output matrix\n\t\tstd::array<uint32_t, 2> layerStrides;\n\t};\n}\n\nHRESULT DirectCompute::Reshaper::createConstants()\n{\n\tconstexpr uint32_t cb = sizeof( Constants );\n\tCD3D11_BUFFER_DESC desc{ cb, D3D11_BIND_CONSTANT_BUFFER, D3D11_USAGE_DYNAMIC, D3D11_CPU_ACCESS_WRITE };\n\treturn device()->CreateBuffer( &desc, nullptr, &constantBuffer );\n}\n\nHRESULT DirectCompute::Reshaper::makePanels( Tensor& tensor, eDataType dataType )\n{\n\tif( !constantBuffer )\n\t\tCHECK( createConstants() );\n\n\tconstexpr uint32_t TILE_SIZE = ReshapedMultiply::TILE_SIZE;\n\n\t// Reshaping into column major horizontal panels, height = TILE_SIZE, width = width of the source matrix\n\n\tstd::array<uint32_t, 4> ne = tensor.ne;\n\tconst uint32_t groupsX = ( ne[ 1 ] + TILE_SIZE - 1 ) / TILE_SIZE;\n\tne[ 1 ] = groupsX * TILE_SIZE;;\n\t// Each panel has [ size.x, TILE_SIZE ] elements\n\tconst uint32_t panelSize = ne[ 0 ] * TILE_SIZE;\n\t\n\tTensor result;\n\tresult.create( dataType, ne, true );\n\n\t{\n\t\tMappedResource mapped;\n\t\tCHECK( mapped.map( constantBuffer, false ) );\n\t\tConstants& cb = *(Constants*)mapped.data();\n\n\t\tstore( cb.arg0.ne, tensor.sizeVec() );\n\t\tstore( cb.arg0.nb, tensor.stridesVec() );\n\t\tcb.panelSize = panelSize;\n\t\tcb.layerStrides[ 0 ] = result.nb[ 2 ];\n\t\tcb.layerStrides[ 1 ] = result.nb[ 3 ];\n\t}\n\n\tcsSetCB( constantBuffer );\n\t{\n\t\tBinder bind;\n\t\tbind.bind( tensor, result );\n\t\tbindShader( eComputeShader::matReshapePanels );\n\t\tcontext()->Dispatch( groupsX, tensor.ne[ 2 ], tensor.ne[ 3 ] );\n\t}\n\n\ttensor.nb[ 0 ] = 0;\n\ttensor.nb[ 1 ] = panelSize;\n\ttensor.nb[ 2 ] = result.nb[ 2 ];\n\ttensor.nb[ 3 ] = result.nb[ 3 ];\n\ttensor.setGpuViews( result );\n\treturn S_OK;\n}\n\nDirectCompute::Reshaper::~Reshaper()\n{\n\tif( constantBuffer )\n\t\tcsSetCB( nullptr );\n}"
  },
  {
    "path": "Whisper/ML/Reshaper.h",
    "content": "﻿#pragma once\n#include \"Tensor.h\"\n\nnamespace DirectCompute\n{\n\t// This class reshapes some of the model’s tensor, immediately after they’re loaded.\n\t// That feature is used on all AMD GPUs.\n\tclass Reshaper\n\t{\n\t\tCComPtr<ID3D11Buffer> constantBuffer;\n\t\tHRESULT createConstants();\n\n\tpublic:\n\t\t~Reshaper();\n\t\tHRESULT makePanels( Tensor& tensor, eDataType dataType );\n\t};\n}"
  },
  {
    "path": "Whisper/ML/TempBuffers.cpp",
    "content": "#include \"stdafx.h\"\n#include \"TempBuffers.h\"\n#include \"../D3D/createBuffer.h\"\n#include \"../D3D/MappedResource.h\"\n#include \"mlUtils.h\"\nusing namespace DirectCompute;\n\n#define CHECK( hr ) { const HRESULT __hr = ( hr ); if( FAILED( __hr ) ) return __hr; }\n\nHRESULT TempBuffers::Buffer::resize( DXGI_FORMAT format, size_t elements, size_t cbElement, bool zeroMemory )\n{\n\tif( elements <= capacity )\n\t{\n\t\tif( zeroMemory )\n\t\t\tDirectCompute::zeroMemory( *this, (uint32_t)elements );\n\t\treturn S_OK;\n\t}\n\tclear();\n\n\tCComPtr<ID3D11Buffer> buffer;\n\tconst size_t totalBytes = elements * cbElement;\n\tCHECK( createBuffer( eBufferUse::ReadWrite, totalBytes, &buffer, nullptr, nullptr ) );\n\tCHECK( TensorGpuViews::create( buffer, format, elements, true ) );\n\tcapacity = elements;\n\treturn S_OK;\n}\n\nconst TensorGpuViews& TempBuffers::fp16( size_t countElements, bool zeroMemory )\n{\n\tHRESULT hr = m_fp16.resize( DXGI_FORMAT_R16_FLOAT, countElements, 2, zeroMemory );\n\tif( FAILED( hr ) )\n\t\tthrow hr;\n\treturn m_fp16;\n}\n\nconst TensorGpuViews& TempBuffers::fp16_2( size_t countElements, bool zeroMemory )\n{\n\tHRESULT hr = m_fp16_2.resize( DXGI_FORMAT_R16_FLOAT, countElements, 2, zeroMemory );\n\tif( FAILED( hr ) )\n\t\tthrow hr;\n\treturn m_fp16_2;\n}\n\nconst TensorGpuViews& TempBuffers::fp32( size_t countElements, bool zeroMemory )\n{\n\tHRESULT hr = m_fp32.resize( DXGI_FORMAT_R32_FLOAT, countElements, 4, zeroMemory );\n\tif( FAILED( hr ) )\n\t\tthrow hr;\n\treturn m_fp32;\n}\n\n__m128i TempBuffers::getMemoryUse() const\n{\n\tsize_t cb = m_fp16.getCapacity() * 2;\n\tcb += m_fp16_2.getCapacity() * 2;\n\tcb += m_fp32.getCapacity() * 4;\n\treturn setHigh_size( cb );\n}"
  },
  {
    "path": "Whisper/ML/TempBuffers.h",
    "content": "#pragma once\n#include \"TensorGpuViews.h\"\n\nnamespace DirectCompute\n{\n\tclass TempBuffers\n\t{\n\t\tclass Buffer : public TensorGpuViews\n\t\t{\n\t\t\tsize_t capacity = 0;\n\n\t\tpublic:\n\n\t\t\tvoid clear()\n\t\t\t{\n\t\t\t\tTensorGpuViews::clear();\n\t\t\t\tcapacity = 0;\n\t\t\t}\n\n\t\t\tHRESULT resize( DXGI_FORMAT format, size_t elements, size_t cbElement, bool zeroMemory );\n\n\t\t\tsize_t getCapacity() const { return capacity; }\n\t\t};\n\n\t\tBuffer m_fp16;\n\t\tBuffer m_fp16_2;\n\t\tBuffer m_fp32;\n\n\tpublic:\n\n\t\tconst TensorGpuViews& fp16( size_t countElements, bool zeroMemory = false );\n\t\tconst TensorGpuViews& fp16_2( size_t countElements, bool zeroMemory = false );\n\t\tconst TensorGpuViews& fp32( size_t countElements, bool zeroMemory = false );\n\n\t\tvoid clear()\n\t\t{\n\t\t\tm_fp16.clear();\n\t\t\tm_fp16_2.clear();\n\t\t\tm_fp32.clear();\n\t\t}\n\n\t\t__m128i getMemoryUse() const;\n\t};\n}"
  },
  {
    "path": "Whisper/ML/Tensor.cpp",
    "content": "#include \"stdafx.h\"\n#include \"Tensor.h\"\n#include \"../D3D/MappedResource.h\"\n#include \"../D3D/createBuffer.h\"\n#include \"../source/ggml.h\"\nusing namespace DirectCompute;\n\nTensor::Tensor( const Tensor& that )\n{\n\tne = that.ne;\n\tnb = that.nb;\n\tsrv = that.srv;\n\tuav = that.uav;\n#ifdef _DEBUG\n\tdbgType = that.dbgType;\n#endif\n}\n\nTensor::Tensor( Tensor&& that ) noexcept\n{\n\tne = that.ne;\n\tnb = that.nb;\n\tsrv.Attach( that.srv.Detach() );\n\tuav.Attach( that.uav.Detach() );\n#ifdef _DEBUG\n\tdbgType = that.dbgType;\n#endif\n}\n\nTensor& Tensor::operator=( const Tensor& that )\n{\n\tne = that.ne;\n\tnb = that.nb;\n\tsrv = that.srv;\n\tuav = that.uav;\n#ifdef _DEBUG\n\tdbgType = that.dbgType;\n#endif\n\treturn *this;\n}\n\nTensor& Tensor::operator=( Tensor&& that ) noexcept\n{\n\tne = that.ne;\n\tnb = that.nb;\n\tsrv.Attach( that.srv.Detach() );\n\tuav.Attach( that.uav.Detach() );\n#ifdef _DEBUG\n\tdbgType = that.dbgType;\n#endif\n\treturn *this;\n}\n\nTensor::Tensor( const TensorShape& shape, CComPtr<ID3D11ShaderResourceView>& srv, CComPtr<ID3D11UnorderedAccessView>& uav ) noexcept :\n\tTensorShape( shape )\n{\n\tTensorGpuViews::srv.Attach( srv.Detach() );\n\tTensorGpuViews::uav.Attach( uav.Detach() );\n}\n\nTensor::Tensor( const TensorShape& shape, const TensorGpuViews& views ) :\n\tTensorShape( shape )\n{\n\tsrv = views;\n\tuav = views;\n}\n\nHRESULT Tensor::create( const ggml_tensor& ggml, eBufferUse usage, bool uploadData )\n{\n\tTensorGpuViews::clear();\n\n\tswitch( usage )\n\t{\n\tcase eBufferUse::Immutable:\n\tcase eBufferUse::ReadWriteDownload:\n\t\tbreak;\n\tdefault:\n\t\treturn E_INVALIDARG;\n\t}\n\n\tCComPtr<ID3D11Buffer> buffer;\n\n\tCHECK( TensorShape::create( ggml ) );\n\tconst ggml_type dataType = ggml.type;\n\tconst uint32_t cbElement = (uint32_t)ggml_type_size( dataType );\n\n\tconst size_t totalBytes = ggml_nbytes( &ggml );\n\tif( totalBytes > INT_MAX )\n\t\treturn DISP_E_OVERFLOW;\n\tconst uint32_t countElements = (uint32_t)( totalBytes / cbElement );\n\n\t{\n\t\tconst void* const rsi = uploadData ? ggml.data : nullptr;\n\t\tCHECK( createBuffer( usage, totalBytes, &buffer, rsi, nullptr ) );\n\t}\n\n\tDXGI_FORMAT format;\n\teDataType type;\n\tswitch( dataType )\n\t{\n\tcase GGML_TYPE_F16:\n\t\tformat = DXGI_FORMAT_R16_FLOAT;\n\t\ttype = eDataType::FP16;\n\t\tbreak;\n\tcase GGML_TYPE_F32:\n\t\tformat = DXGI_FORMAT_R32_FLOAT;\n\t\ttype = eDataType::FP32;\n\t\tbreak;\n\tdefault:\n\t\treturn E_NOTIMPL;\n\t}\n\n\tconst bool makeUav = ( usage == eBufferUse::ReadWrite );\n\n\tCHECK( TensorGpuViews::create( buffer, format, totalBytes / cbElement, makeUav ) );\n#ifdef _DEBUG\n\tdbgType.type = type;\n\tdbgType.usage = usage;\n\tdbgType.hasInitialData = uploadData;\n#endif\n\treturn S_OK;\n}\n\nHRESULT Tensor::createImmutable( eDataType type, const std::array<int, 4>& size, const void* rsi )\n{\n\tsize_t elts = (uint32_t)size[ 0 ];\n\telts *= (uint32_t)size[ 1 ];\n\telts *= (uint32_t)size[ 2 ];\n\telts *= (uint32_t)size[ 3 ];\n\n\tDXGI_FORMAT format;\n\tsize_t cbElement;\n\tswitch( type )\n\t{\n\tcase eDataType::FP16:\n\t\tformat = DXGI_FORMAT_R16_FLOAT;\n\t\tcbElement = 2;\n\t\tbreak;\n\tcase eDataType::FP32:\n\t\tformat = DXGI_FORMAT_R32_FLOAT;\n\t\tcbElement = 4;\n\t\tbreak;\n\tdefault:\n\t\treturn E_NOTIMPL;\n\t}\n\n\tCComPtr<ID3D11Buffer> buffer;\n\tCHECK( createBuffer( eBufferUse::Immutable, cbElement * elts, &buffer, rsi, nullptr ) );\n\tCHECK( TensorGpuViews::create( buffer, format, elts, false ) );\n\n\t__m128i v = _mm_loadu_si128( ( const __m128i* )size.data() );\n\t_mm_storeu_si128( ( __m128i* )ne.data(), v );\n\tsetDenseStrides();\n\treturn S_OK;\n}\n\nHRESULT Tensor::create( eDataType type, std::initializer_list<uint32_t> sizeElements, eBufferUse usage, CComPtr<ID3D11Buffer>& buffer, const void* rsi, ID3D11Buffer** ppStagingBuffer, bool shared )\n{\n\tTensorGpuViews::clear();\n\n\tsize_t nDims = sizeElements.size();\n\tif( 0 == nDims || nDims > 4 )\n\t\treturn E_INVALIDARG;\n\tnDims = std::min( nDims, (size_t)4 );\n\tsize_t totalElements = 1;\n\tfor( size_t i = 0; i < nDims; i++ )\n\t{\n\t\tuint32_t n = sizeElements.begin()[ i ];\n\t\tif( n == 0 )\n\t\t\treturn E_INVALIDARG;\n\t\tne[ i ] = n;\n\t\ttotalElements *= n;\n\t}\n\n\tDXGI_FORMAT format;\n\tsize_t cbElement;\n\tswitch( type )\n\t{\n\tcase eDataType::FP32:\n\t\tformat = DXGI_FORMAT_R32_FLOAT;\n\t\tcbElement = 4;\n\t\tbreak;\n\tcase eDataType::FP16:\n\t\tformat = DXGI_FORMAT_R16_FLOAT;\n\t\tcbElement = 2;\n\t\tbreak;\n\tcase eDataType::U32:\n\t\tformat = DXGI_FORMAT_R32_UINT;\n\t\tcbElement = 4;\n\t\tbreak;\n\tdefault:\n\t\treturn E_NOTIMPL;\n\t}\n\n\tconst size_t totalBytes = cbElement * totalElements;\n\tif( totalBytes > INT_MAX )\n\t\treturn DISP_E_OVERFLOW;\n\n\tfor( size_t i = nDims; i < 4; i++ )\n\t\tne[ i ] = 1;\n\tTensorShape::setDenseStrides();\n\n\tCHECK( createBuffer( usage, totalBytes, &buffer, rsi, ppStagingBuffer, shared ) );\n\n\tCHECK( TensorGpuViews::create( buffer, format, totalBytes / cbElement, true ) );\n#ifdef _DEBUG\n\tdbgType.type = type;\n\tdbgType.usage = usage;\n\tdbgType.hasInitialData = ( nullptr != rsi );\n#endif\n\treturn S_OK;\n}\n\nHRESULT Tensor::create( eDataType type, std::initializer_list<uint32_t> sizeElements, bool shared )\n{\n\tCComPtr<ID3D11Buffer> buffer;\n\treturn create( type, sizeElements, eBufferUse::ReadWrite, buffer, nullptr, nullptr, shared );\n}\n\nHRESULT Tensor::create( eDataType type, const std::array<uint32_t, 4>& sizeElements, bool shared )\n{\n\tstd::initializer_list<uint32_t> il( sizeElements.data(), sizeElements.data() + 4 );\n\treturn create( type, il, shared );\n}\n\neDataType Tensor::getType() const\n{\n\tID3D11ShaderResourceView* const srv = *this;\n\tif( nullptr == srv )\n\t\tthrow OLE_E_BLANK;\n\n\tD3D11_SHADER_RESOURCE_VIEW_DESC viewDesc;\n\tsrv->GetDesc( &viewDesc );\n\tconst DXGI_FORMAT format = viewDesc.Format;\n\tswitch( format )\n\t{\n\tcase DXGI_FORMAT_R32_FLOAT:\n\t\treturn eDataType::FP32;\n\tcase DXGI_FORMAT_R16_FLOAT:\n\t\treturn eDataType::FP16;\n\tcase DXGI_FORMAT_R32_UINT:\n\t\treturn eDataType::U32;\n\t}\n\tthrow E_NOTIMPL;\n}\n\nCComPtr<ID3D11Buffer> Tensor::getBuffer() const\n{\n\tID3D11ShaderResourceView* const srv = *this;\n\tif( nullptr == srv )\n\t\tthrow OLE_E_BLANK;\n\n\tCComPtr<ID3D11Resource> res;\n\tsrv->GetResource( &res );\n\n\tCComPtr<ID3D11Buffer> buff;\n\tcheck( res.QueryInterface( &buff ) );\n\treturn buff;\n}\n\nuint32_t Tensor::dxgiSizeof( DXGI_FORMAT format )\n{\n\tswitch( format )\n\t{\n\tcase DXGI_FORMAT_R16_FLOAT:\n\t\treturn 2;\n\tcase DXGI_FORMAT_R32_FLOAT:\n\tcase DXGI_FORMAT_R32_UINT:\n\t\treturn 4;\n\t}\n\tthrow E_INVALIDARG;\n}\n\nvoid Tensor::downloadImpl( const D3D11_SHADER_RESOURCE_VIEW_DESC& viewDesc, uint32_t countElements, size_t cbElement, void* rdi ) const\n{\n\tassert( viewDesc.ViewDimension == D3D_SRV_DIMENSION_BUFFER );\n\tconst uint32_t idxFirst = viewDesc.Buffer.FirstElement;\n\n\tCComPtr<ID3D11Buffer> buff = getBuffer();\n\tD3D11_BUFFER_DESC desc;\n\tbuff->GetDesc( &desc );\n\tdesc.BindFlags = 0;\n\tdesc.Usage = D3D11_USAGE_STAGING;\n\tdesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;\n\n\tCComPtr<ID3D11Buffer> staging;\n\tcheck( device()->CreateBuffer( &desc, nullptr, &staging ) );\n\tcontext()->CopyResource( staging, buff );\n\n\tMappedResource mapped;\n\tcheck( mapped.map( staging, true ) );\n\tconst uint8_t* rsi = (const uint8_t*)mapped.data();\n\trsi += cbElement * idxFirst;\n\tmemcpy( rdi, rsi, cbElement * countElements );\n}\n\nvoid Tensor::download( std::vector<float>& vec ) const\n{\n\tID3D11ShaderResourceView* const srv = *this;\n\tif( nullptr == srv )\n\t\tthrow OLE_E_BLANK;\n\n\tD3D11_SHADER_RESOURCE_VIEW_DESC viewDesc;\n\tsrv->GetDesc( &viewDesc );\n\tif( viewDesc.Format != DXGI_FORMAT_R32_FLOAT )\n\t\tthrow E_INVALIDARG;\n\n\tuint32_t countElements = viewDesc.Buffer.NumElements;\n\tvec.resize( countElements );\n\tdownloadImpl( viewDesc, countElements, 4, vec.data() );\n}\n\nvoid Tensor::download( std::vector<uint16_t>& vec ) const\n{\n\tID3D11ShaderResourceView* const srv = *this;\n\tif( nullptr == srv )\n\t\tthrow OLE_E_BLANK;\n\n\tD3D11_SHADER_RESOURCE_VIEW_DESC viewDesc;\n\tsrv->GetDesc( &viewDesc );\n\tif( viewDesc.Format != DXGI_FORMAT_R16_FLOAT )\n\t\tthrow E_INVALIDARG;\n\n\tuint32_t countElements = viewDesc.Buffer.NumElements;\n\tvec.resize( countElements );\n\tdownloadImpl( viewDesc, countElements, 2, vec.data() );\n}\n\nTensor Tensor::reshape3d( uint32_t ne0, uint32_t ne1, uint32_t ne2 ) const\n{\n\tif( !isContinuous() )\n\t\tthrow E_NOTIMPL;\n\tif( countElements() != ne0 * ne1 * ne2 )\n\t\tthrow E_INVALIDARG;\n\n\tTensor res = *this;\n\tres.ne = { ne0, ne1, ne2, 1 };\n\tres.setDenseStrides();\n\treturn res;\n}"
  },
  {
    "path": "Whisper/ML/Tensor.h",
    "content": "#pragma once\n#include \"TensorShape.h\"\n#include \"TensorGpuViews.h\"\n#include \"../D3D/enums.h\"\n\nnamespace DirectCompute\n{\n\t// A minimal tensor object sufficient to compute things on GPU, with compute shaders\n\t// This class only takes 48 bytes in system memory, and is very cheap to make copies 'coz GPU objects are reference counted.\n\tclass Tensor : public TensorShape, public TensorGpuViews\n\t{\n\t\tCComPtr<ID3D11Buffer> getBuffer() const;\n\n\t\tstruct TensorType\n\t\t{\n\t\t\teDataType type;\n\t\t\teBufferUse usage;\n\t\t\tbool hasInitialData;\n\t\t};\n#ifdef _DEBUG\n\t\t// In debug builds, we include a few pieces of data to this class.\n\t\tTensorType dbgType;\n#endif\n\tprotected:\n\t\tHRESULT create( eDataType type, std::initializer_list<uint32_t> sizeElements, eBufferUse usage, CComPtr<ID3D11Buffer>& buffer, const void* rsi, ID3D11Buffer** ppStagingBuffer, bool shared = false );\n\n\t\tstatic uint32_t dxgiSizeof( DXGI_FORMAT format );\n\n\t\tvoid downloadImpl( const D3D11_SHADER_RESOURCE_VIEW_DESC& viewDesc, uint32_t countElements, size_t cbElement, void* rdi ) const;\n\n\tpublic:\n\t\tTensor() = default;\n\n\t\t// These copy operators don't copy any data, they merely increment ref.counter of the GPU resources\n\t\tTensor( const Tensor& );\n\t\tTensor( Tensor&& that ) noexcept;\n\t\tTensor& operator=( const Tensor& that );\n\t\tTensor& operator=( Tensor&& that ) noexcept;\n\n\t\t// Move the provided buffer views into this newly created tensor, and assign the shape\n\t\t// This destroys old values in the smart pointers\n\t\tTensor( const TensorShape& shape, CComPtr<ID3D11ShaderResourceView>& srv, CComPtr<ID3D11UnorderedAccessView>& uav ) noexcept;\n\n\t\tTensor( const TensorShape& shape, const TensorGpuViews& views );\n\n\t\t// Create a tensor from the GGML's one\n\t\tHRESULT create( const ggml_tensor& ggml, eBufferUse usage, bool uploadData );\n\n\t\t// Create a new dense tensor of the specified size in elements, without initial data\n\t\tHRESULT create( eDataType type, std::initializer_list<uint32_t> sizeElements, bool shared = false );\n\t\tHRESULT create( eDataType type, const std::array<uint32_t, 4>& sizeElements, bool shared = false );\n\t\tHRESULT createImmutable( eDataType type, const std::array<int, 4>& size, const void* rsi );\n\n\t\teDataType getType() const;\n\n\t\t// This method should probably only be used to test things\n\t\t// TensorEx is better for production usage, because it creates staging buffer in advance.\n\t\tvoid download( std::vector<float>& vec ) const;\n\t\tvoid download( std::vector<uint16_t>& vec ) const;\n\n\t\t// ggml_reshape_3d\n\t\tTensor reshape3d( uint32_t ne0, uint32_t ne1, uint32_t ne2 ) const;\n\n\t\tinline void dbgSetType( eDataType dt, bool hasData = false, eBufferUse use = eBufferUse::ReadWrite )\n\t\t{\n#ifdef _DEBUG\n\t\t\tdbgType.type = dt;\n\t\t\tdbgType.hasInitialData = hasData;\n\t\t\tdbgType.usage = use;\n#endif\n\t\t}\n\n\t\t__m128i getMemoryUse() const\n\t\t{\n\t\t\treturn resourceMemoryUsage( srv );\n\t\t}\n\t};\n}"
  },
  {
    "path": "Whisper/ML/TensorEx.cpp",
    "content": "#include \"stdafx.h\"\n#include \"TensorEx.h\"\n#include \"../D3D/createBuffer.h\"\n#include \"../source/ggml.h\"\n#include \"../D3D/MappedResource.h\"\nusing namespace DirectCompute;\n\nHRESULT TensorEx::create( const ggml_tensor& ggml, eBufferUse usage, bool uploadData )\n{\n\tTensorGpuViews::clear();\n\tbuffer = nullptr;\n\tstagingBuffer = nullptr;\n\n\tCHECK( TensorShape::create( ggml ) );\n\tconst ggml_type dataType = ggml.type;\n\tconst uint32_t cbElement = (uint32_t)ggml_type_size( dataType );\n\n\tconst size_t totalBytes = ggml_nbytes( &ggml );\n\tif( totalBytes > INT_MAX )\n\t\treturn DISP_E_OVERFLOW;\n\tconst uint32_t countElements = (uint32_t)( totalBytes / cbElement );\n\n\t{\n\t\tconst void* const rsi = uploadData ? ggml.data : nullptr;\n\t\tID3D11Buffer** ppStagingBuffer = ( usage == eBufferUse::ReadWriteDownload ) ? &stagingBuffer : nullptr;\n\t\tCHECK( createBuffer( usage, totalBytes, &buffer, rsi, ppStagingBuffer ) );\n\t}\n\n\tDXGI_FORMAT format;\n\tswitch( dataType )\n\t{\n\tcase GGML_TYPE_F16:\n\t\tformat = DXGI_FORMAT_R16_FLOAT;\n\t\tbreak;\n\tcase GGML_TYPE_F32:\n\t\tformat = DXGI_FORMAT_R32_FLOAT;\n\t\tbreak;\n\tdefault:\n\t\treturn E_NOTIMPL;\n\t}\n\n\tconst bool makeUav = usage == eBufferUse::ReadWrite || usage == eBufferUse::ReadWriteDownload;\n\treturn TensorGpuViews::create( buffer, format, totalBytes / cbElement, makeUav );\n}\n\nHRESULT TensorEx::create( eDataType type, eBufferUse usage, const std::array<uint32_t, 4>& sizeElements )\n{\n\tTensorGpuViews::clear();\n\tbuffer = nullptr;\n\tstagingBuffer = nullptr;\n\tstd::initializer_list<uint32_t> il( sizeElements.data(), sizeElements.data() + 4 );\n\n\tID3D11Buffer** ppStaging = ( usage == eBufferUse::ReadWriteDownload ) ? &stagingBuffer : nullptr;\n\treturn Tensor::create( type, il, usage, buffer, nullptr, ppStaging );\n}\n\nHRESULT TensorEx::getViewSize( uint32_t& cbElement, uint32_t& countElements ) const\n{\n\tID3D11ShaderResourceView* const srv = *this;\n\tif( nullptr == srv )\n\t\treturn OLE_E_BLANK;\n\n\tD3D11_SHADER_RESOURCE_VIEW_DESC viewDesc;\n\tsrv->GetDesc( &viewDesc );\n\n\tcbElement = dxgiSizeof( viewDesc.Format );\n\n\tassert( viewDesc.ViewDimension == D3D_SRV_DIMENSION_BUFFER );\n\tassert( viewDesc.Buffer.FirstElement == 0 );\n\tcountElements = viewDesc.Buffer.NumElements;\n\n\treturn S_OK;\n}\n\nHRESULT TensorEx::download( void* rdi, size_t cb ) const\n{\n\tif( nullptr == stagingBuffer )\n\t\treturn HRESULT_FROM_WIN32( ERROR_GPIO_OPERATION_DENIED );\t// The requested operation is not supported for the specified handle.\n\n\tID3D11DeviceContext* const ctx = context();\n\tctx->CopyResource( stagingBuffer, buffer );\n\n\tMappedResource mapped;\n\tCHECK( mapped.map( stagingBuffer, true ) );\n\tmemcpy( rdi, mapped.data(), cb );\n\n\treturn S_OK;\n}\n\nHRESULT TensorEx::download( void* rdi ) const\n{\n\tuint32_t cbElement, numElements;\n\tCHECK( getViewSize( cbElement, numElements ) );\n\n\tsize_t cb = (size_t)cbElement * numElements;\n\treturn download( rdi, cb );\n}"
  },
  {
    "path": "Whisper/ML/TensorEx.h",
    "content": "#pragma once\n#include \"Tensor.h\"\n\nnamespace DirectCompute\n{\n\t// A tensor which supports dynamic updates from CPU, or downloads from VRAM to system RAM\n\tclass TensorEx : public Tensor\n\t{\n\tprotected:\n\t\tCComPtr<ID3D11Buffer> buffer;\n\t\tCComPtr<ID3D11Buffer> stagingBuffer;\n\n\t\tHRESULT getViewSize( uint32_t& cbElement, uint32_t& countElements ) const;\n\n\tpublic:\n\n\t\tHRESULT create( const ggml_tensor& ggml, eBufferUse usage, bool uploadData );\n\t\tHRESULT create( eDataType type, eBufferUse usage, const std::array<uint32_t, 4>& sizeElements );\n\n\t\tHRESULT download( void* rdi, size_t cb ) const;\n\n\t\tHRESULT download( void* rdi ) const;\n\n\t\ttemplate<class E>\n\t\tHRESULT download( std::vector<E>& vec ) const\n\t\t{\n\t\t\tuint32_t cbElement, numElements;\n\t\t\tCHECK( getViewSize( cbElement, numElements ) );\n\n\t\t\ttry\n\t\t\t{\n\t\t\t\tvec.resize( numElements );\n\t\t\t}\n\t\t\tcatch( const std::bad_alloc& )\n\t\t\t{\n\t\t\t\treturn E_OUTOFMEMORY;\n\t\t\t}\n\n\t\t\treturn download( vec.data(), (size_t)cbElement * numElements );\n\t\t}\n\t};\n}"
  },
  {
    "path": "Whisper/ML/TensorGpuViews.cpp",
    "content": "#include \"stdafx.h\"\n#include \"TensorGpuViews.h\"\nusing namespace DirectCompute;\n\nHRESULT TensorGpuViews::create( ID3D11Buffer* gpuBuffer, DXGI_FORMAT format, size_t countElements, bool makeUav )\n{\n\tsrv = nullptr;\n\tuav = nullptr;\n\n\tif( countElements > UINT_MAX )\n\t\treturn DISP_E_OVERFLOW;\n\n\tCD3D11_SHADER_RESOURCE_VIEW_DESC viewDesc{ D3D11_SRV_DIMENSION_BUFFER, format, 0, (UINT)countElements };\n\tCHECK( device()->CreateShaderResourceView( gpuBuffer, &viewDesc, &srv ) );\n\n\tif( makeUav )\n\t{\n\t\tCD3D11_UNORDERED_ACCESS_VIEW_DESC uavDesc{ D3D11_UAV_DIMENSION_BUFFER, format , 0, (UINT)countElements };\n\t\tCHECK( device()->CreateUnorderedAccessView( gpuBuffer, &uavDesc, &uav ) );\n\t}\n\n\treturn S_OK;\n}"
  },
  {
    "path": "Whisper/ML/TensorGpuViews.h",
    "content": "#pragma once\n#include <stdint.h>\n#include \"../D3D/device.h\"\n\nnamespace DirectCompute\n{\n\tclass TensorGpuViews\n\t{\n\tprotected:\n\t\tCComPtr<ID3D11ShaderResourceView> srv;\n\t\tCComPtr<ID3D11UnorderedAccessView> uav;\n\n\tpublic:\n\n\t\toperator ID3D11ShaderResourceView* ( ) const { return srv; }\n\t\toperator ID3D11UnorderedAccessView* ( ) const { return uav; }\n\n\t\tHRESULT create( ID3D11Buffer* buffer, DXGI_FORMAT format, size_t countElements, bool makeUav );\n\n\t\tvoid clear()\n\t\t{\n\t\t\tsrv = nullptr;\n\t\t\tuav = nullptr;\n\t\t}\n\n\t\tvoid setGpuViews( ID3D11ShaderResourceView* read, ID3D11UnorderedAccessView* write = nullptr )\n\t\t{\n\t\t\tsrv = read;\n\t\t\tuav = write;\n\t\t}\n\t};\n}"
  },
  {
    "path": "Whisper/ML/TensorShape.cpp",
    "content": "#include \"stdafx.h\"\n#include \"TensorShape.h\"\n#include \"../source/ggml.h\"\nusing namespace DirectCompute;\n\nTensorShape::TensorShape()\n{\n\tsetZero();\n}\n\nTensorShape::TensorShape( const TensorShape& that )\n{\n\t_mm_storeu_si128( ( __m128i* )ne.data(), that.sizeVec() );\n\t_mm_storeu_si128( ( __m128i* )nb.data(), that.stridesVec() );\n}\n\nvoid TensorShape::operator=( const TensorShape& that )\n{\n\t_mm_storeu_si128( ( __m128i* )ne.data(), that.sizeVec() );\n\t_mm_storeu_si128( ( __m128i* )nb.data(), that.stridesVec() );\n}\n\nHRESULT TensorShape::create( const ggml_tensor& ggml )\n{\n\tfor( size_t i = 0; i < 4; i++ )\n\t\tne[ i ] = (uint32_t)ggml.ne[ i ];\n\n\tconst ggml_type dataType = ggml.type;\n\t// Verify a few things\n\tuint32_t cbElement = (uint32_t)ggml_type_size( dataType );\n\tfor( size_t i = 0; i < 4; i++ )\n\t{\n\t\tsize_t stride = ggml.nb[ i ];\n\t\tif( 0 != stride % cbElement )\n\t\t\treturn E_INVALIDARG;\n\t\tsize_t nn = stride / cbElement;\n\t\tif( nn > UINT_MAX )\n\t\t\treturn DISP_E_OVERFLOW;\n\t\tnb[ i ] = (uint32_t)nn;\n\t}\n\treturn S_OK;\n}\n\nTensorShape::TensorShape( const ggml_tensor& ggml )\n{\n\tHRESULT hr = create( ggml );\n\tif( FAILED( hr ) )\n\t\tthrow hr;\n}\n\nvoid TensorShape::setDenseStrides()\n{\n\tnb[ 0 ] = 1;\n\tnb[ 1 ] = ne[ 0 ];\n\tconst uint32_t p01 = ne[ 0 ] * ne[ 1 ];\n\tnb[ 2 ] = p01;\n\tnb[ 3 ] = p01 * ne[ 2 ];\n}\n\nbool DirectCompute::canMulMat( const TensorShape& t0, const TensorShape& t1 )\n{\n\t/*\n\treturn\n\t\t( t0.ne[ 0 ] == t1.ne[ 0 ] ) &&\n\t\t( t0.ne[ 2 ] == t1.ne[ 2 ] ) &&\n\t\t( t0.ne[ 3 ] == t1.ne[ 3 ] ); */\n\t__m128i a = t0.sizeVec();\n\t__m128i b = t1.sizeVec();\n\t__m128i xx = _mm_xor_si128( a, b );\n\txx = _mm_shuffle_epi32( xx, _MM_SHUFFLE( 3, 2, 0, 0 ) );\n\treturn (bool)_mm_testz_si128( xx, xx );\n}"
  },
  {
    "path": "Whisper/ML/TensorShape.h",
    "content": "﻿#pragma once\n#include <stdint.h>\n#include <array>\n#include <smmintrin.h>\n\nstruct ggml_tensor;\nusing HRESULT = long;\n\nnamespace DirectCompute\n{\n\t// This POD structure describes the shape of a tensor.\n\t// It’s used for both GPU tensors in VRAM, and tensors in system memory used by the Hybrid model.\n\tstruct TensorShape\n\t{\n\t\t// Count of elements, up to 4 coordinates\n\t\t// The unused coordinates are set to 1\n\t\tstd::array<uint32_t, 4> ne;\n\n\t\t// Strides of the tensor\n\t\t// For a dense row-major tensor, these numbers are [ 1, ne[0], ne[0]*ne[1], ne[0]*ne[1]*ne[2] ]\n\t\t// Note that unlike GGML code, these numbers are expressed in elements not bytes, but the meaning is the same\n\t\t// GPU matrices reshaped into panels are keeping different values here: [ 0, panelSize, panelSize * panelsCount, panelSize * panelsCount * ne[ 2 ] ]\n\t\tstd::array<uint32_t, 4> nb;\n\n\t\tTensorShape();\n\t\tTensorShape( const TensorShape& that );\n\t\tvoid operator=( const TensorShape& that );\n\t\tHRESULT create( const ggml_tensor& ggml );\n\t\tTensorShape( const ggml_tensor& ggml );\n\n\t\t__m128i __vectorcall sizeVec() const\n\t\t{\n\t\t\treturn load( ne );\n\t\t}\n\t\t__m128i __vectorcall stridesVec() const\n\t\t{\n\t\t\treturn load( nb );\n\t\t}\n\n\t\tuint32_t countRows() const\n\t\t{\n\t\t\treturn ne[ 1 ] * ne[ 2 ] * ne[ 3 ];\n\t\t}\n\n\t\tuint32_t countElements() const\n\t\t{\n\t\t\t// return ne[ 0 ] * countRows();\n\t\t\tconst __m128i a = sizeVec();\n\t\t\tconst __m128i b = _mm_srli_si128( a, 4 );\n\t\t\tconst __m128i p2 = _mm_mul_epu32( a, b );\n\t\t\tuint64_t res = (uint64_t)_mm_extract_epi64( p2, 1 );\n\t\t\tres *= (uint64_t)_mm_cvtsi128_si64( p2 );\n\t\t\tassert( 0 == ( res >> 32 ) );\n\t\t\treturn (uint32_t)res;\n\t\t}\n\n\t\t// Compute strides from sizes, assuming dense row-major memory layout of the tensor\n\t\tvoid setDenseStrides();\n\n\t\tbool isMatrix() const\n\t\t{\n\t\t\t// return ne[ 2 ] == 1 && ne[ 3 ] == 1;\n\t\t\tconst uint64_t num = *(const uint64_t*)&ne[ 2 ];\n\t\t\treturn num == 0x100000001ull;\n\t\t}\n\t\tbool isVector() const\n\t\t{\n\t\t\treturn 1 == ne[ 1 ] && isMatrix();\n\t\t}\n\n\t\t// True of this tensor is dense and row-major\n\t\tbool isContinuous() const\n\t\t{\n\t\t\t/* return 1 == nb[ 0 ] &&\n\t\t\t\tnb[ 1 ] == nb[ 0 ] * ne[ 0 ] &&\n\t\t\t\tnb[ 2 ] == nb[ 1 ] * ne[ 1 ] &&\n\t\t\t\tnb[ 3 ] == nb[ 2 ] * ne[ 2 ]; */\n\n\t\t\tconst __m128i nbv = stridesVec();\n\t\t\tconst __m128i nev = sizeVec();\n\t\t\t__m128i tmp = _mm_mullo_epi32( nbv, nev );\t// Vertical product of int32 lanes\n\t\t\ttmp = _mm_shuffle_epi32( tmp, _MM_SHUFFLE( 2, 1, 0, 0 ) );\t// Shift left by 1 int32 lane\n\t\t\ttmp = _mm_insert_epi32( tmp, 1, 0 );\t// Reset X lane to 1\n\t\t\treturn vectorEqual( tmp, nbv );\n\t\t}\n\n\t\t// Reset all fields to zero\n\t\tvoid setZero()\n\t\t{\n\t\t\tconst __m128i z = _mm_setzero_si128();\n\t\t\t_mm_storeu_si128( ( __m128i* )ne.data(), z );\n\t\t\t_mm_storeu_si128( ( __m128i* )nb.data(), z );\n\t\t}\n\t};\n\n\t// True when two tensors have equal count of elements\n\tinline bool isSameShape( const TensorShape& t0, const TensorShape& t1 )\n\t{\n\t\t__m128i a = t0.sizeVec();\n\t\t__m128i b = t1.sizeVec();\n\t\treturn vectorEqual( a, b );\n\t}\n\n\t// True when two tensors have equal count of elements, and equal VRAM layout too\n\tinline bool isSameShapeAndLayout( const TensorShape& t0, const TensorShape& t1 )\n\t{\n\t\t__m128i a, b, x;\n\t\ta = t0.sizeVec();\n\t\tb = t1.sizeVec();\n\t\tx = _mm_xor_si128( a, b );\n\n\t\ta = t0.stridesVec();\n\t\tb = t1.stridesVec();\n\t\tx = _mm_or_si128( x, _mm_xor_si128( a, b ) );\n\t\treturn (bool)_mm_testz_si128( x, x );\n\t}\n\n\t// True when we can multiply two tensors of the provided shapes\n\tbool canMulMat( const TensorShape& t0, const TensorShape& t1 );\n}"
  },
  {
    "path": "Whisper/ML/TensorsArena.cpp",
    "content": "#include \"stdafx.h\"\n#include \"TensorsArena.h\"\n#include \"../D3D/createBuffer.h\"\n#include \"mlUtils.h\"\n\nstatic inline uint32_t roundUpPower2( uint32_t x )\n{\n\t// std::bit_ceil from C++/20 standard library implements runtime dispatch, uses LZCNT when AVX2 is available, otherwise BSR\n\t// That's not what we want.\n\t// BSR is only slightly slower than LZCNT: same speed on Intel, on AMD it's 3 versus 1 cycles.\n\t// defaultNewCapacity function is only called occasionally, that branch is therefore unpredictable.\n\tassert( x > 1 );\n\tunsigned long idx;\n\t_BitScanReverse( &idx, x - 1 );\n\treturn 2u << idx;\n}\n\nuint32_t DirectCompute::defaultNewCapacity( uint32_t current, uint32_t requested )\n{\n\t// Implement some reasonable tactics to compute capacity of these buffers\n\n\tconstexpr uint32_t minAlloc = 1024;\t// 1k elements = 4kb of VRAM for FP32 tensors\n\tconstexpr uint32_t allocGranularity = 1u << 14;\t// 16k elements = 64kb of VRAM for FP32 tensors\n\n\tif( requested > minAlloc )\n\t{\n\t\tconst uint32_t roundedUpPowerOf2 = roundUpPower2( requested );\n\n\t\tconstexpr uint32_t lowMask = allocGranularity - 1;\n\t\tconstexpr uint32_t highMask = ~lowMask;\n\t\tconst uint32_t roundedUpGranularity = ( requested + lowMask ) & highMask;\n\n\t\tconst uint32_t res = std::min( roundedUpPowerOf2, roundedUpGranularity );\n\n\t\tassert( res >= requested );\n\t\treturn res;\n\t}\n\n\treturn minAlloc;\n}\n\nusing namespace DirectCompute;\n\nTensorsArena::ArenaImpl::ArenaImpl( eDataType dataType, const sArenaConfig& config ) :\n\ttype( dataType ),\n\tpfnNewCap( nullptr != config.pfnCapInner ? config.pfnCapInner : &defaultNewCapacity )\n{\n\tpool.reserve( config.initialCapOuter );\n}\n\nTensor PooledTensor::tensor( eDataType type, const std::array<uint32_t, 4>& ne, pfnNewCapacity pfnNewCap )\n{\n\tconst uint32_t p1 = ne[ 0 ] * ne[ 1 ];\n\tconst uint32_t p2 = ne[ 2 ] * ne[ 3 ];\n\tconst uint32_t count = p1 * p2;\n\n\tif( count > capacity )\n\t{\n\t\tviews.clear();\n\t\tconst uint32_t newCap = pfnNewCap( capacity, count );\n\t\tassert( newCap >= count );\n\n\t\tconst size_t cb = elementSize( type ) * newCap;\n\t\tCComPtr<ID3D11Buffer> buffer;\n\t\tcheck( createBuffer( eBufferUse::ReadWrite, cb, &buffer, nullptr, nullptr ) );\n\t\tcheck( views.create( buffer, viewFormat( type ), newCap, true ) );\n\t\tcapacity = newCap;\n\t}\n\n\tTensorShape shape;\n\tshape.ne = ne;\n\tshape.setDenseStrides();\n\tTensor res{ shape, views };\n\tres.dbgSetType( type );\n\n#if DBG_TEST_NAN\n\tfillTensorWithNaN( res );\n#endif\n\treturn res;\n}\n\nTensor TensorsArena::ArenaImpl::tensor( const std::array<uint32_t, 4>& ne )\n{\n\tPooledTensor* res;\n\tif( index >= pool.size() )\n\t{\n\t\tassert( index == pool.size() );\n\t\tres = &pool.emplace_back();\n\t}\n\telse\n\t\tres = &pool[ index ];\n\n\tindex++;\n\treturn res->tensor( type, ne, pfnNewCap );\n}\n\nTensorsArena::TensorsArena( const sArenaConfigs& configs ) :\n\tarenas{ ArenaImpl{ eDataType::FP16, configs.fp16 }, ArenaImpl{ eDataType::FP32, configs.fp32 } }\n{\n\tstatic_assert( 0 == (uint8_t)eDataType::FP16 );\n\tstatic_assert( 1 == (uint8_t)eDataType::FP32 );\n}\n\nTensor TensorsArena::tensor( eDataType type, const std::array<uint32_t, 4>& ne )\n{\n\tArenaImpl& arena = arenas[ (uint8_t)type ];\n\treturn arena.tensor( ne );\n}\n\nvoid TensorsArena::reset()\n{\n\tfor( ArenaImpl& a : arenas )\n\t\ta.reset();\n}\n\nvoid TensorsArena::clear()\n{\n\tfor( ArenaImpl& a : arenas )\n\t\ta.clear();\n}\n\n__m128i TensorsArena::ArenaImpl::getMemoryUse() const\n{\n\tconst size_t cbElement = elementSize( type );\n\tsize_t countElts = 0;\n\tfor( const auto& t : pool )\n\t\tcountElts += t.getCapacity();\n\n\tconst size_t cbVideo = cbElement * countElts;\n\tconst size_t cbSystem = vectorMemoryUse( pool );\n\treturn setr_size( cbSystem, cbVideo );\n}\n\n__m128i TensorsArena::getMemoryUse() const\n{\n\t__m128i res = _mm_setzero_si128();\n\tfor( const auto& a : arenas )\n\t\tres = _mm_add_epi64( res, a.getMemoryUse() );\n\treturn res;\n}\n\nHRESULT PooledTensor::zeroMemory()\n{\n\tif( 0 == capacity )\n\t\treturn S_FALSE;\n\ttry\n\t{\n\t\tDirectCompute::zeroMemory( views, capacity );\n\t\treturn S_OK;\n\t}\n\tcatch( HRESULT hr )\n\t{\n\t\treturn hr;\n\t}\n}\n\nHRESULT TensorsArena::ArenaImpl::zeroMemory()\n{\n\tfor( PooledTensor& e : pool )\n\t\tCHECK( e.zeroMemory() );\n\treturn S_OK;\n}\n\nHRESULT TensorsArena::zeroMemory()\n{\n\tfor( ArenaImpl& e : arenas )\n\t\tCHECK( e.zeroMemory() );\n\treturn S_OK;\n}"
  },
  {
    "path": "Whisper/ML/TensorsArena.h",
    "content": "#pragma once\n#include \"Tensor.h\"\n\nnamespace DirectCompute\n{\n\tusing pfnNewCapacity = uint32_t( * )( uint32_t current, uint32_t requested );\n\n\tuint32_t defaultNewCapacity( uint32_t current, uint32_t requested );\n\n\tclass PooledTensor\n\t{\n\t\tTensorGpuViews views;\n\t\tuint32_t capacity = 0;\n\tpublic:\n\t\tTensor tensor( eDataType type, const std::array<uint32_t, 4>& ne, pfnNewCapacity pfnNewCap );\n\t\tsize_t getCapacity() const { return capacity; }\n\t\tvoid clear()\n\t\t{\n\t\t\tviews.clear();\n\t\t\tcapacity = 0;\n\t\t}\n\t\tHRESULT zeroMemory();\n\t};\n\n\t__interface iTensorArena\n\t{\n\t\tTensor tensor( eDataType type, const std::array<uint32_t, 4>& ne );\n\t\tvoid reset();\n\t};\n\n\tclass TensorsArena: public iTensorArena\n\t{\n\tpublic:\n\t\tstruct sArenaConfig\n\t\t{\n\t\t\tpfnNewCapacity pfnCapInner;\n\t\t\tsize_t initialCapOuter;\n\t\t};\n\n\t\tstruct sArenaConfigs\n\t\t{\n\t\t\tsArenaConfig fp16, fp32;\n\t\t};\n\n\t\tTensorsArena( const sArenaConfigs& configs );\n\n\t\tTensor tensor( eDataType type, const std::array<uint32_t, 4>& ne ) override final;\n\t\tvoid reset() override final;\n\n\t\tvoid clear();\n\t\t__m128i getMemoryUse() const;\n\t\tHRESULT zeroMemory();\n\n\tprivate:\n\n\t\tstruct ArenaImpl\n\t\t{\n\t\t\tArenaImpl( eDataType dataType, const sArenaConfig& config );\n\n\t\t\tvoid reset()\n\t\t\t{\n\t\t\t\tindex = 0;\n\t\t\t}\n\n\t\t\tvoid clear()\n\t\t\t{\n\t\t\t\tindex = 0;\n\t\t\t\tpool.clear();\n\t\t\t}\n\n\t\t\tTensor tensor( const std::array<uint32_t, 4>& ne );\n\t\t\t__m128i getMemoryUse() const;\n\t\t\tHRESULT zeroMemory();\n\n\t\tprivate:\n\n\t\t\tconst eDataType type;\n\t\t\tconst pfnNewCapacity pfnNewCap;\n\n\t\t\tstd::vector<PooledTensor> pool;\n\t\t\tsize_t index = 0;\n\t\t};\n\n\t\tstatic constexpr size_t countTypes = 2;\n\t\tstd::array<ArenaImpl, countTypes> arenas;\n\t};\n}"
  },
  {
    "path": "Whisper/ML/mlUtils.cpp",
    "content": "#include \"stdafx.h\"\n#include \"LookupTables.h\"\n#include \"../D3D/MappedResource.h\"\n#include \"mlUtils.h\"\n#include \"../D3D/shaders.h\"\n#include \"../D3D/Binder.h\"\n#include \"DbgNanTest.h\"\n\nnamespace DirectCompute\n{\n\tvoid zeroMemory( ID3D11UnorderedAccessView* uav, uint32_t length, bool fillWithNaN )\n\t{\n\t\t__m128i cbData = _mm_cvtsi32_si128( (int)length );\n\t\tcbData = _mm_insert_epi32( cbData, fillWithNaN ? 1 : 0, 1 );\n\t\tID3D11Buffer* cb = updateSmallCb( cbData );\n\n\t\tID3D11DeviceContext* ctx = context();\n\t\tctx->CSSetUnorderedAccessViews( 0, 1, &uav, nullptr );\n\t\tcsSetCB( cb );\n\n\t\tconstexpr uint32_t THREADS = 512;\n\t\tconstexpr uint32_t ITERATIONS = 128;\n\t\tconstexpr uint32_t elementsPerGroup = THREADS * ITERATIONS;\n\t\tconst uint32_t countGroups = ( length + elementsPerGroup - 1 ) / elementsPerGroup;\n\t\tbindShader( eComputeShader::zeroMemory );\n\t\tctx->Dispatch( countGroups, 1, 1 );\n\t}\n\n\tvoid fillTensorWithNaN( ID3D11UnorderedAccessView* uav )\n\t{\n\t\t// Note we fill the complete unordered access view, ignoring the current size of the tensor. This is deliberate.\n\t\tD3D11_UNORDERED_ACCESS_VIEW_DESC desc;\n\t\tuav->GetDesc( &desc );\n\t\tassert( desc.Format == DXGI_FORMAT_R32_FLOAT || desc.Format == DXGI_FORMAT_R16_FLOAT );\n\t\tzeroMemory( uav, desc.Buffer.NumElements, true );\n\t}\n\n\tbool scanTensorForNaN( ID3D11ShaderResourceView* tensor, uint32_t length )\n\t{\n#if DBG_TEST_NAN\n\t\t// Unlike fillTensorWithNaN function, this one only tests initial portion of the buffer\n\t\tconst DbgNanTest& buffers = getNanTestBuffers();\n\n\t\t// Update constant buffer with elements = length, reset = true\n\t\t__m128i cbData = _mm_cvtsi32_si128( (int)length );\n\t\tcbData = _mm_insert_epi32( cbData, 1, 1 );\n\t\tID3D11Buffer* cb = updateSmallCb( cbData );\n\n\t\t// Bind the compute shader and resources\n\t\tbindShader( eComputeShader::dbgFindNaN );\n\t\tcsSetCB( cb );\n\t\tBinder bind;\n\t\tbind.bind( tensor, buffers );\n\t\t// Dispatch exactly 1 thread group of that shader\n\t\tcontext()->Dispatch( 1, 1, 1 );\n\n\t\t// Update constant buffer with elements = length, reset = false\n\t\tcbData = _mm_cvtsi32_si128( (int)length );\n\t\tupdateSmallCb( cbData );\n\n\t\t// Dispatch the necessary count of that shader\n\t\tconstexpr uint32_t THREADS = 512;\n\t\tconstexpr uint32_t ITERATIONS = 128;\n\t\tconstexpr uint32_t elementsPerGroup = THREADS * ITERATIONS;\n\t\tconst uint32_t countGroups = ( length + elementsPerGroup - 1 ) / elementsPerGroup;\n\t\tcontext()->Dispatch( countGroups, 1, 1 );\n\n\t\t// Download result from GPU. This stalls the GPU pipeline, and ruins the performance.\n\t\t// This code better be disabled with DBG_TEST_NAN=0 macro\n\t\treturn buffers.test();\n#else\n\t\treturn false;\n#endif\n\t}\n\n\tHRESULT cloneResourceView( ID3D11ShaderResourceView* rsi, ID3D11ShaderResourceView** rdi )\n\t{\n\t\tif( nullptr == rdi || nullptr == rsi )\n\t\t\treturn E_POINTER;\n\n\t\tif( nullptr == rsi )\n\t\t{\n\t\t\t*rdi = nullptr;\n\t\t\treturn S_FALSE;\n\t\t}\n\n\t\t// Open shared resource on another device\n\t\tCComPtr<ID3D11Resource> sourceRes;\n\t\trsi->GetResource( &sourceRes );\n\n#ifdef DEBUG\n\t\tCComPtr<ID3D11Buffer> sourceBuffer;\n\t\tCHECK( sourceRes->QueryInterface( &sourceBuffer ) );\n\t\tD3D11_BUFFER_DESC buffDesc;\n\t\tsourceBuffer->GetDesc( &buffDesc );\n\t\tconst uint32_t miscFlags = buffDesc.MiscFlags;\n\t\tif( 0 == ( miscFlags & D3D11_RESOURCE_MISC_SHARED ) )\n\t\t{\n\t\t\tlogError( u8\"Source buffer doesn't have D3D11_RESOURCE_MISC_SHARED flag\" );\n\t\t\treturn E_INVALIDARG;\n\t\t}\n#endif\n\n\t\tCComPtr<IDXGIResource> sourceDxgiRes;\n\t\tCHECK( sourceRes->QueryInterface( &sourceDxgiRes ) );\n\n\t\tHANDLE h = nullptr;\n\t\tCHECK( sourceDxgiRes->GetSharedHandle( &h ) );\n\n\t\tCComPtr<ID3D11Buffer> newBuffer;\n\t\tCHECK( device()->OpenSharedResource( h, IID_PPV_ARGS( &newBuffer ) ) );\n\n\t\t// Create shader resource view on the new device, using the same specs\n\t\tD3D11_SHADER_RESOURCE_VIEW_DESC desc;\n\t\trsi->GetDesc( &desc );\n\t\tCHECK( device()->CreateShaderResourceView( newBuffer, &desc, rdi ) );\n\t\treturn S_OK;\n\t}\n}"
  },
  {
    "path": "Whisper/ML/mlUtils.h",
    "content": "#pragma once\n\nnamespace DirectCompute\n{\n\t// Update the small dynamic constant buffer\n\tID3D11Buffer* __vectorcall updateSmallCb( __m128i cbData );\n\n\t// Fill the tensor with either 0.0 or NaN values\n\tvoid zeroMemory( ID3D11UnorderedAccessView* uav, uint32_t length, bool fillWithNaN = false );\n\n\t// Fill the complete UAV with NaN values\n\tvoid fillTensorWithNaN( ID3D11UnorderedAccessView* uav );\n\n\t// true when the tensor contains at least 1 NaN value\n\tbool scanTensorForNaN( ID3D11ShaderResourceView* tensor, uint32_t length );\n\n\t// Create SRV on another device, reusing the resource\n\tHRESULT cloneResourceView( ID3D11ShaderResourceView* rsi, ID3D11ShaderResourceView** rdi );\n}"
  },
  {
    "path": "Whisper/ML/reshapedMultiply.h",
    "content": "#pragma once\n#include <stdint.h>\n\nnamespace DirectCompute\n{\n\tnamespace ReshapedMultiply\n\t{\n\t\tconstexpr uint32_t TILE_SIZE = 32;\n\t}\n}"
  },
  {
    "path": "Whisper/ML/tensorOpsTests.cpp",
    "content": "#include \"stdafx.h\"\n#include \"tensorOpsTests.h\"\n#include \"MlContext.h\"\n#include \"TensorEx.h\"\n#include \"../D3D/shaders.h\"\n#include \"../D3D/Binder.h\"\n#include \"testUtils.h\"\n#include \"../Whisper/WhisperContext.h\"\n\nvoid DirectCompute::testMulMat( const ggml_tensor* src0, const ggml_tensor* src1, const ggml_tensor* dst, const void* tempBuffer )\n{\n\treturn;\n\tCaptureRaii capture;\n\tconst size_t nb00 = src0->nb[ 0 ];\n\tconst size_t nb01 = src0->nb[ 1 ];\n\n\tif( src0->type != GGML_TYPE_F16 )\n\t\treturn; // TODO\n\n\tif( nb01 < nb00 )\n\t\treturn;\t// TODO\n\n\tWhisperContext& ctx = WhisperContext::current();\n\n\tTensor arg0, arg1;\n\tcheck( arg0.create( *src0, eBufferUse::Immutable, true ) );\n\tcheck( arg1.create( *src1, eBufferUse::Immutable, true ) );\n\tTensorEx res;\n\tcheck( res.create( *dst, eBufferUse::ReadWriteDownload, false ) );\n\n\tctx.mulMat( arg0, arg1, res );\n\n\tstd::vector<float> tv;\n\tcheck( res.download( tv ) );\n\n\tconst size_t len = tv.size();\n\tcomputeDiff( tv.data(), (const float*)dst->data, len ).print( \"testMulMat-product\" );\n\n#if 0\n\tdbgWriteBinaryFile( L\"product-orig.bin\", dst->data, len * 4 );\n\tdbgWriteBinaryFile( L\"product-gpu.bin\", tv.data(), len * 4 );\n\t__debugbreak();\n#endif\n}\n\n#if 0\nvoid DirectCompute::testMulMatReshape( const ggml_tensor* src1, const void* tempBuffer )\n{\n\tTensor src;\n\tcheck( src.create( *src1, eBufferUse::Immutable, true ) );\n\n\tconst size_t ne10 = (uint32_t)src1->ne[ 0 ];\n\tconst size_t ne11 = (uint32_t)src1->ne[ 1 ];\n\tconst size_t ne12 = (uint32_t)src1->ne[ 2 ];\n\tconst size_t ne13 = (uint32_t)src1->ne[ 3 ];\n\tif( 1 != ne13 )\n\t\tthrow E_UNEXPECTED;\n\tconst size_t tempLength = ne10 * ne11 * ne12 * ne13;\n\n\tContext& ctx = Context::current();\n\tconst ReadWriteViews& temp = ctx.temp.fp16( tempLength );\n\n\t{\n\t\tBinder bind;\n\t\tctx.cb.bind();\n\n\t\tbindShader( eComputeShader::mulMatDotReshape );\n\n\t\tctx.cb.update( src );\n\t\tbind.bind( src, temp );\n\t\tcontext()->Dispatch( (UINT)ne11, (UINT)ne12, 1 );\n\t}\n\n\tstd::vector<uint16_t> reshaped;\n\tcheck( downloadBuffer( temp, reshaped ) );\n\tcomputeDiff( reshaped.data(), (const uint16_t*)tempBuffer, reshaped.size() ).print( \"testMulMatReshape\" );\n\n#if 0\n\tdbgWriteBinaryFile( L\"fp32.bin\", src1->data, ggml_nbytes( src1 ) );\n\tdbgWriteBinaryFile( L\"fp16-cpu.bin\", tempBuffer, reshaped.size() * 2 );\n\tdbgWriteBinaryFile( L\"fp16-gpu.bin\", reshaped.data(), reshaped.size() * 2 );\n\t__debugbreak();\n#endif\n}\n#endif\n\nvoid DirectCompute::computeMulMat( const ggml_tensor* src0, const ggml_tensor* src1, ggml_tensor* dst )\n{\n\tCaptureRaii capture;\n\tconst size_t nb00 = src0->nb[ 0 ];\n\tconst size_t nb01 = src0->nb[ 1 ];\n\n\tif( src0->type != GGML_TYPE_F16 )\n\t\tthrow E_INVALIDARG;\n\tif( nb01 < nb00 )\n\t\tthrow E_INVALIDARG;\n\n\tWhisperContext& ctx = WhisperContext::current();\n\n\tTensor arg0, arg1;\n\tcheck( arg0.create( *src0, eBufferUse::Immutable, true ) );\n\tcheck( arg1.create( *src1, eBufferUse::Immutable, true ) );\n\tTensorEx res;\n\tcheck( res.create( *dst, eBufferUse::ReadWriteDownload, false ) );\n\n\tctx.mulMat( arg0, arg1, res );\n\n\tcheck( res.download( dst->data ) );\n}\n\nvoid DirectCompute::testFlashAttention( const ggml_tensor* q, const ggml_tensor* k, const ggml_tensor* v, bool masked, const ggml_tensor* dst )\n{\n\tCaptureRaii capture;\n\n\tTensor Q, K, V;\n\tTensorEx res;\n\tcheck( Q.create( *q, eBufferUse::Immutable, true ) );\n\tcheck( K.create( *k, eBufferUse::Immutable, true ) );\n\tcheck( V.create( *v, eBufferUse::Immutable, true ) );\n\tcheck( res.create( *dst, eBufferUse::ReadWriteDownload, false ) );\n\n\tWhisperContext& ctx = WhisperContext::current();\n\tctx.flashAttention( Q, K, V, res, masked );\n\n\tstd::vector<float> tv;\n\tcheck( res.download( tv ) );\n\n\tconst size_t len = tv.size();\n\tcomputeDiff( tv.data(), (const float*)dst->data, len ).print( \"testFlashAttention\" );\n}\n\nvoid DirectCompute::computeFlashAttention( const ggml_tensor* q, const ggml_tensor* k, const ggml_tensor* v, bool masked, ggml_tensor* dst )\n{\n\tCaptureRaii capture;\n\n\tTensor Q, K, V;\n\tTensorEx res;\n\tcheck( Q.create( *q, eBufferUse::Immutable, true ) );\n\tcheck( K.create( *k, eBufferUse::Immutable, true ) );\n\tcheck( V.create( *v, eBufferUse::Immutable, true ) );\n\tcheck( res.create( *dst, eBufferUse::ReadWriteDownload, false ) );\n\n\tWhisperContext& ctx = WhisperContext::current();\n\tctx.flashAttention( Q, K, V, res, masked );\n\n\tcheck( res.download( dst->data ) );\n}\n\nvoid DirectCompute::testConvolution( const ggml_tensor* src0, const ggml_tensor* src1, const ggml_tensor* dst )\n{\n\tCaptureRaii capture;\n\n\tTensor arg0, arg1;\n\tcheck( arg0.create( *src0, eBufferUse::Immutable, true ) );\n\tcheck( arg1.create( *src1, eBufferUse::Immutable, true ) );\n\tTensorEx res;\n\tcheck( res.create( *dst, eBufferUse::ReadWriteDownload, false ) );\n\n\tWhisperContext& ctx = WhisperContext::current();\n\tctx.convolution( arg0, arg1, res );\n\n\tstd::vector<float> tv;\n\tcheck( res.download( tv ) );\n\n\tconst size_t len = tv.size();\n\tcomputeDiff( tv.data(), (const float*)dst->data, len ).print( \"testConvolution\" );\n}\n\nvoid DirectCompute::computeConvolution( const ggml_tensor* src0, const ggml_tensor* src1, ggml_tensor* dst )\n{\n\tCaptureRaii capture;\n\n\tTensor arg0, arg1;\n\tcheck( arg0.create( *src0, eBufferUse::Immutable, true ) );\n\tcheck( arg1.create( *src1, eBufferUse::Immutable, true ) );\n\tTensorEx res;\n\tcheck( res.create( *dst, eBufferUse::ReadWriteDownload, false ) );\n\n\tWhisperContext& ctx = WhisperContext::current();\n\tctx.convolution( arg0, arg1, res );\n\n\tres.download( dst->data );\n}"
  },
  {
    "path": "Whisper/ML/tensorOpsTests.h",
    "content": "#pragma once\n#include \"../source/ggml.h\"\n\nnamespace DirectCompute\n{\n\t// void testMulMatReshape( const ggml_tensor* src1, const void* tempBuffer );\n\tvoid testMulMat( const ggml_tensor* src0, const ggml_tensor* src1, const ggml_tensor* dst, const void* tempBuffer );\n\tvoid computeMulMat( const ggml_tensor* src0, const ggml_tensor* src1, ggml_tensor* dst );\n\n\tvoid testFlashAttention( const ggml_tensor* q, const ggml_tensor* k, const ggml_tensor* v, bool masked, const ggml_tensor* dst );\n\tvoid computeFlashAttention( const ggml_tensor* q, const ggml_tensor* k, const ggml_tensor* v, bool masked, ggml_tensor* dst );\n\n\tvoid testConvolution( const ggml_tensor* src0, const ggml_tensor* src1, const ggml_tensor* dst );\n\tvoid computeConvolution( const ggml_tensor* src0, const ggml_tensor* src1, ggml_tensor* dst );\n}"
  },
  {
    "path": "Whisper/ML/testUtils.cpp",
    "content": "#include \"stdafx.h\"\n#include \"testUtils.h\"\n#include <immintrin.h>\n#include <atlfile.h>\n#include <atlpath.h>\n\nnamespace\n{\n\tusing DirectCompute::sTensorDiff;\n\n\t__forceinline __m256 load( const float* rsi )\n\t{\n\t\treturn _mm256_loadu_ps( rsi );\n\t}\n\n\t__forceinline __m256 load( const uint16_t* rsi )\n\t{\n\t\tconst __m128i iv = _mm_load_si128( ( const __m128i* )rsi );\n\t\treturn _mm256_cvtph_ps( iv );\n\t}\n\n\t__forceinline void loadPartial( const uint16_t* x, const uint16_t* y, size_t count, __m256& fx, __m256& fy )\n\t{\n\t\t__m128i ix, iy;\n\t\tswitch( count )\n\t\t{\n\t\tcase 1: // load 2 bytes\n\t\t\tix = _mm_cvtsi32_si128( *x );\n\t\t\tiy = _mm_cvtsi32_si128( *y );\n\t\t\tbreak;\n\t\tcase 2: // load 4 bytes\n\t\t\tix = _mm_cvtsi32_si128( *(const int*)x );\n\t\t\tiy = _mm_cvtsi32_si128( *(const int*)y );\n\t\t\tbreak;\n\t\tcase 3: // load 6 bytes\n\t\t\tix = _mm_cvtsi32_si128( *(const int*)x );\n\t\t\tiy = _mm_cvtsi32_si128( *(const int*)y );\n\t\t\tix = _mm_insert_epi16( ix, x[ 2 ], 2 );\n\t\t\tiy = _mm_insert_epi16( iy, y[ 2 ], 2 );\n\t\t\tbreak;\n\t\tcase 4: // load 8 bytes\n\t\t\tix = _mm_cvtsi64_si128( *(const int64_t*)x );\n\t\t\tiy = _mm_cvtsi64_si128( *(const int64_t*)y );\n\t\t\tbreak;\n\t\tcase 5: // load 10 bytes\n\t\t\tix = _mm_cvtsi64_si128( *(const int64_t*)x );\n\t\t\tiy = _mm_cvtsi64_si128( *(const int64_t*)y );\n\t\t\tix = _mm_insert_epi16( ix, x[ 4 ], 4 );\n\t\t\tiy = _mm_insert_epi16( iy, y[ 4 ], 4 );\n\t\t\tbreak;\n\t\tcase 6: // load 12 bytes\n\t\t\tix = _mm_cvtsi64_si128( *(const int64_t*)x );\n\t\t\tiy = _mm_cvtsi64_si128( *(const int64_t*)y );\n\t\t\tix = _mm_insert_epi32( ix, *(const int*)( x + 4 ), 2 );\n\t\t\tiy = _mm_insert_epi32( iy, *(const int*)( y + 4 ), 2 );\n\t\t\tbreak;\n\t\tcase 7: // load 14 bytes\n\t\t\tix = _mm_cvtsi64_si128( *(const int64_t*)x );\n\t\t\tiy = _mm_cvtsi64_si128( *(const int64_t*)y );\n\t\t\tix = _mm_insert_epi32( ix, *(const int*)( x + 4 ), 2 );\n\t\t\tiy = _mm_insert_epi32( iy, *(const int*)( y + 4 ), 2 );\n\t\t\tix = _mm_insert_epi16( ix, x[ 6 ], 6 );\n\t\t\tiy = _mm_insert_epi16( iy, y[ 6 ], 6 );\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tfx = fy = _mm256_setzero_ps();\n\t\t\treturn;\n\t\t}\n\n\t\tfx = _mm256_cvtph_ps( ix );\n\t\tfy = _mm256_cvtph_ps( iy );\n\t}\n\n\tinline __m128 loadFloat2( const float* rsi )\n\t{\n\t\treturn _mm_castpd_ps( _mm_load_sd( (const double*)rsi ) );\n\t}\n\tinline __m128 loadFloat3( const float* rsi )\n\t{\n\t\t__m128 f = loadFloat2( rsi );\n\t\tf = _mm_insert_ps( f, _mm_load_ss( rsi + 2 ), 0x20 );\n\t\treturn f;\n\t}\n\t__forceinline void loadPartial( const float* x, const float* y, size_t count, __m256& fx, __m256& fy )\n\t{\n\t\t__m128 low1, high1;\n\t\t__m128 low2, high2;\n\t\thigh1 = high2 = _mm_setzero_ps();\n\t\tswitch( count )\n\t\t{\n\t\tcase 1:\n\t\t\tlow1 = _mm_load_ss( x );\n\t\t\tlow2 = _mm_load_ss( y );\n\t\t\tbreak;\n\t\tcase 2:\n\t\t\tlow1 = loadFloat2( x );\n\t\t\tlow2 = loadFloat2( y );\n\t\t\tbreak;\n\t\tcase 3:\n\t\t\tlow1 = loadFloat3( x );\n\t\t\tlow2 = loadFloat3( y );\n\t\t\tbreak;\n\t\tcase 4:\n\t\t\tlow1 = _mm_loadu_ps( x );\n\t\t\tlow2 = _mm_loadu_ps( y );\n\t\t\tbreak;\n\t\tcase 5:\n\t\t\tlow1 = _mm_loadu_ps( x );\n\t\t\tlow2 = _mm_loadu_ps( y );\n\t\t\thigh1 = _mm_load_ss( x + 4 );\n\t\t\thigh2 = _mm_load_ss( y + 4 );\n\t\t\tbreak;\n\t\tcase 6:\n\t\t\tlow1 = _mm_loadu_ps( x );\n\t\t\tlow2 = _mm_loadu_ps( y );\n\t\t\thigh1 = loadFloat2( x + 4 );\n\t\t\thigh2 = loadFloat2( y + 4 );\n\t\t\tbreak;\n\t\tcase 7: // load 14 bytes\n\t\t\tlow1 = _mm_loadu_ps( x );\n\t\t\tlow2 = _mm_loadu_ps( y );\n\t\t\thigh1 = loadFloat3( x + 4 );\n\t\t\thigh2 = loadFloat3( y + 4 );\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tfx = fy = _mm256_setzero_ps();\n\t\t\treturn;\n\t\t}\n\n\t\tfx = _mm256_setr_m128( low1, high1 );\n\t\tfy = _mm256_setr_m128( low2, high2 );\n\t}\n\n\t__forceinline float horizontalMaximum( __m256 v )\n\t{\n\t\t__m128 s = _mm256_extractf128_ps( v, 1 );\n\t\ts = _mm_max_ps( s, _mm256_castps256_ps128( v ) );\n\t\ts = _mm_max_ps( s, _mm_movehl_ps( s, s ) );\n\t\ts = _mm_max_ss( s, _mm_movehdup_ps( s ) );\n\t\treturn _mm_cvtss_f32( s );\n\t}\n\n\t__forceinline double horizontalSum( __m256 v )\n\t{\n\t\t__m256d d = _mm256_cvtps_pd( _mm256_extractf128_ps( v, 1 ) );\n\t\td = _mm256_add_pd( d, _mm256_cvtps_pd( _mm256_castps256_ps128( v ) ) );\n\n\t\t__m128d s = _mm256_extractf128_pd( d, 1 );\n\t\ts = _mm_add_pd( s, _mm256_castpd256_pd128( d ) );\n\t\ts = _mm_add_sd( s, _mm_unpackhi_pd( s, s ) );\n\t\treturn _mm_cvtsd_f64( s );\n\t}\n\n\t__m256 maskInfNan( __m256 diff, __m256 a, __m256 b )\n\t{\n\t\t__m256i ai = _mm256_castps_si256( a );\n\t\t__m256i bi = _mm256_castps_si256( b );\n\t\t__m256i eqi = _mm256_cmpeq_epi32( ai, bi );\n\t\t__m256 eq = _mm256_castsi256_ps( eqi );\n\t\treturn _mm256_andnot_ps( eq, diff );\n\t}\n\n\tclass DiffAcc\n\t{\n\t\t__m256 maxAbs = _mm256_setzero_ps();\n\t\t__m256 sumSquares = _mm256_setzero_ps();\n\n\tpublic:\n\n\t\t__forceinline void add( __m256 a, __m256 b )\n\t\t{\n\t\t\tconst __m256 neg0 = _mm256_set1_ps( -0.0f );\n\t\t\t__m256 diff = _mm256_sub_ps( b, a );\n\t\t\tdiff = maskInfNan( diff, a, b );\n\t\t\tsumSquares = _mm256_fmadd_ps( diff, diff, sumSquares );\n\t\t\tconst __m256 absDiff = _mm256_andnot_ps( neg0, diff );\n\t\t\tmaxAbs = _mm256_max_ps( maxAbs, absDiff );\n\t\t}\n\n\t\t__forceinline sTensorDiff reduce( size_t count )\n\t\t{\n\t\t\tsTensorDiff res;\n\t\t\tres.maxAbsDiff = horizontalMaximum( maxAbs );\n\t\t\tres.avgDiffSquared = (float)( horizontalSum( sumSquares ) / (double)(int64_t)count );\n\t\t\tres.length = count;\n\t\t\treturn res;\n\t\t}\n\t};\n\n\ttemplate<class E>\n\tstatic sTensorDiff __declspec( noinline ) diffVectors( const E* a, const E* b, size_t length )\n\t{\n\t\t// const E* const aEnd = a + length;\n\t\tconst E* const aEndAligned = a + ( length / 8 ) * 8;\n\t\tconst size_t remainder = length % 8;\n\n\t\tDiffAcc acc;\n\t\tfor( ; a < aEndAligned; a += 8, b += 8 )\n\t\t\tacc.add( load( a ), load( b ) );\n\n\t\tif( remainder != 0 )\n\t\t{\n\t\t\t__m256 va, vb;\n\t\t\tloadPartial( a, b, remainder, va, vb );\n\t\t\tacc.add( va, vb );\n\t\t}\n\n\t\treturn acc.reduce( length );\n\t}\n}\n\nsTensorDiff DirectCompute::computeDiff( const float* a, const float* b, size_t length )\n{\n\treturn diffVectors( a, b, length );\n}\n\nsTensorDiff DirectCompute::computeDiff( const uint16_t* a, const uint16_t* b, size_t length )\n{\n\treturn diffVectors( a, b, length );\n}\n\nvoid DirectCompute::sTensorDiff::print( const char* what ) const\n{\n\tlogDebug( u8\"%s: length %zu, maxAbsDiff = %g, avgDiffSquared = %g\", what, length, maxAbsDiff, avgDiffSquared );\n}\nvoid DirectCompute::sTensorDiff::print() const\n{\n\tlogDebug( u8\"%zu elements, maxAbsDiff = %g, avgDiffSquared = %g\", length, maxAbsDiff, avgDiffSquared );\n}\n\nHRESULT DirectCompute::dbgWriteBinaryFile( LPCTSTR fileName, const void* rsi, size_t cb )\n{\n\tCPath path;\n\tpath.m_strPath = LR\"(C:\\Temp\\2remove\\Whisper)\";\n\tpath.Append( fileName );\n\n\tCAtlFile file;\n\tCHECK( file.Create( path, GENERIC_WRITE, 0, CREATE_ALWAYS ) );\n\tCHECK( file.Write( rsi, (DWORD)cb ) );\n\tCHECK( file.Flush() );\n\treturn S_OK;\n}\n\n#include \"Tensor.h\"\n\nsTensorDiff DirectCompute::computeDiff( const Tensor& a, const Tensor& b )\n{\n\tassert( isSameShapeAndLayout( a, b ) );\n\tconst eDataType dt = a.getType();\n\tassert( dt == b.getType() );\n\tswitch( dt )\n\t{\n\tcase eDataType::FP32:\n\t{\n\t\tstd::vector<float> v1, v2;\n\t\ta.download( v1 );\n\t\tb.download( v2 );\n\t\tassert( v1.size() == v2.size() );\n#if 0\n\t\tconst size_t firstZero = std::find( v2.begin(), v2.end(), 0.0f ) - v2.begin();\n\t\t\n\t\tstd::vector<float> delta;\n\t\tdelta.resize( v1.size() );\n\t\tfor( size_t i = 0; i < v1.size(); i++ )\n\t\t\tdelta[ i ] = std::abs( v1[ i ] - v2[ i ] );\n\t\tconst size_t maxIndex = std::max_element( delta.begin(), delta.end() ) - delta.begin();\n#endif\n\t\treturn computeDiff( v1.data(), v2.data(), v1.size() );\n\t}\n\t}\n\tthrow E_NOTIMPL;\n}\n\nusing namespace DirectCompute;\n\nvoid PrintUniqueTensorSizes::printImpl( const std::array<uint32_t, 8>& a )\n{\n\tauto pair = set.emplace( a );\n\tif( !pair.second )\n\t\treturn;\t// was already there\n\n\tconst __m128i rhs = _mm_loadu_si128( ( const __m128i* ) ( &a[ 4 ] ) );\n\n\tif( _mm_testz_si128( rhs, rhs ) )\n\t{\n\t\tlogDebug( u8\"%s: [ %i, %i, %i, %i ]\", what,\n\t\t\ta[ 0 ], a[ 1 ], a[ 2 ], a[ 3 ] );\n\t}\n\telse\n\t{\n\t\tlogDebug( u8\"%s: [ %i, %i, %i, %i ], [ %i, %i, %i, %i ]\", what,\n\t\t\ta[ 0 ], a[ 1 ], a[ 2 ], a[ 3 ], a[ 4 ], a[ 5 ], a[ 6 ], a[ 7 ] );\n\t}\n}\n\nvoid PrintUniqueTensorSizes::print( const Tensor& lhs, const Tensor& rhs )\n{\n\tstd::array<uint32_t, 8> arr;\n\t__m128i* const rdi = ( __m128i* )arr.data();\n\t_mm_storeu_si128( rdi, lhs.sizeVec() );\n\t_mm_storeu_si128( rdi + 1, rhs.sizeVec() );\n\n\tprintImpl( arr );\n}\n\nvoid PrintUniqueTensorSizes::print( const int* lhs, const int* rhs )\n{\n\tstd::array<uint32_t, 8> arr;\n\t__m128i* const rdi = ( __m128i* )arr.data();\n\t_mm_storeu_si128( rdi, load16( lhs ) );\n\t_mm_storeu_si128( rdi + 1, load16( rhs ) );\n\n\tprintImpl( arr );\n}\n\nvoid PrintUniqueTensorSizes::print( const Tensor& lhs )\n{\n\tstd::array<uint32_t, 8> arr;\n\t__m128i* const rdi = ( __m128i* )arr.data();\n\t_mm_storeu_si128( rdi, lhs.sizeVec() );\n\t_mm_storeu_si128( rdi + 1, _mm_setzero_si128() );\n\n\tprintImpl( arr );\n}\n\n#include \"testUtilsC.h\"\n\nvoid printUniqueTensorSize( const char* name, const int* lhs, const int* rhs )\n{\n\tusing TS = DirectCompute::PrintUniqueTensorSizes;\n\tstatic std::unordered_map<std::string, TS> map;\n\tTS& ts = map.try_emplace( name, name ).first->second;\n\tts.print( lhs, rhs );\n}"
  },
  {
    "path": "Whisper/ML/testUtils.h",
    "content": "#pragma once\n#include \"../D3D/downloadBuffer.h\"\n#include \"../D3D/RenderDoc/renderDoc.h\"\n#include <unordered_set>\n#include <functional>\n\n// Funfact: this code written by ChatGPT\nnamespace std\n{\n\ttemplate<>\n\tstruct hash<array<uint32_t, 8>>\n\t{\n\t\tsize_t operator()( const array<uint32_t, 8>& arr ) const\n\t\t{\n\t\t\tsize_t result = 0;\n\t\t\tfor( uint32_t element : arr )\n\t\t\t\tresult = ( result * 31 ) ^ element;\n\t\t\treturn result;\n\t\t}\n\t};\n}\n\nnamespace DirectCompute\n{\n\tstruct sTensorDiff\n\t{\n\t\t// maximum( absolute( a - b ) )\n\t\tfloat maxAbsDiff;\n\t\t// average( ( a - b )^2 )\n\t\tfloat avgDiffSquared;\n\t\tsize_t length;\n\n\t\tvoid print() const;\n\t\tvoid print( const char* what ) const;\n\t};\n\n\t// Compute difference between 2 FP32 vectors\n\tsTensorDiff computeDiff( const float* a, const float* b, size_t length );\n\n\t// Compute difference between 2 FP16 vectors\n\tsTensorDiff computeDiff( const uint16_t* a, const uint16_t* b, size_t length );\n\n\tclass Tensor;\n\tsTensorDiff computeDiff( const Tensor& a, const Tensor& b );\n\n\tHRESULT dbgWriteBinaryFile( LPCTSTR fileName, const void* rsi, size_t cb );\n\n\t// Print unique sizes of the two tensors\n\tclass PrintUniqueTensorSizes\n\t{\n\t\tstd::unordered_set<std::array<uint32_t, 8>> set;\n\t\tconst char* const what;\n\t\tvoid printImpl( const std::array<uint32_t, 8>& a );\n\n\tpublic:\n\t\tPrintUniqueTensorSizes( const char* w ) : what( w ) { }\n\n\t\tvoid print( const Tensor& lhs, const Tensor& rhs );\n\t\tvoid print( const Tensor& lhs );\n\t\tvoid print( const int* lhs, const int* rhs );\n\t};\n}"
  },
  {
    "path": "Whisper/ML/testUtilsC.h",
    "content": "#pragma once\n\n#ifdef  __cplusplus\nextern \"C\"\n{\n#endif\n\tvoid printUniqueTensorSize( const char* name, const int* lhs, const int* rhs );\n#ifdef  __cplusplus\n}\n#endif"
  },
  {
    "path": "Whisper/Readme.txt",
    "content": "﻿This C++ project builds a DLL which actually does the heavy lifting of this project.\n\nIt implements the ML model, handles multimedia files with Media Foundation, captures audio (also with MF), does voice activity detection (custom code running on CPU), and a few smaller things.\n\nThe code requires C++/20, and only tested with Visual Studio 2022.\n\nWhen running pure GPGPU model, the DLL requires SSE 4.1 instruction set.\n\nWhen running a hybrid model, the DLL requires AVX1, FMA3, F16C, and BMI1 instruction set extensions."
  },
  {
    "path": "Whisper/Utils/CpuProfiler.cpp",
    "content": "#include \"stdafx.h\"\n#include \"CpuProfiler.h\"\n\nnamespace\n{\n\tusing namespace Whisper;\n\n\tinline int64_t qpcNow()\n\t{\n\t\tint64_t res;\n\t\tQueryPerformanceCounter( (LARGE_INTEGER*)&res );\n\t\treturn res;\n\t}\n\n\tclass CpuTimescale\n\t{\n\t\tuint64_t frequency = 0;\n\t\tconst int64_t tscStart;\n\t\tconst int64_t qpcStart;\n\n\t\tuint64_t computeTscFrequency();\n\n\tpublic:\n\n\t\tCpuTimescale() :\n\t\t\ttscStart( tscNow() ),\n\t\t\tqpcStart( qpcNow() )\n\t\t{ }\n\n\t\tinline uint64_t computeTicks( uint64_t tsc )\n\t\t{\n\t\t\tuint64_t freq = frequency;\n\t\t\tif( freq == 0 )\n\t\t\t\tfreq = computeTscFrequency();\n\n\t\t\treturn makeTime( tsc, freq );\n\t\t}\n\t};\n\n\tuint64_t __declspec( noinline ) CpuTimescale::computeTscFrequency()\n\t{\n\t\tint64_t tsc = tscNow();\n\t\tint64_t qpc = qpcNow();\n\t\ttsc -= tscStart;\n\t\tqpc -= qpcStart;\n\n\t\tuint64_t qpcFreq;\n\t\tQueryPerformanceFrequency( (LARGE_INTEGER*)&qpcFreq );\n\n\t\t// Seconds = qpc / qpcFreq\n\t\t// ticks per second = tsc / seconds = tsc * qpcFreq / qpc\n\t\tuint64_t res = ( (uint64_t)tsc * qpcFreq + ( (uint64_t)qpc / 2 ) - 1 ) / (uint64_t)qpc;\n\t\tfrequency = res;\n\t\tconst double GHz = (double)(int64_t)res * 1.0E-9;\n\t\tlogDebug( u8\"Computed CPU base frequency: %g GHz\", GHz );\n\t\treturn res;\n\t}\n\n\tstatic CpuTimescale timescale;\n}\n\nuint64_t Whisper::ticksFromTsc( uint64_t tscDiff )\n{\n\treturn timescale.computeTicks( tscDiff );\n}"
  },
  {
    "path": "Whisper/Utils/CpuProfiler.h",
    "content": "#pragma once\n\nnamespace Whisper\n{\n\t// Get current time in CPU clock\n\t// More specifically, each CPU core has a timestamp counter which runs at CPU's base frequency, regardless on the frequency scaling of that core.\n\tinline int64_t tscNow()\n\t{\n\t\treturn __rdtsc();\n\t}\n\n\t// Scale the time interval from CPU time stamp counter clock into 100-nanosecond ticks, rounding to nearest\n\tuint64_t ticksFromTsc( uint64_t tscDiff );\n\n\tclass CpuProfiler\n\t{\n\t\tconst int64_t started = tscNow();\n\n\tpublic:\n\n\t\tuint64_t elapsed() const\n\t\t{\n\t\t\treturn ticksFromTsc( (uint64_t)( tscNow() - started ) );\n\t\t}\n\t};\n}"
  },
  {
    "path": "Whisper/Utils/DelayExecution.cpp",
    "content": "#include \"stdafx.h\"\n#include \"DelayExecution.h\"\n\nnamespace\n{\n\tconstexpr bool useHighRezTimer = false;\n\n\tconstexpr int64_t sleepMicroseconds = 200;\n\n\tinline HRESULT sleepImpl( HANDLE timer )\n\t{\n\t\tconstexpr int64_t sleepTicks = sleepMicroseconds * 10;\n\n\t\tLARGE_INTEGER li;\n\t\t// Negative values indicate relative time\n\t\tli.QuadPart = -sleepTicks;\n\t\tif( !SetWaitableTimerEx( timer, &li, 0, nullptr, nullptr, nullptr, 0 ) )\n\t\t\treturn getLastHr();\n\t\tconst DWORD res = WaitForSingleObject( timer, 50 );\n\t\tif( res == WAIT_OBJECT_0 )\n\t\t\treturn S_OK;\n\t\tif( res == WAIT_FAILED )\n\t\t\treturn getLastHr();\n\t\treturn E_FAIL;\n\t}\n}\n\nvoid DelayExecution::sleepOnTheTimer( const DelayExecution& delay )\n{\n\tHRESULT hr = sleepImpl( delay.timer );\n\tif( SUCCEEDED( hr ) )\n\t\treturn;\n\tlogWarningHr( hr, u8\"DelayExecution.sleepOnTheTimer\" );\n}\n\nvoid DelayExecution::spinWait( const DelayExecution& )\n{\n\tfor( size_t i = 0; i < 1024; i++ )\n\t\t_mm_pause();\n}\n\nvoid DelayExecution::sleep( const DelayExecution& )\n{\n\tSleep( 0 );\n}\n\nDelayExecution::DelayExecution()\n{\n\tif constexpr( useHighRezTimer )\n\t{\n\t\tconstexpr DWORD flags = CREATE_WAITABLE_TIMER_HIGH_RESOLUTION;\n\t\tHANDLE h = CreateWaitableTimerEx( nullptr, nullptr, flags, TIMER_ALL_ACCESS );\n\t\tif( nullptr != h )\n\t\t{\n\t\t\ttimer.Attach( h );\n\t\t\tpfn = &sleepOnTheTimer;\n\t\t\treturn;\n\t\t}\n\n\t\tconst HRESULT hr = getLastHr();\n\t\tlogWarningHr( hr, u8\"CreateWaitableTimerEx\" );\n\t}\n\n\tpfn = &spinWait;\n\t// pfn = &sleep;\n}"
  },
  {
    "path": "Whisper/Utils/DelayExecution.h",
    "content": "#pragma once\n#include <atlbase.h>\n\n// Utility class implementing a high-resolution Sleep() function\nclass DelayExecution\n{\n\tusing pfnDelay = void( * )( const DelayExecution& de );\n\tpfnDelay pfn = nullptr;\n\tCHandle timer;\n\n\tstatic void sleepOnTheTimer( const DelayExecution& delay );\n\tstatic void spinWait( const DelayExecution& );\n\tstatic void sleep( const DelayExecution& );\n\npublic:\n\tDelayExecution();\n\tDelayExecution( const DelayExecution& ) = delete;\n\t~DelayExecution() = default;\n\n\tvoid delay() const\n\t{\n\t\tpfn( *this );\n\t}\n};"
  },
  {
    "path": "Whisper/Utils/GpuProfiler.cpp",
    "content": "#include \"stdafx.h\"\n#include \"GpuProfiler.h\"\n#include \"GpuProfilerSimple.h\"\nusing namespace DirectCompute;\n\ninline void GpuProfiler::sProfilerData::reset()\n{\n\t_mm_storeu_si128( ( __m128i* ) & callsPending, _mm_setzero_si128() );\n}\n\ninline void GpuProfiler::sProfilerData::addPending( int64_t time )\n{\n\tcallsPending++;\n\ttimePending += time;\n}\n\ninline void GpuProfiler::sProfilerData::dropPending()\n{\n\tcallsPending = 0;\n\ttimePending = 0;\n}\n\ninline void GpuProfiler::sProfilerData::makeTime( uint64_t freq )\n{\n\tdest->count += callsPending;\n\tdest->totalTicks += ::makeTime( timePending, freq );\n\tcallsPending = 0;\n\ttimePending = 0;\n}\n\nHRESULT GpuProfiler::Queue::create()\n{\n\tID3D11Device* const dev = device();\n\n\tCD3D11_QUERY_DESC desc{ D3D11_QUERY_TIMESTAMP };\n\tfor( Entry& e : queue )\n\t{\n\t\tCHECK( dev->CreateQuery( &desc, &e.query ) );\n\t\te.block = nullptr;\n\t\te.event = eEvent::None;\n\t\te.shader = EmptyShader;\n\t}\n\treturn S_OK;\n}\n\nnamespace\n{\n\tstatic uint64_t getTimestamp( ID3D11Query* query, const DelayExecution& delay )\n\t{\n\t\tID3D11DeviceContext* const ctx = context();\n\n\t\tuint64_t res = 0;\n\t\twhile( true )\n\t\t{\n\t\t\tconst HRESULT hr = ctx->GetData( query, &res, sizeof( uint64_t ), 0 );\n\t\t\tcheck( hr );\n\t\t\tif( S_OK == hr )\n\t\t\t\treturn res;\n\t\t\tdelay.delay();\n\t\t}\n\t}\n\n\tstatic D3D11_QUERY_DATA_TIMESTAMP_DISJOINT waitForDisjointData( ID3D11Query* query )\n\t{\n\t\tID3D11DeviceContext* const ctx = context();\n\t\tctx->End( query );\n\n\t\tD3D11_QUERY_DATA_TIMESTAMP_DISJOINT res;\n\t\twhile( true )\n\t\t{\n\t\t\tconst HRESULT hr = ctx->GetData( query, &res, sizeof( D3D11_QUERY_DATA_TIMESTAMP_DISJOINT ), 0 );\n\t\t\tcheck( hr );\n\t\t\tif( S_OK == hr )\n\t\t\t\treturn res;\n\t\t\tSleep( 1 );\n\t\t}\n\t}\n}\n\nvoid GpuProfiler::Queue::Entry::join( GpuProfiler& owner )\n{\n\tassert( nullptr != block );\n\n\tuint64_t res = getTimestamp( query, owner.delay );\n#if PROFILER_COLLECT_TAGS\n\tblock->haveTimestamp( event, shader, tag, res, owner );\n#else\n\tblock->haveTimestamp( event, shader, 0, res, owner );\n#endif\n\tblock = nullptr;\n\tevent = eEvent::None;\n\tshader = EmptyShader;\n}\n\nvoid GpuProfiler::Queue::submit( BlockState* block, eEvent evt, uint16_t shader, uint16_t tag )\n{\n\t// if( evt == GpuProfiler::eEvent::Shader && shader == 0 ) __debugbreak();\n\tassert( nullptr != block );\n\n\tEntry& e = queue[ nextEntry ];\n\tif( nullptr != e.block )\n\t\te.join( owner );\n\n\te.block = block;\n\te.event = evt;\n\te.shader = shader;\n#if PROFILER_COLLECT_TAGS\n\te.tag = tag;\n#endif\n\tcontext()->End( e.query );\n\tnextEntry = ( nextEntry + 1 ) % queueLength;\n}\n\nvoid GpuProfiler::Queue::join()\n{\n\twhile( true )\n\t{\n\t\tEntry& e = queue[ nextEntry ];\n\t\tif( nullptr == e.block )\n\t\t\treturn;\n\t\te.join( owner );\n\t\tnextEntry = ( nextEntry + 1 ) % queueLength;\n\t}\n}\n\nstatic inline uint32_t makeTagKey( uint16_t cs, uint16_t tag )\n{\n\tuint32_t r = cs;\n\tr = r << 16;\n\tr |= tag;\n\treturn r;\n}\n\nvoid GpuProfiler::BlockState::completePrevShader( uint64_t time, GpuProfiler& profiler )\n{\n\tif( shaderStart == -1 )\n\t\treturn;\n\tassert( prevShader != EmptyShader );\n\tconst int64_t elapsed = (int64_t)time - shaderStart;\n\n\tsProfilerData* dest = nullptr;\n\tauto* p = profiler.results.Lookup( prevShader );\n\tif( nullptr != p )\n\t\tdest = &p->m_value;\n\telse\n\t{\n\t\tsProfilerData& res = profiler.results[ prevShader ];\n\t\tres.dest = &profiler.dest.measure( (eComputeShader)prevShader );\n\t\tdest = &res;\n\t}\n\tdest->addPending( elapsed );\n\n#if PROFILER_COLLECT_TAGS\n\tif( 0 != prevShaderTag )\n\t{\n\t\tconst uint32_t key = makeTagKey( prevShader, prevShaderTag );\n\t\tauto* pt = profiler.resultsTagged.Lookup( key );\n\t\tif( nullptr != pt )\n\t\t\tdest = &pt->m_value;\n\t\telse\n\t\t{\n\t\t\tsProfilerData& res = profiler.resultsTagged[ key ];\n\t\t\tres.dest = &profiler.dest.measure( (eComputeShader)prevShader, prevShaderTag );\n\t\t\tdest = &res;\n\t\t}\n\t\tdest->addPending( elapsed );\n\t}\n#endif\n\tprevShader = EmptyShader;\n\tprevShaderTag = 0;\n\tshaderStart = -1;\n}\n\nvoid GpuProfiler::BlockState::haveTimestamp( eEvent evt, uint16_t cs, uint16_t tag, uint64_t time, GpuProfiler& profiler )\n{\n\tswitch( evt )\n\t{\n\tcase eEvent::BlockStart:\n\t\tassert( -1 == timeStart );\n\t\tassert( -1 == shaderStart );\n\t\tassert( cs == EmptyShader );\n\t\ttimeStart = (int64_t)time;\n\t\tif( nullptr != parentBlock )\n\t\t\tparentBlock->completePrevShader( time, profiler );\n\t\treturn;\n\tcase eEvent::BlockEnd:\n\t\tassert( -1 != timeStart );\n\t\tassert( cs == EmptyShader );\n\t\tcompletePrevShader( time, profiler );\n\t\tdestBlock->addPending( (int64_t)time - timeStart );\n\t\ttimeStart = -1;\n\t\treturn;\n\tcase eEvent::Shader:\n\t\tassert( cs != EmptyShader );\n\t\t// if( cs == (uint16_t)0 ) __debugbreak();\n\t\tcompletePrevShader( time, profiler );\n\t\tprevShader = cs;\n\t\tprevShaderTag = tag;\n\t\tshaderStart = (int64_t)time;\n\t\treturn;\n\t}\n\tassert( false );\n}\n\nHRESULT GpuProfiler::create( size_t maxDepth )\n{\n\tCD3D11_QUERY_DESC desc{ D3D11_QUERY_TIMESTAMP_DISJOINT };\n\tCHECK( device()->CreateQuery( &desc, &disjoint ) );\n\tCHECK( queries.create() );\n\tstack.reserve( maxDepth );\n\treturn S_OK;\n}\n\nvoid GpuProfiler::blockStart( eProfilerBlock which )\n{\n\tBlockState* parentBlock;\n\tif( stack.empty() )\n\t{\n\t\tcontext()->Begin( disjoint );\n\t\tparentBlock = nullptr;\n\t}\n\telse\n\t\tparentBlock = *stack.rbegin();\n\n\tBlockState* bs = nullptr;\n\tauto p = blockStates.Lookup( which );\n\tif( nullptr != p )\n\t\tbs = &p->m_value;\n\telse\n\t{\n\t\tBlockState& block = blockStates[ which ];\n\t\tblock.destBlock = &results[ (uint16_t)which ];\n\t\tblock.destBlock->dest = &dest.measure( which );\n\t\tbs = &block;\n\t}\n\tbs->parentBlock = parentBlock;\n\tqueries.submit( bs, eEvent::BlockStart );\n\tstack.push_back( bs );\n}\n\nvoid GpuProfiler::blockEnd()\n{\n\tassert( !stack.empty() );\n\tBlockState* const bs = *stack.rbegin();\n\tqueries.submit( bs, eEvent::BlockEnd );\n\tstack.pop_back();\n\n\tif( !stack.empty() )\n\t\treturn;\n\n\tconst D3D11_QUERY_DATA_TIMESTAMP_DISJOINT dtsd = waitForDisjointData( disjoint );\n\tqueries.join();\n\n\tif( !dtsd.Disjoint )\n\t{\n\t\t// Fortunately, these timers appear to be relatively high resolution.\n\t\t// Specifically, on the iGPU inside Ryzen 7 5700G that frequency is 1E+8 = 100 MHz\n\t\t// On nVidia 1080Ti, that frequency is 1E+9 = 1 GHz\n\t\tconst uint64_t freq = dtsd.Frequency;\n\t\tresultsMakeTime( freq );\n\t}\n\telse\n\t{\n\t\t// Something occurred in between the query's ID3D11DeviceContext::Begin and ID3D11DeviceContext::End calls \n\t\t// that caused the timestamp counter to become discontinuous or disjoint, such as unplugging the AC cord on a laptop, overheating, or throttling up/down due to laptop savings events.\n\t\t// The timestamp returned by ID3D11DeviceContext::GetData for a timestamp query is only reliable if Disjoint is FALSE.\n\t\tresultsDropPending();\n\t}\n}\n\nvoid GpuProfiler::computeShader( eComputeShader cs )\n{\n\tassert( !stack.empty() );\n\tif( !profileShaders )\n\t\treturn;\n\n\tBlockState* const bs = *stack.rbegin();\n#if PROFILER_COLLECT_TAGS\n\tqueries.submit( bs, eEvent::Shader, (uint16_t)cs, m_nextTag );\n\tm_nextTag = 0;\n#else\n\tqueries.submit( bs, eEvent::Shader, (uint16_t)cs );\n#endif\n}\n\nvoid GpuProfiler::resultsDropPending()\n{\n\tfor( POSITION pos = results.GetStartPosition(); nullptr != pos; )\n\t\tresults.GetNextValue( pos ).dropPending();\n#if PROFILER_COLLECT_TAGS\n\tfor( POSITION pos = resultsTagged.GetStartPosition(); nullptr != pos; )\n\t\tresultsTagged.GetNextValue( pos ).dropPending();\n#endif\n}\n\nvoid GpuProfiler::resultsMakeTime( uint64_t freq )\n{\n\tfor( POSITION pos = results.GetStartPosition(); nullptr != pos; )\n\t\tresults.GetNextValue( pos ).makeTime( freq );\n#if PROFILER_COLLECT_TAGS\n\tfor( POSITION pos = resultsTagged.GetStartPosition(); nullptr != pos; )\n\t\tresultsTagged.GetNextValue( pos ).makeTime( freq );\n#endif\n}\n\nvoid GpuProfiler::resultsReset()\n{\n\tfor( POSITION pos = results.GetStartPosition(); nullptr != pos; )\n\t\tresults.GetNextValue( pos ).reset();\n#if PROFILER_COLLECT_TAGS\n\tfor( POSITION pos = resultsTagged.GetStartPosition(); nullptr != pos; )\n\t\tresultsTagged.GetNextValue( pos ).reset();\n#endif\n}\n\n#if PROFILER_COLLECT_TAGS\nuint16_t __declspec( noinline ) GpuProfiler::setNextTag( const char* name )\n{\n\tuint16_t tag = dest.makeTagId( name );\n\tm_nextTag = tag;\n\treturn tag;\n}\n#endif\n\nHRESULT GpuProfilerSimple::create()\n{\n\tID3D11Device* const dev = device();\n\n\tCD3D11_QUERY_DESC desc{ D3D11_QUERY_TIMESTAMP_DISJOINT };\n\tCHECK( dev->CreateQuery( &desc, &disjoint ) );\n\n\tdesc.Query = D3D11_QUERY_TIMESTAMP;\n\tCHECK( dev->CreateQuery( &desc, &begin ) );\n\tCHECK( dev->CreateQuery( &desc, &end ) );\n\n\tcontext()->Begin( disjoint );\n\tcontext()->End( begin );\n\treturn S_OK;\n}\n\nHRESULT GpuProfilerSimple::time( uint64_t& rdi ) const\n{\n\tcontext()->End( end );\n\n\ttry\n\t{\n\t\tconst D3D11_QUERY_DATA_TIMESTAMP_DISJOINT dtsd = waitForDisjointData( disjoint );\n\t\tconst uint64_t t2 = getTimestamp( end, delay );\n\t\tconst uint64_t t1 = getTimestamp( begin, delay );\n\n\t\tif( !dtsd.Disjoint )\n\t\t{\n\t\t\trdi = makeTime( t2 - t1, dtsd.Frequency );\n\t\t\treturn S_OK;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// Something occurred in between the query's ID3D11DeviceContext::Begin and ID3D11DeviceContext::End calls \n\t\t\t// that caused the timestamp counter to become discontinuous or disjoint, such as unplugging the AC cord on a laptop, overheating, or throttling up/down due to laptop savings events.\n\t\t\t// The timestamp returned by ID3D11DeviceContext::GetData for a timestamp query is only reliable if Disjoint is FALSE.\n\t\t\trdi = -1;\n\t\t\treturn S_FALSE;\n\t\t}\n\t}\n\tcatch( HRESULT hr )\n\t{\n\t\treturn hr;\n\t}\n}"
  },
  {
    "path": "Whisper/Utils/GpuProfiler.h",
    "content": "#pragma once\n#include \"../D3D/device.h\"\n#include \"ProfileCollection.h\"\n#include \"DelayExecution.h\"\n\nnamespace DirectCompute\n{\n\tenum struct eProfilerBlock : uint16_t\n\t{\n\t\tLoadModel = 0x1000,\n\t\tRun = 0x2000,\n\t\tEncode = 0x3000,\n\t\tEncodeLayer = 0x4000,\n\t\tDecode = 0x5000,\n\t\tDecodeStep = 0x6000,\n\t\tDecodeLayer = 0x7000,\n\t};\n\n\tenum struct eComputeShader : uint16_t;\n\n\tclass GpuProfiler\n\t{\n\t\tDelayExecution delay;\n\t\tCComPtr<ID3D11Query> disjoint;\n\n\t\tenum struct eEvent\n\t\t{\n\t\t\tNone = 0,\n\t\t\tBlockStart,\n\t\t\tBlockEnd,\n\t\t\tShader\n\t\t};\n\n\t\tstruct BlockState;\n\t\tstatic constexpr uint16_t EmptyShader = ~(uint16_t)0;\n\n\t\t// A circular buffer with in-flight queries which feeds timestamps into the iTimestampSink interface\n\t\tclass Queue\n\t\t{\n\t\t\tstatic constexpr size_t queueLength = 32;\n\n\t\t\t// Ring buffer for individual measures\n\t\t\tstruct Entry\n\t\t\t{\n\t\t\t\tCComPtr<ID3D11Query> query;\n\t\t\t\tBlockState* block;\n\t\t\t\teEvent event;\n\t\t\t\tuint16_t shader;\n#if PROFILER_COLLECT_TAGS\n\t\t\t\tuint16_t tag = 0;\n#endif\n\t\t\t\tvoid join( GpuProfiler& owner );\n\t\t\t};\n\n\t\t\tGpuProfiler& owner;\n\t\t\tstd::array<Entry, queueLength> queue;\n\t\t\tsize_t nextEntry = 0;\n\n\t\tpublic:\n\t\t\tQueue( GpuProfiler& gp ) : owner( gp ) {}\n\n\t\t\tHRESULT create();\n\n\t\t\t// Begin a next query. Eventually, this will result in the BlockState.haveTimestamp callback\n\t\t\tvoid submit( BlockState* block, eEvent evt, uint16_t shader = EmptyShader, uint16_t tag = 0 );\n\n\t\t\t// Wait for all the pending queries, and call their callbacks\n\t\t\tvoid join();\n\t\t};\n\t\tQueue queries;\n\n\t\tstruct sProfilerData;\n\t\tstruct BlockState\n\t\t{\n\t\t\tint64_t timeStart = -1;\n\t\t\tsProfilerData* destBlock = nullptr;\n\t\t\tint64_t shaderStart = -1;\n\t\t\tuint16_t prevShader = EmptyShader;\n\t\t\tuint16_t prevShaderTag = 0;\n\t\t\tBlockState* parentBlock = nullptr;\n\t\t\tvoid haveTimestamp( eEvent evt, uint16_t cs, uint16_t tag, uint64_t time, GpuProfiler& profiler );\n\t\tprivate:\n\t\t\tvoid completePrevShader( uint64_t time, GpuProfiler& profiler );\n\t\t};\n\t\tCAtlMap<eProfilerBlock, BlockState> blockStates;\n\t\tstd::vector<BlockState*> stack;\n\n\t\tstruct sProfilerData\n\t\t{\n\t\t\t// Count of accumulated measures\n\t\t\tsize_t callsPending;\n\t\t\t// Total time spent running all instances of that measure, expressed in GPU ticks\n\t\t\tuint64_t timePending;\n\n\t\t\tWhisper::ProfileCollection::Measure* dest;\n\n\t\t\tinline void makeTime( uint64_t freq );\n\t\t\tinline void addPending( int64_t time );\n\t\t\tinline void reset();\n\t\t\tinline void dropPending();\n\n\t\t\tsProfilerData()\n\t\t\t{\n\t\t\t\treset();\n\t\t\t}\n\t\t};\n\n\t\tCAtlMap<uint16_t, sProfilerData> results;\n#if PROFILER_COLLECT_TAGS\n\t\tCAtlMap<uint32_t, sProfilerData> resultsTagged;\n#endif\n\t\tvoid resultsMakeTime( uint64_t freq );\n\t\tvoid resultsDropPending();\n\t\tvoid resultsReset();\n\n\t\tvoid blockStart( eProfilerBlock which );\n\t\tvoid blockEnd();\n\n\t\tWhisper::ProfileCollection& dest;\n#if PROFILER_COLLECT_TAGS\n\t\tuint16_t m_nextTag = 0;\n#endif\n\tpublic:\n\n\t\tGpuProfiler( Whisper::ProfileCollection& pc ) :\n\t\t\tdest( pc ), queries( *this ) { }\n\n\t\tHRESULT create( size_t maxDepth = 3 );\n\n\t\tclass BlockRaii\n\t\t{\n\t\t\tGpuProfiler* profiler;\n\n\t\tpublic:\n\t\t\tBlockRaii( GpuProfiler& owner, eProfilerBlock which )\n\t\t\t{\n\t\t\t\towner.blockStart( which );\n\t\t\t\tprofiler = &owner;\n\t\t\t}\n\t\t\t~BlockRaii()\n\t\t\t{\n\t\t\t\tif( nullptr != profiler )\n\t\t\t\t{\n\t\t\t\t\tprofiler->blockEnd();\n\t\t\t\t\tprofiler = nullptr;\n\t\t\t\t}\n\t\t\t}\n\t\t\tBlockRaii( BlockRaii&& that ) noexcept :\n\t\t\t\tprofiler( that.profiler )\n\t\t\t{\n\t\t\t\tthat.profiler = nullptr;\n\t\t\t}\n\t\t\tBlockRaii( const BlockRaii& ) = delete;\n\t\t\tvoid operator=( const BlockRaii& ) = delete;\n\t\t\tvoid operator=( BlockRaii&& ) = delete;\n\t\t};\n\n\t\tBlockRaii block( eProfilerBlock which )\n\t\t{\n\t\t\treturn BlockRaii{ *this, which };\n\t\t}\n\n\t\tvoid computeShader( eComputeShader cs );\n\n\t\tbool profileShaders = false;\n\t\t// bool profileShaders = true;\n\n\t\tdecltype( auto ) cpuBlock( Whisper::eCpuBlock block )\n\t\t{\n\t\t\treturn dest.cpuBlock( block );\n\t\t}\n\t\tWhisper::ProfileCollection& profiler() { return dest; }\n\n\t\t// Set tag string for the next compute shader\n\t\t// The string should be readonly: for performance reason the implementation doesnt copy nor compare any strings, it only keeps the pointer\n#if PROFILER_COLLECT_TAGS\n\t\tuint16_t setNextTag( const char* name );\n#else\n\t\tinline uint16_t setNextTag( const char* name ) { return 0; }\n#endif\n\n\t\tvoid setNextTag( uint16_t tag )\n\t\t{\n#if PROFILER_COLLECT_TAGS\n\t\t\tm_nextTag = tag;\n#endif\n\t\t}\n\t};\n}"
  },
  {
    "path": "Whisper/Utils/GpuProfilerSimple.h",
    "content": "#pragma once\n#include \"../D3D/device.h\"\n#include \"DelayExecution.h\"\n\nnamespace DirectCompute\n{\n\t// A simple profiler which doesn't collect anything, used to measure time it took to load the model\n\tclass GpuProfilerSimple\n\t{\n\t\tDelayExecution delay;\n\t\tCComPtr<ID3D11Query> disjoint, begin, end;\n\tpublic:\n\t\tHRESULT create();\n\t\tHRESULT time( uint64_t& rdi ) const;\n\t};\n}"
  },
  {
    "path": "Whisper/Utils/LZ4/LICENSE",
    "content": "LZ4 Library\nCopyright (c) 2011-2020, Yann Collet\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without modification,\nare permitted provided that the following conditions are met:\n\n* Redistributions of source code must retain the above copyright notice, this\n  list of conditions and the following disclaimer.\n\n* Redistributions in binary form must reproduce the above copyright notice, this\n  list of conditions and the following disclaimer in the documentation and/or\n  other materials provided with the distribution.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\nANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\nWARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR\nANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\nLOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\nANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\nSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "Whisper/Utils/LZ4/lz4.c",
    "content": "/*\n   LZ4 - Fast LZ compression algorithm\n   Copyright (C) 2011-2020, Yann Collet.\n\n   BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)\n\n   Redistribution and use in source and binary forms, with or without\n   modification, are permitted provided that the following conditions are\n   met:\n\n       * Redistributions of source code must retain the above copyright\n   notice, this list of conditions and the following disclaimer.\n       * Redistributions in binary form must reproduce the above\n   copyright notice, this list of conditions and the following disclaimer\n   in the documentation and/or other materials provided with the\n   distribution.\n\n   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n   \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n   You can contact the author at :\n    - LZ4 homepage : http://www.lz4.org\n    - LZ4 source repository : https://github.com/lz4/lz4\n*/\n\n/*-************************************\n*  Tuning parameters\n**************************************/\n/*\n * LZ4_HEAPMODE :\n * Select how default compression functions will allocate memory for their hash table,\n * in memory stack (0:default, fastest), or in memory heap (1:requires malloc()).\n */\n#ifndef LZ4_HEAPMODE\n#  define LZ4_HEAPMODE 0\n#endif\n\n/*\n * LZ4_ACCELERATION_DEFAULT :\n * Select \"acceleration\" for LZ4_compress_fast() when parameter value <= 0\n */\n#define LZ4_ACCELERATION_DEFAULT 1\n/*\n * LZ4_ACCELERATION_MAX :\n * Any \"acceleration\" value higher than this threshold\n * get treated as LZ4_ACCELERATION_MAX instead (fix #876)\n */\n#define LZ4_ACCELERATION_MAX 65537\n\n\n/*-************************************\n*  CPU Feature Detection\n**************************************/\n/* LZ4_FORCE_MEMORY_ACCESS\n * By default, access to unaligned memory is controlled by `memcpy()`, which is safe and portable.\n * Unfortunately, on some target/compiler combinations, the generated assembly is sub-optimal.\n * The below switch allow to select different access method for improved performance.\n * Method 0 (default) : use `memcpy()`. Safe and portable.\n * Method 1 : `__packed` statement. It depends on compiler extension (ie, not portable).\n *            This method is safe if your compiler supports it, and *generally* as fast or faster than `memcpy`.\n * Method 2 : direct access. This method is portable but violate C standard.\n *            It can generate buggy code on targets which assembly generation depends on alignment.\n *            But in some circumstances, it's the only known way to get the most performance (ie GCC + ARMv6)\n * See https://fastcompression.blogspot.fr/2015/08/accessing-unaligned-memory.html for details.\n * Prefer these methods in priority order (0 > 1 > 2)\n */\n#ifndef LZ4_FORCE_MEMORY_ACCESS   /* can be defined externally */\n#  if defined(__GNUC__) && \\\n  ( defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) || defined(__ARM_ARCH_6K__) \\\n  || defined(__ARM_ARCH_6Z__) || defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) )\n#    define LZ4_FORCE_MEMORY_ACCESS 2\n#  elif (defined(__INTEL_COMPILER) && !defined(_WIN32)) || defined(__GNUC__)\n#    define LZ4_FORCE_MEMORY_ACCESS 1\n#  endif\n#endif\n\n/*\n * LZ4_FORCE_SW_BITCOUNT\n * Define this parameter if your target system or compiler does not support hardware bit count\n */\n#if defined(_MSC_VER) && defined(_WIN32_WCE)   /* Visual Studio for WinCE doesn't support Hardware bit count */\n#  undef  LZ4_FORCE_SW_BITCOUNT  /* avoid double def */\n#  define LZ4_FORCE_SW_BITCOUNT\n#endif\n\n\n\n/*-************************************\n*  Dependency\n**************************************/\n/*\n * LZ4_SRC_INCLUDED:\n * Amalgamation flag, whether lz4.c is included\n */\n#ifndef LZ4_SRC_INCLUDED\n#  define LZ4_SRC_INCLUDED 1\n#endif\n\n#ifndef LZ4_STATIC_LINKING_ONLY\n#define LZ4_STATIC_LINKING_ONLY\n#endif\n\n#ifndef LZ4_DISABLE_DEPRECATE_WARNINGS\n#define LZ4_DISABLE_DEPRECATE_WARNINGS /* due to LZ4_decompress_safe_withPrefix64k */\n#endif\n\n#define LZ4_STATIC_LINKING_ONLY  /* LZ4_DISTANCE_MAX */\n#include \"lz4.h\"\n/* see also \"memory routines\" below */\n\n\n/*-************************************\n*  Compiler Options\n**************************************/\n#if defined(_MSC_VER) && (_MSC_VER >= 1400)  /* Visual Studio 2005+ */\n#  include <intrin.h>               /* only present in VS2005+ */\n#  pragma warning(disable : 4127)   /* disable: C4127: conditional expression is constant */\n#  pragma warning(disable : 6237)   /* disable: C6237: conditional expression is always 0 */\n#endif  /* _MSC_VER */\n\n#ifndef LZ4_FORCE_INLINE\n#  ifdef _MSC_VER    /* Visual Studio */\n#    define LZ4_FORCE_INLINE static __forceinline\n#  else\n#    if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L   /* C99 */\n#      ifdef __GNUC__\n#        define LZ4_FORCE_INLINE static inline __attribute__((always_inline))\n#      else\n#        define LZ4_FORCE_INLINE static inline\n#      endif\n#    else\n#      define LZ4_FORCE_INLINE static\n#    endif /* __STDC_VERSION__ */\n#  endif  /* _MSC_VER */\n#endif /* LZ4_FORCE_INLINE */\n\n/* LZ4_FORCE_O2 and LZ4_FORCE_INLINE\n * gcc on ppc64le generates an unrolled SIMDized loop for LZ4_wildCopy8,\n * together with a simple 8-byte copy loop as a fall-back path.\n * However, this optimization hurts the decompression speed by >30%,\n * because the execution does not go to the optimized loop\n * for typical compressible data, and all of the preamble checks\n * before going to the fall-back path become useless overhead.\n * This optimization happens only with the -O3 flag, and -O2 generates\n * a simple 8-byte copy loop.\n * With gcc on ppc64le, all of the LZ4_decompress_* and LZ4_wildCopy8\n * functions are annotated with __attribute__((optimize(\"O2\"))),\n * and also LZ4_wildCopy8 is forcibly inlined, so that the O2 attribute\n * of LZ4_wildCopy8 does not affect the compression speed.\n */\n#if defined(__PPC64__) && defined(__LITTLE_ENDIAN__) && defined(__GNUC__) && !defined(__clang__)\n#  define LZ4_FORCE_O2  __attribute__((optimize(\"O2\")))\n#  undef LZ4_FORCE_INLINE\n#  define LZ4_FORCE_INLINE  static __inline __attribute__((optimize(\"O2\"),always_inline))\n#else\n#  define LZ4_FORCE_O2\n#endif\n\n#if (defined(__GNUC__) && (__GNUC__ >= 3)) || (defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 800)) || defined(__clang__)\n#  define expect(expr,value)    (__builtin_expect ((expr),(value)) )\n#else\n#  define expect(expr,value)    (expr)\n#endif\n\n#ifndef likely\n#define likely(expr)     expect((expr) != 0, 1)\n#endif\n#ifndef unlikely\n#define unlikely(expr)   expect((expr) != 0, 0)\n#endif\n\n/* Should the alignment test prove unreliable, for some reason,\n * it can be disabled by setting LZ4_ALIGN_TEST to 0 */\n#ifndef LZ4_ALIGN_TEST  /* can be externally provided */\n# define LZ4_ALIGN_TEST 1\n#endif\n\n\n/*-************************************\n*  Memory routines\n**************************************/\n\n/*! LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION :\n *  Disable relatively high-level LZ4/HC functions that use dynamic memory\n *  allocation functions (malloc(), calloc(), free()).\n *\n *  Note that this is a compile-time switch. And since it disables\n *  public/stable LZ4 v1 API functions, we don't recommend using this\n *  symbol to generate a library for distribution.\n *\n *  The following public functions are removed when this symbol is defined.\n *  - lz4   : LZ4_createStream, LZ4_freeStream,\n *            LZ4_createStreamDecode, LZ4_freeStreamDecode, LZ4_create (deprecated)\n *  - lz4hc : LZ4_createStreamHC, LZ4_freeStreamHC,\n *            LZ4_createHC (deprecated), LZ4_freeHC  (deprecated)\n *  - lz4frame, lz4file : All LZ4F_* functions\n */\n#if defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION)\n#  define ALLOC(s)          lz4_error_memory_allocation_is_disabled\n#  define ALLOC_AND_ZERO(s) lz4_error_memory_allocation_is_disabled\n#  define FREEMEM(p)        lz4_error_memory_allocation_is_disabled\n#elif defined(LZ4_USER_MEMORY_FUNCTIONS)\n/* memory management functions can be customized by user project.\n * Below functions must exist somewhere in the Project\n * and be available at link time */\nvoid* LZ4_malloc(size_t s);\nvoid* LZ4_calloc(size_t n, size_t s);\nvoid  LZ4_free(void* p);\n# define ALLOC(s)          LZ4_malloc(s)\n# define ALLOC_AND_ZERO(s) LZ4_calloc(1,s)\n# define FREEMEM(p)        LZ4_free(p)\n#else\n# include <stdlib.h>   /* malloc, calloc, free */\n# define ALLOC(s)          malloc(s)\n# define ALLOC_AND_ZERO(s) calloc(1,s)\n# define FREEMEM(p)        free(p)\n#endif\n\n#if ! LZ4_FREESTANDING\n#  include <string.h>   /* memset, memcpy */\n#endif\n#if !defined(LZ4_memset)\n#  define LZ4_memset(p,v,s) memset((p),(v),(s))\n#endif\n#define MEM_INIT(p,v,s)   LZ4_memset((p),(v),(s))\n\n\n/*-************************************\n*  Common Constants\n**************************************/\n#define MINMATCH 4\n\n#define WILDCOPYLENGTH 8\n#define LASTLITERALS   5   /* see ../doc/lz4_Block_format.md#parsing-restrictions */\n#define MFLIMIT       12   /* see ../doc/lz4_Block_format.md#parsing-restrictions */\n#define MATCH_SAFEGUARD_DISTANCE  ((2*WILDCOPYLENGTH) - MINMATCH)   /* ensure it's possible to write 2 x wildcopyLength without overflowing output buffer */\n#define FASTLOOP_SAFE_DISTANCE 64\nstatic const int LZ4_minLength = (MFLIMIT+1);\n\n#define KB *(1 <<10)\n#define MB *(1 <<20)\n#define GB *(1U<<30)\n\n#define LZ4_DISTANCE_ABSOLUTE_MAX 65535\n#if (LZ4_DISTANCE_MAX > LZ4_DISTANCE_ABSOLUTE_MAX)   /* max supported by LZ4 format */\n#  error \"LZ4_DISTANCE_MAX is too big : must be <= 65535\"\n#endif\n\n#define ML_BITS  4\n#define ML_MASK  ((1U<<ML_BITS)-1)\n#define RUN_BITS (8-ML_BITS)\n#define RUN_MASK ((1U<<RUN_BITS)-1)\n\n\n/*-************************************\n*  Error detection\n**************************************/\n#if defined(LZ4_DEBUG) && (LZ4_DEBUG>=1)\n#  include <assert.h>\n#else\n#  ifndef assert\n#    define assert(condition) ((void)0)\n#  endif\n#endif\n\n#define LZ4_STATIC_ASSERT(c)   { enum { LZ4_static_assert = 1/(int)(!!(c)) }; }   /* use after variable declarations */\n\n#if defined(LZ4_DEBUG) && (LZ4_DEBUG>=2)\n#  include <stdio.h>\n   static int g_debuglog_enable = 1;\n#  define DEBUGLOG(l, ...) {                          \\\n        if ((g_debuglog_enable) && (l<=LZ4_DEBUG)) {  \\\n            fprintf(stderr, __FILE__ \": \");           \\\n            fprintf(stderr, __VA_ARGS__);             \\\n            fprintf(stderr, \" \\n\");                   \\\n    }   }\n#else\n#  define DEBUGLOG(l, ...) {}    /* disabled */\n#endif\n\nstatic int LZ4_isAligned(const void* ptr, size_t alignment)\n{\n    return ((size_t)ptr & (alignment -1)) == 0;\n}\n\n\n/*-************************************\n*  Types\n**************************************/\n#include <limits.h>\n#if defined(__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */)\n# include <stdint.h>\n  typedef  uint8_t BYTE;\n  typedef uint16_t U16;\n  typedef uint32_t U32;\n  typedef  int32_t S32;\n  typedef uint64_t U64;\n  typedef uintptr_t uptrval;\n#else\n# if UINT_MAX != 4294967295UL\n#   error \"LZ4 code (when not C++ or C99) assumes that sizeof(int) == 4\"\n# endif\n  typedef unsigned char       BYTE;\n  typedef unsigned short      U16;\n  typedef unsigned int        U32;\n  typedef   signed int        S32;\n  typedef unsigned long long  U64;\n  typedef size_t              uptrval;   /* generally true, except OpenVMS-64 */\n#endif\n\n#if defined(__x86_64__)\n  typedef U64    reg_t;   /* 64-bits in x32 mode */\n#else\n  typedef size_t reg_t;   /* 32-bits in x32 mode */\n#endif\n\ntypedef enum {\n    notLimited = 0,\n    limitedOutput = 1,\n    fillOutput = 2\n} limitedOutput_directive;\n\n\n/*-************************************\n*  Reading and writing into memory\n**************************************/\n\n/**\n * LZ4 relies on memcpy with a constant size being inlined. In freestanding\n * environments, the compiler can't assume the implementation of memcpy() is\n * standard compliant, so it can't apply its specialized memcpy() inlining\n * logic. When possible, use __builtin_memcpy() to tell the compiler to analyze\n * memcpy() as if it were standard compliant, so it can inline it in freestanding\n * environments. This is needed when decompressing the Linux Kernel, for example.\n */\n#if !defined(LZ4_memcpy)\n#  if defined(__GNUC__) && (__GNUC__ >= 4)\n#    define LZ4_memcpy(dst, src, size) __builtin_memcpy(dst, src, size)\n#  else\n#    define LZ4_memcpy(dst, src, size) memcpy(dst, src, size)\n#  endif\n#endif\n\n#if !defined(LZ4_memmove)\n#  if defined(__GNUC__) && (__GNUC__ >= 4)\n#    define LZ4_memmove __builtin_memmove\n#  else\n#    define LZ4_memmove memmove\n#  endif\n#endif\n\nstatic unsigned LZ4_isLittleEndian(void)\n{\n    const union { U32 u; BYTE c[4]; } one = { 1 };   /* don't use static : performance detrimental */\n    return one.c[0];\n}\n\n\n#if defined(LZ4_FORCE_MEMORY_ACCESS) && (LZ4_FORCE_MEMORY_ACCESS==2)\n/* lie to the compiler about data alignment; use with caution */\n\nstatic U16 LZ4_read16(const void* memPtr) { return *(const U16*) memPtr; }\nstatic U32 LZ4_read32(const void* memPtr) { return *(const U32*) memPtr; }\nstatic reg_t LZ4_read_ARCH(const void* memPtr) { return *(const reg_t*) memPtr; }\n\nstatic void LZ4_write16(void* memPtr, U16 value) { *(U16*)memPtr = value; }\nstatic void LZ4_write32(void* memPtr, U32 value) { *(U32*)memPtr = value; }\n\n#elif defined(LZ4_FORCE_MEMORY_ACCESS) && (LZ4_FORCE_MEMORY_ACCESS==1)\n\n/* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */\n/* currently only defined for gcc and icc */\ntypedef union { U16 u16; U32 u32; reg_t uArch; } __attribute__((packed)) LZ4_unalign;\n\nstatic U16 LZ4_read16(const void* ptr) { return ((const LZ4_unalign*)ptr)->u16; }\nstatic U32 LZ4_read32(const void* ptr) { return ((const LZ4_unalign*)ptr)->u32; }\nstatic reg_t LZ4_read_ARCH(const void* ptr) { return ((const LZ4_unalign*)ptr)->uArch; }\n\nstatic void LZ4_write16(void* memPtr, U16 value) { ((LZ4_unalign*)memPtr)->u16 = value; }\nstatic void LZ4_write32(void* memPtr, U32 value) { ((LZ4_unalign*)memPtr)->u32 = value; }\n\n#else  /* safe and portable access using memcpy() */\n\nstatic U16 LZ4_read16(const void* memPtr)\n{\n    U16 val; LZ4_memcpy(&val, memPtr, sizeof(val)); return val;\n}\n\nstatic U32 LZ4_read32(const void* memPtr)\n{\n    U32 val; LZ4_memcpy(&val, memPtr, sizeof(val)); return val;\n}\n\nstatic reg_t LZ4_read_ARCH(const void* memPtr)\n{\n    reg_t val; LZ4_memcpy(&val, memPtr, sizeof(val)); return val;\n}\n\nstatic void LZ4_write16(void* memPtr, U16 value)\n{\n    LZ4_memcpy(memPtr, &value, sizeof(value));\n}\n\nstatic void LZ4_write32(void* memPtr, U32 value)\n{\n    LZ4_memcpy(memPtr, &value, sizeof(value));\n}\n\n#endif /* LZ4_FORCE_MEMORY_ACCESS */\n\n\nstatic U16 LZ4_readLE16(const void* memPtr)\n{\n    if (LZ4_isLittleEndian()) {\n        return LZ4_read16(memPtr);\n    } else {\n        const BYTE* p = (const BYTE*)memPtr;\n        return (U16)((U16)p[0] + (p[1]<<8));\n    }\n}\n\nstatic void LZ4_writeLE16(void* memPtr, U16 value)\n{\n    if (LZ4_isLittleEndian()) {\n        LZ4_write16(memPtr, value);\n    } else {\n        BYTE* p = (BYTE*)memPtr;\n        p[0] = (BYTE) value;\n        p[1] = (BYTE)(value>>8);\n    }\n}\n\n/* customized variant of memcpy, which can overwrite up to 8 bytes beyond dstEnd */\nLZ4_FORCE_INLINE\nvoid LZ4_wildCopy8(void* dstPtr, const void* srcPtr, void* dstEnd)\n{\n    BYTE* d = (BYTE*)dstPtr;\n    const BYTE* s = (const BYTE*)srcPtr;\n    BYTE* const e = (BYTE*)dstEnd;\n\n    do { LZ4_memcpy(d,s,8); d+=8; s+=8; } while (d<e);\n}\n\nstatic const unsigned inc32table[8] = {0, 1, 2,  1,  0,  4, 4, 4};\nstatic const int      dec64table[8] = {0, 0, 0, -1, -4,  1, 2, 3};\n\n\n#ifndef LZ4_FAST_DEC_LOOP\n#  if defined __i386__ || defined _M_IX86 || defined __x86_64__ || defined _M_X64\n#    define LZ4_FAST_DEC_LOOP 1\n#  elif defined(__aarch64__) && defined(__APPLE__)\n#    define LZ4_FAST_DEC_LOOP 1\n#  elif defined(__aarch64__) && !defined(__clang__)\n     /* On non-Apple aarch64, we disable this optimization for clang because\n      * on certain mobile chipsets, performance is reduced with clang. For\n      * more information refer to https://github.com/lz4/lz4/pull/707 */\n#    define LZ4_FAST_DEC_LOOP 1\n#  else\n#    define LZ4_FAST_DEC_LOOP 0\n#  endif\n#endif\n\n#if LZ4_FAST_DEC_LOOP\n\nLZ4_FORCE_INLINE void\nLZ4_memcpy_using_offset_base(BYTE* dstPtr, const BYTE* srcPtr, BYTE* dstEnd, const size_t offset)\n{\n    assert(srcPtr + offset == dstPtr);\n    if (offset < 8) {\n        LZ4_write32(dstPtr, 0);   /* silence an msan warning when offset==0 */\n        dstPtr[0] = srcPtr[0];\n        dstPtr[1] = srcPtr[1];\n        dstPtr[2] = srcPtr[2];\n        dstPtr[3] = srcPtr[3];\n        srcPtr += inc32table[offset];\n        LZ4_memcpy(dstPtr+4, srcPtr, 4);\n        srcPtr -= dec64table[offset];\n        dstPtr += 8;\n    } else {\n        LZ4_memcpy(dstPtr, srcPtr, 8);\n        dstPtr += 8;\n        srcPtr += 8;\n    }\n\n    LZ4_wildCopy8(dstPtr, srcPtr, dstEnd);\n}\n\n/* customized variant of memcpy, which can overwrite up to 32 bytes beyond dstEnd\n * this version copies two times 16 bytes (instead of one time 32 bytes)\n * because it must be compatible with offsets >= 16. */\nLZ4_FORCE_INLINE void\nLZ4_wildCopy32(void* dstPtr, const void* srcPtr, void* dstEnd)\n{\n    BYTE* d = (BYTE*)dstPtr;\n    const BYTE* s = (const BYTE*)srcPtr;\n    BYTE* const e = (BYTE*)dstEnd;\n\n    do { LZ4_memcpy(d,s,16); LZ4_memcpy(d+16,s+16,16); d+=32; s+=32; } while (d<e);\n}\n\n/* LZ4_memcpy_using_offset()  presumes :\n * - dstEnd >= dstPtr + MINMATCH\n * - there is at least 8 bytes available to write after dstEnd */\nLZ4_FORCE_INLINE void\nLZ4_memcpy_using_offset(BYTE* dstPtr, const BYTE* srcPtr, BYTE* dstEnd, const size_t offset)\n{\n    BYTE v[8];\n\n    assert(dstEnd >= dstPtr + MINMATCH);\n\n    switch(offset) {\n    case 1:\n        MEM_INIT(v, *srcPtr, 8);\n        break;\n    case 2:\n        LZ4_memcpy(v, srcPtr, 2);\n        LZ4_memcpy(&v[2], srcPtr, 2);\n#if defined(_MSC_VER) && (_MSC_VER <= 1933) /* MSVC 2022 ver 17.3 or earlier */\n#  pragma warning(push)\n#  pragma warning(disable : 6385) /* warning C6385: Reading invalid data from 'v'. */\n#endif\n        LZ4_memcpy(&v[4], v, 4);\n#if defined(_MSC_VER) && (_MSC_VER <= 1933) /* MSVC 2022 ver 17.3 or earlier */\n#  pragma warning(pop)\n#endif\n        break;\n    case 4:\n        LZ4_memcpy(v, srcPtr, 4);\n        LZ4_memcpy(&v[4], srcPtr, 4);\n        break;\n    default:\n        LZ4_memcpy_using_offset_base(dstPtr, srcPtr, dstEnd, offset);\n        return;\n    }\n\n    LZ4_memcpy(dstPtr, v, 8);\n    dstPtr += 8;\n    while (dstPtr < dstEnd) {\n        LZ4_memcpy(dstPtr, v, 8);\n        dstPtr += 8;\n    }\n}\n#endif\n\n\n/*-************************************\n*  Common functions\n**************************************/\nstatic unsigned LZ4_NbCommonBytes (reg_t val)\n{\n    assert(val != 0);\n    if (LZ4_isLittleEndian()) {\n        if (sizeof(val) == 8) {\n#       if defined(_MSC_VER) && (_MSC_VER >= 1800) && (defined(_M_AMD64) && !defined(_M_ARM64EC)) && !defined(LZ4_FORCE_SW_BITCOUNT)\n/*-*************************************************************************************************\n* ARM64EC is a Microsoft-designed ARM64 ABI compatible with AMD64 applications on ARM64 Windows 11.\n* The ARM64EC ABI does not support AVX/AVX2/AVX512 instructions, nor their relevant intrinsics\n* including _tzcnt_u64. Therefore, we need to neuter the _tzcnt_u64 code path for ARM64EC.\n****************************************************************************************************/\n#         if defined(__clang__) && (__clang_major__ < 10)\n            /* Avoid undefined clang-cl intrinsics issue.\n             * See https://github.com/lz4/lz4/pull/1017 for details. */\n            return (unsigned)__builtin_ia32_tzcnt_u64(val) >> 3;\n#         else\n            /* x64 CPUS without BMI support interpret `TZCNT` as `REP BSF` */\n            return (unsigned)_tzcnt_u64(val) >> 3;\n#         endif\n#       elif defined(_MSC_VER) && defined(_WIN64) && !defined(LZ4_FORCE_SW_BITCOUNT)\n            unsigned long r = 0;\n            _BitScanForward64(&r, (U64)val);\n            return (unsigned)r >> 3;\n#       elif (defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 3) || \\\n                            ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 4))))) && \\\n                                        !defined(LZ4_FORCE_SW_BITCOUNT)\n            return (unsigned)__builtin_ctzll((U64)val) >> 3;\n#       else\n            const U64 m = 0x0101010101010101ULL;\n            val ^= val - 1;\n            return (unsigned)(((U64)((val & (m - 1)) * m)) >> 56);\n#       endif\n        } else /* 32 bits */ {\n#       if defined(_MSC_VER) && (_MSC_VER >= 1400) && !defined(LZ4_FORCE_SW_BITCOUNT)\n            unsigned long r;\n            _BitScanForward(&r, (U32)val);\n            return (unsigned)r >> 3;\n#       elif (defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 3) || \\\n                            ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 4))))) && \\\n                        !defined(__TINYC__) && !defined(LZ4_FORCE_SW_BITCOUNT)\n            return (unsigned)__builtin_ctz((U32)val) >> 3;\n#       else\n            const U32 m = 0x01010101;\n            return (unsigned)((((val - 1) ^ val) & (m - 1)) * m) >> 24;\n#       endif\n        }\n    } else   /* Big Endian CPU */ {\n        if (sizeof(val)==8) {\n#       if (defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 3) || \\\n                            ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 4))))) && \\\n                        !defined(__TINYC__) && !defined(LZ4_FORCE_SW_BITCOUNT)\n            return (unsigned)__builtin_clzll((U64)val) >> 3;\n#       else\n#if 1\n            /* this method is probably faster,\n             * but adds a 128 bytes lookup table */\n            static const unsigned char ctz7_tab[128] = {\n                7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,\n                4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,\n                5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,\n                4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,\n                6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,\n                4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,\n                5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,\n                4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,\n            };\n            U64 const mask = 0x0101010101010101ULL;\n            U64 const t = (((val >> 8) - mask) | val) & mask;\n            return ctz7_tab[(t * 0x0080402010080402ULL) >> 57];\n#else\n            /* this method doesn't consume memory space like the previous one,\n             * but it contains several branches,\n             * that may end up slowing execution */\n            static const U32 by32 = sizeof(val)*4;  /* 32 on 64 bits (goal), 16 on 32 bits.\n            Just to avoid some static analyzer complaining about shift by 32 on 32-bits target.\n            Note that this code path is never triggered in 32-bits mode. */\n            unsigned r;\n            if (!(val>>by32)) { r=4; } else { r=0; val>>=by32; }\n            if (!(val>>16)) { r+=2; val>>=8; } else { val>>=24; }\n            r += (!val);\n            return r;\n#endif\n#       endif\n        } else /* 32 bits */ {\n#       if (defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 3) || \\\n                            ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 4))))) && \\\n                                        !defined(LZ4_FORCE_SW_BITCOUNT)\n            return (unsigned)__builtin_clz((U32)val) >> 3;\n#       else\n            val >>= 8;\n            val = ((((val + 0x00FFFF00) | 0x00FFFFFF) + val) |\n              (val + 0x00FF0000)) >> 24;\n            return (unsigned)val ^ 3;\n#       endif\n        }\n    }\n}\n\n\n#define STEPSIZE sizeof(reg_t)\nLZ4_FORCE_INLINE\nunsigned LZ4_count(const BYTE* pIn, const BYTE* pMatch, const BYTE* pInLimit)\n{\n    const BYTE* const pStart = pIn;\n\n    if (likely(pIn < pInLimit-(STEPSIZE-1))) {\n        reg_t const diff = LZ4_read_ARCH(pMatch) ^ LZ4_read_ARCH(pIn);\n        if (!diff) {\n            pIn+=STEPSIZE; pMatch+=STEPSIZE;\n        } else {\n            return LZ4_NbCommonBytes(diff);\n    }   }\n\n    while (likely(pIn < pInLimit-(STEPSIZE-1))) {\n        reg_t const diff = LZ4_read_ARCH(pMatch) ^ LZ4_read_ARCH(pIn);\n        if (!diff) { pIn+=STEPSIZE; pMatch+=STEPSIZE; continue; }\n        pIn += LZ4_NbCommonBytes(diff);\n        return (unsigned)(pIn - pStart);\n    }\n\n    if ((STEPSIZE==8) && (pIn<(pInLimit-3)) && (LZ4_read32(pMatch) == LZ4_read32(pIn))) { pIn+=4; pMatch+=4; }\n    if ((pIn<(pInLimit-1)) && (LZ4_read16(pMatch) == LZ4_read16(pIn))) { pIn+=2; pMatch+=2; }\n    if ((pIn<pInLimit) && (*pMatch == *pIn)) pIn++;\n    return (unsigned)(pIn - pStart);\n}\n\n\n#ifndef LZ4_COMMONDEFS_ONLY\n/*-************************************\n*  Local Constants\n**************************************/\nstatic const int LZ4_64Klimit = ((64 KB) + (MFLIMIT-1));\nstatic const U32 LZ4_skipTrigger = 6;  /* Increase this value ==> compression run slower on incompressible data */\n\n\n/*-************************************\n*  Local Structures and types\n**************************************/\ntypedef enum { clearedTable = 0, byPtr, byU32, byU16 } tableType_t;\n\n/**\n * This enum distinguishes several different modes of accessing previous\n * content in the stream.\n *\n * - noDict        : There is no preceding content.\n * - withPrefix64k : Table entries up to ctx->dictSize before the current blob\n *                   blob being compressed are valid and refer to the preceding\n *                   content (of length ctx->dictSize), which is available\n *                   contiguously preceding in memory the content currently\n *                   being compressed.\n * - usingExtDict  : Like withPrefix64k, but the preceding content is somewhere\n *                   else in memory, starting at ctx->dictionary with length\n *                   ctx->dictSize.\n * - usingDictCtx  : Everything concerning the preceding content is\n *                   in a separate context, pointed to by ctx->dictCtx.\n *                   ctx->dictionary, ctx->dictSize, and table entries\n *                   in the current context that refer to positions\n *                   preceding the beginning of the current compression are\n *                   ignored. Instead, ctx->dictCtx->dictionary and ctx->dictCtx\n *                   ->dictSize describe the location and size of the preceding\n *                   content, and matches are found by looking in the ctx\n *                   ->dictCtx->hashTable.\n */\ntypedef enum { noDict = 0, withPrefix64k, usingExtDict, usingDictCtx } dict_directive;\ntypedef enum { noDictIssue = 0, dictSmall } dictIssue_directive;\n\n\n/*-************************************\n*  Local Utils\n**************************************/\nint LZ4_versionNumber (void) { return LZ4_VERSION_NUMBER; }\nconst char* LZ4_versionString(void) { return LZ4_VERSION_STRING; }\nint LZ4_compressBound(int isize)  { return LZ4_COMPRESSBOUND(isize); }\nint LZ4_sizeofState(void) { return sizeof(LZ4_stream_t); }\n\n\n/*-****************************************\n*  Internal Definitions, used only in Tests\n*******************************************/\n#if defined (__cplusplus)\nextern \"C\" {\n#endif\n\nint LZ4_compress_forceExtDict (LZ4_stream_t* LZ4_dict, const char* source, char* dest, int srcSize);\n\nint LZ4_decompress_safe_forceExtDict(const char* source, char* dest,\n                                     int compressedSize, int maxOutputSize,\n                                     const void* dictStart, size_t dictSize);\nint LZ4_decompress_safe_partial_forceExtDict(const char* source, char* dest,\n                                     int compressedSize, int targetOutputSize, int dstCapacity,\n                                     const void* dictStart, size_t dictSize);\n#if defined (__cplusplus)\n}\n#endif\n\n/*-******************************\n*  Compression functions\n********************************/\nLZ4_FORCE_INLINE U32 LZ4_hash4(U32 sequence, tableType_t const tableType)\n{\n    if (tableType == byU16)\n        return ((sequence * 2654435761U) >> ((MINMATCH*8)-(LZ4_HASHLOG+1)));\n    else\n        return ((sequence * 2654435761U) >> ((MINMATCH*8)-LZ4_HASHLOG));\n}\n\nLZ4_FORCE_INLINE U32 LZ4_hash5(U64 sequence, tableType_t const tableType)\n{\n    const U32 hashLog = (tableType == byU16) ? LZ4_HASHLOG+1 : LZ4_HASHLOG;\n    if (LZ4_isLittleEndian()) {\n        const U64 prime5bytes = 889523592379ULL;\n        return (U32)(((sequence << 24) * prime5bytes) >> (64 - hashLog));\n    } else {\n        const U64 prime8bytes = 11400714785074694791ULL;\n        return (U32)(((sequence >> 24) * prime8bytes) >> (64 - hashLog));\n    }\n}\n\nLZ4_FORCE_INLINE U32 LZ4_hashPosition(const void* const p, tableType_t const tableType)\n{\n    if ((sizeof(reg_t)==8) && (tableType != byU16)) return LZ4_hash5(LZ4_read_ARCH(p), tableType);\n    return LZ4_hash4(LZ4_read32(p), tableType);\n}\n\nLZ4_FORCE_INLINE void LZ4_clearHash(U32 h, void* tableBase, tableType_t const tableType)\n{\n    switch (tableType)\n    {\n    default: /* fallthrough */\n    case clearedTable: { /* illegal! */ assert(0); return; }\n    case byPtr: { const BYTE** hashTable = (const BYTE**)tableBase; hashTable[h] = NULL; return; }\n    case byU32: { U32* hashTable = (U32*) tableBase; hashTable[h] = 0; return; }\n    case byU16: { U16* hashTable = (U16*) tableBase; hashTable[h] = 0; return; }\n    }\n}\n\nLZ4_FORCE_INLINE void LZ4_putIndexOnHash(U32 idx, U32 h, void* tableBase, tableType_t const tableType)\n{\n    switch (tableType)\n    {\n    default: /* fallthrough */\n    case clearedTable: /* fallthrough */\n    case byPtr: { /* illegal! */ assert(0); return; }\n    case byU32: { U32* hashTable = (U32*) tableBase; hashTable[h] = idx; return; }\n    case byU16: { U16* hashTable = (U16*) tableBase; assert(idx < 65536); hashTable[h] = (U16)idx; return; }\n    }\n}\n\nLZ4_FORCE_INLINE void LZ4_putPositionOnHash(const BYTE* p, U32 h,\n                                  void* tableBase, tableType_t const tableType,\n                            const BYTE* srcBase)\n{\n    switch (tableType)\n    {\n    case clearedTable: { /* illegal! */ assert(0); return; }\n    case byPtr: { const BYTE** hashTable = (const BYTE**)tableBase; hashTable[h] = p; return; }\n    case byU32: { U32* hashTable = (U32*) tableBase; hashTable[h] = (U32)(p-srcBase); return; }\n    case byU16: { U16* hashTable = (U16*) tableBase; hashTable[h] = (U16)(p-srcBase); return; }\n    }\n}\n\nLZ4_FORCE_INLINE void LZ4_putPosition(const BYTE* p, void* tableBase, tableType_t tableType, const BYTE* srcBase)\n{\n    U32 const h = LZ4_hashPosition(p, tableType);\n    LZ4_putPositionOnHash(p, h, tableBase, tableType, srcBase);\n}\n\n/* LZ4_getIndexOnHash() :\n * Index of match position registered in hash table.\n * hash position must be calculated by using base+index, or dictBase+index.\n * Assumption 1 : only valid if tableType == byU32 or byU16.\n * Assumption 2 : h is presumed valid (within limits of hash table)\n */\nLZ4_FORCE_INLINE U32 LZ4_getIndexOnHash(U32 h, const void* tableBase, tableType_t tableType)\n{\n    LZ4_STATIC_ASSERT(LZ4_MEMORY_USAGE > 2);\n    if (tableType == byU32) {\n        const U32* const hashTable = (const U32*) tableBase;\n        assert(h < (1U << (LZ4_MEMORY_USAGE-2)));\n        return hashTable[h];\n    }\n    if (tableType == byU16) {\n        const U16* const hashTable = (const U16*) tableBase;\n        assert(h < (1U << (LZ4_MEMORY_USAGE-1)));\n        return hashTable[h];\n    }\n    assert(0); return 0;  /* forbidden case */\n}\n\nstatic const BYTE* LZ4_getPositionOnHash(U32 h, const void* tableBase, tableType_t tableType, const BYTE* srcBase)\n{\n    if (tableType == byPtr) { const BYTE* const* hashTable = (const BYTE* const*) tableBase; return hashTable[h]; }\n    if (tableType == byU32) { const U32* const hashTable = (const U32*) tableBase; return hashTable[h] + srcBase; }\n    { const U16* const hashTable = (const U16*) tableBase; return hashTable[h] + srcBase; }   /* default, to ensure a return */\n}\n\nLZ4_FORCE_INLINE const BYTE*\nLZ4_getPosition(const BYTE* p,\n                const void* tableBase, tableType_t tableType,\n                const BYTE* srcBase)\n{\n    U32 const h = LZ4_hashPosition(p, tableType);\n    return LZ4_getPositionOnHash(h, tableBase, tableType, srcBase);\n}\n\nLZ4_FORCE_INLINE void\nLZ4_prepareTable(LZ4_stream_t_internal* const cctx,\n           const int inputSize,\n           const tableType_t tableType) {\n    /* If the table hasn't been used, it's guaranteed to be zeroed out, and is\n     * therefore safe to use no matter what mode we're in. Otherwise, we figure\n     * out if it's safe to leave as is or whether it needs to be reset.\n     */\n    if ((tableType_t)cctx->tableType != clearedTable) {\n        assert(inputSize >= 0);\n        if ((tableType_t)cctx->tableType != tableType\n          || ((tableType == byU16) && cctx->currentOffset + (unsigned)inputSize >= 0xFFFFU)\n          || ((tableType == byU32) && cctx->currentOffset > 1 GB)\n          || tableType == byPtr\n          || inputSize >= 4 KB)\n        {\n            DEBUGLOG(4, \"LZ4_prepareTable: Resetting table in %p\", cctx);\n            MEM_INIT(cctx->hashTable, 0, LZ4_HASHTABLESIZE);\n            cctx->currentOffset = 0;\n            cctx->tableType = (U32)clearedTable;\n        } else {\n            DEBUGLOG(4, \"LZ4_prepareTable: Re-use hash table (no reset)\");\n        }\n    }\n\n    /* Adding a gap, so all previous entries are > LZ4_DISTANCE_MAX back,\n     * is faster than compressing without a gap.\n     * However, compressing with currentOffset == 0 is faster still,\n     * so we preserve that case.\n     */\n    if (cctx->currentOffset != 0 && tableType == byU32) {\n        DEBUGLOG(5, \"LZ4_prepareTable: adding 64KB to currentOffset\");\n        cctx->currentOffset += 64 KB;\n    }\n\n    /* Finally, clear history */\n    cctx->dictCtx = NULL;\n    cctx->dictionary = NULL;\n    cctx->dictSize = 0;\n}\n\n/** LZ4_compress_generic() :\n *  inlined, to ensure branches are decided at compilation time.\n *  Presumed already validated at this stage:\n *  - source != NULL\n *  - inputSize > 0\n */\nLZ4_FORCE_INLINE int LZ4_compress_generic_validated(\n                 LZ4_stream_t_internal* const cctx,\n                 const char* const source,\n                 char* const dest,\n                 const int inputSize,\n                 int*  inputConsumed, /* only written when outputDirective == fillOutput */\n                 const int maxOutputSize,\n                 const limitedOutput_directive outputDirective,\n                 const tableType_t tableType,\n                 const dict_directive dictDirective,\n                 const dictIssue_directive dictIssue,\n                 const int acceleration)\n{\n    int result;\n    const BYTE* ip = (const BYTE*) source;\n\n    U32 const startIndex = cctx->currentOffset;\n    const BYTE* base = (const BYTE*) source - startIndex;\n    const BYTE* lowLimit;\n\n    const LZ4_stream_t_internal* dictCtx = (const LZ4_stream_t_internal*) cctx->dictCtx;\n    const BYTE* const dictionary =\n        dictDirective == usingDictCtx ? dictCtx->dictionary : cctx->dictionary;\n    const U32 dictSize =\n        dictDirective == usingDictCtx ? dictCtx->dictSize : cctx->dictSize;\n    const U32 dictDelta = (dictDirective == usingDictCtx) ? startIndex - dictCtx->currentOffset : 0;   /* make indexes in dictCtx comparable with index in current context */\n\n    int const maybe_extMem = (dictDirective == usingExtDict) || (dictDirective == usingDictCtx);\n    U32 const prefixIdxLimit = startIndex - dictSize;   /* used when dictDirective == dictSmall */\n    const BYTE* const dictEnd = dictionary ? dictionary + dictSize : dictionary;\n    const BYTE* anchor = (const BYTE*) source;\n    const BYTE* const iend = ip + inputSize;\n    const BYTE* const mflimitPlusOne = iend - MFLIMIT + 1;\n    const BYTE* const matchlimit = iend - LASTLITERALS;\n\n    /* the dictCtx currentOffset is indexed on the start of the dictionary,\n     * while a dictionary in the current context precedes the currentOffset */\n    const BYTE* dictBase = (dictionary == NULL) ? NULL :\n                           (dictDirective == usingDictCtx) ?\n                            dictionary + dictSize - dictCtx->currentOffset :\n                            dictionary + dictSize - startIndex;\n\n    BYTE* op = (BYTE*) dest;\n    BYTE* const olimit = op + maxOutputSize;\n\n    U32 offset = 0;\n    U32 forwardH;\n\n    DEBUGLOG(5, \"LZ4_compress_generic_validated: srcSize=%i, tableType=%u\", inputSize, tableType);\n    assert(ip != NULL);\n    /* If init conditions are not met, we don't have to mark stream\n     * as having dirty context, since no action was taken yet */\n    if (outputDirective == fillOutput && maxOutputSize < 1) { return 0; } /* Impossible to store anything */\n    if ((tableType == byU16) && (inputSize>=LZ4_64Klimit)) { return 0; }  /* Size too large (not within 64K limit) */\n    if (tableType==byPtr) assert(dictDirective==noDict);      /* only supported use case with byPtr */\n    assert(acceleration >= 1);\n\n    lowLimit = (const BYTE*)source - (dictDirective == withPrefix64k ? dictSize : 0);\n\n    /* Update context state */\n    if (dictDirective == usingDictCtx) {\n        /* Subsequent linked blocks can't use the dictionary. */\n        /* Instead, they use the block we just compressed. */\n        cctx->dictCtx = NULL;\n        cctx->dictSize = (U32)inputSize;\n    } else {\n        cctx->dictSize += (U32)inputSize;\n    }\n    cctx->currentOffset += (U32)inputSize;\n    cctx->tableType = (U32)tableType;\n\n    if (inputSize<LZ4_minLength) goto _last_literals;        /* Input too small, no compression (all literals) */\n\n    /* First Byte */\n    LZ4_putPosition(ip, cctx->hashTable, tableType, base);\n    ip++; forwardH = LZ4_hashPosition(ip, tableType);\n\n    /* Main Loop */\n    for ( ; ; ) {\n        const BYTE* match;\n        BYTE* token;\n        const BYTE* filledIp;\n\n        /* Find a match */\n        if (tableType == byPtr) {\n            const BYTE* forwardIp = ip;\n            int step = 1;\n            int searchMatchNb = acceleration << LZ4_skipTrigger;\n            do {\n                U32 const h = forwardH;\n                ip = forwardIp;\n                forwardIp += step;\n                step = (searchMatchNb++ >> LZ4_skipTrigger);\n\n                if (unlikely(forwardIp > mflimitPlusOne)) goto _last_literals;\n                assert(ip < mflimitPlusOne);\n\n                match = LZ4_getPositionOnHash(h, cctx->hashTable, tableType, base);\n                forwardH = LZ4_hashPosition(forwardIp, tableType);\n                LZ4_putPositionOnHash(ip, h, cctx->hashTable, tableType, base);\n\n            } while ( (match+LZ4_DISTANCE_MAX < ip)\n                   || (LZ4_read32(match) != LZ4_read32(ip)) );\n\n        } else {   /* byU32, byU16 */\n\n            const BYTE* forwardIp = ip;\n            int step = 1;\n            int searchMatchNb = acceleration << LZ4_skipTrigger;\n            do {\n                U32 const h = forwardH;\n                U32 const current = (U32)(forwardIp - base);\n                U32 matchIndex = LZ4_getIndexOnHash(h, cctx->hashTable, tableType);\n                assert(matchIndex <= current);\n                assert(forwardIp - base < (ptrdiff_t)(2 GB - 1));\n                ip = forwardIp;\n                forwardIp += step;\n                step = (searchMatchNb++ >> LZ4_skipTrigger);\n\n                if (unlikely(forwardIp > mflimitPlusOne)) goto _last_literals;\n                assert(ip < mflimitPlusOne);\n\n                if (dictDirective == usingDictCtx) {\n                    if (matchIndex < startIndex) {\n                        /* there was no match, try the dictionary */\n                        assert(tableType == byU32);\n                        matchIndex = LZ4_getIndexOnHash(h, dictCtx->hashTable, byU32);\n                        match = dictBase + matchIndex;\n                        matchIndex += dictDelta;   /* make dictCtx index comparable with current context */\n                        lowLimit = dictionary;\n                    } else {\n                        match = base + matchIndex;\n                        lowLimit = (const BYTE*)source;\n                    }\n                } else if (dictDirective == usingExtDict) {\n                    if (matchIndex < startIndex) {\n                        DEBUGLOG(7, \"extDict candidate: matchIndex=%5u  <  startIndex=%5u\", matchIndex, startIndex);\n                        assert(startIndex - matchIndex >= MINMATCH);\n                        assert(dictBase);\n                        match = dictBase + matchIndex;\n                        lowLimit = dictionary;\n                    } else {\n                        match = base + matchIndex;\n                        lowLimit = (const BYTE*)source;\n                    }\n                } else {   /* single continuous memory segment */\n                    match = base + matchIndex;\n                }\n                forwardH = LZ4_hashPosition(forwardIp, tableType);\n                LZ4_putIndexOnHash(current, h, cctx->hashTable, tableType);\n\n                DEBUGLOG(7, \"candidate at pos=%u  (offset=%u \\n\", matchIndex, current - matchIndex);\n                if ((dictIssue == dictSmall) && (matchIndex < prefixIdxLimit)) { continue; }    /* match outside of valid area */\n                assert(matchIndex < current);\n                if ( ((tableType != byU16) || (LZ4_DISTANCE_MAX < LZ4_DISTANCE_ABSOLUTE_MAX))\n                  && (matchIndex+LZ4_DISTANCE_MAX < current)) {\n                    continue;\n                } /* too far */\n                assert((current - matchIndex) <= LZ4_DISTANCE_MAX);  /* match now expected within distance */\n\n                if (LZ4_read32(match) == LZ4_read32(ip)) {\n                    if (maybe_extMem) offset = current - matchIndex;\n                    break;   /* match found */\n                }\n\n            } while(1);\n        }\n\n        /* Catch up */\n        filledIp = ip;\n        while (((ip>anchor) & (match > lowLimit)) && (unlikely(ip[-1]==match[-1]))) { ip--; match--; }\n\n        /* Encode Literals */\n        {   unsigned const litLength = (unsigned)(ip - anchor);\n            token = op++;\n            if ((outputDirective == limitedOutput) &&  /* Check output buffer overflow */\n                (unlikely(op + litLength + (2 + 1 + LASTLITERALS) + (litLength/255) > olimit)) ) {\n                return 0;   /* cannot compress within `dst` budget. Stored indexes in hash table are nonetheless fine */\n            }\n            if ((outputDirective == fillOutput) &&\n                (unlikely(op + (litLength+240)/255 /* litlen */ + litLength /* literals */ + 2 /* offset */ + 1 /* token */ + MFLIMIT - MINMATCH /* min last literals so last match is <= end - MFLIMIT */ > olimit))) {\n                op--;\n                goto _last_literals;\n            }\n            if (litLength >= RUN_MASK) {\n                int len = (int)(litLength - RUN_MASK);\n                *token = (RUN_MASK<<ML_BITS);\n                for(; len >= 255 ; len-=255) *op++ = 255;\n                *op++ = (BYTE)len;\n            }\n            else *token = (BYTE)(litLength<<ML_BITS);\n\n            /* Copy Literals */\n            LZ4_wildCopy8(op, anchor, op+litLength);\n            op+=litLength;\n            DEBUGLOG(6, \"seq.start:%i, literals=%u, match.start:%i\",\n                        (int)(anchor-(const BYTE*)source), litLength, (int)(ip-(const BYTE*)source));\n        }\n\n_next_match:\n        /* at this stage, the following variables must be correctly set :\n         * - ip : at start of LZ operation\n         * - match : at start of previous pattern occurrence; can be within current prefix, or within extDict\n         * - offset : if maybe_ext_memSegment==1 (constant)\n         * - lowLimit : must be == dictionary to mean \"match is within extDict\"; must be == source otherwise\n         * - token and *token : position to write 4-bits for match length; higher 4-bits for literal length supposed already written\n         */\n\n        if ((outputDirective == fillOutput) &&\n            (op + 2 /* offset */ + 1 /* token */ + MFLIMIT - MINMATCH /* min last literals so last match is <= end - MFLIMIT */ > olimit)) {\n            /* the match was too close to the end, rewind and go to last literals */\n            op = token;\n            goto _last_literals;\n        }\n\n        /* Encode Offset */\n        if (maybe_extMem) {   /* static test */\n            DEBUGLOG(6, \"             with offset=%u  (ext if > %i)\", offset, (int)(ip - (const BYTE*)source));\n            assert(offset <= LZ4_DISTANCE_MAX && offset > 0);\n            LZ4_writeLE16(op, (U16)offset); op+=2;\n        } else  {\n            DEBUGLOG(6, \"             with offset=%u  (same segment)\", (U32)(ip - match));\n            assert(ip-match <= LZ4_DISTANCE_MAX);\n            LZ4_writeLE16(op, (U16)(ip - match)); op+=2;\n        }\n\n        /* Encode MatchLength */\n        {   unsigned matchCode;\n\n            if ( (dictDirective==usingExtDict || dictDirective==usingDictCtx)\n              && (lowLimit==dictionary) /* match within extDict */ ) {\n                const BYTE* limit = ip + (dictEnd-match);\n                assert(dictEnd > match);\n                if (limit > matchlimit) limit = matchlimit;\n                matchCode = LZ4_count(ip+MINMATCH, match+MINMATCH, limit);\n                ip += (size_t)matchCode + MINMATCH;\n                if (ip==limit) {\n                    unsigned const more = LZ4_count(limit, (const BYTE*)source, matchlimit);\n                    matchCode += more;\n                    ip += more;\n                }\n                DEBUGLOG(6, \"             with matchLength=%u starting in extDict\", matchCode+MINMATCH);\n            } else {\n                matchCode = LZ4_count(ip+MINMATCH, match+MINMATCH, matchlimit);\n                ip += (size_t)matchCode + MINMATCH;\n                DEBUGLOG(6, \"             with matchLength=%u\", matchCode+MINMATCH);\n            }\n\n            if ((outputDirective) &&    /* Check output buffer overflow */\n                (unlikely(op + (1 + LASTLITERALS) + (matchCode+240)/255 > olimit)) ) {\n                if (outputDirective == fillOutput) {\n                    /* Match description too long : reduce it */\n                    U32 newMatchCode = 15 /* in token */ - 1 /* to avoid needing a zero byte */ + ((U32)(olimit - op) - 1 - LASTLITERALS) * 255;\n                    ip -= matchCode - newMatchCode;\n                    assert(newMatchCode < matchCode);\n                    matchCode = newMatchCode;\n                    if (unlikely(ip <= filledIp)) {\n                        /* We have already filled up to filledIp so if ip ends up less than filledIp\n                         * we have positions in the hash table beyond the current position. This is\n                         * a problem if we reuse the hash table. So we have to remove these positions\n                         * from the hash table.\n                         */\n                        const BYTE* ptr;\n                        DEBUGLOG(5, \"Clearing %u positions\", (U32)(filledIp - ip));\n                        for (ptr = ip; ptr <= filledIp; ++ptr) {\n                            U32 const h = LZ4_hashPosition(ptr, tableType);\n                            LZ4_clearHash(h, cctx->hashTable, tableType);\n                        }\n                    }\n                } else {\n                    assert(outputDirective == limitedOutput);\n                    return 0;   /* cannot compress within `dst` budget. Stored indexes in hash table are nonetheless fine */\n                }\n            }\n            if (matchCode >= ML_MASK) {\n                *token += ML_MASK;\n                matchCode -= ML_MASK;\n                LZ4_write32(op, 0xFFFFFFFF);\n                while (matchCode >= 4*255) {\n                    op+=4;\n                    LZ4_write32(op, 0xFFFFFFFF);\n                    matchCode -= 4*255;\n                }\n                op += matchCode / 255;\n                *op++ = (BYTE)(matchCode % 255);\n            } else\n                *token += (BYTE)(matchCode);\n        }\n        /* Ensure we have enough space for the last literals. */\n        assert(!(outputDirective == fillOutput && op + 1 + LASTLITERALS > olimit));\n\n        anchor = ip;\n\n        /* Test end of chunk */\n        if (ip >= mflimitPlusOne) break;\n\n        /* Fill table */\n        LZ4_putPosition(ip-2, cctx->hashTable, tableType, base);\n\n        /* Test next position */\n        if (tableType == byPtr) {\n\n            match = LZ4_getPosition(ip, cctx->hashTable, tableType, base);\n            LZ4_putPosition(ip, cctx->hashTable, tableType, base);\n            if ( (match+LZ4_DISTANCE_MAX >= ip)\n              && (LZ4_read32(match) == LZ4_read32(ip)) )\n            { token=op++; *token=0; goto _next_match; }\n\n        } else {   /* byU32, byU16 */\n\n            U32 const h = LZ4_hashPosition(ip, tableType);\n            U32 const current = (U32)(ip-base);\n            U32 matchIndex = LZ4_getIndexOnHash(h, cctx->hashTable, tableType);\n            assert(matchIndex < current);\n            if (dictDirective == usingDictCtx) {\n                if (matchIndex < startIndex) {\n                    /* there was no match, try the dictionary */\n                    matchIndex = LZ4_getIndexOnHash(h, dictCtx->hashTable, byU32);\n                    match = dictBase + matchIndex;\n                    lowLimit = dictionary;   /* required for match length counter */\n                    matchIndex += dictDelta;\n                } else {\n                    match = base + matchIndex;\n                    lowLimit = (const BYTE*)source;  /* required for match length counter */\n                }\n            } else if (dictDirective==usingExtDict) {\n                if (matchIndex < startIndex) {\n                    assert(dictBase);\n                    match = dictBase + matchIndex;\n                    lowLimit = dictionary;   /* required for match length counter */\n                } else {\n                    match = base + matchIndex;\n                    lowLimit = (const BYTE*)source;   /* required for match length counter */\n                }\n            } else {   /* single memory segment */\n                match = base + matchIndex;\n            }\n            LZ4_putIndexOnHash(current, h, cctx->hashTable, tableType);\n            assert(matchIndex < current);\n            if ( ((dictIssue==dictSmall) ? (matchIndex >= prefixIdxLimit) : 1)\n              && (((tableType==byU16) && (LZ4_DISTANCE_MAX == LZ4_DISTANCE_ABSOLUTE_MAX)) ? 1 : (matchIndex+LZ4_DISTANCE_MAX >= current))\n              && (LZ4_read32(match) == LZ4_read32(ip)) ) {\n                token=op++;\n                *token=0;\n                if (maybe_extMem) offset = current - matchIndex;\n                DEBUGLOG(6, \"seq.start:%i, literals=%u, match.start:%i\",\n                            (int)(anchor-(const BYTE*)source), 0, (int)(ip-(const BYTE*)source));\n                goto _next_match;\n            }\n        }\n\n        /* Prepare next loop */\n        forwardH = LZ4_hashPosition(++ip, tableType);\n\n    }\n\n_last_literals:\n    /* Encode Last Literals */\n    {   size_t lastRun = (size_t)(iend - anchor);\n        if ( (outputDirective) &&  /* Check output buffer overflow */\n            (op + lastRun + 1 + ((lastRun+255-RUN_MASK)/255) > olimit)) {\n            if (outputDirective == fillOutput) {\n                /* adapt lastRun to fill 'dst' */\n                assert(olimit >= op);\n                lastRun  = (size_t)(olimit-op) - 1/*token*/;\n                lastRun -= (lastRun + 256 - RUN_MASK) / 256;  /*additional length tokens*/\n            } else {\n                assert(outputDirective == limitedOutput);\n                return 0;   /* cannot compress within `dst` budget. Stored indexes in hash table are nonetheless fine */\n            }\n        }\n        DEBUGLOG(6, \"Final literal run : %i literals\", (int)lastRun);\n        if (lastRun >= RUN_MASK) {\n            size_t accumulator = lastRun - RUN_MASK;\n            *op++ = RUN_MASK << ML_BITS;\n            for(; accumulator >= 255 ; accumulator-=255) *op++ = 255;\n            *op++ = (BYTE) accumulator;\n        } else {\n            *op++ = (BYTE)(lastRun<<ML_BITS);\n        }\n        LZ4_memcpy(op, anchor, lastRun);\n        ip = anchor + lastRun;\n        op += lastRun;\n    }\n\n    if (outputDirective == fillOutput) {\n        *inputConsumed = (int) (((const char*)ip)-source);\n    }\n    result = (int)(((char*)op) - dest);\n    assert(result > 0);\n    DEBUGLOG(5, \"LZ4_compress_generic: compressed %i bytes into %i bytes\", inputSize, result);\n    return result;\n}\n\n/** LZ4_compress_generic() :\n *  inlined, to ensure branches are decided at compilation time;\n *  takes care of src == (NULL, 0)\n *  and forward the rest to LZ4_compress_generic_validated */\nLZ4_FORCE_INLINE int LZ4_compress_generic(\n                 LZ4_stream_t_internal* const cctx,\n                 const char* const src,\n                 char* const dst,\n                 const int srcSize,\n                 int *inputConsumed, /* only written when outputDirective == fillOutput */\n                 const int dstCapacity,\n                 const limitedOutput_directive outputDirective,\n                 const tableType_t tableType,\n                 const dict_directive dictDirective,\n                 const dictIssue_directive dictIssue,\n                 const int acceleration)\n{\n    DEBUGLOG(5, \"LZ4_compress_generic: srcSize=%i, dstCapacity=%i\",\n                srcSize, dstCapacity);\n\n    if ((U32)srcSize > (U32)LZ4_MAX_INPUT_SIZE) { return 0; }  /* Unsupported srcSize, too large (or negative) */\n    if (srcSize == 0) {   /* src == NULL supported if srcSize == 0 */\n        if (outputDirective != notLimited && dstCapacity <= 0) return 0;  /* no output, can't write anything */\n        DEBUGLOG(5, \"Generating an empty block\");\n        assert(outputDirective == notLimited || dstCapacity >= 1);\n        assert(dst != NULL);\n        dst[0] = 0;\n        if (outputDirective == fillOutput) {\n            assert (inputConsumed != NULL);\n            *inputConsumed = 0;\n        }\n        return 1;\n    }\n    assert(src != NULL);\n\n    return LZ4_compress_generic_validated(cctx, src, dst, srcSize,\n                inputConsumed, /* only written into if outputDirective == fillOutput */\n                dstCapacity, outputDirective,\n                tableType, dictDirective, dictIssue, acceleration);\n}\n\n\nint LZ4_compress_fast_extState(void* state, const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration)\n{\n    LZ4_stream_t_internal* const ctx = & LZ4_initStream(state, sizeof(LZ4_stream_t)) -> internal_donotuse;\n    assert(ctx != NULL);\n    if (acceleration < 1) acceleration = LZ4_ACCELERATION_DEFAULT;\n    if (acceleration > LZ4_ACCELERATION_MAX) acceleration = LZ4_ACCELERATION_MAX;\n    if (maxOutputSize >= LZ4_compressBound(inputSize)) {\n        if (inputSize < LZ4_64Klimit) {\n            return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, 0, notLimited, byU16, noDict, noDictIssue, acceleration);\n        } else {\n            const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)source > LZ4_DISTANCE_MAX)) ? byPtr : byU32;\n            return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, 0, notLimited, tableType, noDict, noDictIssue, acceleration);\n        }\n    } else {\n        if (inputSize < LZ4_64Klimit) {\n            return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, byU16, noDict, noDictIssue, acceleration);\n        } else {\n            const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)source > LZ4_DISTANCE_MAX)) ? byPtr : byU32;\n            return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, noDict, noDictIssue, acceleration);\n        }\n    }\n}\n\n/**\n * LZ4_compress_fast_extState_fastReset() :\n * A variant of LZ4_compress_fast_extState().\n *\n * Using this variant avoids an expensive initialization step. It is only safe\n * to call if the state buffer is known to be correctly initialized already\n * (see comment in lz4.h on LZ4_resetStream_fast() for a definition of\n * \"correctly initialized\").\n */\nint LZ4_compress_fast_extState_fastReset(void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration)\n{\n    LZ4_stream_t_internal* ctx = &((LZ4_stream_t*)state)->internal_donotuse;\n    if (acceleration < 1) acceleration = LZ4_ACCELERATION_DEFAULT;\n    if (acceleration > LZ4_ACCELERATION_MAX) acceleration = LZ4_ACCELERATION_MAX;\n\n    if (dstCapacity >= LZ4_compressBound(srcSize)) {\n        if (srcSize < LZ4_64Klimit) {\n            const tableType_t tableType = byU16;\n            LZ4_prepareTable(ctx, srcSize, tableType);\n            if (ctx->currentOffset) {\n                return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, 0, notLimited, tableType, noDict, dictSmall, acceleration);\n            } else {\n                return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, 0, notLimited, tableType, noDict, noDictIssue, acceleration);\n            }\n        } else {\n            const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)src > LZ4_DISTANCE_MAX)) ? byPtr : byU32;\n            LZ4_prepareTable(ctx, srcSize, tableType);\n            return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, 0, notLimited, tableType, noDict, noDictIssue, acceleration);\n        }\n    } else {\n        if (srcSize < LZ4_64Klimit) {\n            const tableType_t tableType = byU16;\n            LZ4_prepareTable(ctx, srcSize, tableType);\n            if (ctx->currentOffset) {\n                return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, dstCapacity, limitedOutput, tableType, noDict, dictSmall, acceleration);\n            } else {\n                return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, dstCapacity, limitedOutput, tableType, noDict, noDictIssue, acceleration);\n            }\n        } else {\n            const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)src > LZ4_DISTANCE_MAX)) ? byPtr : byU32;\n            LZ4_prepareTable(ctx, srcSize, tableType);\n            return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, dstCapacity, limitedOutput, tableType, noDict, noDictIssue, acceleration);\n        }\n    }\n}\n\n\nint LZ4_compress_fast(const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration)\n{\n    int result;\n#if (LZ4_HEAPMODE)\n    LZ4_stream_t* ctxPtr = (LZ4_stream_t*)ALLOC(sizeof(LZ4_stream_t));   /* malloc-calloc always properly aligned */\n    if (ctxPtr == NULL) return 0;\n#else\n    LZ4_stream_t ctx;\n    LZ4_stream_t* const ctxPtr = &ctx;\n#endif\n    result = LZ4_compress_fast_extState(ctxPtr, source, dest, inputSize, maxOutputSize, acceleration);\n\n#if (LZ4_HEAPMODE)\n    FREEMEM(ctxPtr);\n#endif\n    return result;\n}\n\n\nint LZ4_compress_default(const char* src, char* dst, int srcSize, int maxOutputSize)\n{\n    return LZ4_compress_fast(src, dst, srcSize, maxOutputSize, 1);\n}\n\n\n/* Note!: This function leaves the stream in an unclean/broken state!\n * It is not safe to subsequently use the same state with a _fastReset() or\n * _continue() call without resetting it. */\nstatic int LZ4_compress_destSize_extState (LZ4_stream_t* state, const char* src, char* dst, int* srcSizePtr, int targetDstSize)\n{\n    void* const s = LZ4_initStream(state, sizeof (*state));\n    assert(s != NULL); (void)s;\n\n    if (targetDstSize >= LZ4_compressBound(*srcSizePtr)) {  /* compression success is guaranteed */\n        return LZ4_compress_fast_extState(state, src, dst, *srcSizePtr, targetDstSize, 1);\n    } else {\n        if (*srcSizePtr < LZ4_64Klimit) {\n            return LZ4_compress_generic(&state->internal_donotuse, src, dst, *srcSizePtr, srcSizePtr, targetDstSize, fillOutput, byU16, noDict, noDictIssue, 1);\n        } else {\n            tableType_t const addrMode = ((sizeof(void*)==4) && ((uptrval)src > LZ4_DISTANCE_MAX)) ? byPtr : byU32;\n            return LZ4_compress_generic(&state->internal_donotuse, src, dst, *srcSizePtr, srcSizePtr, targetDstSize, fillOutput, addrMode, noDict, noDictIssue, 1);\n    }   }\n}\n\n\nint LZ4_compress_destSize(const char* src, char* dst, int* srcSizePtr, int targetDstSize)\n{\n#if (LZ4_HEAPMODE)\n    LZ4_stream_t* ctx = (LZ4_stream_t*)ALLOC(sizeof(LZ4_stream_t));   /* malloc-calloc always properly aligned */\n    if (ctx == NULL) return 0;\n#else\n    LZ4_stream_t ctxBody;\n    LZ4_stream_t* ctx = &ctxBody;\n#endif\n\n    int result = LZ4_compress_destSize_extState(ctx, src, dst, srcSizePtr, targetDstSize);\n\n#if (LZ4_HEAPMODE)\n    FREEMEM(ctx);\n#endif\n    return result;\n}\n\n\n\n/*-******************************\n*  Streaming functions\n********************************/\n\n#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION)\nLZ4_stream_t* LZ4_createStream(void)\n{\n    LZ4_stream_t* const lz4s = (LZ4_stream_t*)ALLOC(sizeof(LZ4_stream_t));\n    LZ4_STATIC_ASSERT(sizeof(LZ4_stream_t) >= sizeof(LZ4_stream_t_internal));\n    DEBUGLOG(4, \"LZ4_createStream %p\", lz4s);\n    if (lz4s == NULL) return NULL;\n    LZ4_initStream(lz4s, sizeof(*lz4s));\n    return lz4s;\n}\n#endif\n\nstatic size_t LZ4_stream_t_alignment(void)\n{\n#if LZ4_ALIGN_TEST\n    typedef struct { char c; LZ4_stream_t t; } t_a;\n    return sizeof(t_a) - sizeof(LZ4_stream_t);\n#else\n    return 1;  /* effectively disabled */\n#endif\n}\n\nLZ4_stream_t* LZ4_initStream (void* buffer, size_t size)\n{\n    DEBUGLOG(5, \"LZ4_initStream\");\n    if (buffer == NULL) { return NULL; }\n    if (size < sizeof(LZ4_stream_t)) { return NULL; }\n    if (!LZ4_isAligned(buffer, LZ4_stream_t_alignment())) return NULL;\n    MEM_INIT(buffer, 0, sizeof(LZ4_stream_t_internal));\n    return (LZ4_stream_t*)buffer;\n}\n\n/* resetStream is now deprecated,\n * prefer initStream() which is more general */\nvoid LZ4_resetStream (LZ4_stream_t* LZ4_stream)\n{\n    DEBUGLOG(5, \"LZ4_resetStream (ctx:%p)\", LZ4_stream);\n    MEM_INIT(LZ4_stream, 0, sizeof(LZ4_stream_t_internal));\n}\n\nvoid LZ4_resetStream_fast(LZ4_stream_t* ctx) {\n    LZ4_prepareTable(&(ctx->internal_donotuse), 0, byU32);\n}\n\n#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION)\nint LZ4_freeStream (LZ4_stream_t* LZ4_stream)\n{\n    if (!LZ4_stream) return 0;   /* support free on NULL */\n    DEBUGLOG(5, \"LZ4_freeStream %p\", LZ4_stream);\n    FREEMEM(LZ4_stream);\n    return (0);\n}\n#endif\n\n\n#define HASH_UNIT sizeof(reg_t)\nint LZ4_loadDict (LZ4_stream_t* LZ4_dict, const char* dictionary, int dictSize)\n{\n    LZ4_stream_t_internal* dict = &LZ4_dict->internal_donotuse;\n    const tableType_t tableType = byU32;\n    const BYTE* p = (const BYTE*)dictionary;\n    const BYTE* const dictEnd = p + dictSize;\n    const BYTE* base;\n\n    DEBUGLOG(4, \"LZ4_loadDict (%i bytes from %p into %p)\", dictSize, dictionary, LZ4_dict);\n\n    /* It's necessary to reset the context,\n     * and not just continue it with prepareTable()\n     * to avoid any risk of generating overflowing matchIndex\n     * when compressing using this dictionary */\n    LZ4_resetStream(LZ4_dict);\n\n    /* We always increment the offset by 64 KB, since, if the dict is longer,\n     * we truncate it to the last 64k, and if it's shorter, we still want to\n     * advance by a whole window length so we can provide the guarantee that\n     * there are only valid offsets in the window, which allows an optimization\n     * in LZ4_compress_fast_continue() where it uses noDictIssue even when the\n     * dictionary isn't a full 64k. */\n    dict->currentOffset += 64 KB;\n\n    if (dictSize < (int)HASH_UNIT) {\n        return 0;\n    }\n\n    if ((dictEnd - p) > 64 KB) p = dictEnd - 64 KB;\n    base = dictEnd - dict->currentOffset;\n    dict->dictionary = p;\n    dict->dictSize = (U32)(dictEnd - p);\n    dict->tableType = (U32)tableType;\n\n    while (p <= dictEnd-HASH_UNIT) {\n        LZ4_putPosition(p, dict->hashTable, tableType, base);\n        p+=3;\n    }\n\n    return (int)dict->dictSize;\n}\n\nvoid LZ4_attach_dictionary(LZ4_stream_t* workingStream, const LZ4_stream_t* dictionaryStream)\n{\n    const LZ4_stream_t_internal* dictCtx = (dictionaryStream == NULL) ? NULL :\n        &(dictionaryStream->internal_donotuse);\n\n    DEBUGLOG(4, \"LZ4_attach_dictionary (%p, %p, size %u)\",\n             workingStream, dictionaryStream,\n             dictCtx != NULL ? dictCtx->dictSize : 0);\n\n    if (dictCtx != NULL) {\n        /* If the current offset is zero, we will never look in the\n         * external dictionary context, since there is no value a table\n         * entry can take that indicate a miss. In that case, we need\n         * to bump the offset to something non-zero.\n         */\n        if (workingStream->internal_donotuse.currentOffset == 0) {\n            workingStream->internal_donotuse.currentOffset = 64 KB;\n        }\n\n        /* Don't actually attach an empty dictionary.\n         */\n        if (dictCtx->dictSize == 0) {\n            dictCtx = NULL;\n        }\n    }\n    workingStream->internal_donotuse.dictCtx = dictCtx;\n}\n\n\nstatic void LZ4_renormDictT(LZ4_stream_t_internal* LZ4_dict, int nextSize)\n{\n    assert(nextSize >= 0);\n    if (LZ4_dict->currentOffset + (unsigned)nextSize > 0x80000000) {   /* potential ptrdiff_t overflow (32-bits mode) */\n        /* rescale hash table */\n        U32 const delta = LZ4_dict->currentOffset - 64 KB;\n        const BYTE* dictEnd = LZ4_dict->dictionary + LZ4_dict->dictSize;\n        int i;\n        DEBUGLOG(4, \"LZ4_renormDictT\");\n        for (i=0; i<LZ4_HASH_SIZE_U32; i++) {\n            if (LZ4_dict->hashTable[i] < delta) LZ4_dict->hashTable[i]=0;\n            else LZ4_dict->hashTable[i] -= delta;\n        }\n        LZ4_dict->currentOffset = 64 KB;\n        if (LZ4_dict->dictSize > 64 KB) LZ4_dict->dictSize = 64 KB;\n        LZ4_dict->dictionary = dictEnd - LZ4_dict->dictSize;\n    }\n}\n\n\nint LZ4_compress_fast_continue (LZ4_stream_t* LZ4_stream,\n                                const char* source, char* dest,\n                                int inputSize, int maxOutputSize,\n                                int acceleration)\n{\n    const tableType_t tableType = byU32;\n    LZ4_stream_t_internal* const streamPtr = &LZ4_stream->internal_donotuse;\n    const char* dictEnd = streamPtr->dictSize ? (const char*)streamPtr->dictionary + streamPtr->dictSize : NULL;\n\n    DEBUGLOG(5, \"LZ4_compress_fast_continue (inputSize=%i, dictSize=%u)\", inputSize, streamPtr->dictSize);\n\n    LZ4_renormDictT(streamPtr, inputSize);   /* fix index overflow */\n    if (acceleration < 1) acceleration = LZ4_ACCELERATION_DEFAULT;\n    if (acceleration > LZ4_ACCELERATION_MAX) acceleration = LZ4_ACCELERATION_MAX;\n\n    /* invalidate tiny dictionaries */\n    if ( (streamPtr->dictSize < 4)     /* tiny dictionary : not enough for a hash */\n      && (dictEnd != source)           /* prefix mode */\n      && (inputSize > 0)               /* tolerance : don't lose history, in case next invocation would use prefix mode */\n      && (streamPtr->dictCtx == NULL)  /* usingDictCtx */\n      ) {\n        DEBUGLOG(5, \"LZ4_compress_fast_continue: dictSize(%u) at addr:%p is too small\", streamPtr->dictSize, streamPtr->dictionary);\n        /* remove dictionary existence from history, to employ faster prefix mode */\n        streamPtr->dictSize = 0;\n        streamPtr->dictionary = (const BYTE*)source;\n        dictEnd = source;\n    }\n\n    /* Check overlapping input/dictionary space */\n    {   const char* const sourceEnd = source + inputSize;\n        if ((sourceEnd > (const char*)streamPtr->dictionary) && (sourceEnd < dictEnd)) {\n            streamPtr->dictSize = (U32)(dictEnd - sourceEnd);\n            if (streamPtr->dictSize > 64 KB) streamPtr->dictSize = 64 KB;\n            if (streamPtr->dictSize < 4) streamPtr->dictSize = 0;\n            streamPtr->dictionary = (const BYTE*)dictEnd - streamPtr->dictSize;\n        }\n    }\n\n    /* prefix mode : source data follows dictionary */\n    if (dictEnd == source) {\n        if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset))\n            return LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, withPrefix64k, dictSmall, acceleration);\n        else\n            return LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, withPrefix64k, noDictIssue, acceleration);\n    }\n\n    /* external dictionary mode */\n    {   int result;\n        if (streamPtr->dictCtx) {\n            /* We depend here on the fact that dictCtx'es (produced by\n             * LZ4_loadDict) guarantee that their tables contain no references\n             * to offsets between dictCtx->currentOffset - 64 KB and\n             * dictCtx->currentOffset - dictCtx->dictSize. This makes it safe\n             * to use noDictIssue even when the dict isn't a full 64 KB.\n             */\n            if (inputSize > 4 KB) {\n                /* For compressing large blobs, it is faster to pay the setup\n                 * cost to copy the dictionary's tables into the active context,\n                 * so that the compression loop is only looking into one table.\n                 */\n                LZ4_memcpy(streamPtr, streamPtr->dictCtx, sizeof(*streamPtr));\n                result = LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, usingExtDict, noDictIssue, acceleration);\n            } else {\n                result = LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, usingDictCtx, noDictIssue, acceleration);\n            }\n        } else {  /* small data <= 4 KB */\n            if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset)) {\n                result = LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, usingExtDict, dictSmall, acceleration);\n            } else {\n                result = LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, usingExtDict, noDictIssue, acceleration);\n            }\n        }\n        streamPtr->dictionary = (const BYTE*)source;\n        streamPtr->dictSize = (U32)inputSize;\n        return result;\n    }\n}\n\n\n/* Hidden debug function, to force-test external dictionary mode */\nint LZ4_compress_forceExtDict (LZ4_stream_t* LZ4_dict, const char* source, char* dest, int srcSize)\n{\n    LZ4_stream_t_internal* streamPtr = &LZ4_dict->internal_donotuse;\n    int result;\n\n    LZ4_renormDictT(streamPtr, srcSize);\n\n    if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset)) {\n        result = LZ4_compress_generic(streamPtr, source, dest, srcSize, NULL, 0, notLimited, byU32, usingExtDict, dictSmall, 1);\n    } else {\n        result = LZ4_compress_generic(streamPtr, source, dest, srcSize, NULL, 0, notLimited, byU32, usingExtDict, noDictIssue, 1);\n    }\n\n    streamPtr->dictionary = (const BYTE*)source;\n    streamPtr->dictSize = (U32)srcSize;\n\n    return result;\n}\n\n\n/*! LZ4_saveDict() :\n *  If previously compressed data block is not guaranteed to remain available at its memory location,\n *  save it into a safer place (char* safeBuffer).\n *  Note : no need to call LZ4_loadDict() afterwards, dictionary is immediately usable,\n *         one can therefore call LZ4_compress_fast_continue() right after.\n * @return : saved dictionary size in bytes (necessarily <= dictSize), or 0 if error.\n */\nint LZ4_saveDict (LZ4_stream_t* LZ4_dict, char* safeBuffer, int dictSize)\n{\n    LZ4_stream_t_internal* const dict = &LZ4_dict->internal_donotuse;\n\n    DEBUGLOG(5, \"LZ4_saveDict : dictSize=%i, safeBuffer=%p\", dictSize, safeBuffer);\n\n    if ((U32)dictSize > 64 KB) { dictSize = 64 KB; } /* useless to define a dictionary > 64 KB */\n    if ((U32)dictSize > dict->dictSize) { dictSize = (int)dict->dictSize; }\n\n    if (safeBuffer == NULL) assert(dictSize == 0);\n    if (dictSize > 0) {\n        const BYTE* const previousDictEnd = dict->dictionary + dict->dictSize;\n        assert(dict->dictionary);\n        LZ4_memmove(safeBuffer, previousDictEnd - dictSize, (size_t)dictSize);\n    }\n\n    dict->dictionary = (const BYTE*)safeBuffer;\n    dict->dictSize = (U32)dictSize;\n\n    return dictSize;\n}\n\n\n\n/*-*******************************\n *  Decompression functions\n ********************************/\n\ntypedef enum { decode_full_block = 0, partial_decode = 1 } earlyEnd_directive;\n\n#undef MIN\n#define MIN(a,b)    ( (a) < (b) ? (a) : (b) )\n\n\n/* variant for decompress_unsafe()\n * does not know end of input\n * presumes input is well formed\n * note : will consume at least one byte */\nsize_t read_long_length_no_check(const BYTE** pp)\n{\n    size_t b, l = 0;\n    do { b = **pp; (*pp)++; l += b; } while (b==255);\n    DEBUGLOG(6, \"read_long_length_no_check: +length=%zu using %zu input bytes\", l, l/255 + 1)\n    return l;\n}\n\n/* core decoder variant for LZ4_decompress_fast*()\n * for legacy support only : these entry points are deprecated.\n * - Presumes input is correctly formed (no defense vs malformed inputs)\n * - Does not know input size (presume input buffer is \"large enough\")\n * - Decompress a full block (only)\n * @return : nb of bytes read from input.\n * Note : this variant is not optimized for speed, just for maintenance.\n *        the goal is to remove support of decompress_fast*() variants by v2.0\n**/\nLZ4_FORCE_INLINE int\nLZ4_decompress_unsafe_generic(\n                 const BYTE* const istart,\n                 BYTE* const ostart,\n                 int decompressedSize,\n\n                 size_t prefixSize,\n                 const BYTE* const dictStart,  /* only if dict==usingExtDict */\n                 const size_t dictSize         /* note: =0 if dictStart==NULL */\n                 )\n{\n    const BYTE* ip = istart;\n    BYTE* op = (BYTE*)ostart;\n    BYTE* const oend = ostart + decompressedSize;\n    const BYTE* const prefixStart = ostart - prefixSize;\n\n    DEBUGLOG(5, \"LZ4_decompress_unsafe_generic\");\n    if (dictStart == NULL) assert(dictSize == 0);\n\n    while (1) {\n        /* start new sequence */\n        unsigned token = *ip++;\n\n        /* literals */\n        {   size_t ll = token >> ML_BITS;\n            if (ll==15) {\n                /* long literal length */\n                ll += read_long_length_no_check(&ip);\n            }\n            if ((size_t)(oend-op) < ll) return -1; /* output buffer overflow */\n            LZ4_memmove(op, ip, ll); /* support in-place decompression */\n            op += ll;\n            ip += ll;\n            if ((size_t)(oend-op) < MFLIMIT) {\n                if (op==oend) break;  /* end of block */\n                DEBUGLOG(5, \"invalid: literals end at distance %zi from end of block\", oend-op);\n                /* incorrect end of block :\n                 * last match must start at least MFLIMIT==12 bytes before end of output block */\n                return -1;\n        }   }\n\n        /* match */\n        {   size_t ml = token & 15;\n            size_t const offset = LZ4_readLE16(ip);\n            ip+=2;\n\n            if (ml==15) {\n                /* long literal length */\n                ml += read_long_length_no_check(&ip);\n            }\n            ml += MINMATCH;\n\n            if ((size_t)(oend-op) < ml) return -1; /* output buffer overflow */\n\n            {   const BYTE* match = op - offset;\n\n                /* out of range */\n                if (offset > (size_t)(op - prefixStart) + dictSize) {\n                    DEBUGLOG(6, \"offset out of range\");\n                    return -1;\n                }\n\n                /* check special case : extDict */\n                if (offset > (size_t)(op - prefixStart)) {\n                    /* extDict scenario */\n                    const BYTE* const dictEnd = dictStart + dictSize;\n                    const BYTE* extMatch = dictEnd - (offset - (size_t)(op-prefixStart));\n                    size_t const extml = (size_t)(dictEnd - extMatch);\n                    if (extml > ml) {\n                        /* match entirely within extDict */\n                        LZ4_memmove(op, extMatch, ml);\n                        op += ml;\n                        ml = 0;\n                    } else {\n                        /* match split between extDict & prefix */\n                        LZ4_memmove(op, extMatch, extml);\n                        op += extml;\n                        ml -= extml;\n                    }\n                    match = prefixStart;\n                }\n\n                /* match copy - slow variant, supporting overlap copy */\n                {   size_t u;\n                    for (u=0; u<ml; u++) {\n                        op[u] = match[u];\n            }   }   }\n            op += ml;\n            if ((size_t)(oend-op) < LASTLITERALS) {\n                DEBUGLOG(5, \"invalid: match ends at distance %zi from end of block\", oend-op);\n                /* incorrect end of block :\n                 * last match must stop at least LASTLITERALS==5 bytes before end of output block */\n                return -1;\n            }\n        } /* match */\n    } /* main loop */\n    return (int)(ip - istart);\n}\n\n\n/* Read the variable-length literal or match length.\n *\n * @ip : input pointer\n * @ilimit : position after which if length is not decoded, the input is necessarily corrupted.\n * @initial_check - check ip >= ipmax before start of loop.  Returns initial_error if so.\n * @error (output) - error code.  Must be set to 0 before call.\n**/\ntypedef size_t Rvl_t;\nstatic const Rvl_t rvl_error = (Rvl_t)(-1);\nLZ4_FORCE_INLINE Rvl_t\nread_variable_length(const BYTE** ip, const BYTE* ilimit,\n                     int initial_check)\n{\n    Rvl_t s, length = 0;\n    assert(ip != NULL);\n    assert(*ip !=  NULL);\n    assert(ilimit != NULL);\n    if (initial_check && unlikely((*ip) >= ilimit)) {    /* read limit reached */\n        return rvl_error;\n    }\n    do {\n        s = **ip;\n        (*ip)++;\n        length += s;\n        if (unlikely((*ip) > ilimit)) {    /* read limit reached */\n            return rvl_error;\n        }\n        /* accumulator overflow detection (32-bit mode only) */\n        if ((sizeof(length)<8) && unlikely(length > ((Rvl_t)(-1)/2)) ) {\n            return rvl_error;\n        }\n    } while (s==255);\n\n    return length;\n}\n\n/*! LZ4_decompress_generic() :\n *  This generic decompression function covers all use cases.\n *  It shall be instantiated several times, using different sets of directives.\n *  Note that it is important for performance that this function really get inlined,\n *  in order to remove useless branches during compilation optimization.\n */\nLZ4_FORCE_INLINE int\nLZ4_decompress_generic(\n                 const char* const src,\n                 char* const dst,\n                 int srcSize,\n                 int outputSize,         /* If endOnInput==endOnInputSize, this value is `dstCapacity` */\n\n                 earlyEnd_directive partialDecoding,  /* full, partial */\n                 dict_directive dict,                 /* noDict, withPrefix64k, usingExtDict */\n                 const BYTE* const lowPrefix,  /* always <= dst, == dst when no prefix */\n                 const BYTE* const dictStart,  /* only if dict==usingExtDict */\n                 const size_t dictSize         /* note : = 0 if noDict */\n                 )\n{\n    if ((src == NULL) || (outputSize < 0)) { return -1; }\n\n    {   const BYTE* ip = (const BYTE*) src;\n        const BYTE* const iend = ip + srcSize;\n\n        BYTE* op = (BYTE*) dst;\n        BYTE* const oend = op + outputSize;\n        BYTE* cpy;\n\n        const BYTE* const dictEnd = (dictStart == NULL) ? NULL : dictStart + dictSize;\n\n        const int checkOffset = (dictSize < (int)(64 KB));\n\n\n        /* Set up the \"end\" pointers for the shortcut. */\n        const BYTE* const shortiend = iend - 14 /*maxLL*/ - 2 /*offset*/;\n        const BYTE* const shortoend = oend - 14 /*maxLL*/ - 18 /*maxML*/;\n\n        const BYTE* match;\n        size_t offset;\n        unsigned token;\n        size_t length;\n\n\n        DEBUGLOG(5, \"LZ4_decompress_generic (srcSize:%i, dstSize:%i)\", srcSize, outputSize);\n\n        /* Special cases */\n        assert(lowPrefix <= op);\n        if (unlikely(outputSize==0)) {\n            /* Empty output buffer */\n            if (partialDecoding) return 0;\n            return ((srcSize==1) && (*ip==0)) ? 0 : -1;\n        }\n        if (unlikely(srcSize==0)) { return -1; }\n\n    /* LZ4_FAST_DEC_LOOP:\n     * designed for modern OoO performance cpus,\n     * where copying reliably 32-bytes is preferable to an unpredictable branch.\n     * note : fast loop may show a regression for some client arm chips. */\n#if LZ4_FAST_DEC_LOOP\n        if ((oend - op) < FASTLOOP_SAFE_DISTANCE) {\n            DEBUGLOG(6, \"skip fast decode loop\");\n            goto safe_decode;\n        }\n\n        /* Fast loop : decode sequences as long as output < oend-FASTLOOP_SAFE_DISTANCE */\n        while (1) {\n            /* Main fastloop assertion: We can always wildcopy FASTLOOP_SAFE_DISTANCE */\n            assert(oend - op >= FASTLOOP_SAFE_DISTANCE);\n            assert(ip < iend);\n            token = *ip++;\n            length = token >> ML_BITS;  /* literal length */\n\n            /* decode literal length */\n            if (length == RUN_MASK) {\n                size_t const addl = read_variable_length(&ip, iend-RUN_MASK, 1);\n                if (addl == rvl_error) { goto _output_error; }\n                length += addl;\n                if (unlikely((uptrval)(op)+length<(uptrval)(op))) { goto _output_error; } /* overflow detection */\n                if (unlikely((uptrval)(ip)+length<(uptrval)(ip))) { goto _output_error; } /* overflow detection */\n\n                /* copy literals */\n                cpy = op+length;\n                LZ4_STATIC_ASSERT(MFLIMIT >= WILDCOPYLENGTH);\n                if ((cpy>oend-32) || (ip+length>iend-32)) { goto safe_literal_copy; }\n                LZ4_wildCopy32(op, ip, cpy);\n                ip += length; op = cpy;\n            } else {\n                cpy = op+length;\n                DEBUGLOG(7, \"copy %u bytes in a 16-bytes stripe\", (unsigned)length);\n                /* We don't need to check oend, since we check it once for each loop below */\n                if (ip > iend-(16 + 1/*max lit + offset + nextToken*/)) { goto safe_literal_copy; }\n                /* Literals can only be <= 14, but hope compilers optimize better when copy by a register size */\n                LZ4_memcpy(op, ip, 16);\n                ip += length; op = cpy;\n            }\n\n            /* get offset */\n            offset = LZ4_readLE16(ip); ip+=2;\n            match = op - offset;\n            assert(match <= op);  /* overflow check */\n\n            /* get matchlength */\n            length = token & ML_MASK;\n\n            if (length == ML_MASK) {\n                size_t const addl = read_variable_length(&ip, iend - LASTLITERALS + 1, 0);\n                if (addl == rvl_error) { goto _output_error; }\n                length += addl;\n                length += MINMATCH;\n                if (unlikely((uptrval)(op)+length<(uptrval)op)) { goto _output_error; } /* overflow detection */\n                if ((checkOffset) && (unlikely(match + dictSize < lowPrefix))) { goto _output_error; } /* Error : offset outside buffers */\n                if (op + length >= oend - FASTLOOP_SAFE_DISTANCE) {\n                    goto safe_match_copy;\n                }\n            } else {\n                length += MINMATCH;\n                if (op + length >= oend - FASTLOOP_SAFE_DISTANCE) {\n                    goto safe_match_copy;\n                }\n\n                /* Fastpath check: skip LZ4_wildCopy32 when true */\n                if ((dict == withPrefix64k) || (match >= lowPrefix)) {\n                    if (offset >= 8) {\n                        assert(match >= lowPrefix);\n                        assert(match <= op);\n                        assert(op + 18 <= oend);\n\n                        LZ4_memcpy(op, match, 8);\n                        LZ4_memcpy(op+8, match+8, 8);\n                        LZ4_memcpy(op+16, match+16, 2);\n                        op += length;\n                        continue;\n            }   }   }\n\n            if (checkOffset && (unlikely(match + dictSize < lowPrefix))) { goto _output_error; } /* Error : offset outside buffers */\n            /* match starting within external dictionary */\n            if ((dict==usingExtDict) && (match < lowPrefix)) {\n                assert(dictEnd != NULL);\n                if (unlikely(op+length > oend-LASTLITERALS)) {\n                    if (partialDecoding) {\n                        DEBUGLOG(7, \"partialDecoding: dictionary match, close to dstEnd\");\n                        length = MIN(length, (size_t)(oend-op));\n                    } else {\n                        goto _output_error;  /* end-of-block condition violated */\n                }   }\n\n                if (length <= (size_t)(lowPrefix-match)) {\n                    /* match fits entirely within external dictionary : just copy */\n                    LZ4_memmove(op, dictEnd - (lowPrefix-match), length);\n                    op += length;\n                } else {\n                    /* match stretches into both external dictionary and current block */\n                    size_t const copySize = (size_t)(lowPrefix - match);\n                    size_t const restSize = length - copySize;\n                    LZ4_memcpy(op, dictEnd - copySize, copySize);\n                    op += copySize;\n                    if (restSize > (size_t)(op - lowPrefix)) {  /* overlap copy */\n                        BYTE* const endOfMatch = op + restSize;\n                        const BYTE* copyFrom = lowPrefix;\n                        while (op < endOfMatch) { *op++ = *copyFrom++; }\n                    } else {\n                        LZ4_memcpy(op, lowPrefix, restSize);\n                        op += restSize;\n                }   }\n                continue;\n            }\n\n            /* copy match within block */\n            cpy = op + length;\n\n            assert((op <= oend) && (oend-op >= 32));\n            if (unlikely(offset<16)) {\n                LZ4_memcpy_using_offset(op, match, cpy, offset);\n            } else {\n                LZ4_wildCopy32(op, match, cpy);\n            }\n\n            op = cpy;   /* wildcopy correction */\n        }\n    safe_decode:\n#endif\n\n        /* Main Loop : decode remaining sequences where output < FASTLOOP_SAFE_DISTANCE */\n        while (1) {\n            assert(ip < iend);\n            token = *ip++;\n            length = token >> ML_BITS;  /* literal length */\n\n            /* A two-stage shortcut for the most common case:\n             * 1) If the literal length is 0..14, and there is enough space,\n             * enter the shortcut and copy 16 bytes on behalf of the literals\n             * (in the fast mode, only 8 bytes can be safely copied this way).\n             * 2) Further if the match length is 4..18, copy 18 bytes in a similar\n             * manner; but we ensure that there's enough space in the output for\n             * those 18 bytes earlier, upon entering the shortcut (in other words,\n             * there is a combined check for both stages).\n             */\n            if ( (length != RUN_MASK)\n                /* strictly \"less than\" on input, to re-enter the loop with at least one byte */\n              && likely((ip < shortiend) & (op <= shortoend)) ) {\n                /* Copy the literals */\n                LZ4_memcpy(op, ip, 16);\n                op += length; ip += length;\n\n                /* The second stage: prepare for match copying, decode full info.\n                 * If it doesn't work out, the info won't be wasted. */\n                length = token & ML_MASK; /* match length */\n                offset = LZ4_readLE16(ip); ip += 2;\n                match = op - offset;\n                assert(match <= op); /* check overflow */\n\n                /* Do not deal with overlapping matches. */\n                if ( (length != ML_MASK)\n                  && (offset >= 8)\n                  && (dict==withPrefix64k || match >= lowPrefix) ) {\n                    /* Copy the match. */\n                    LZ4_memcpy(op + 0, match + 0, 8);\n                    LZ4_memcpy(op + 8, match + 8, 8);\n                    LZ4_memcpy(op +16, match +16, 2);\n                    op += length + MINMATCH;\n                    /* Both stages worked, load the next token. */\n                    continue;\n                }\n\n                /* The second stage didn't work out, but the info is ready.\n                 * Propel it right to the point of match copying. */\n                goto _copy_match;\n            }\n\n            /* decode literal length */\n            if (length == RUN_MASK) {\n                size_t const addl = read_variable_length(&ip, iend-RUN_MASK, 1);\n                if (addl == rvl_error) { goto _output_error; }\n                length += addl;\n                if (unlikely((uptrval)(op)+length<(uptrval)(op))) { goto _output_error; } /* overflow detection */\n                if (unlikely((uptrval)(ip)+length<(uptrval)(ip))) { goto _output_error; } /* overflow detection */\n            }\n\n            /* copy literals */\n            cpy = op+length;\n#if LZ4_FAST_DEC_LOOP\n        safe_literal_copy:\n#endif\n            LZ4_STATIC_ASSERT(MFLIMIT >= WILDCOPYLENGTH);\n            if ((cpy>oend-MFLIMIT) || (ip+length>iend-(2+1+LASTLITERALS))) {\n                /* We've either hit the input parsing restriction or the output parsing restriction.\n                 * In the normal scenario, decoding a full block, it must be the last sequence,\n                 * otherwise it's an error (invalid input or dimensions).\n                 * In partialDecoding scenario, it's necessary to ensure there is no buffer overflow.\n                 */\n                if (partialDecoding) {\n                    /* Since we are partial decoding we may be in this block because of the output parsing\n                     * restriction, which is not valid since the output buffer is allowed to be undersized.\n                     */\n                    DEBUGLOG(7, \"partialDecoding: copying literals, close to input or output end\")\n                    DEBUGLOG(7, \"partialDecoding: literal length = %u\", (unsigned)length);\n                    DEBUGLOG(7, \"partialDecoding: remaining space in dstBuffer : %i\", (int)(oend - op));\n                    DEBUGLOG(7, \"partialDecoding: remaining space in srcBuffer : %i\", (int)(iend - ip));\n                    /* Finishing in the middle of a literals segment,\n                     * due to lack of input.\n                     */\n                    if (ip+length > iend) {\n                        length = (size_t)(iend-ip);\n                        cpy = op + length;\n                    }\n                    /* Finishing in the middle of a literals segment,\n                     * due to lack of output space.\n                     */\n                    if (cpy > oend) {\n                        cpy = oend;\n                        assert(op<=oend);\n                        length = (size_t)(oend-op);\n                    }\n                } else {\n                     /* We must be on the last sequence (or invalid) because of the parsing limitations\n                      * so check that we exactly consume the input and don't overrun the output buffer.\n                      */\n                    if ((ip+length != iend) || (cpy > oend)) {\n                        DEBUGLOG(6, \"should have been last run of literals\")\n                        DEBUGLOG(6, \"ip(%p) + length(%i) = %p != iend (%p)\", ip, (int)length, ip+length, iend);\n                        DEBUGLOG(6, \"or cpy(%p) > oend(%p)\", cpy, oend);\n                        goto _output_error;\n                    }\n                }\n                LZ4_memmove(op, ip, length);  /* supports overlapping memory regions, for in-place decompression scenarios */\n                ip += length;\n                op += length;\n                /* Necessarily EOF when !partialDecoding.\n                 * When partialDecoding, it is EOF if we've either\n                 * filled the output buffer or\n                 * can't proceed with reading an offset for following match.\n                 */\n                if (!partialDecoding || (cpy == oend) || (ip >= (iend-2))) {\n                    break;\n                }\n            } else {\n                LZ4_wildCopy8(op, ip, cpy);   /* can overwrite up to 8 bytes beyond cpy */\n                ip += length; op = cpy;\n            }\n\n            /* get offset */\n            offset = LZ4_readLE16(ip); ip+=2;\n            match = op - offset;\n\n            /* get matchlength */\n            length = token & ML_MASK;\n\n    _copy_match:\n            if (length == ML_MASK) {\n                size_t const addl = read_variable_length(&ip, iend - LASTLITERALS + 1, 0);\n                if (addl == rvl_error) { goto _output_error; }\n                length += addl;\n                if (unlikely((uptrval)(op)+length<(uptrval)op)) goto _output_error;   /* overflow detection */\n            }\n            length += MINMATCH;\n\n#if LZ4_FAST_DEC_LOOP\n        safe_match_copy:\n#endif\n            if ((checkOffset) && (unlikely(match + dictSize < lowPrefix))) goto _output_error;   /* Error : offset outside buffers */\n            /* match starting within external dictionary */\n            if ((dict==usingExtDict) && (match < lowPrefix)) {\n                assert(dictEnd != NULL);\n                if (unlikely(op+length > oend-LASTLITERALS)) {\n                    if (partialDecoding) length = MIN(length, (size_t)(oend-op));\n                    else goto _output_error;   /* doesn't respect parsing restriction */\n                }\n\n                if (length <= (size_t)(lowPrefix-match)) {\n                    /* match fits entirely within external dictionary : just copy */\n                    LZ4_memmove(op, dictEnd - (lowPrefix-match), length);\n                    op += length;\n                } else {\n                    /* match stretches into both external dictionary and current block */\n                    size_t const copySize = (size_t)(lowPrefix - match);\n                    size_t const restSize = length - copySize;\n                    LZ4_memcpy(op, dictEnd - copySize, copySize);\n                    op += copySize;\n                    if (restSize > (size_t)(op - lowPrefix)) {  /* overlap copy */\n                        BYTE* const endOfMatch = op + restSize;\n                        const BYTE* copyFrom = lowPrefix;\n                        while (op < endOfMatch) *op++ = *copyFrom++;\n                    } else {\n                        LZ4_memcpy(op, lowPrefix, restSize);\n                        op += restSize;\n                }   }\n                continue;\n            }\n            assert(match >= lowPrefix);\n\n            /* copy match within block */\n            cpy = op + length;\n\n            /* partialDecoding : may end anywhere within the block */\n            assert(op<=oend);\n            if (partialDecoding && (cpy > oend-MATCH_SAFEGUARD_DISTANCE)) {\n                size_t const mlen = MIN(length, (size_t)(oend-op));\n                const BYTE* const matchEnd = match + mlen;\n                BYTE* const copyEnd = op + mlen;\n                if (matchEnd > op) {   /* overlap copy */\n                    while (op < copyEnd) { *op++ = *match++; }\n                } else {\n                    LZ4_memcpy(op, match, mlen);\n                }\n                op = copyEnd;\n                if (op == oend) { break; }\n                continue;\n            }\n\n            if (unlikely(offset<8)) {\n                LZ4_write32(op, 0);   /* silence msan warning when offset==0 */\n                op[0] = match[0];\n                op[1] = match[1];\n                op[2] = match[2];\n                op[3] = match[3];\n                match += inc32table[offset];\n                LZ4_memcpy(op+4, match, 4);\n                match -= dec64table[offset];\n            } else {\n                LZ4_memcpy(op, match, 8);\n                match += 8;\n            }\n            op += 8;\n\n            if (unlikely(cpy > oend-MATCH_SAFEGUARD_DISTANCE)) {\n                BYTE* const oCopyLimit = oend - (WILDCOPYLENGTH-1);\n                if (cpy > oend-LASTLITERALS) { goto _output_error; } /* Error : last LASTLITERALS bytes must be literals (uncompressed) */\n                if (op < oCopyLimit) {\n                    LZ4_wildCopy8(op, match, oCopyLimit);\n                    match += oCopyLimit - op;\n                    op = oCopyLimit;\n                }\n                while (op < cpy) { *op++ = *match++; }\n            } else {\n                LZ4_memcpy(op, match, 8);\n                if (length > 16)  { LZ4_wildCopy8(op+8, match+8, cpy); }\n            }\n            op = cpy;   /* wildcopy correction */\n        }\n\n        /* end of decoding */\n        DEBUGLOG(5, \"decoded %i bytes\", (int) (((char*)op)-dst));\n        return (int) (((char*)op)-dst);     /* Nb of output bytes decoded */\n\n        /* Overflow error detected */\n    _output_error:\n        return (int) (-(((const char*)ip)-src))-1;\n    }\n}\n\n\n/*===== Instantiate the API decoding functions. =====*/\n\nLZ4_FORCE_O2\nint LZ4_decompress_safe(const char* source, char* dest, int compressedSize, int maxDecompressedSize)\n{\n    return LZ4_decompress_generic(source, dest, compressedSize, maxDecompressedSize,\n                                  decode_full_block, noDict,\n                                  (BYTE*)dest, NULL, 0);\n}\n\nLZ4_FORCE_O2\nint LZ4_decompress_safe_partial(const char* src, char* dst, int compressedSize, int targetOutputSize, int dstCapacity)\n{\n    dstCapacity = MIN(targetOutputSize, dstCapacity);\n    return LZ4_decompress_generic(src, dst, compressedSize, dstCapacity,\n                                  partial_decode,\n                                  noDict, (BYTE*)dst, NULL, 0);\n}\n\nLZ4_FORCE_O2\nint LZ4_decompress_fast(const char* source, char* dest, int originalSize)\n{\n    DEBUGLOG(5, \"LZ4_decompress_fast\");\n    return LZ4_decompress_unsafe_generic(\n                (const BYTE*)source, (BYTE*)dest, originalSize,\n                0, NULL, 0);\n}\n\n/*===== Instantiate a few more decoding cases, used more than once. =====*/\n\nLZ4_FORCE_O2 /* Exported, an obsolete API function. */\nint LZ4_decompress_safe_withPrefix64k(const char* source, char* dest, int compressedSize, int maxOutputSize)\n{\n    return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize,\n                                  decode_full_block, withPrefix64k,\n                                  (BYTE*)dest - 64 KB, NULL, 0);\n}\n\nLZ4_FORCE_O2\nstatic int LZ4_decompress_safe_partial_withPrefix64k(const char* source, char* dest, int compressedSize, int targetOutputSize, int dstCapacity)\n{\n    dstCapacity = MIN(targetOutputSize, dstCapacity);\n    return LZ4_decompress_generic(source, dest, compressedSize, dstCapacity,\n                                  partial_decode, withPrefix64k,\n                                  (BYTE*)dest - 64 KB, NULL, 0);\n}\n\n/* Another obsolete API function, paired with the previous one. */\nint LZ4_decompress_fast_withPrefix64k(const char* source, char* dest, int originalSize)\n{\n    return LZ4_decompress_unsafe_generic(\n                (const BYTE*)source, (BYTE*)dest, originalSize,\n                64 KB, NULL, 0);\n}\n\nLZ4_FORCE_O2\nstatic int LZ4_decompress_safe_withSmallPrefix(const char* source, char* dest, int compressedSize, int maxOutputSize,\n                                               size_t prefixSize)\n{\n    return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize,\n                                  decode_full_block, noDict,\n                                  (BYTE*)dest-prefixSize, NULL, 0);\n}\n\nLZ4_FORCE_O2\nstatic int LZ4_decompress_safe_partial_withSmallPrefix(const char* source, char* dest, int compressedSize, int targetOutputSize, int dstCapacity,\n                                               size_t prefixSize)\n{\n    dstCapacity = MIN(targetOutputSize, dstCapacity);\n    return LZ4_decompress_generic(source, dest, compressedSize, dstCapacity,\n                                  partial_decode, noDict,\n                                  (BYTE*)dest-prefixSize, NULL, 0);\n}\n\nLZ4_FORCE_O2\nint LZ4_decompress_safe_forceExtDict(const char* source, char* dest,\n                                     int compressedSize, int maxOutputSize,\n                                     const void* dictStart, size_t dictSize)\n{\n    return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize,\n                                  decode_full_block, usingExtDict,\n                                  (BYTE*)dest, (const BYTE*)dictStart, dictSize);\n}\n\nLZ4_FORCE_O2\nint LZ4_decompress_safe_partial_forceExtDict(const char* source, char* dest,\n                                     int compressedSize, int targetOutputSize, int dstCapacity,\n                                     const void* dictStart, size_t dictSize)\n{\n    dstCapacity = MIN(targetOutputSize, dstCapacity);\n    return LZ4_decompress_generic(source, dest, compressedSize, dstCapacity,\n                                  partial_decode, usingExtDict,\n                                  (BYTE*)dest, (const BYTE*)dictStart, dictSize);\n}\n\nLZ4_FORCE_O2\nstatic int LZ4_decompress_fast_extDict(const char* source, char* dest, int originalSize,\n                                       const void* dictStart, size_t dictSize)\n{\n    return LZ4_decompress_unsafe_generic(\n                (const BYTE*)source, (BYTE*)dest, originalSize,\n                0, (const BYTE*)dictStart, dictSize);\n}\n\n/* The \"double dictionary\" mode, for use with e.g. ring buffers: the first part\n * of the dictionary is passed as prefix, and the second via dictStart + dictSize.\n * These routines are used only once, in LZ4_decompress_*_continue().\n */\nLZ4_FORCE_INLINE\nint LZ4_decompress_safe_doubleDict(const char* source, char* dest, int compressedSize, int maxOutputSize,\n                                   size_t prefixSize, const void* dictStart, size_t dictSize)\n{\n    return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize,\n                                  decode_full_block, usingExtDict,\n                                  (BYTE*)dest-prefixSize, (const BYTE*)dictStart, dictSize);\n}\n\n/*===== streaming decompression functions =====*/\n\n#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION)\nLZ4_streamDecode_t* LZ4_createStreamDecode(void)\n{\n    LZ4_STATIC_ASSERT(sizeof(LZ4_streamDecode_t) >= sizeof(LZ4_streamDecode_t_internal));\n    return (LZ4_streamDecode_t*) ALLOC_AND_ZERO(sizeof(LZ4_streamDecode_t));\n}\n\nint LZ4_freeStreamDecode (LZ4_streamDecode_t* LZ4_stream)\n{\n    if (LZ4_stream == NULL) { return 0; }  /* support free on NULL */\n    FREEMEM(LZ4_stream);\n    return 0;\n}\n#endif\n\n/*! LZ4_setStreamDecode() :\n *  Use this function to instruct where to find the dictionary.\n *  This function is not necessary if previous data is still available where it was decoded.\n *  Loading a size of 0 is allowed (same effect as no dictionary).\n * @return : 1 if OK, 0 if error\n */\nint LZ4_setStreamDecode (LZ4_streamDecode_t* LZ4_streamDecode, const char* dictionary, int dictSize)\n{\n    LZ4_streamDecode_t_internal* lz4sd = &LZ4_streamDecode->internal_donotuse;\n    lz4sd->prefixSize = (size_t)dictSize;\n    if (dictSize) {\n        assert(dictionary != NULL);\n        lz4sd->prefixEnd = (const BYTE*) dictionary + dictSize;\n    } else {\n        lz4sd->prefixEnd = (const BYTE*) dictionary;\n    }\n    lz4sd->externalDict = NULL;\n    lz4sd->extDictSize  = 0;\n    return 1;\n}\n\n/*! LZ4_decoderRingBufferSize() :\n *  when setting a ring buffer for streaming decompression (optional scenario),\n *  provides the minimum size of this ring buffer\n *  to be compatible with any source respecting maxBlockSize condition.\n *  Note : in a ring buffer scenario,\n *  blocks are presumed decompressed next to each other.\n *  When not enough space remains for next block (remainingSize < maxBlockSize),\n *  decoding resumes from beginning of ring buffer.\n * @return : minimum ring buffer size,\n *           or 0 if there is an error (invalid maxBlockSize).\n */\nint LZ4_decoderRingBufferSize(int maxBlockSize)\n{\n    if (maxBlockSize < 0) return 0;\n    if (maxBlockSize > LZ4_MAX_INPUT_SIZE) return 0;\n    if (maxBlockSize < 16) maxBlockSize = 16;\n    return LZ4_DECODER_RING_BUFFER_SIZE(maxBlockSize);\n}\n\n/*\n*_continue() :\n    These decoding functions allow decompression of multiple blocks in \"streaming\" mode.\n    Previously decoded blocks must still be available at the memory position where they were decoded.\n    If it's not possible, save the relevant part of decoded data into a safe buffer,\n    and indicate where it stands using LZ4_setStreamDecode()\n*/\nLZ4_FORCE_O2\nint LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int compressedSize, int maxOutputSize)\n{\n    LZ4_streamDecode_t_internal* lz4sd = &LZ4_streamDecode->internal_donotuse;\n    int result;\n\n    if (lz4sd->prefixSize == 0) {\n        /* The first call, no dictionary yet. */\n        assert(lz4sd->extDictSize == 0);\n        result = LZ4_decompress_safe(source, dest, compressedSize, maxOutputSize);\n        if (result <= 0) return result;\n        lz4sd->prefixSize = (size_t)result;\n        lz4sd->prefixEnd = (BYTE*)dest + result;\n    } else if (lz4sd->prefixEnd == (BYTE*)dest) {\n        /* They're rolling the current segment. */\n        if (lz4sd->prefixSize >= 64 KB - 1)\n            result = LZ4_decompress_safe_withPrefix64k(source, dest, compressedSize, maxOutputSize);\n        else if (lz4sd->extDictSize == 0)\n            result = LZ4_decompress_safe_withSmallPrefix(source, dest, compressedSize, maxOutputSize,\n                                                         lz4sd->prefixSize);\n        else\n            result = LZ4_decompress_safe_doubleDict(source, dest, compressedSize, maxOutputSize,\n                                                    lz4sd->prefixSize, lz4sd->externalDict, lz4sd->extDictSize);\n        if (result <= 0) return result;\n        lz4sd->prefixSize += (size_t)result;\n        lz4sd->prefixEnd  += result;\n    } else {\n        /* The buffer wraps around, or they're switching to another buffer. */\n        lz4sd->extDictSize = lz4sd->prefixSize;\n        lz4sd->externalDict = lz4sd->prefixEnd - lz4sd->extDictSize;\n        result = LZ4_decompress_safe_forceExtDict(source, dest, compressedSize, maxOutputSize,\n                                                  lz4sd->externalDict, lz4sd->extDictSize);\n        if (result <= 0) return result;\n        lz4sd->prefixSize = (size_t)result;\n        lz4sd->prefixEnd  = (BYTE*)dest + result;\n    }\n\n    return result;\n}\n\nLZ4_FORCE_O2 int\nLZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode,\n                        const char* source, char* dest, int originalSize)\n{\n    LZ4_streamDecode_t_internal* const lz4sd =\n        (assert(LZ4_streamDecode!=NULL), &LZ4_streamDecode->internal_donotuse);\n    int result;\n\n    DEBUGLOG(5, \"LZ4_decompress_fast_continue (toDecodeSize=%i)\", originalSize);\n    assert(originalSize >= 0);\n\n    if (lz4sd->prefixSize == 0) {\n        DEBUGLOG(5, \"first invocation : no prefix nor extDict\");\n        assert(lz4sd->extDictSize == 0);\n        result = LZ4_decompress_fast(source, dest, originalSize);\n        if (result <= 0) return result;\n        lz4sd->prefixSize = (size_t)originalSize;\n        lz4sd->prefixEnd = (BYTE*)dest + originalSize;\n    } else if (lz4sd->prefixEnd == (BYTE*)dest) {\n        DEBUGLOG(5, \"continue using existing prefix\");\n        result = LZ4_decompress_unsafe_generic(\n                        (const BYTE*)source, (BYTE*)dest, originalSize,\n                        lz4sd->prefixSize,\n                        lz4sd->externalDict, lz4sd->extDictSize);\n        if (result <= 0) return result;\n        lz4sd->prefixSize += (size_t)originalSize;\n        lz4sd->prefixEnd  += originalSize;\n    } else {\n        DEBUGLOG(5, \"prefix becomes extDict\");\n        lz4sd->extDictSize = lz4sd->prefixSize;\n        lz4sd->externalDict = lz4sd->prefixEnd - lz4sd->extDictSize;\n        result = LZ4_decompress_fast_extDict(source, dest, originalSize,\n                                             lz4sd->externalDict, lz4sd->extDictSize);\n        if (result <= 0) return result;\n        lz4sd->prefixSize = (size_t)originalSize;\n        lz4sd->prefixEnd  = (BYTE*)dest + originalSize;\n    }\n\n    return result;\n}\n\n\n/*\nAdvanced decoding functions :\n*_usingDict() :\n    These decoding functions work the same as \"_continue\" ones,\n    the dictionary must be explicitly provided within parameters\n*/\n\nint LZ4_decompress_safe_usingDict(const char* source, char* dest, int compressedSize, int maxOutputSize, const char* dictStart, int dictSize)\n{\n    if (dictSize==0)\n        return LZ4_decompress_safe(source, dest, compressedSize, maxOutputSize);\n    if (dictStart+dictSize == dest) {\n        if (dictSize >= 64 KB - 1) {\n            return LZ4_decompress_safe_withPrefix64k(source, dest, compressedSize, maxOutputSize);\n        }\n        assert(dictSize >= 0);\n        return LZ4_decompress_safe_withSmallPrefix(source, dest, compressedSize, maxOutputSize, (size_t)dictSize);\n    }\n    assert(dictSize >= 0);\n    return LZ4_decompress_safe_forceExtDict(source, dest, compressedSize, maxOutputSize, dictStart, (size_t)dictSize);\n}\n\nint LZ4_decompress_safe_partial_usingDict(const char* source, char* dest, int compressedSize, int targetOutputSize, int dstCapacity, const char* dictStart, int dictSize)\n{\n    if (dictSize==0)\n        return LZ4_decompress_safe_partial(source, dest, compressedSize, targetOutputSize, dstCapacity);\n    if (dictStart+dictSize == dest) {\n        if (dictSize >= 64 KB - 1) {\n            return LZ4_decompress_safe_partial_withPrefix64k(source, dest, compressedSize, targetOutputSize, dstCapacity);\n        }\n        assert(dictSize >= 0);\n        return LZ4_decompress_safe_partial_withSmallPrefix(source, dest, compressedSize, targetOutputSize, dstCapacity, (size_t)dictSize);\n    }\n    assert(dictSize >= 0);\n    return LZ4_decompress_safe_partial_forceExtDict(source, dest, compressedSize, targetOutputSize, dstCapacity, dictStart, (size_t)dictSize);\n}\n\nint LZ4_decompress_fast_usingDict(const char* source, char* dest, int originalSize, const char* dictStart, int dictSize)\n{\n    if (dictSize==0 || dictStart+dictSize == dest)\n        return LZ4_decompress_unsafe_generic(\n                        (const BYTE*)source, (BYTE*)dest, originalSize,\n                        (size_t)dictSize, NULL, 0);\n    assert(dictSize >= 0);\n    return LZ4_decompress_fast_extDict(source, dest, originalSize, dictStart, (size_t)dictSize);\n}\n\n\n/*=*************************************************\n*  Obsolete Functions\n***************************************************/\n/* obsolete compression functions */\nint LZ4_compress_limitedOutput(const char* source, char* dest, int inputSize, int maxOutputSize)\n{\n    return LZ4_compress_default(source, dest, inputSize, maxOutputSize);\n}\nint LZ4_compress(const char* src, char* dest, int srcSize)\n{\n    return LZ4_compress_default(src, dest, srcSize, LZ4_compressBound(srcSize));\n}\nint LZ4_compress_limitedOutput_withState (void* state, const char* src, char* dst, int srcSize, int dstSize)\n{\n    return LZ4_compress_fast_extState(state, src, dst, srcSize, dstSize, 1);\n}\nint LZ4_compress_withState (void* state, const char* src, char* dst, int srcSize)\n{\n    return LZ4_compress_fast_extState(state, src, dst, srcSize, LZ4_compressBound(srcSize), 1);\n}\nint LZ4_compress_limitedOutput_continue (LZ4_stream_t* LZ4_stream, const char* src, char* dst, int srcSize, int dstCapacity)\n{\n    return LZ4_compress_fast_continue(LZ4_stream, src, dst, srcSize, dstCapacity, 1);\n}\nint LZ4_compress_continue (LZ4_stream_t* LZ4_stream, const char* source, char* dest, int inputSize)\n{\n    return LZ4_compress_fast_continue(LZ4_stream, source, dest, inputSize, LZ4_compressBound(inputSize), 1);\n}\n\n/*\nThese decompression functions are deprecated and should no longer be used.\nThey are only provided here for compatibility with older user programs.\n- LZ4_uncompress is totally equivalent to LZ4_decompress_fast\n- LZ4_uncompress_unknownOutputSize is totally equivalent to LZ4_decompress_safe\n*/\nint LZ4_uncompress (const char* source, char* dest, int outputSize)\n{\n    return LZ4_decompress_fast(source, dest, outputSize);\n}\nint LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize)\n{\n    return LZ4_decompress_safe(source, dest, isize, maxOutputSize);\n}\n\n/* Obsolete Streaming functions */\n\nint LZ4_sizeofStreamState(void) { return sizeof(LZ4_stream_t); }\n\nint LZ4_resetStreamState(void* state, char* inputBuffer)\n{\n    (void)inputBuffer;\n    LZ4_resetStream((LZ4_stream_t*)state);\n    return 0;\n}\n\n#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION)\nvoid* LZ4_create (char* inputBuffer)\n{\n    (void)inputBuffer;\n    return LZ4_createStream();\n}\n#endif\n\nchar* LZ4_slideInputBuffer (void* state)\n{\n    /* avoid const char * -> char * conversion warning */\n    return (char *)(uptrval)((LZ4_stream_t*)state)->internal_donotuse.dictionary;\n}\n\n#endif   /* LZ4_COMMONDEFS_ONLY */\n"
  },
  {
    "path": "Whisper/Utils/LZ4/lz4.h",
    "content": "/*\n *  LZ4 - Fast LZ compression algorithm\n *  Header File\n *  Copyright (C) 2011-2020, Yann Collet.\n\n   BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)\n\n   Redistribution and use in source and binary forms, with or without\n   modification, are permitted provided that the following conditions are\n   met:\n\n       * Redistributions of source code must retain the above copyright\n   notice, this list of conditions and the following disclaimer.\n       * Redistributions in binary form must reproduce the above\n   copyright notice, this list of conditions and the following disclaimer\n   in the documentation and/or other materials provided with the\n   distribution.\n\n   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n   \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n   You can contact the author at :\n    - LZ4 homepage : http://www.lz4.org\n    - LZ4 source repository : https://github.com/lz4/lz4\n*/\n#if defined (__cplusplus)\nextern \"C\" {\n#endif\n\n#ifndef LZ4_H_2983827168210\n#define LZ4_H_2983827168210\n\n/* --- Dependency --- */\n#include <stddef.h>   /* size_t */\n\n\n/**\n  Introduction\n\n  LZ4 is lossless compression algorithm, providing compression speed >500 MB/s per core,\n  scalable with multi-cores CPU. It features an extremely fast decoder, with speed in\n  multiple GB/s per core, typically reaching RAM speed limits on multi-core systems.\n\n  The LZ4 compression library provides in-memory compression and decompression functions.\n  It gives full buffer control to user.\n  Compression can be done in:\n    - a single step (described as Simple Functions)\n    - a single step, reusing a context (described in Advanced Functions)\n    - unbounded multiple steps (described as Streaming compression)\n\n  lz4.h generates and decodes LZ4-compressed blocks (doc/lz4_Block_format.md).\n  Decompressing such a compressed block requires additional metadata.\n  Exact metadata depends on exact decompression function.\n  For the typical case of LZ4_decompress_safe(),\n  metadata includes block's compressed size, and maximum bound of decompressed size.\n  Each application is free to encode and pass such metadata in whichever way it wants.\n\n  lz4.h only handle blocks, it can not generate Frames.\n\n  Blocks are different from Frames (doc/lz4_Frame_format.md).\n  Frames bundle both blocks and metadata in a specified manner.\n  Embedding metadata is required for compressed data to be self-contained and portable.\n  Frame format is delivered through a companion API, declared in lz4frame.h.\n  The `lz4` CLI can only manage frames.\n*/\n\n/*^***************************************************************\n*  Export parameters\n*****************************************************************/\n/*\n*  LZ4_DLL_EXPORT :\n*  Enable exporting of functions when building a Windows DLL\n*  LZ4LIB_VISIBILITY :\n*  Control library symbols visibility.\n*/\n#ifndef LZ4LIB_VISIBILITY\n#  if defined(__GNUC__) && (__GNUC__ >= 4)\n#    define LZ4LIB_VISIBILITY __attribute__ ((visibility (\"default\")))\n#  else\n#    define LZ4LIB_VISIBILITY\n#  endif\n#endif\n#if defined(LZ4_DLL_EXPORT) && (LZ4_DLL_EXPORT==1)\n#  define LZ4LIB_API __declspec(dllexport) LZ4LIB_VISIBILITY\n#elif defined(LZ4_DLL_IMPORT) && (LZ4_DLL_IMPORT==1)\n#  define LZ4LIB_API __declspec(dllimport) LZ4LIB_VISIBILITY /* It isn't required but allows to generate better code, saving a function pointer load from the IAT and an indirect jump.*/\n#else\n#  define LZ4LIB_API LZ4LIB_VISIBILITY\n#endif\n\n/*! LZ4_FREESTANDING :\n *  When this macro is set to 1, it enables \"freestanding mode\" that is\n *  suitable for typical freestanding environment which doesn't support\n *  standard C library.\n *\n *  - LZ4_FREESTANDING is a compile-time switch.\n *  - It requires the following macros to be defined:\n *    LZ4_memcpy, LZ4_memmove, LZ4_memset.\n *  - It only enables LZ4/HC functions which don't use heap.\n *    All LZ4F_* functions are not supported.\n *  - See tests/freestanding.c to check its basic setup.\n */\n#if defined(LZ4_FREESTANDING) && (LZ4_FREESTANDING == 1)\n#  define LZ4_HEAPMODE 0\n#  define LZ4HC_HEAPMODE 0\n#  define LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION 1\n#  if !defined(LZ4_memcpy)\n#    error \"LZ4_FREESTANDING requires macro 'LZ4_memcpy'.\"\n#  endif\n#  if !defined(LZ4_memset)\n#    error \"LZ4_FREESTANDING requires macro 'LZ4_memset'.\"\n#  endif\n#  if !defined(LZ4_memmove)\n#    error \"LZ4_FREESTANDING requires macro 'LZ4_memmove'.\"\n#  endif\n#elif ! defined(LZ4_FREESTANDING)\n#  define LZ4_FREESTANDING 0\n#endif\n\n\n/*------   Version   ------*/\n#define LZ4_VERSION_MAJOR    1    /* for breaking interface changes  */\n#define LZ4_VERSION_MINOR    9    /* for new (non-breaking) interface capabilities */\n#define LZ4_VERSION_RELEASE  4    /* for tweaks, bug-fixes, or development */\n\n#define LZ4_VERSION_NUMBER (LZ4_VERSION_MAJOR *100*100 + LZ4_VERSION_MINOR *100 + LZ4_VERSION_RELEASE)\n\n#define LZ4_LIB_VERSION LZ4_VERSION_MAJOR.LZ4_VERSION_MINOR.LZ4_VERSION_RELEASE\n#define LZ4_QUOTE(str) #str\n#define LZ4_EXPAND_AND_QUOTE(str) LZ4_QUOTE(str)\n#define LZ4_VERSION_STRING LZ4_EXPAND_AND_QUOTE(LZ4_LIB_VERSION)  /* requires v1.7.3+ */\n\nLZ4LIB_API int LZ4_versionNumber (void);  /**< library version number; useful to check dll version; requires v1.3.0+ */\nLZ4LIB_API const char* LZ4_versionString (void);   /**< library version string; useful to check dll version; requires v1.7.5+ */\n\n\n/*-************************************\n*  Tuning parameter\n**************************************/\n#define LZ4_MEMORY_USAGE_MIN 10\n#define LZ4_MEMORY_USAGE_DEFAULT 14\n#define LZ4_MEMORY_USAGE_MAX 20\n\n/*!\n * LZ4_MEMORY_USAGE :\n * Memory usage formula : N->2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB; )\n * Increasing memory usage improves compression ratio, at the cost of speed.\n * Reduced memory usage may improve speed at the cost of ratio, thanks to better cache locality.\n * Default value is 14, for 16KB, which nicely fits into Intel x86 L1 cache\n */\n#ifndef LZ4_MEMORY_USAGE\n# define LZ4_MEMORY_USAGE LZ4_MEMORY_USAGE_DEFAULT\n#endif\n\n#if (LZ4_MEMORY_USAGE < LZ4_MEMORY_USAGE_MIN)\n#  error \"LZ4_MEMORY_USAGE is too small !\"\n#endif\n\n#if (LZ4_MEMORY_USAGE > LZ4_MEMORY_USAGE_MAX)\n#  error \"LZ4_MEMORY_USAGE is too large !\"\n#endif\n\n/*-************************************\n*  Simple Functions\n**************************************/\n/*! LZ4_compress_default() :\n *  Compresses 'srcSize' bytes from buffer 'src'\n *  into already allocated 'dst' buffer of size 'dstCapacity'.\n *  Compression is guaranteed to succeed if 'dstCapacity' >= LZ4_compressBound(srcSize).\n *  It also runs faster, so it's a recommended setting.\n *  If the function cannot compress 'src' into a more limited 'dst' budget,\n *  compression stops *immediately*, and the function result is zero.\n *  In which case, 'dst' content is undefined (invalid).\n *      srcSize : max supported value is LZ4_MAX_INPUT_SIZE.\n *      dstCapacity : size of buffer 'dst' (which must be already allocated)\n *     @return  : the number of bytes written into buffer 'dst' (necessarily <= dstCapacity)\n *                or 0 if compression fails\n * Note : This function is protected against buffer overflow scenarios (never writes outside 'dst' buffer, nor read outside 'source' buffer).\n */\nLZ4LIB_API int LZ4_compress_default(const char* src, char* dst, int srcSize, int dstCapacity);\n\n/*! LZ4_decompress_safe() :\n *  compressedSize : is the exact complete size of the compressed block.\n *  dstCapacity : is the size of destination buffer (which must be already allocated), presumed an upper bound of decompressed size.\n * @return : the number of bytes decompressed into destination buffer (necessarily <= dstCapacity)\n *           If destination buffer is not large enough, decoding will stop and output an error code (negative value).\n *           If the source stream is detected malformed, the function will stop decoding and return a negative result.\n * Note 1 : This function is protected against malicious data packets :\n *          it will never writes outside 'dst' buffer, nor read outside 'source' buffer,\n *          even if the compressed block is maliciously modified to order the decoder to do these actions.\n *          In such case, the decoder stops immediately, and considers the compressed block malformed.\n * Note 2 : compressedSize and dstCapacity must be provided to the function, the compressed block does not contain them.\n *          The implementation is free to send / store / derive this information in whichever way is most beneficial.\n *          If there is a need for a different format which bundles together both compressed data and its metadata, consider looking at lz4frame.h instead.\n */\nLZ4LIB_API int LZ4_decompress_safe (const char* src, char* dst, int compressedSize, int dstCapacity);\n\n\n/*-************************************\n*  Advanced Functions\n**************************************/\n#define LZ4_MAX_INPUT_SIZE        0x7E000000   /* 2 113 929 216 bytes */\n#define LZ4_COMPRESSBOUND(isize)  ((unsigned)(isize) > (unsigned)LZ4_MAX_INPUT_SIZE ? 0 : (isize) + ((isize)/255) + 16)\n\n/*! LZ4_compressBound() :\n    Provides the maximum size that LZ4 compression may output in a \"worst case\" scenario (input data not compressible)\n    This function is primarily useful for memory allocation purposes (destination buffer size).\n    Macro LZ4_COMPRESSBOUND() is also provided for compilation-time evaluation (stack memory allocation for example).\n    Note that LZ4_compress_default() compresses faster when dstCapacity is >= LZ4_compressBound(srcSize)\n        inputSize  : max supported value is LZ4_MAX_INPUT_SIZE\n        return : maximum output size in a \"worst case\" scenario\n              or 0, if input size is incorrect (too large or negative)\n*/\nLZ4LIB_API int LZ4_compressBound(int inputSize);\n\n/*! LZ4_compress_fast() :\n    Same as LZ4_compress_default(), but allows selection of \"acceleration\" factor.\n    The larger the acceleration value, the faster the algorithm, but also the lesser the compression.\n    It's a trade-off. It can be fine tuned, with each successive value providing roughly +~3% to speed.\n    An acceleration value of \"1\" is the same as regular LZ4_compress_default()\n    Values <= 0 will be replaced by LZ4_ACCELERATION_DEFAULT (currently == 1, see lz4.c).\n    Values > LZ4_ACCELERATION_MAX will be replaced by LZ4_ACCELERATION_MAX (currently == 65537, see lz4.c).\n*/\nLZ4LIB_API int LZ4_compress_fast (const char* src, char* dst, int srcSize, int dstCapacity, int acceleration);\n\n\n/*! LZ4_compress_fast_extState() :\n *  Same as LZ4_compress_fast(), using an externally allocated memory space for its state.\n *  Use LZ4_sizeofState() to know how much memory must be allocated,\n *  and allocate it on 8-bytes boundaries (using `malloc()` typically).\n *  Then, provide this buffer as `void* state` to compression function.\n */\nLZ4LIB_API int LZ4_sizeofState(void);\nLZ4LIB_API int LZ4_compress_fast_extState (void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration);\n\n\n/*! LZ4_compress_destSize() :\n *  Reverse the logic : compresses as much data as possible from 'src' buffer\n *  into already allocated buffer 'dst', of size >= 'targetDestSize'.\n *  This function either compresses the entire 'src' content into 'dst' if it's large enough,\n *  or fill 'dst' buffer completely with as much data as possible from 'src'.\n *  note: acceleration parameter is fixed to \"default\".\n *\n * *srcSizePtr : will be modified to indicate how many bytes where read from 'src' to fill 'dst'.\n *               New value is necessarily <= input value.\n * @return : Nb bytes written into 'dst' (necessarily <= targetDestSize)\n *           or 0 if compression fails.\n *\n * Note : from v1.8.2 to v1.9.1, this function had a bug (fixed un v1.9.2+):\n *        the produced compressed content could, in specific circumstances,\n *        require to be decompressed into a destination buffer larger\n *        by at least 1 byte than the content to decompress.\n *        If an application uses `LZ4_compress_destSize()`,\n *        it's highly recommended to update liblz4 to v1.9.2 or better.\n *        If this can't be done or ensured,\n *        the receiving decompression function should provide\n *        a dstCapacity which is > decompressedSize, by at least 1 byte.\n *        See https://github.com/lz4/lz4/issues/859 for details\n */\nLZ4LIB_API int LZ4_compress_destSize (const char* src, char* dst, int* srcSizePtr, int targetDstSize);\n\n\n/*! LZ4_decompress_safe_partial() :\n *  Decompress an LZ4 compressed block, of size 'srcSize' at position 'src',\n *  into destination buffer 'dst' of size 'dstCapacity'.\n *  Up to 'targetOutputSize' bytes will be decoded.\n *  The function stops decoding on reaching this objective.\n *  This can be useful to boost performance\n *  whenever only the beginning of a block is required.\n *\n * @return : the number of bytes decoded in `dst` (necessarily <= targetOutputSize)\n *           If source stream is detected malformed, function returns a negative result.\n *\n *  Note 1 : @return can be < targetOutputSize, if compressed block contains less data.\n *\n *  Note 2 : targetOutputSize must be <= dstCapacity\n *\n *  Note 3 : this function effectively stops decoding on reaching targetOutputSize,\n *           so dstCapacity is kind of redundant.\n *           This is because in older versions of this function,\n *           decoding operation would still write complete sequences.\n *           Therefore, there was no guarantee that it would stop writing at exactly targetOutputSize,\n *           it could write more bytes, though only up to dstCapacity.\n *           Some \"margin\" used to be required for this operation to work properly.\n *           Thankfully, this is no longer necessary.\n *           The function nonetheless keeps the same signature, in an effort to preserve API compatibility.\n *\n *  Note 4 : If srcSize is the exact size of the block,\n *           then targetOutputSize can be any value,\n *           including larger than the block's decompressed size.\n *           The function will, at most, generate block's decompressed size.\n *\n *  Note 5 : If srcSize is _larger_ than block's compressed size,\n *           then targetOutputSize **MUST** be <= block's decompressed size.\n *           Otherwise, *silent corruption will occur*.\n */\nLZ4LIB_API int LZ4_decompress_safe_partial (const char* src, char* dst, int srcSize, int targetOutputSize, int dstCapacity);\n\n\n/*-*********************************************\n*  Streaming Compression Functions\n***********************************************/\ntypedef union LZ4_stream_u LZ4_stream_t;  /* incomplete type (defined later) */\n\n/**\n Note about RC_INVOKED\n\n - RC_INVOKED is predefined symbol of rc.exe (the resource compiler which is part of MSVC/Visual Studio).\n   https://docs.microsoft.com/en-us/windows/win32/menurc/predefined-macros\n\n - Since rc.exe is a legacy compiler, it truncates long symbol (> 30 chars)\n   and reports warning \"RC4011: identifier truncated\".\n\n - To eliminate the warning, we surround long preprocessor symbol with\n   \"#if !defined(RC_INVOKED) ... #endif\" block that means\n   \"skip this block when rc.exe is trying to read it\".\n*/\n#if !defined(RC_INVOKED) /* https://docs.microsoft.com/en-us/windows/win32/menurc/predefined-macros */\n#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION)\nLZ4LIB_API LZ4_stream_t* LZ4_createStream(void);\nLZ4LIB_API int           LZ4_freeStream (LZ4_stream_t* streamPtr);\n#endif /* !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) */\n#endif\n\n/*! LZ4_resetStream_fast() : v1.9.0+\n *  Use this to prepare an LZ4_stream_t for a new chain of dependent blocks\n *  (e.g., LZ4_compress_fast_continue()).\n *\n *  An LZ4_stream_t must be initialized once before usage.\n *  This is automatically done when created by LZ4_createStream().\n *  However, should the LZ4_stream_t be simply declared on stack (for example),\n *  it's necessary to initialize it first, using LZ4_initStream().\n *\n *  After init, start any new stream with LZ4_resetStream_fast().\n *  A same LZ4_stream_t can be re-used multiple times consecutively\n *  and compress multiple streams,\n *  provided that it starts each new stream with LZ4_resetStream_fast().\n *\n *  LZ4_resetStream_fast() is much faster than LZ4_initStream(),\n *  but is not compatible with memory regions containing garbage data.\n *\n *  Note: it's only useful to call LZ4_resetStream_fast()\n *        in the context of streaming compression.\n *        The *extState* functions perform their own resets.\n *        Invoking LZ4_resetStream_fast() before is redundant, and even counterproductive.\n */\nLZ4LIB_API void LZ4_resetStream_fast (LZ4_stream_t* streamPtr);\n\n/*! LZ4_loadDict() :\n *  Use this function to reference a static dictionary into LZ4_stream_t.\n *  The dictionary must remain available during compression.\n *  LZ4_loadDict() triggers a reset, so any previous data will be forgotten.\n *  The same dictionary will have to be loaded on decompression side for successful decoding.\n *  Dictionary are useful for better compression of small data (KB range).\n *  While LZ4 accept any input as dictionary,\n *  results are generally better when using Zstandard's Dictionary Builder.\n *  Loading a size of 0 is allowed, and is the same as reset.\n * @return : loaded dictionary size, in bytes (necessarily <= 64 KB)\n */\nLZ4LIB_API int LZ4_loadDict (LZ4_stream_t* streamPtr, const char* dictionary, int dictSize);\n\n/*! LZ4_compress_fast_continue() :\n *  Compress 'src' content using data from previously compressed blocks, for better compression ratio.\n * 'dst' buffer must be already allocated.\n *  If dstCapacity >= LZ4_compressBound(srcSize), compression is guaranteed to succeed, and runs faster.\n *\n * @return : size of compressed block\n *           or 0 if there is an error (typically, cannot fit into 'dst').\n *\n *  Note 1 : Each invocation to LZ4_compress_fast_continue() generates a new block.\n *           Each block has precise boundaries.\n *           Each block must be decompressed separately, calling LZ4_decompress_*() with relevant metadata.\n *           It's not possible to append blocks together and expect a single invocation of LZ4_decompress_*() to decompress them together.\n *\n *  Note 2 : The previous 64KB of source data is __assumed__ to remain present, unmodified, at same address in memory !\n *\n *  Note 3 : When input is structured as a double-buffer, each buffer can have any size, including < 64 KB.\n *           Make sure that buffers are separated, by at least one byte.\n *           This construction ensures that each block only depends on previous block.\n *\n *  Note 4 : If input buffer is a ring-buffer, it can have any size, including < 64 KB.\n *\n *  Note 5 : After an error, the stream status is undefined (invalid), it can only be reset or freed.\n */\nLZ4LIB_API int LZ4_compress_fast_continue (LZ4_stream_t* streamPtr, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration);\n\n/*! LZ4_saveDict() :\n *  If last 64KB data cannot be guaranteed to remain available at its current memory location,\n *  save it into a safer place (char* safeBuffer).\n *  This is schematically equivalent to a memcpy() followed by LZ4_loadDict(),\n *  but is much faster, because LZ4_saveDict() doesn't need to rebuild tables.\n * @return : saved dictionary size in bytes (necessarily <= maxDictSize), or 0 if error.\n */\nLZ4LIB_API int LZ4_saveDict (LZ4_stream_t* streamPtr, char* safeBuffer, int maxDictSize);\n\n\n/*-**********************************************\n*  Streaming Decompression Functions\n*  Bufferless synchronous API\n************************************************/\ntypedef union LZ4_streamDecode_u LZ4_streamDecode_t;   /* tracking context */\n\n/*! LZ4_createStreamDecode() and LZ4_freeStreamDecode() :\n *  creation / destruction of streaming decompression tracking context.\n *  A tracking context can be re-used multiple times.\n */\n#if !defined(RC_INVOKED) /* https://docs.microsoft.com/en-us/windows/win32/menurc/predefined-macros */\n#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION)\nLZ4LIB_API LZ4_streamDecode_t* LZ4_createStreamDecode(void);\nLZ4LIB_API int                 LZ4_freeStreamDecode (LZ4_streamDecode_t* LZ4_stream);\n#endif /* !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) */\n#endif\n\n/*! LZ4_setStreamDecode() :\n *  An LZ4_streamDecode_t context can be allocated once and re-used multiple times.\n *  Use this function to start decompression of a new stream of blocks.\n *  A dictionary can optionally be set. Use NULL or size 0 for a reset order.\n *  Dictionary is presumed stable : it must remain accessible and unmodified during next decompression.\n * @return : 1 if OK, 0 if error\n */\nLZ4LIB_API int LZ4_setStreamDecode (LZ4_streamDecode_t* LZ4_streamDecode, const char* dictionary, int dictSize);\n\n/*! LZ4_decoderRingBufferSize() : v1.8.2+\n *  Note : in a ring buffer scenario (optional),\n *  blocks are presumed decompressed next to each other\n *  up to the moment there is not enough remaining space for next block (remainingSize < maxBlockSize),\n *  at which stage it resumes from beginning of ring buffer.\n *  When setting such a ring buffer for streaming decompression,\n *  provides the minimum size of this ring buffer\n *  to be compatible with any source respecting maxBlockSize condition.\n * @return : minimum ring buffer size,\n *           or 0 if there is an error (invalid maxBlockSize).\n */\nLZ4LIB_API int LZ4_decoderRingBufferSize(int maxBlockSize);\n#define LZ4_DECODER_RING_BUFFER_SIZE(maxBlockSize) (65536 + 14 + (maxBlockSize))  /* for static allocation; maxBlockSize presumed valid */\n\n/*! LZ4_decompress_*_continue() :\n *  These decoding functions allow decompression of consecutive blocks in \"streaming\" mode.\n *  A block is an unsplittable entity, it must be presented entirely to a decompression function.\n *  Decompression functions only accepts one block at a time.\n *  The last 64KB of previously decoded data *must* remain available and unmodified at the memory position where they were decoded.\n *  If less than 64KB of data has been decoded, all the data must be present.\n *\n *  Special : if decompression side sets a ring buffer, it must respect one of the following conditions :\n *  - Decompression buffer size is _at least_ LZ4_decoderRingBufferSize(maxBlockSize).\n *    maxBlockSize is the maximum size of any single block. It can have any value > 16 bytes.\n *    In which case, encoding and decoding buffers do not need to be synchronized.\n *    Actually, data can be produced by any source compliant with LZ4 format specification, and respecting maxBlockSize.\n *  - Synchronized mode :\n *    Decompression buffer size is _exactly_ the same as compression buffer size,\n *    and follows exactly same update rule (block boundaries at same positions),\n *    and decoding function is provided with exact decompressed size of each block (exception for last block of the stream),\n *    _then_ decoding & encoding ring buffer can have any size, including small ones ( < 64 KB).\n *  - Decompression buffer is larger than encoding buffer, by a minimum of maxBlockSize more bytes.\n *    In which case, encoding and decoding buffers do not need to be synchronized,\n *    and encoding ring buffer can have any size, including small ones ( < 64 KB).\n *\n *  Whenever these conditions are not possible,\n *  save the last 64KB of decoded data into a safe buffer where it can't be modified during decompression,\n *  then indicate where this data is saved using LZ4_setStreamDecode(), before decompressing next block.\n*/\nLZ4LIB_API int\nLZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode,\n                        const char* src, char* dst,\n                        int srcSize, int dstCapacity);\n\n\n/*! LZ4_decompress_*_usingDict() :\n *  These decoding functions work the same as\n *  a combination of LZ4_setStreamDecode() followed by LZ4_decompress_*_continue()\n *  They are stand-alone, and don't need an LZ4_streamDecode_t structure.\n *  Dictionary is presumed stable : it must remain accessible and unmodified during decompression.\n *  Performance tip : Decompression speed can be substantially increased\n *                    when dst == dictStart + dictSize.\n */\nLZ4LIB_API int\nLZ4_decompress_safe_usingDict(const char* src, char* dst,\n                              int srcSize, int dstCapacity,\n                              const char* dictStart, int dictSize);\n\nLZ4LIB_API int\nLZ4_decompress_safe_partial_usingDict(const char* src, char* dst,\n                                      int compressedSize,\n                                      int targetOutputSize, int maxOutputSize,\n                                      const char* dictStart, int dictSize);\n\n#endif /* LZ4_H_2983827168210 */\n\n\n/*^*************************************\n * !!!!!!   STATIC LINKING ONLY   !!!!!!\n ***************************************/\n\n/*-****************************************************************************\n * Experimental section\n *\n * Symbols declared in this section must be considered unstable. Their\n * signatures or semantics may change, or they may be removed altogether in the\n * future. They are therefore only safe to depend on when the caller is\n * statically linked against the library.\n *\n * To protect against unsafe usage, not only are the declarations guarded,\n * the definitions are hidden by default\n * when building LZ4 as a shared/dynamic library.\n *\n * In order to access these declarations,\n * define LZ4_STATIC_LINKING_ONLY in your application\n * before including LZ4's headers.\n *\n * In order to make their implementations accessible dynamically, you must\n * define LZ4_PUBLISH_STATIC_FUNCTIONS when building the LZ4 library.\n ******************************************************************************/\n\n#ifdef LZ4_STATIC_LINKING_ONLY\n\n#ifndef LZ4_STATIC_3504398509\n#define LZ4_STATIC_3504398509\n\n#ifdef LZ4_PUBLISH_STATIC_FUNCTIONS\n#define LZ4LIB_STATIC_API LZ4LIB_API\n#else\n#define LZ4LIB_STATIC_API\n#endif\n\n\n/*! LZ4_compress_fast_extState_fastReset() :\n *  A variant of LZ4_compress_fast_extState().\n *\n *  Using this variant avoids an expensive initialization step.\n *  It is only safe to call if the state buffer is known to be correctly initialized already\n *  (see above comment on LZ4_resetStream_fast() for a definition of \"correctly initialized\").\n *  From a high level, the difference is that\n *  this function initializes the provided state with a call to something like LZ4_resetStream_fast()\n *  while LZ4_compress_fast_extState() starts with a call to LZ4_resetStream().\n */\nLZ4LIB_STATIC_API int LZ4_compress_fast_extState_fastReset (void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration);\n\n/*! LZ4_attach_dictionary() :\n *  This is an experimental API that allows\n *  efficient use of a static dictionary many times.\n *\n *  Rather than re-loading the dictionary buffer into a working context before\n *  each compression, or copying a pre-loaded dictionary's LZ4_stream_t into a\n *  working LZ4_stream_t, this function introduces a no-copy setup mechanism,\n *  in which the working stream references the dictionary stream in-place.\n *\n *  Several assumptions are made about the state of the dictionary stream.\n *  Currently, only streams which have been prepared by LZ4_loadDict() should\n *  be expected to work.\n *\n *  Alternatively, the provided dictionaryStream may be NULL,\n *  in which case any existing dictionary stream is unset.\n *\n *  If a dictionary is provided, it replaces any pre-existing stream history.\n *  The dictionary contents are the only history that can be referenced and\n *  logically immediately precede the data compressed in the first subsequent\n *  compression call.\n *\n *  The dictionary will only remain attached to the working stream through the\n *  first compression call, at the end of which it is cleared. The dictionary\n *  stream (and source buffer) must remain in-place / accessible / unchanged\n *  through the completion of the first compression call on the stream.\n */\nLZ4LIB_STATIC_API void\nLZ4_attach_dictionary(LZ4_stream_t* workingStream,\n                const LZ4_stream_t* dictionaryStream);\n\n\n/*! In-place compression and decompression\n *\n * It's possible to have input and output sharing the same buffer,\n * for highly constrained memory environments.\n * In both cases, it requires input to lay at the end of the buffer,\n * and decompression to start at beginning of the buffer.\n * Buffer size must feature some margin, hence be larger than final size.\n *\n * |<------------------------buffer--------------------------------->|\n *                             |<-----------compressed data--------->|\n * |<-----------decompressed size------------------>|\n *                                                  |<----margin---->|\n *\n * This technique is more useful for decompression,\n * since decompressed size is typically larger,\n * and margin is short.\n *\n * In-place decompression will work inside any buffer\n * which size is >= LZ4_DECOMPRESS_INPLACE_BUFFER_SIZE(decompressedSize).\n * This presumes that decompressedSize > compressedSize.\n * Otherwise, it means compression actually expanded data,\n * and it would be more efficient to store such data with a flag indicating it's not compressed.\n * This can happen when data is not compressible (already compressed, or encrypted).\n *\n * For in-place compression, margin is larger, as it must be able to cope with both\n * history preservation, requiring input data to remain unmodified up to LZ4_DISTANCE_MAX,\n * and data expansion, which can happen when input is not compressible.\n * As a consequence, buffer size requirements are much higher,\n * and memory savings offered by in-place compression are more limited.\n *\n * There are ways to limit this cost for compression :\n * - Reduce history size, by modifying LZ4_DISTANCE_MAX.\n *   Note that it is a compile-time constant, so all compressions will apply this limit.\n *   Lower values will reduce compression ratio, except when input_size < LZ4_DISTANCE_MAX,\n *   so it's a reasonable trick when inputs are known to be small.\n * - Require the compressor to deliver a \"maximum compressed size\".\n *   This is the `dstCapacity` parameter in `LZ4_compress*()`.\n *   When this size is < LZ4_COMPRESSBOUND(inputSize), then compression can fail,\n *   in which case, the return code will be 0 (zero).\n *   The caller must be ready for these cases to happen,\n *   and typically design a backup scheme to send data uncompressed.\n * The combination of both techniques can significantly reduce\n * the amount of margin required for in-place compression.\n *\n * In-place compression can work in any buffer\n * which size is >= (maxCompressedSize)\n * with maxCompressedSize == LZ4_COMPRESSBOUND(srcSize) for guaranteed compression success.\n * LZ4_COMPRESS_INPLACE_BUFFER_SIZE() depends on both maxCompressedSize and LZ4_DISTANCE_MAX,\n * so it's possible to reduce memory requirements by playing with them.\n */\n\n#define LZ4_DECOMPRESS_INPLACE_MARGIN(compressedSize)          (((compressedSize) >> 8) + 32)\n#define LZ4_DECOMPRESS_INPLACE_BUFFER_SIZE(decompressedSize)   ((decompressedSize) + LZ4_DECOMPRESS_INPLACE_MARGIN(decompressedSize))  /**< note: presumes that compressedSize < decompressedSize. note2: margin is overestimated a bit, since it could use compressedSize instead */\n\n#ifndef LZ4_DISTANCE_MAX   /* history window size; can be user-defined at compile time */\n#  define LZ4_DISTANCE_MAX 65535   /* set to maximum value by default */\n#endif\n\n#define LZ4_COMPRESS_INPLACE_MARGIN                           (LZ4_DISTANCE_MAX + 32)   /* LZ4_DISTANCE_MAX can be safely replaced by srcSize when it's smaller */\n#define LZ4_COMPRESS_INPLACE_BUFFER_SIZE(maxCompressedSize)   ((maxCompressedSize) + LZ4_COMPRESS_INPLACE_MARGIN)  /**< maxCompressedSize is generally LZ4_COMPRESSBOUND(inputSize), but can be set to any lower value, with the risk that compression can fail (return code 0(zero)) */\n\n#endif   /* LZ4_STATIC_3504398509 */\n#endif   /* LZ4_STATIC_LINKING_ONLY */\n\n\n\n#ifndef LZ4_H_98237428734687\n#define LZ4_H_98237428734687\n\n/*-************************************************************\n *  Private Definitions\n **************************************************************\n * Do not use these definitions directly.\n * They are only exposed to allow static allocation of `LZ4_stream_t` and `LZ4_streamDecode_t`.\n * Accessing members will expose user code to API and/or ABI break in future versions of the library.\n **************************************************************/\n#define LZ4_HASHLOG   (LZ4_MEMORY_USAGE-2)\n#define LZ4_HASHTABLESIZE (1 << LZ4_MEMORY_USAGE)\n#define LZ4_HASH_SIZE_U32 (1 << LZ4_HASHLOG)       /* required as macro for static allocation */\n\n#if defined(__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */)\n# include <stdint.h>\n  typedef  int8_t  LZ4_i8;\n  typedef uint8_t  LZ4_byte;\n  typedef uint16_t LZ4_u16;\n  typedef uint32_t LZ4_u32;\n#else\n  typedef   signed char  LZ4_i8;\n  typedef unsigned char  LZ4_byte;\n  typedef unsigned short LZ4_u16;\n  typedef unsigned int   LZ4_u32;\n#endif\n\n/*! LZ4_stream_t :\n *  Never ever use below internal definitions directly !\n *  These definitions are not API/ABI safe, and may change in future versions.\n *  If you need static allocation, declare or allocate an LZ4_stream_t object.\n**/\n\ntypedef struct LZ4_stream_t_internal LZ4_stream_t_internal;\nstruct LZ4_stream_t_internal {\n    LZ4_u32 hashTable[LZ4_HASH_SIZE_U32];\n    const LZ4_byte* dictionary;\n    const LZ4_stream_t_internal* dictCtx;\n    LZ4_u32 currentOffset;\n    LZ4_u32 tableType;\n    LZ4_u32 dictSize;\n    /* Implicit padding to ensure structure is aligned */\n};\n\n#define LZ4_STREAM_MINSIZE  ((1UL << LZ4_MEMORY_USAGE) + 32)  /* static size, for inter-version compatibility */\nunion LZ4_stream_u {\n    char minStateSize[LZ4_STREAM_MINSIZE];\n    LZ4_stream_t_internal internal_donotuse;\n}; /* previously typedef'd to LZ4_stream_t */\n\n\n/*! LZ4_initStream() : v1.9.0+\n *  An LZ4_stream_t structure must be initialized at least once.\n *  This is automatically done when invoking LZ4_createStream(),\n *  but it's not when the structure is simply declared on stack (for example).\n *\n *  Use LZ4_initStream() to properly initialize a newly declared LZ4_stream_t.\n *  It can also initialize any arbitrary buffer of sufficient size,\n *  and will @return a pointer of proper type upon initialization.\n *\n *  Note : initialization fails if size and alignment conditions are not respected.\n *         In which case, the function will @return NULL.\n *  Note2: An LZ4_stream_t structure guarantees correct alignment and size.\n *  Note3: Before v1.9.0, use LZ4_resetStream() instead\n**/\nLZ4LIB_API LZ4_stream_t* LZ4_initStream (void* buffer, size_t size);\n\n\n/*! LZ4_streamDecode_t :\n *  Never ever use below internal definitions directly !\n *  These definitions are not API/ABI safe, and may change in future versions.\n *  If you need static allocation, declare or allocate an LZ4_streamDecode_t object.\n**/\ntypedef struct {\n    const LZ4_byte* externalDict;\n    const LZ4_byte* prefixEnd;\n    size_t extDictSize;\n    size_t prefixSize;\n} LZ4_streamDecode_t_internal;\n\n#define LZ4_STREAMDECODE_MINSIZE 32\nunion LZ4_streamDecode_u {\n    char minStateSize[LZ4_STREAMDECODE_MINSIZE];\n    LZ4_streamDecode_t_internal internal_donotuse;\n} ;   /* previously typedef'd to LZ4_streamDecode_t */\n\n\n\n/*-************************************\n*  Obsolete Functions\n**************************************/\n\n/*! Deprecation warnings\n *\n *  Deprecated functions make the compiler generate a warning when invoked.\n *  This is meant to invite users to update their source code.\n *  Should deprecation warnings be a problem, it is generally possible to disable them,\n *  typically with -Wno-deprecated-declarations for gcc\n *  or _CRT_SECURE_NO_WARNINGS in Visual.\n *\n *  Another method is to define LZ4_DISABLE_DEPRECATE_WARNINGS\n *  before including the header file.\n */\n#ifdef LZ4_DISABLE_DEPRECATE_WARNINGS\n#  define LZ4_DEPRECATED(message)   /* disable deprecation warnings */\n#else\n#  if defined (__cplusplus) && (__cplusplus >= 201402) /* C++14 or greater */\n#    define LZ4_DEPRECATED(message) [[deprecated(message)]]\n#  elif defined(_MSC_VER)\n#    define LZ4_DEPRECATED(message) __declspec(deprecated(message))\n#  elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ * 10 + __GNUC_MINOR__ >= 45))\n#    define LZ4_DEPRECATED(message) __attribute__((deprecated(message)))\n#  elif defined(__GNUC__) && (__GNUC__ * 10 + __GNUC_MINOR__ >= 31)\n#    define LZ4_DEPRECATED(message) __attribute__((deprecated))\n#  else\n#    pragma message(\"WARNING: LZ4_DEPRECATED needs custom implementation for this compiler\")\n#    define LZ4_DEPRECATED(message)   /* disabled */\n#  endif\n#endif /* LZ4_DISABLE_DEPRECATE_WARNINGS */\n\n/*! Obsolete compression functions (since v1.7.3) */\nLZ4_DEPRECATED(\"use LZ4_compress_default() instead\")       LZ4LIB_API int LZ4_compress               (const char* src, char* dest, int srcSize);\nLZ4_DEPRECATED(\"use LZ4_compress_default() instead\")       LZ4LIB_API int LZ4_compress_limitedOutput (const char* src, char* dest, int srcSize, int maxOutputSize);\nLZ4_DEPRECATED(\"use LZ4_compress_fast_extState() instead\") LZ4LIB_API int LZ4_compress_withState               (void* state, const char* source, char* dest, int inputSize);\nLZ4_DEPRECATED(\"use LZ4_compress_fast_extState() instead\") LZ4LIB_API int LZ4_compress_limitedOutput_withState (void* state, const char* source, char* dest, int inputSize, int maxOutputSize);\nLZ4_DEPRECATED(\"use LZ4_compress_fast_continue() instead\") LZ4LIB_API int LZ4_compress_continue                (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize);\nLZ4_DEPRECATED(\"use LZ4_compress_fast_continue() instead\") LZ4LIB_API int LZ4_compress_limitedOutput_continue  (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize, int maxOutputSize);\n\n/*! Obsolete decompression functions (since v1.8.0) */\nLZ4_DEPRECATED(\"use LZ4_decompress_fast() instead\") LZ4LIB_API int LZ4_uncompress (const char* source, char* dest, int outputSize);\nLZ4_DEPRECATED(\"use LZ4_decompress_safe() instead\") LZ4LIB_API int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize);\n\n/* Obsolete streaming functions (since v1.7.0)\n * degraded functionality; do not use!\n *\n * In order to perform streaming compression, these functions depended on data\n * that is no longer tracked in the state. They have been preserved as well as\n * possible: using them will still produce a correct output. However, they don't\n * actually retain any history between compression calls. The compression ratio\n * achieved will therefore be no better than compressing each chunk\n * independently.\n */\nLZ4_DEPRECATED(\"Use LZ4_createStream() instead\") LZ4LIB_API void* LZ4_create (char* inputBuffer);\nLZ4_DEPRECATED(\"Use LZ4_createStream() instead\") LZ4LIB_API int   LZ4_sizeofStreamState(void);\nLZ4_DEPRECATED(\"Use LZ4_resetStream() instead\")  LZ4LIB_API int   LZ4_resetStreamState(void* state, char* inputBuffer);\nLZ4_DEPRECATED(\"Use LZ4_saveDict() instead\")     LZ4LIB_API char* LZ4_slideInputBuffer (void* state);\n\n/*! Obsolete streaming decoding functions (since v1.7.0) */\nLZ4_DEPRECATED(\"use LZ4_decompress_safe_usingDict() instead\") LZ4LIB_API int LZ4_decompress_safe_withPrefix64k (const char* src, char* dst, int compressedSize, int maxDstSize);\nLZ4_DEPRECATED(\"use LZ4_decompress_fast_usingDict() instead\") LZ4LIB_API int LZ4_decompress_fast_withPrefix64k (const char* src, char* dst, int originalSize);\n\n/*! Obsolete LZ4_decompress_fast variants (since v1.9.0) :\n *  These functions used to be faster than LZ4_decompress_safe(),\n *  but this is no longer the case. They are now slower.\n *  This is because LZ4_decompress_fast() doesn't know the input size,\n *  and therefore must progress more cautiously into the input buffer to not read beyond the end of block.\n *  On top of that `LZ4_decompress_fast()` is not protected vs malformed or malicious inputs, making it a security liability.\n *  As a consequence, LZ4_decompress_fast() is strongly discouraged, and deprecated.\n *\n *  The last remaining LZ4_decompress_fast() specificity is that\n *  it can decompress a block without knowing its compressed size.\n *  Such functionality can be achieved in a more secure manner\n *  by employing LZ4_decompress_safe_partial().\n *\n *  Parameters:\n *  originalSize : is the uncompressed size to regenerate.\n *                 `dst` must be already allocated, its size must be >= 'originalSize' bytes.\n * @return : number of bytes read from source buffer (== compressed size).\n *           The function expects to finish at block's end exactly.\n *           If the source stream is detected malformed, the function stops decoding and returns a negative result.\n *  note : LZ4_decompress_fast*() requires originalSize. Thanks to this information, it never writes past the output buffer.\n *         However, since it doesn't know its 'src' size, it may read an unknown amount of input, past input buffer bounds.\n *         Also, since match offsets are not validated, match reads from 'src' may underflow too.\n *         These issues never happen if input (compressed) data is correct.\n *         But they may happen if input data is invalid (error or intentional tampering).\n *         As a consequence, use these functions in trusted environments with trusted data **only**.\n */\nLZ4_DEPRECATED(\"This function is deprecated and unsafe. Consider using LZ4_decompress_safe() instead\")\nLZ4LIB_API int LZ4_decompress_fast (const char* src, char* dst, int originalSize);\nLZ4_DEPRECATED(\"This function is deprecated and unsafe. Consider using LZ4_decompress_safe_continue() instead\")\nLZ4LIB_API int LZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* src, char* dst, int originalSize);\nLZ4_DEPRECATED(\"This function is deprecated and unsafe. Consider using LZ4_decompress_safe_usingDict() instead\")\nLZ4LIB_API int LZ4_decompress_fast_usingDict (const char* src, char* dst, int originalSize, const char* dictStart, int dictSize);\n\n/*! LZ4_resetStream() :\n *  An LZ4_stream_t structure must be initialized at least once.\n *  This is done with LZ4_initStream(), or LZ4_resetStream().\n *  Consider switching to LZ4_initStream(),\n *  invoking LZ4_resetStream() will trigger deprecation warnings in the future.\n */\nLZ4LIB_API void LZ4_resetStream (LZ4_stream_t* streamPtr);\n\n\n#endif /* LZ4_H_98237428734687 */\n\n\n#if defined (__cplusplus)\n}\n#endif\n"
  },
  {
    "path": "Whisper/Utils/Logger.cpp",
    "content": "#include \"stdafx.h\"\n#include \"Logger.h\"\n#include \"../API/iContext.cl.h\"\n#include <cstdarg>\n#include <atlstr.h>\n\nnamespace\n{\n\twchar_t* formatMessage( HRESULT hr )\n\t{\n\t\twchar_t* err;\n\t\tif( FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,\n\t\t\tNULL,\n\t\t\thr,\n\t\t\tMAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ),\n\t\t\t(LPTSTR)&err,\n\t\t\t0,\n\t\t\tnullptr ) )\n\t\t\treturn err;\n\t\treturn nullptr;\n\t}\n\n\tclass Utf\n\t{\n\t\tCStringA utf8;\n\t\tCStringW utf16;\n\n\t\tvoid appendError( HRESULT hr )\n\t\t{\n\t\t\tconst wchar_t* err = formatMessage( hr );\n\t\t\tif( nullptr != err )\n\t\t\t{\n\t\t\t\tutf16 += err;\n\t\t\t\tLocalFree( (HLOCAL)err );\n\t\t\t\tutf16.TrimRight();\n\t\t\t}\n\t\t\telse\n\t\t\t\tutf16.AppendFormat( L\"error code %i (0x%08X)\", hr, hr );\n\t\t}\n\n\tpublic:\n\t\tconst char* print( const char* pszFormat, std::va_list va )\n\t\t{\n\t\t\tutf8.FormatV( pszFormat, va );\n\t\t\treturn utf8;\n\t\t}\n\t\tconst wchar_t* print( const wchar_t* pszFormat, std::va_list va )\n\t\t{\n\t\t\tutf16.FormatV( pszFormat, va );\n\t\t\treturn utf16;\n\t\t}\n\t\tconst wchar_t* upcast( const char* message, int len )\n\t\t{\n\t\t\tint count = MultiByteToWideChar( CP_UTF8, 0, message, len, nullptr, 0 );\n\t\t\tif( count == 0 )\n\t\t\t\treturn nullptr;\n\t\t\twchar_t* b = utf16.GetBufferSetLength( len + 1 );\n\t\t\tcount = MultiByteToWideChar( CP_UTF8, 0, message, len, b, len );\n\t\t\tutf16.ReleaseBuffer( count );\n\t\t\treturn utf16;\n\t\t}\n\t\tint utf8Length() const\n\t\t{\n\t\t\treturn utf8.GetLength();\n\t\t}\n\t\tconst wchar_t* printError( HRESULT hr, const char* pszFormat, std::va_list va )\n\t\t{\n\t\t\tprint( pszFormat, va );\n\t\t\tupcast( utf8, utf8.GetLength() );\n\t\t\tutf16 += L\": \";\n\t\t\tappendError( hr );\n\t\t\treturn utf16;\n\t\t}\n\t\tconst char* downcast()\n\t\t{\n\t\t\tint count = WideCharToMultiByte( CP_UTF8, 0, utf16, utf16.GetLength(), nullptr, 0, nullptr, nullptr );\n\t\t\tchar* s = utf8.GetBufferSetLength( count + 1 );\n\t\t\tcount = WideCharToMultiByte( CP_UTF8, 0, utf16, utf16.GetLength(), s, count, nullptr, nullptr );\n\t\t\tutf8.ReleaseBufferSetLength( count );\n\t\t\treturn utf8;\n\t\t}\n\t};\n\tthread_local Utf ts_utf;\n\tusing Whisper::eLoggerFlags;\n\n\tclass Logger : Whisper::sLoggerSetup\n\t{\n\t\tinline bool hasFlag( eLoggerFlags bit ) const\n\t\t{\n\t\t\treturn 0 != ( (uint8_t)flags & (uint8_t)bit );\n\t\t}\n\n\t\tbool useStdError() const\n\t\t{\n\t\t\treturn hasFlag( eLoggerFlags::UseStandardError );\n\t\t}\n\n\t\tstatic void writeStdError( Whisper::eLogLevel lvl, const char* message, int len )\n\t\t{\n\t\t\tconst wchar_t* w = ts_utf.upcast( message, len );\n\t\t\tif( nullptr != w )\n\t\t\t\tfwprintf( stderr, L\"%s\\n\", w );\n\t\t}\n\n\tpublic:\n\t\tLogger()\n\t\t{\n\t\t\tmemset( this, 0, sizeof( Logger ) );\n\t\t}\n\n\t\tbool willLog( Whisper::eLogLevel lvl ) const\n\t\t{\n\t\t\tif( (uint8_t)lvl > (uint8_t)level )\n\t\t\t\treturn false;\n\t\t\tif( useStdError() )\n\t\t\t\treturn true;\n\t\t\treturn nullptr != sink;\n\t\t}\n\n\t\tvoid message( Whisper::eLogLevel lvl, const char8_t* pszFormat, std::va_list va ) const\n\t\t{\n\t\t\tconst char* s = ts_utf.print( (const char*)pszFormat, va );\n\t\t\tauto pfn = sink;\n\t\t\tif( nullptr != pfn )\n\t\t\t\tpfn( context, lvl, s );\n\t\t\tif( useStdError() )\n\t\t\t\twriteStdError( lvl, s, ts_utf.utf8Length() );\n\t\t}\n\t\tvoid message( Whisper::eLogLevel lvl, const wchar_t* pszFormat, std::va_list va ) const\n\t\t{\n\t\t\tUtf& u = ts_utf;\n\t\t\tconst wchar_t* w = u.print( pszFormat, va );\n\t\t\tauto pfn = sink;\n\t\t\tif( nullptr != pfn )\n\t\t\t\tpfn( context, lvl, u.downcast() );\n\t\t\tif( useStdError() )\n\t\t\t\tfwprintf( stderr, L\"%s\\n\", w );\n\t\t}\n\t\tvoid message( Whisper::eLogLevel lvl, HRESULT hr, const char* pszFormat, std::va_list va ) const\n\t\t{\n\t\t\tif( hasFlag( eLoggerFlags::SkipFormatMessage ) )\n\t\t\t{\n\t\t\t\tmessage( lvl, (const char8_t*)pszFormat, va );\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tUtf& u = ts_utf;\n\t\t\tconst wchar_t* w = ts_utf.printError( hr, (const char*)pszFormat, va );\n\t\t\tauto pfn = sink;\n\t\t\tif( nullptr != pfn )\n\t\t\t\tpfn( context, lvl, u.downcast() );\n\t\t\tif( useStdError() )\n\t\t\t\tfwprintf( stderr, L\"%s\\n\", w );\n\t\t}\n\n\t\tvoid operator=( const sLoggerSetup& rsi )\n\t\t{\n\t\t\tsink = rsi.sink;\n\t\t\tcontext = rsi.context;\n\t\t\tlevel = rsi.level;\n\t\t\tflags = rsi.flags;\n\t\t}\n\t};\n\n\tstatic Logger s_logger;\n}\n\nbool willLogMessage( Whisper::eLogLevel lvl )\n{\n\treturn s_logger.willLog( lvl );\n}\n\nusing Whisper::eLogLevel;\n\n#define LOG_MESSAGE_IMPL( lvl )                \\\n\tif( !s_logger.willLog( lvl ) )             \\\n\t\treturn;                                \\\n\tstd::va_list args;                         \\\n\tva_start( args, pszFormat );               \\\n\ts_logger.message( lvl, pszFormat, args );  \\\n\tva_end( args );\n\nvoid logError( const char8_t* pszFormat, ... )\n{\n\tLOG_MESSAGE_IMPL( eLogLevel::Error );\n}\nvoid logError16( const wchar_t* pszFormat, ... )\n{\n\tLOG_MESSAGE_IMPL( eLogLevel::Error );\n}\nvoid logWarning( const char8_t* pszFormat, ... )\n{\n\tLOG_MESSAGE_IMPL( eLogLevel::Warning );\n}\nvoid logWarning16( const wchar_t* pszFormat, ... )\n{\n\tLOG_MESSAGE_IMPL( eLogLevel::Warning );\n}\nvoid logInfo( const char8_t* pszFormat, ... )\n{\n\tLOG_MESSAGE_IMPL( eLogLevel::Info );\n}\nvoid logInfo16( const wchar_t* pszFormat, ... )\n{\n\tLOG_MESSAGE_IMPL( eLogLevel::Info );\n}\nvoid logDebug( const char8_t* pszFormat, ... )\n{\n\tLOG_MESSAGE_IMPL( eLogLevel::Debug );\n}\nvoid logDebug16( const wchar_t* pszFormat, ... )\n{\n\tLOG_MESSAGE_IMPL( eLogLevel::Debug );\n}\n#undef LOG_MESSAGE_IMPL\n\n#define LOG_MESSAGE_IMPL( lvl )                \\\n\tif( !s_logger.willLog( lvl ) )             \\\n\t\treturn;                                \\\n\tstd::va_list args;                         \\\n\tva_start( args, pszFormat );               \\\n\ts_logger.message( lvl, hr, (const char*)pszFormat, args );  \\\n\tva_end( args );\n\nvoid logErrorHr( long hr, const char8_t* pszFormat, ... )\n{\n\tLOG_MESSAGE_IMPL( eLogLevel::Error );\n}\nvoid logWarningHr( long hr, const char8_t* pszFormat, ... )\n{\n\tLOG_MESSAGE_IMPL( eLogLevel::Warning );\n}\n\n#undef LOG_MESSAGE_IMPL\n\n// DLL entry point\nHRESULT COMLIGHTCALL Whisper::setupLogger( const sLoggerSetup& setup )\n{\n\ts_logger = setup;\n\treturn S_OK;\n}"
  },
  {
    "path": "Whisper/Utils/Logger.h",
    "content": "#pragma once\n#include \"../API/loggerApi.h\"\n\n#ifdef  __cplusplus\nextern \"C\" {\n#endif\n\nvoid logError( const char8_t* pszFormat, ... );\nvoid logError16( const wchar_t* pszFormat, ... );\nvoid logErrorHr( long hr, const char8_t* pszFormat, ... );\nvoid logWarning( const char8_t* pszFormat, ... );\nvoid logWarning16( const wchar_t* pszFormat, ... );\nvoid logWarningHr( long hr, const char8_t* pszFormat, ... );\nvoid logInfo( const char8_t* pszFormat, ... );\nvoid logInfo16( const wchar_t* pszFormat, ... );\nvoid logDebug( const char8_t* pszFormat, ... );\nvoid logDebug16( const wchar_t* pszFormat, ... );\n\nbool willLogMessage( Whisper::eLogLevel lvl );\n\n#ifdef  __cplusplus\n}\n#endif\n"
  },
  {
    "path": "Whisper/Utils/MurmurHash3.cpp",
    "content": "// https://github.com/aappleby/smhasher/blob/master/src/MurmurHash3.cpp\n#include <stdafx.h>\n//-----------------------------------------------------------------------------\n// MurmurHash3 was written by Austin Appleby, and is placed in the public\n// domain. The author hereby disclaims copyright to this source code.\n\n// Note - The x86 and x64 versions do _not_ produce the same results, as the\n// algorithms are optimized for their respective platforms. You can still\n// compile and run any of them on any platform, but your performance with the\n// non-native version will be less than optimal.\n#include \"MurmurHash3.h\"\n\n//-----------------------------------------------------------------------------\n// Platform-specific functions and macros\n// Microsoft Visual Studio\n\n#if defined(_MSC_VER)\n\n#define FORCE_INLINE\t__forceinline\n\n#include <stdlib.h>\n\n#define ROTL32(x,y)\t_rotl(x,y)\n#define ROTL64(x,y)\t_rotl64(x,y)\n\n#define BIG_CONSTANT(x) (x)\n\n// Other compilers\n\n#else\t// defined(_MSC_VER)\n\n#define\tFORCE_INLINE inline __attribute__((always_inline))\n\ninline uint32_t rotl32( uint32_t x, int8_t r )\n{\n\treturn ( x << r ) | ( x >> ( 32 - r ) );\n}\n\ninline uint64_t rotl64( uint64_t x, int8_t r )\n{\n\treturn ( x << r ) | ( x >> ( 64 - r ) );\n}\n\n#define\tROTL32(x,y)\trotl32(x,y)\n#define ROTL64(x,y)\trotl64(x,y)\n\n#define BIG_CONSTANT(x) (x##LLU)\n\n#endif // !defined(_MSC_VER)\n\n//-----------------------------------------------------------------------------\n// Block read - if your platform needs to do endian-swapping or can only\n// handle aligned reads, do the conversion here\n\nFORCE_INLINE uint32_t getblock32( const uint32_t* p, int i )\n{\n\treturn p[ i ];\n}\n\nFORCE_INLINE uint64_t getblock64( const uint64_t* p, int i )\n{\n\treturn p[ i ];\n}\n\n//-----------------------------------------------------------------------------\n// Finalization mix - force all bits of a hash block to avalanche\n\nFORCE_INLINE uint32_t fmix32( uint32_t h )\n{\n\th ^= h >> 16;\n\th *= 0x85ebca6b;\n\th ^= h >> 13;\n\th *= 0xc2b2ae35;\n\th ^= h >> 16;\n\n\treturn h;\n}\n\n//----------\n\nFORCE_INLINE uint64_t fmix64( uint64_t k )\n{\n\tk ^= k >> 33;\n\tk *= BIG_CONSTANT( 0xff51afd7ed558ccd );\n\tk ^= k >> 33;\n\tk *= BIG_CONSTANT( 0xc4ceb9fe1a85ec53 );\n\tk ^= k >> 33;\n\n\treturn k;\n}\n\n//-----------------------------------------------------------------------------\n\nvoid MurmurHash3_x86_32( const void* key, int len,\n\tuint32_t seed, void* out )\n{\n\tconst uint8_t* data = (const uint8_t*)key;\n\tconst int nblocks = len / 4;\n\n\tuint32_t h1 = seed;\n\n\tconst uint32_t c1 = 0xcc9e2d51;\n\tconst uint32_t c2 = 0x1b873593;\n\n\t//----------\n\t// body\n\n\tconst uint32_t* blocks = (const uint32_t*)( data + nblocks * 4 );\n\n\tfor( int i = -nblocks; i; i++ )\n\t{\n\t\tuint32_t k1 = getblock32( blocks, i );\n\n\t\tk1 *= c1;\n\t\tk1 = ROTL32( k1, 15 );\n\t\tk1 *= c2;\n\n\t\th1 ^= k1;\n\t\th1 = ROTL32( h1, 13 );\n\t\th1 = h1 * 5 + 0xe6546b64;\n\t}\n\n\t//----------\n\t// tail\n\n\tconst uint8_t* tail = (const uint8_t*)( data + nblocks * 4 );\n\n\tuint32_t k1 = 0;\n\n\tswitch( len & 3 )\n\t{\n\tcase 3: k1 ^= tail[ 2 ] << 16;\n\tcase 2: k1 ^= tail[ 1 ] << 8;\n\tcase 1: k1 ^= tail[ 0 ];\n\t\tk1 *= c1; k1 = ROTL32( k1, 15 ); k1 *= c2; h1 ^= k1;\n\t};\n\n\t//----------\n\t// finalization\n\n\th1 ^= len;\n\n\th1 = fmix32( h1 );\n\n\t*(uint32_t*)out = h1;\n}\n\n//-----------------------------------------------------------------------------\n\nvoid MurmurHash3_x86_128( const void* key, const int len,\n\tuint32_t seed, void* out )\n{\n\tconst uint8_t* data = (const uint8_t*)key;\n\tconst int nblocks = len / 16;\n\n\tuint32_t h1 = seed;\n\tuint32_t h2 = seed;\n\tuint32_t h3 = seed;\n\tuint32_t h4 = seed;\n\n\tconst uint32_t c1 = 0x239b961b;\n\tconst uint32_t c2 = 0xab0e9789;\n\tconst uint32_t c3 = 0x38b34ae5;\n\tconst uint32_t c4 = 0xa1e38b93;\n\n\t//----------\n\t// body\n\n\tconst uint32_t* blocks = (const uint32_t*)( data + nblocks * 16 );\n\n\tfor( int i = -nblocks; i; i++ )\n\t{\n\t\tuint32_t k1 = getblock32( blocks, i * 4 + 0 );\n\t\tuint32_t k2 = getblock32( blocks, i * 4 + 1 );\n\t\tuint32_t k3 = getblock32( blocks, i * 4 + 2 );\n\t\tuint32_t k4 = getblock32( blocks, i * 4 + 3 );\n\n\t\tk1 *= c1; k1 = ROTL32( k1, 15 ); k1 *= c2; h1 ^= k1;\n\n\t\th1 = ROTL32( h1, 19 ); h1 += h2; h1 = h1 * 5 + 0x561ccd1b;\n\n\t\tk2 *= c2; k2 = ROTL32( k2, 16 ); k2 *= c3; h2 ^= k2;\n\n\t\th2 = ROTL32( h2, 17 ); h2 += h3; h2 = h2 * 5 + 0x0bcaa747;\n\n\t\tk3 *= c3; k3 = ROTL32( k3, 17 ); k3 *= c4; h3 ^= k3;\n\n\t\th3 = ROTL32( h3, 15 ); h3 += h4; h3 = h3 * 5 + 0x96cd1c35;\n\n\t\tk4 *= c4; k4 = ROTL32( k4, 18 ); k4 *= c1; h4 ^= k4;\n\n\t\th4 = ROTL32( h4, 13 ); h4 += h1; h4 = h4 * 5 + 0x32ac3b17;\n\t}\n\n\t//----------\n\t// tail\n\n\tconst uint8_t* tail = (const uint8_t*)( data + nblocks * 16 );\n\n\tuint32_t k1 = 0;\n\tuint32_t k2 = 0;\n\tuint32_t k3 = 0;\n\tuint32_t k4 = 0;\n\n\tswitch( len & 15 )\n\t{\n\tcase 15: k4 ^= tail[ 14 ] << 16;\n\tcase 14: k4 ^= tail[ 13 ] << 8;\n\tcase 13: k4 ^= tail[ 12 ] << 0;\n\t\tk4 *= c4; k4 = ROTL32( k4, 18 ); k4 *= c1; h4 ^= k4;\n\n\tcase 12: k3 ^= tail[ 11 ] << 24;\n\tcase 11: k3 ^= tail[ 10 ] << 16;\n\tcase 10: k3 ^= tail[ 9 ] << 8;\n\tcase  9: k3 ^= tail[ 8 ] << 0;\n\t\tk3 *= c3; k3 = ROTL32( k3, 17 ); k3 *= c4; h3 ^= k3;\n\n\tcase  8: k2 ^= tail[ 7 ] << 24;\n\tcase  7: k2 ^= tail[ 6 ] << 16;\n\tcase  6: k2 ^= tail[ 5 ] << 8;\n\tcase  5: k2 ^= tail[ 4 ] << 0;\n\t\tk2 *= c2; k2 = ROTL32( k2, 16 ); k2 *= c3; h2 ^= k2;\n\n\tcase  4: k1 ^= tail[ 3 ] << 24;\n\tcase  3: k1 ^= tail[ 2 ] << 16;\n\tcase  2: k1 ^= tail[ 1 ] << 8;\n\tcase  1: k1 ^= tail[ 0 ] << 0;\n\t\tk1 *= c1; k1 = ROTL32( k1, 15 ); k1 *= c2; h1 ^= k1;\n\t};\n\n\t//----------\n\t// finalization\n\n\th1 ^= len; h2 ^= len; h3 ^= len; h4 ^= len;\n\n\th1 += h2; h1 += h3; h1 += h4;\n\th2 += h1; h3 += h1; h4 += h1;\n\n\th1 = fmix32( h1 );\n\th2 = fmix32( h2 );\n\th3 = fmix32( h3 );\n\th4 = fmix32( h4 );\n\n\th1 += h2; h1 += h3; h1 += h4;\n\th2 += h1; h3 += h1; h4 += h1;\n\n\t( (uint32_t*)out )[ 0 ] = h1;\n\t( (uint32_t*)out )[ 1 ] = h2;\n\t( (uint32_t*)out )[ 2 ] = h3;\n\t( (uint32_t*)out )[ 3 ] = h4;\n}\n\n//-----------------------------------------------------------------------------\n\nvoid MurmurHash3_x64_128( const void* key, const int len,\n\tconst uint32_t seed, void* out )\n{\n\tconst uint8_t* data = (const uint8_t*)key;\n\tconst int nblocks = len / 16;\n\n\tuint64_t h1 = seed;\n\tuint64_t h2 = seed;\n\n\tconst uint64_t c1 = BIG_CONSTANT( 0x87c37b91114253d5 );\n\tconst uint64_t c2 = BIG_CONSTANT( 0x4cf5ad432745937f );\n\n\t//----------\n\t// body\n\n\tconst uint64_t* blocks = (const uint64_t*)( data );\n\n\tfor( int i = 0; i < nblocks; i++ )\n\t{\n\t\tuint64_t k1 = getblock64( blocks, i * 2 + 0 );\n\t\tuint64_t k2 = getblock64( blocks, i * 2 + 1 );\n\n\t\tk1 *= c1; k1 = ROTL64( k1, 31 ); k1 *= c2; h1 ^= k1;\n\n\t\th1 = ROTL64( h1, 27 ); h1 += h2; h1 = h1 * 5 + 0x52dce729;\n\n\t\tk2 *= c2; k2 = ROTL64( k2, 33 ); k2 *= c1; h2 ^= k2;\n\n\t\th2 = ROTL64( h2, 31 ); h2 += h1; h2 = h2 * 5 + 0x38495ab5;\n\t}\n\n\t//----------\n\t// tail\n\n\tconst uint8_t* tail = (const uint8_t*)( data + nblocks * 16 );\n\n\tuint64_t k1 = 0;\n\tuint64_t k2 = 0;\n\n\tswitch( len & 15 )\n\t{\n\tcase 15: k2 ^= ( (uint64_t)tail[ 14 ] ) << 48;\n\tcase 14: k2 ^= ( (uint64_t)tail[ 13 ] ) << 40;\n\tcase 13: k2 ^= ( (uint64_t)tail[ 12 ] ) << 32;\n\tcase 12: k2 ^= ( (uint64_t)tail[ 11 ] ) << 24;\n\tcase 11: k2 ^= ( (uint64_t)tail[ 10 ] ) << 16;\n\tcase 10: k2 ^= ( (uint64_t)tail[ 9 ] ) << 8;\n\tcase  9: k2 ^= ( (uint64_t)tail[ 8 ] ) << 0;\n\t\tk2 *= c2; k2 = ROTL64( k2, 33 ); k2 *= c1; h2 ^= k2;\n\n\tcase  8: k1 ^= ( (uint64_t)tail[ 7 ] ) << 56;\n\tcase  7: k1 ^= ( (uint64_t)tail[ 6 ] ) << 48;\n\tcase  6: k1 ^= ( (uint64_t)tail[ 5 ] ) << 40;\n\tcase  5: k1 ^= ( (uint64_t)tail[ 4 ] ) << 32;\n\tcase  4: k1 ^= ( (uint64_t)tail[ 3 ] ) << 24;\n\tcase  3: k1 ^= ( (uint64_t)tail[ 2 ] ) << 16;\n\tcase  2: k1 ^= ( (uint64_t)tail[ 1 ] ) << 8;\n\tcase  1: k1 ^= ( (uint64_t)tail[ 0 ] ) << 0;\n\t\tk1 *= c1; k1 = ROTL64( k1, 31 ); k1 *= c2; h1 ^= k1;\n\t};\n\n\t//----------\n\t// finalization\n\n\th1 ^= len; h2 ^= len;\n\n\th1 += h2;\n\th2 += h1;\n\n\th1 = fmix64( h1 );\n\th2 = fmix64( h2 );\n\n\th1 += h2;\n\th2 += h1;\n\n\t( (uint64_t*)out )[ 0 ] = h1;\n\t( (uint64_t*)out )[ 1 ] = h2;\n}\n\n//-----------------------------------------------------------------------------\n"
  },
  {
    "path": "Whisper/Utils/MurmurHash3.h",
    "content": "#pragma once\n#include <stdint.h>\n\nvoid MurmurHash3_x86_32( const void* key, int len, uint32_t seed, void* out );\nvoid MurmurHash3_x86_128( const void* key, int len, uint32_t seed, void* out );\nvoid MurmurHash3_x64_128( const void* key, int len, uint32_t seed, void* out );\n\n#include <atlcoll.h>\n\n// Traits class for `CAtlMap<const char*>` which does not copy nor owns these strings\nstruct StringPtrTraits : public ATL::CDefaultElementTraits<const char*>\n{\n\tusing INARGTYPE = const char*;\n\n\tstatic inline bool CompareElements( const char* a, const char* b )\n\t{\n\t\treturn 0 == strcmp( a, b );\n\t}\n\n\tstatic inline int CompareElementsOrdered( const char* a, const char* b )\n\t{\n\t\treturn strcmp( a, b );\n\t}\n\n\tstatic inline ULONG Hash( const char* ptr )\n\t{\n\t\tuint32_t hash = UINT_MAX;\n\t\tif( nullptr != ptr )\n\t\t{\n\t\t\tconst int len = (int)strlen( ptr );\n\t\t\tconstexpr uint32_t seed = 0;\n\t\t\tMurmurHash3_x86_32( ptr, len, seed, &hash );\n\t\t}\n\t\treturn hash;\n\t}\n};"
  },
  {
    "path": "Whisper/Utils/ProfileCollection.cpp",
    "content": "#include \"stdafx.h\"\n#include \"ProfileCollection.h\"\n#include \"GpuProfiler.h\"\n#include \"../Whisper/WhisperModel.h\"\n#include \"../D3D/shaderNames.h\"\nusing namespace Whisper;\n\nProfileCollection::Measure& ProfileCollection::measure( DirectCompute::eProfilerBlock which )\n{\n\tuint32_t key = (uint16_t)which;\n\tkey |= 0x20000;\n\treturn measures[ key ];\n}\n\nProfileCollection::Measure& ProfileCollection::measure( DirectCompute::eComputeShader which )\n{\n\tuint32_t key = (uint16_t)which;\n\tkey |= 0x30000;\n\treturn measures[ key ];\n}\n\nProfileCollection::Measure& ProfileCollection::measure( eCpuBlock which )\n{\n\tuint32_t key = (uint8_t)which;\n\tkey |= 0x10000;\n\tCComCritSecLock<CComAutoCriticalSection> lock{ critSec };\n\treturn measures[ key ];\n}\n\n#if PROFILER_COLLECT_TAGS\nProfileCollection::Measure& ProfileCollection::measure( DirectCompute::eComputeShader which, uint16_t tag )\n{\n\tuint32_t key = (uint8_t)which;\n\tkey = key << 16;\n\tkey |= tag;\n\tCComCritSecLock<CComAutoCriticalSection> lock{ critSec };\n\treturn taggedShaders[ key ];\n}\n#endif\n\nnamespace\n{\n\tusing pfnPrintEnum = const char* ( * )( uint16_t val );\n\n\tstatic const char* printCpuBlock( uint16_t id )\n\t{\n\t\tconst eCpuBlock which = (eCpuBlock)id;\n\t\tswitch( which )\n\t\t{\n#define V(x) case eCpuBlock::x: return #x\n\t\t\tV( LoadModel );\n\t\t\tV( RunComplete );\n\t\t\tV( Run );\n\t\t\tV( Callbacks );\n\t\t\tV( Spectrogram );\n\t\t\tV( Sample );\n\t\t\tV( VAD );\n\t\t\tV( Encode );\n\t\t\tV( Decode );\n\t\t\tV( DecodeStep );\n\t\t\tV( DecodeLayer );\n#undef V\n\t\t}\n\t\tassert( false );\n\t\treturn nullptr;\n\t}\n\n\tstatic const char* printGpuBlock( uint16_t id )\n\t{\n\t\tusing DirectCompute::eProfilerBlock;\n\t\tconst eProfilerBlock which = (eProfilerBlock)id;\n\n\t\tswitch( which )\n\t\t{\n#define V(x) case eProfilerBlock::x: return #x\n\t\t\tV( LoadModel );\n\t\t\tV( Run );\n\t\t\tV( Encode );\n\t\t\tV( EncodeLayer );\n\t\t\tV( Decode );\n\t\t\tV( DecodeStep );\n\t\t\tV( DecodeLayer );\n#undef V\n\t\t}\n\t\tassert( false );\n\t\treturn nullptr;\n\t}\n\n\tstatic const char* printShader( uint16_t id )\n\t{\n\t\treturn DirectCompute::computeShaderName( (DirectCompute::eComputeShader)id );\n\t}\n\n\tstatic pfnPrintEnum printSectionStart( uint16_t type )\n\t{\n\t\tswitch( type )\n\t\t{\n\t\tcase 1:\n\t\t\tlogInfo( u8\"    CPU Tasks\" );\n\t\t\treturn &printCpuBlock;\n\t\tcase 2:\n\t\t\tlogInfo( u8\"    GPU Tasks\" );\n\t\t\treturn &printGpuBlock;\n\t\tcase 3:\n\t\t\tlogInfo( u8\"    Compute Shaders\" );\n\t\t\treturn &printShader;\n\t\tdefault:\n\t\t\treturn nullptr;\n\t\t}\n\t}\n\n\tstruct PrintedTime\n\t{\n\t\tdouble value;\n\t\tconst char* unit;\n\n\t\tPrintedTime( uint64_t ticks )\n\t\t{\n\t\t\tconst double dbl = (double)(int64_t)ticks;\n\t\t\tif( ticks >= 10'000'000 )\n\t\t\t{\n\t\t\t\tvalue = dbl / 1.0E+7;\n\t\t\t\tunit = \"seconds\";\n\t\t\t}\n\t\t\telse if( ticks >= 10'000 )\n\t\t\t{\n\t\t\t\tvalue = dbl / 1.0E+4;\n\t\t\t\tunit = \"milliseconds\";\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tvalue = dbl / 1.0E+1;\n\t\t\t\tunit = \"microseconds\";\n\t\t\t}\n\t\t}\n\t\tPrintedTime( double dbl )\n\t\t{\n\t\t\tif( dbl >= 10'000'000 )\n\t\t\t{\n\t\t\t\tvalue = dbl / 1.0E+7;\n\t\t\t\tunit = \"seconds\";\n\t\t\t}\n\t\t\telse if( dbl >= 10'000 )\n\t\t\t{\n\t\t\t\tvalue = dbl / 1.0E+4;\n\t\t\t\tunit = \"milliseconds\";\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tvalue = dbl / 1.0E+1;\n\t\t\t\tunit = \"microseconds\";\n\t\t\t}\n\t\t}\n\t};\n}\n\nvoid ProfileCollection::Measure::print( const char* name ) const\n{\n\tPrintedTime total{ totalTicks };\n\tif( 1 == count )\n\t\tlogInfo( u8\"%s\\t%g %s\", name, total.value, total.unit );\n\telse\n\t{\n\t\tPrintedTime avg = (double)totalTicks / (double)(int64_t)count;\n\t\tlogInfo( u8\"%s\\t%g %s, %zu calls, %g %s average\", name, total.value, total.unit, count, avg.value, avg.unit );\n\t}\n}\n\n#if PROFILER_COLLECT_TAGS\nstruct TaggedShaderCmp\n{\n\tbool operator()( uint16_t cs, uint32_t key ) const\n\t{\n\t\treturn cs < key >> 16;\n\t}\n\tbool operator()( uint32_t key, uint16_t cs ) const\n\t{\n\t\treturn key >> 16 < cs;\n\t}\n};\n\nvoid ProfileCollection::TaggedTemp::print() const\n{\n\tPrintedTime total{ ticks };\n\tif( 1 == count )\n\t\tlogInfo( u8\"  %s\\t%g %s\", name, total.value, total.unit );\n\telse\n\t{\n\t\tPrintedTime avg = (double)ticks / (double)(int64_t)count;\n\t\tlogInfo( u8\"  %s\\t%g %s, %zu calls, %g %s average\", name, total.value, total.unit, count, avg.value, avg.unit );\n\t}\n}\n#endif\n\nvoid ProfileCollection::print()\n{\n\tkeysTemp.clear();\n\tfor( POSITION pos = measures.GetStartPosition(); nullptr != pos; )\n\t{\n\t\tauto* p = measures.GetNext( pos );\n\t\tif( p->m_value.count == 0 )\n\t\t\tcontinue;\n\t\tkeysTemp.push_back( p->m_key );\n\t}\n\n\tstd::sort( keysTemp.begin(), keysTemp.end() );\n\tauto it = std::lower_bound( keysTemp.begin(), keysTemp.end(), 0x30000u );\n\tif( it != keysTemp.end() )\n\t{\n\t\tauto lambda = [ this ]( uint32_t a, uint32_t b )\n\t\t{\n\t\t\tconst uint64_t ta = measures.Lookup( a )->m_value.totalTicks;\n\t\t\tconst uint64_t tb = measures.Lookup( b )->m_value.totalTicks;\n\t\t\treturn ta > tb;\n\t\t};\n\t\tstd::stable_sort( it, keysTemp.end(), lambda );\n\t}\n\n#if PROFILER_COLLECT_TAGS\n\ttaggedKeysTemp.clear();\n\tfor( POSITION pos = taggedShaders.GetStartPosition(); nullptr != pos; )\n\t{\n\t\tauto* p = taggedShaders.GetNext( pos );\n\t\tif( p->m_value.count == 0 )\n\t\t\tcontinue;\n\t\ttaggedKeysTemp.push_back( p->m_key );\n\t}\n\tstd::sort( taggedKeysTemp.begin(), taggedKeysTemp.end() );\n#endif\n\n\tuint16_t prevKeyType = 0;\n\tpfnPrintEnum pfn = nullptr;\n\tfor( uint32_t k : keysTemp )\n\t{\n\t\tconst uint16_t type = (uint16_t)( k >> 16 );\n\t\tif( type != prevKeyType )\n\t\t{\n\t\t\tprevKeyType = type;\n\t\t\tpfn = printSectionStart( type );\n\t\t}\n\t\tif( pfn == nullptr )\n\t\t\tcontinue;\n\t\tconst auto* p = measures.Lookup( k );\n\t\tassert( nullptr != p );\n\t\tp->m_value.print( pfn( (uint16_t)k ) );\n\n#if PROFILER_COLLECT_TAGS\n\t\tif( type == 3 )\t\n\t\t{\n\t\t\t// Compute shader\n\t\t\tauto range = std::equal_range( taggedKeysTemp.begin(), taggedKeysTemp.end(), (uint16_t)k, TaggedShaderCmp{} );\n\t\t\tif( range.first != range.second )\n\t\t\t{\n\t\t\t\t// We have at least 1 tag for that compute shader\n\t\t\t\ttaggedTimes.clear();\n\t\t\t\tuint64_t totalTicks = 0;\n\t\t\t\tsize_t totalCount = 0;\n\t\t\t\tfor( auto it = range.first; it != range.second; it++ )\n\t\t\t\t{\n\t\t\t\t\tconst uint32_t key = *it;\n\t\t\t\t\tconst uint16_t tagId = (uint16_t)key;\n\t\t\t\t\tassert( 0 != tagId );\n\t\t\t\t\tconst auto* p = taggedShaders.Lookup( key );\n\t\t\t\t\tassert( nullptr != p );\n\n\t\t\t\t\tauto& rdi = taggedTimes.emplace_back();\n\t\t\t\t\trdi.ticks = p->m_value.totalTicks;\n\t\t\t\t\ttotalTicks += p->m_value.totalTicks;\n\n\t\t\t\t\trdi.count = p->m_value.count;\n\t\t\t\t\ttotalCount += p->m_value.count;\n\n\t\t\t\t\trdi.name = tagNames[ tagId ];\n\t\t\t\t}\n\n\t\t\t\tassert( totalCount <= p->m_value.count );\n\t\t\t\tif( totalCount < p->m_value.count )\n\t\t\t\t{\n\t\t\t\t\tauto& rdi = taggedTimes.emplace_back();\n\t\t\t\t\trdi.ticks = p->m_value.totalTicks - totalTicks;\n\t\t\t\t\trdi.count = p->m_value.count - totalCount;\n\t\t\t\t\trdi.name = tagNames[ 0 ];\n\t\t\t\t}\n\t\t\t\tstd::stable_sort( taggedTimes.begin(), taggedTimes.end() );\n\t\t\t\tfor( const auto& e : taggedTimes )\n\t\t\t\t\te.print();\n\t\t\t}\n\t\t}\n#endif\n\t}\n}\n\nvoid ProfileCollection::reset()\n{\n\tfor( POSITION pos = measures.GetStartPosition(); nullptr != pos; )\n\t\tmeasures.GetNextValue( pos ).reset();\n}\n\nProfileCollection::ProfileCollection( const WhisperModel& model )\n{\n\tconst __m128i vals = model.getLoadTimes();\n\n\tuint64_t s = (uint64_t)_mm_cvtsi128_si64( vals );\n\tmeasure( eCpuBlock::LoadModel ).add( s );\n\n\ts = (uint64_t)_mm_extract_epi64( vals, 1 );\n\tmeasure( DirectCompute::eProfilerBlock::LoadModel ).add( s );\n#if PROFILER_COLLECT_TAGS\n\t// Tag ID 0 means no tag at all. makeTagId() method returns 0 for nullptr name, and starts numbering with 1 for non-empoty tag names\n\t// Push the tag name corresponding to ID = 0, this way we can index directly with tag IDs.\n\ttagNames.push_back( \"<untagged>\" );\n#endif\n}\n\nuint16_t ProfileCollection::makeTagId( const char* tag )\n{\n#if PROFILER_COLLECT_TAGS\n\tif( nullptr == tag )\n\t\treturn 0;\n\tauto p = tagIDs.Lookup( tag );\n\tif( nullptr != p )\n\t\treturn p->m_value;\n\tconst size_t newTag = tagIDs.GetCount() + 1;\n\tif( newTag <= 0xFFFF )\n\t{\n\t\ttagIDs.SetAt( tag, (uint16_t)newTag );\n\t\ttagNames.push_back( tag );\n\t\treturn (uint16_t)newTag;\n\t}\n\tthrow DISP_E_OVERFLOW;\n#else\n\treturn 0;\n#endif\n}"
  },
  {
    "path": "Whisper/Utils/ProfileCollection.h",
    "content": "#pragma once\n#include <atlcoll.h>\n#include \"CpuProfiler.h\"\n\nnamespace DirectCompute\n{\n\tenum struct eComputeShader : uint16_t;\n\tenum struct eProfilerBlock : uint16_t;\n}\n\nnamespace Whisper\n{\n\tstruct WhisperModel;\n\n\tenum struct eCpuBlock : uint8_t\n\t{\n\t\tLoadModel,\n\t\tRunComplete,\n\t\tRun,\n\t\tCallbacks,\n\t\tSpectrogram,\n\t\tSample,\n\t\tVAD,\n\t\tEncode,\n\t\tDecode,\n\t\tDecodeStep,\n\t\tDecodeLayer,\n\t};\n\n\tclass ProfileCollection\n\t{\n\tpublic:\n\t\tProfileCollection( const WhisperModel& model );\n\n\t\tstruct Measure\n\t\t{\n\t\t\tsize_t count = 0;\n\t\t\t// 100-nanosecond ticks\n\t\t\tuint64_t totalTicks = 0;\n\n\t\t\tvoid reset()\n\t\t\t{\n\t\t\t\tcount = 0;\n\t\t\t\ttotalTicks = 0;\n\t\t\t}\n\n\t\t\tvoid print( const char* name ) const;\n\n\t\t\tvoid add( uint64_t val )\n\t\t\t{\n\t\t\t\tcount++;\n\t\t\t\ttotalTicks += val;\n\t\t\t}\n\t\t};\n\n\t\tMeasure& measure( DirectCompute::eProfilerBlock which );\n\t\tMeasure& measure( DirectCompute::eComputeShader which );\n\t\tMeasure& measure( eCpuBlock which );\n#if PROFILER_COLLECT_TAGS\n\t\tMeasure& measure( DirectCompute::eComputeShader which, uint16_t tag );\n#endif\n\t\tvoid print();\n\n\t\tvoid reset();\n\n\t\tclass CpuRaii\n\t\t{\n\t\t\tMeasure* dest;\n\t\t\tconst int64_t tsc;\n\n\t\tpublic:\n\t\t\tCpuRaii( Measure& m ) : dest( &m ), tsc( tscNow() )\n\t\t\t{ }\n\t\t\tCpuRaii( const CpuRaii& ) = delete;\n\t\t\tCpuRaii( CpuRaii&& that ) noexcept :\n\t\t\t\ttsc( that.tsc )\n\t\t\t{\n\t\t\t\tdest = that.dest;\n\t\t\t\tthat.dest = nullptr;\n\t\t\t}\n\n\t\t\t~CpuRaii()\n\t\t\t{\n\t\t\t\tif( nullptr != dest )\n\t\t\t\t{\n\t\t\t\t\tconst int64_t elapsed = tscNow() - tsc;\n\t\t\t\t\tdest->add( ticksFromTsc( elapsed ) );\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\n\t\tdecltype( auto ) cpuBlock( eCpuBlock which )\n\t\t{\n\t\t\treturn CpuRaii{ measure( which ) };\n\t\t}\n\n\t\tuint16_t makeTagId( const char* tag );\n\n\tprivate:\n\t\tCAtlMap<uint32_t, Measure> measures;\n\t\tCComAutoCriticalSection critSec;\n#if PROFILER_COLLECT_TAGS\n\t\tCAtlMap<const char*, uint16_t> tagIDs;\n\t\tstd::vector<const char*> tagNames;\n\t\tCAtlMap<uint32_t, Measure> taggedShaders;\n\t\tstd::vector<uint32_t> taggedKeysTemp;\n\t\tstruct TaggedTemp\n\t\t{\n\t\t\tuint64_t ticks;\n\t\t\tsize_t count;\n\t\t\tconst char* name;\n\n\t\t\tbool operator<( const TaggedTemp& that ) const\n\t\t\t{\n\t\t\t\t// Flipping the comparison to sort in descending order\n\t\t\t\treturn ticks > that.ticks;\n\t\t\t}\n\n\t\t\tvoid print() const;\n\t\t};\n\t\tstd::vector<TaggedTemp> taggedTimes;\n#endif\n\t\tstd::vector<uint32_t> keysTemp;\n\t};\n}"
  },
  {
    "path": "Whisper/Utils/ReadStream.h",
    "content": "#pragma once\n#include \"../ComLightLib/streams.h\"\n#include \"../ComLightLib/comLightServer.h\"\n#define WIN32_LEAN_AND_MEAN\n#include <atlfile.h>\n\nclass ReadStream : public ComLight::ObjectRoot<ComLight::iReadStream>\n{\n\tCAtlFile file;\n\t// TODO: implement a buffer in this class, at least 256kb\n\n\tHRESULT COMLIGHTCALL read( void* lpBuffer, int nNumberOfBytesToRead, int& lpNumberOfBytesRead ) override final\n\t{\n\t\treturn file.Read( lpBuffer, (DWORD)nNumberOfBytesToRead, *(DWORD*)&lpNumberOfBytesRead );\n\t}\n\tHRESULT COMLIGHTCALL seek( int64_t offset, ComLight::eSeekOrigin origin ) override final\n\t{\n\t\treturn file.Seek( offset, (uint8_t)origin );\n\t}\n\tHRESULT COMLIGHTCALL getPosition( int64_t& position ) override final\n\t{\n\t\treturn file.GetPosition( *(ULONGLONG*)&position );\n\t}\n\tHRESULT COMLIGHTCALL getLength( int64_t& length ) override final\n\t{\n\t\treturn file.GetSize( *(ULONGLONG*)&length );\n\t}\n\npublic:\n\n\tHRESULT open( const wchar_t* path )\n\t{\n\t\tif( file )\n\t\t\treturn HRESULT_CODE( ERROR_ALREADY_INITIALIZED );\n\t\treturn file.Create( path, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN );\n\t}\n};"
  },
  {
    "path": "Whisper/Utils/Trace/TraceStructures.cpp",
    "content": "#include \"stdafx.h\"\n#include \"TraceStructures.h\"\nusing namespace Tracing;\n\nuint64_t sTraceItem::buffer( uint64_t off, size_t length, eDataType type )\n{\n\tpayloadOffset = off;\n\tpayloadSize = length * DirectCompute::elementSize( type );\n\t*(uint64_t*)( &size[ 0 ] ) = length;\n\t*(uint64_t*)( &size[ 2 ] ) = 0;\n\t_mm_storeu_si128( ( __m128i* )stride.data(), _mm_setzero_si128() );\n\titemType = eItemType::Buffer;\n\tdataType = type;\n\treturn payloadSize;\n}\n\nuint64_t sTraceItem::tensor( uint64_t off, __m128i ne, __m128i nb, eDataType type )\n{\n\tpayloadOffset = off;\n\t_mm_storeu_si128( ( __m128i* )size.data(), ne );\n\t_mm_storeu_si128( ( __m128i* )stride.data(), nb );\n\tuint64_t count = 1;\n\tfor( uint32_t i : size )\n\t\tif( i != 0 )\n\t\t\tcount *= i;\n\n\tpayloadSize = count * DirectCompute::elementSize( type );\n\titemType = eItemType::Tensor;\n\tdataType = type;\n\treturn payloadSize;\n}"
  },
  {
    "path": "Whisper/Utils/Trace/TraceStructures.h",
    "content": "﻿#pragma once\n#include <array>\n#include <emmintrin.h>\n#include \"../../D3D/enums.h\"\n\nnamespace Tracing\n{\n\tusing DirectCompute::eDataType;\n\n\t// File header of the trace file\n\tstruct sFileHeader\n\t{\n\t\tstatic constexpr uint32_t correctMagic = 0xE6B4A12Du;\t// random.org\n\n\t\tuint32_t magic;\n\t\tuint8_t formatVersion;\n\t\tuint8_t zzPadding;\n\t\tuint16_t cbItem;\n\t\tuint32_t countItems;\n\t\tuint32_t zzPadding2;\n\t\tuint64_t bytesPayload;\n\t\tuint32_t countStrings, bytesStrings;\n\t};\n\t// Payload data starts immediately after the header, bytesPayload bytes in total.\n\t// Then `bytesStrings` with string names, first countStrings * 4 of them are offsets, then ( bytesStrings - countStrings * 4 ) bytes with the string data.\n\t// The strings in the file are null-terminated.\n\t// Immediately after the strings, the next `cbItem` * `countItems` bytes are actual items (tensors and vectors) saved in the trace.\n\t// The format is weird because optimized for streaming.\n\t// These traces can grow large, we can’t afford memory keeping the payload data in memory.\n\t// Metadata is tiny compared to payload, we accumulate that in memory, and write to the end of the file when closed.\n\n\tenum struct eItemType : uint8_t\n\t{\n\t\tBuffer = 1,\n\t\tTensor = 2,\n\t};\n\n\tstruct sTraceItem\n\t{\n\t\tuint64_t payloadOffset;\n\t\tuint64_t payloadSize;\n\t\tstd::array<uint32_t, 4> size;\n\t\tstd::array<uint32_t, 4> stride;\n\t\tstd::array<uint32_t, 4> formatArgs;\n\t\teItemType itemType;\n\t\teDataType dataType;\n\t\tuint8_t countFormatArgs = 0;\n\t\tuint8_t zzPadding = 0;\n\t\tuint32_t stringIndex;\n\n\t\tuint64_t buffer( uint64_t off, size_t length, eDataType type );\n\n\t\tuint64_t tensor( uint64_t off, __m128i ne, __m128i nb, eDataType type );\n\t};\n}"
  },
  {
    "path": "Whisper/Utils/Trace/TraceWriter.cpp",
    "content": "#include \"stdafx.h\"\n#include \"TraceWriter.h\"\n#include <atlfile.h>\n#include <atlcoll.h>\n#include <atlstr.h>\n#include \"TraceStructures.h\"\n#include \"../../ML/Tensor.h\"\n#include \"../../CPU/Tensor.h\"\n#include <Shlobj.h>\nusing namespace Tracing;\n\nnamespace\n{\n\tstatic HRESULT createDir( LPCTSTR pathFile )\n\t{\n\t\tLPCWSTR fn = PathFindFileName( pathFile );\n\t\tif( fn == pathFile )\n\t\t\treturn E_FAIL;\n\n\t\tconst int cc = (int)( fn - pathFile );\n\t\tCString dir{ pathFile, cc };\n\t\tif( PathIsDirectory( dir ) )\n\t\t\treturn S_OK;\n\t\tconst int status = SHCreateDirectoryEx( nullptr, dir, nullptr );\n\t\tif( 0 == status )\n\t\t\treturn S_OK;\n\t\treturn HRESULT_FROM_WIN32( status );\n\t}\n\n\tclass TraceFileWriter\n\t{\n\t\tCAtlFile file;\n\t\t// Concatenated strings, including the 0 terminators\n\t\tstd::vector<char> stringsData;\n\t\t// Index = string ID, value = start offset into stringsData\n\t\tstd::vector<uint32_t> stringsIndex;\n\t\t// Hash map to unduplicate these strings\n\t\tCAtlMap<CStringA, uint32_t> stringsHash;\n\n\t\tuint32_t addString( const CStringA& s )\n\t\t{\n\t\t\tauto p = stringsHash.Lookup( s );\n\t\t\tif( p != nullptr )\n\t\t\t\treturn p->m_value;\n\n\t\t\tconst uint32_t off = (uint32_t)stringsData.size();\n\t\t\tconst char* rsi = s;\n\t\t\tstringsData.insert( stringsData.end(), rsi, rsi + s.GetLength() + 1 );\n\t\t\tstringsIndex.push_back( off );\n\n\t\t\tconst uint32_t newId = (uint32_t)stringsHash.GetCount();\n\t\t\tstringsHash.SetAt( s, newId );\n\t\t\treturn newId;\n\t\t}\n\n\t\tvoid addString( sTraceItem& rdi, const ItemName& name )\n\t\t{\n\t\t\trdi.countFormatArgs = name.countArgs;\n\t\t\trdi.stringIndex = addString( name.pointer );\n\t\t\trdi.formatArgs = name.args;\n\t\t}\n\n\t\tstd::vector<sTraceItem> items;\n\t\tuint64_t offset = 0;\n\n\tpublic:\n\n\t\tHRESULT create( LPCTSTR path )\n\t\t{\n\t\t\tCHECK( createDir( path ) );\n\t\t\tCHECK( file.Create( path, GENERIC_WRITE, 0, CREATE_ALWAYS ) );\n\n\t\t\tconstexpr uint64_t cbHeader = sizeof( sFileHeader );\n\t\t\tCHECK( file.SetSize( cbHeader ) );\n\t\t\tCHECK( file.Seek( 0, SEEK_END ) );\n\t\t\toffset = 0;\n\n\t\t\treturn S_OK;\n\t\t}\n\n\t\tHRESULT buffer( const ItemName& name, const void* rsi, size_t length, eDataType dt )\n\t\t{\n\t\t\tsTraceItem& rdi = items.emplace_back();\n\t\t\tconst uint64_t cb = rdi.buffer( offset, length, dt );\n\t\t\taddString( rdi, name );\n\t\t\tassert( cb <= UINT_MAX );\n\t\t\tCHECK( file.Write( rsi, (DWORD)cb ) );\n\t\t\toffset += cb;\n\t\t\treturn S_OK;\n\t\t}\n\n\t\tHRESULT tensor( const ItemName& name, const void* rsi, __m128i size, __m128i strides, eDataType dt )\n\t\t{\n\t\t\tsTraceItem& rdi = items.emplace_back();\n\t\t\tconst uint64_t cb = rdi.tensor( offset, size, strides, dt );\n\t\t\taddString( rdi, name );\n\t\t\tassert( cb <= UINT_MAX );\n\t\t\tCHECK( file.Write( rsi, (DWORD)cb ) );\n\t\t\toffset += cb;\n\t\t\treturn S_OK;\n\t\t}\n\n\t\tHRESULT close()\n\t\t{\n\t\t\tif( !file )\n\t\t\t\treturn S_FALSE;\n\n\t\t\tconst uint32_t cbStringsData = (uint32_t)stringsData.size();\n\t\t\tconst uint32_t cbStringsIndex = (uint32_t)( stringsIndex.size() * 4 );\n\t\t\tif( !stringsIndex.empty() )\n\t\t\t\tCHECK( file.Write( stringsIndex.data(), cbStringsIndex ) );\n\t\t\tif( !stringsData.empty() )\n\t\t\t\tCHECK( file.Write( stringsData.data(), cbStringsData ) );\n\n\t\t\tconst uint32_t cbItems = (uint32_t)items.size() * (uint32_t)sizeof( sTraceItem );\n\t\t\tif( !items.empty() )\n\t\t\t\tCHECK( file.Write( items.data(), cbItems ) );\n\t\t\tCHECK( file.Seek( 0, FILE_BEGIN ) );\n\n\t\t\tsFileHeader header;\n\t\t\tmemset( &header, 0, sizeof( header ) );\n\t\t\theader.magic = header.correctMagic;\n\t\t\theader.cbItem = sizeof( sTraceItem );\n\t\t\theader.countItems = (uint32_t)items.size();\n\t\t\theader.bytesPayload = offset;\n\t\t\theader.countStrings = (uint32_t)stringsIndex.size();\n\t\t\theader.bytesStrings = cbStringsData + cbStringsIndex;\n\t\t\tCHECK( file.Write( &header, sizeof( header ) ) );\n\t\t\tCHECK( file.Flush() );\n\t\t\tfile.Close();\n\n\t\t\treturn S_OK;\n\t\t}\n\t};\n\n\tclass TraceWriter : public iTraceWriter\n\t{\n\t\tTraceFileWriter file;\n\n\t\tHRESULT buffer( const ItemName& name, const void* rsi, size_t length, eDataType dt ) override final\n\t\t{\n\t\t\treturn file.buffer( name, rsi, length, dt );\n\t\t}\n\n\t\tHRESULT tensor( const ItemName& name, const void* rsi, __m128i size, __m128i strides, eDataType dt ) override final\n\t\t{\n\t\t\treturn file.tensor( name, rsi, size, strides, dt );\n\t\t}\n\n\tpublic:\n\n\t\tTraceWriter( LPCTSTR path )\n\t\t{\n\t\t\tcheck( file.create( path ) );\n\t\t}\n\n\t\t~TraceWriter()\n\t\t{\n\t\t\tcheck( file.close() );\n\t\t}\n\t};\n}\n\nstd::unique_ptr<iTraceWriter> iTraceWriter::create( LPCTSTR path )\n{\n\treturn std::make_unique<TraceWriter>( path );\n}\n\nnamespace\n{\n\tstatic std::vector<float> tempFp32;\n\tstatic std::vector<uint16_t> tempFp16;\n\n\ttemplate<class E>\n\tinline const void* ptr( const std::vector<E>& vec )\n\t{\n\t\treturn vec.empty() ? nullptr : vec.data();\n\t}\n}\n\nHRESULT iTraceWriter::tensor( const ItemName& name, const DirectCompute::Tensor& source )\n{\n\tconst __m128i size = source.sizeVec();\n\tconst __m128i strides = source.stridesVec();\n\tconst eDataType dt = source.getType();\n\tif( dt == eDataType::FP32 )\n\t{\n\t\tsource.download( tempFp32 );\n\t\treturn tensor( name, ptr( tempFp32 ), size, strides, eDataType::FP32 );\n\t}\n\telse if( dt == eDataType::FP16 )\n\t{\n\t\tsource.download( tempFp16 );\n\t\treturn tensor( name, ptr( tempFp16 ), size, strides, eDataType::FP16 );\n\t}\n\treturn E_NOTIMPL;\n}\n\nHRESULT iTraceWriter::tensor( const ItemName& name, const CpuCompute::Tensor& source )\n{\n\tconst __m128i size = source.sizeVec();\n\tconst __m128i strides = source.stridesVec();\n\tconst eDataType dt = source.type();\n\n\tif( dt == eDataType::FP32 )\n\t\treturn tensor( name, source.fp32(), size, strides, eDataType::FP32 );\n\telse if( dt == eDataType::FP16 )\n\t\treturn tensor( name, source.fp16(), size, strides, eDataType::FP16 );\n\telse\n\t\treturn E_NOTIMPL;\n}\n\n#if BUILD_BOTH_VERSIONS\n#include \"../../source/ggml.h\"\nHRESULT __declspec( noinline ) iTraceWriter::tensor( const ItemName& name, const ggml_tensor& source )\n{\n\t__m128i size = load16( source.ne );\n\t__m128i strides = _mm_setr_epi32(\n\t\t(int)(uint32_t)source.nb[ 0 ],\n\t\t(int)(uint32_t)source.nb[ 1 ],\n\t\t(int)(uint32_t)source.nb[ 2 ],\n\t\t(int)(uint32_t)source.nb[ 3 ] );\n\n\tconst __m128i ones = _mm_set1_epi32( 1 );\n\tswitch( source.n_dims )\n\t{\n\tcase 0:\n\t\tsize = ones;\n\t\tbreak;\n\tcase 1:\n\t\tsize = _mm_blend_epi16( size, ones, 0b11111100 );\n\t\tbreak;\n\tcase 2:\n\t\tsize = _mm_blend_epi16( size, ones, 0b11110000 );\n\t\tbreak;\n\tcase 3:\n\t\tsize = _mm_blend_epi16( size, ones, 0b11000000 );\n\t\tbreak;\n\tcase 4:\n\t\tbreak;\n\tdefault:\n\t\treturn E_INVALIDARG;\n\t}\n\n\tconst ggml_type dt = source.type;\n\tswitch( dt )\n\t{\n\tcase GGML_TYPE_F16:\n\t\tstrides = _mm_srli_epi32( strides, 1 );\n\t\treturn tensor( name, source.data, size, strides, eDataType::FP16 );\n\tcase GGML_TYPE_F32:\n\t\tstrides = _mm_srli_epi32( strides, 2 );\n\t\treturn tensor( name, source.data, size, strides, eDataType::FP32 );\n\tdefault:\n\t\treturn E_NOTIMPL;\n}\n}\n#else\nHRESULT iTraceWriter::tensor( const ItemName& name, const ggml_tensor& source )\n{\n\treturn E_NOTIMPL;\n}\n#endif"
  },
  {
    "path": "Whisper/Utils/Trace/TraceWriter.h",
    "content": "#pragma once\n#include <memory>\n#include \"../../D3D/enums.h\"\n\nnamespace DirectCompute\n{\n\tclass Tensor;\n}\nnamespace CpuCompute\n{\n\tclass Tensor;\n}\n\nstruct ggml_tensor;\n\nnamespace Tracing\n{\n\tusing DirectCompute::eDataType;\n\n\tstruct ItemName\n\t{\n\t\tconst char* pointer;\n\t\tstd::array<uint32_t, 4> args;\n\t\tuint8_t countArgs;\n\n\t\tItemName( const char* str )\n\t\t{\n\t\t\tpointer = str;\n\t\t\t_mm_storeu_si128( ( __m128i* )args.data(), _mm_setzero_si128() );\n\t\t\tcountArgs = 0;\n\t\t}\n\t\tItemName( const char* str, int a0 )\n\t\t{\n\t\t\tpointer = str;\n\t\t\t__m128i v = _mm_cvtsi32_si128( a0 );\n\t\t\t_mm_storeu_si128( ( __m128i* )args.data(), v );\n\t\t\tcountArgs = 1;\n\t\t}\n\t\tItemName( const char* str, uint32_t a0 )\n\t\t{\n\t\t\tpointer = str;\n\t\t\t__m128i v = _mm_cvtsi32_si128( (int)a0 );\n\t\t\t_mm_storeu_si128( ( __m128i* )args.data(), v );\n\t\t\tcountArgs = 1;\n\t\t}\n\t\tItemName( const char* str, size_t a0 )\n\t\t{\n\t\t\tpointer = str;\n\t\t\t__m128i v = _mm_cvtsi32_si128( (int)a0 );\n\t\t\t_mm_storeu_si128( ( __m128i* )args.data(), v );\n\t\t\tcountArgs = 1;\n\t\t}\n\t};\n\n\tclass iTraceWriter\n\t{\n\tpublic:\n\t\tvirtual ~iTraceWriter() {}\n\n\t\tstatic std::unique_ptr<iTraceWriter> create( LPCTSTR path );\n\n\t\tvirtual HRESULT buffer( const ItemName& name, const void* rsi, size_t length, eDataType dt ) = 0;\n\n\t\tvirtual HRESULT tensor( const ItemName& name, const void* rsi, __m128i size, __m128i strides, eDataType dt ) = 0;\n\n\t\tHRESULT tensor( const ItemName& name, const DirectCompute::Tensor& tensor );\n\t\tHRESULT tensor( const ItemName& name, const CpuCompute::Tensor& tensor );\n\t\tHRESULT tensor( const ItemName& name, const ggml_tensor& tensor );\n\t};\n}"
  },
  {
    "path": "Whisper/Utils/Trace/tracing.cpp",
    "content": "#include \"stdafx.h\"\n#include \"tracing.h\"\n#include \"../../source/ggml.h\"\n#include \"../../ML/Tensor.h\"\n\nnamespace Tracing\n{\n#if SAVE_DEBUG_TRACE\n\tstd::unique_ptr<iTraceWriter> s_writer;\n\n\tstatic BOOL __stdcall consoleHandler( DWORD dwCtrlType )\n\t{\n\t\tif( dwCtrlType == CTRL_C_EVENT )\n\t\t\ts_writer = nullptr;\n\n\t\t// Return TRUE if handled this message, further handler functions won't be called.\n\t\t// Return FALSE to pass this message to further handlers until default handler calls ExitProcess().\n\t\treturn FALSE;\n\t}\n\n\tvoid traceCreate( LPCTSTR path )\n\t{\n\t\ts_writer = iTraceWriter::create( path );\n\t\tSetConsoleCtrlHandler( &consoleHandler, TRUE );\n\t}\n\n\tvoid traceClose()\n\t{\n\t\ts_writer = nullptr;\n\t}\n\n\tiTraceWriter* getWriter()\n\t{\n\t\treturn s_writer.get();\n\t}\n\n\tusing Pair = std::pair<ItemName, ggml_tensor>;\n\tstatic std::vector<Pair> delayed;\n\n\tvoid delayTensor( const ItemName& name, const ggml_tensor* tensor )\n\t{\n\t\tdelayed.emplace_back( name, *tensor );\n\t}\n\n\tHRESULT writeDelayedTensors()\n\t{\n\t\tif( delayed.empty() )\n\t\t\treturn S_FALSE;\n\t\tiTraceWriter* w = getWriter();\n\t\tif( nullptr == w )\n\t\t{\n\t\t\tdelayed.clear();\n\t\t\treturn S_FALSE;\n\t\t}\n\t\tfor( const Pair& p : delayed )\n\t\t\tw->tensor( p.first, p.second );\n\t\tdelayed.clear();\n\t\treturn S_OK;\n\t}\n#elif DBG_TEST_NAN\n\tHRESULT tensor( const ItemName& name, const DirectCompute::Tensor& tensor )\n\t{\n\t\tconst bool found = scanTensorForNaN( tensor, tensor.countElements() );\n\t\tif( !found )\n\t\t\treturn S_FALSE;\n\t\t__debugbreak();\n\t\treturn S_FALSE;\n\t}\n#endif\n}"
  },
  {
    "path": "Whisper/Utils/Trace/tracing.h",
    "content": "#pragma once\n#include \"TraceWriter.h\"\n#include \"../../ML/mlUtils.h\"\n\nnamespace Tracing\n{\n#if SAVE_DEBUG_TRACE\n\tvoid traceCreate( LPCTSTR path );\n\tvoid traceClose();\n\n\tiTraceWriter* getWriter();\n\n\tinline HRESULT tensor( const ItemName& name, const DirectCompute::Tensor& tensor )\n\t{\n\t\tiTraceWriter* w = getWriter();\n\t\tif( w )\n\t\t\treturn w->tensor( name, tensor );\n\t\treturn S_FALSE;\n\t}\n\tinline HRESULT tensor( const ItemName& name, const CpuCompute::Tensor& tensor )\n\t{\n\t\tiTraceWriter* w = getWriter();\n\t\tif( w )\n\t\t\treturn w->tensor( name, tensor );\n\t\treturn S_FALSE;\n\t}\n\n\tinline HRESULT tensor( const ItemName& name, const ggml_tensor* tensor )\n\t{\n\t\tiTraceWriter* w = getWriter();\n\t\tif( w )\n\t\t\treturn w->tensor( name, *tensor );\n\t\treturn S_FALSE;\n\t}\n\n\tvoid delayTensor( const ItemName& name, const ggml_tensor* tensor );\n\tHRESULT writeDelayedTensors();\n\n\tinline HRESULT buffer( const ItemName& name, const void* rsi, size_t length, eDataType dt )\n\t{\n\t\tiTraceWriter* w = getWriter();\n\t\tif( w )\n\t\t\treturn w->buffer( name, rsi, length, dt );\n\t\treturn S_FALSE;\n\t}\n\n\tinline HRESULT vector( const ItemName& name, const std::vector<float>& vec )\n\t{\n\t\tconst float* rsi = vec.empty() ? nullptr : vec.data();\n\t\treturn buffer( name, rsi, vec.size(), eDataType::FP32 );\n\t}\n\tinline HRESULT vector( const ItemName& name, const float* rsi, size_t length )\n\t{\n\t\treturn buffer( name, rsi, length, eDataType::FP32 );\n\t}\n#else\n\tinline void traceCreate( LPCTSTR path ) { }\n\tinline void traceClose() { }\n#if DBG_TEST_NAN\n\tHRESULT tensor( const ItemName& name, const DirectCompute::Tensor& tensor );\n#else\n\tinline HRESULT tensor( const ItemName& name, const DirectCompute::Tensor& tensor ) { return S_FALSE; }\n#endif\n\tinline HRESULT tensor( const ItemName& name, const CpuCompute::Tensor& tensor ) { return S_FALSE; }\n\tinline HRESULT tensor( const ItemName& name, const ggml_tensor* tensor ) { return S_FALSE; }\n\tinline HRESULT buffer( const ItemName& name, const void* rsi, size_t length, eDataType dt ) { return S_FALSE; }\n\tinline HRESULT vector( const ItemName& name, const std::vector<float>& vec ) { return S_FALSE; }\n\tinline void delayTensor( const ItemName& name, const ggml_tensor* tensor ) { }\n\tinline HRESULT writeDelayedTensors() { return S_FALSE; }\n\tinline HRESULT vector( const ItemName& name, const float* rsi, size_t length ) { }\n#endif\n}"
  },
  {
    "path": "Whisper/Utils/miscUtils.cpp",
    "content": "#include \"stdafx.h\"\n#include \"miscUtils.h\"\n#include <cmath>\n\nvoid setCurrentThreadName( const char* threadName )\n{\n\tconst DWORD dwThreadID = GetCurrentThreadId();\n\n\t// https://stackoverflow.com/a/10364541/126995\n#pragma pack(push,8)\n\ttypedef struct tagTHREADNAME_INFO\n\t{\n\t\tDWORD dwType;      // Must be 0x1000.\n\t\tLPCSTR szName;     // Pointer to name (in user addr space).\n\t\tDWORD dwThreadID;  // Thread ID (-1=caller thread).\n\t\tDWORD dwFlags;     // Reserved for future use, must be zero.\n\t} THREADNAME_INFO;\n#pragma pack(pop)\n\n\tTHREADNAME_INFO info;\n\tinfo.dwType = 0x1000;\n\tinfo.szName = threadName;\n\tinfo.dwThreadID = dwThreadID;\n\tinfo.dwFlags = 0;\n\n\tconstexpr DWORD MS_VC_EXCEPTION = 0x406D1388;\n\t__try\n\t{\n\t\tRaiseException( MS_VC_EXCEPTION, 0, sizeof( info ) / sizeof( ULONG_PTR ), (ULONG_PTR*)&info );\n\t}\n\t__except( EXCEPTION_EXECUTE_HANDLER )\n\t{\n\t}\n}\n\nfloat computeScaling( int mul, int div )\n{\n#ifdef _DEBUG\n\tconst float ref = (float)std::pow( (double)mul / (double)div, -0.25 );\n#endif\n\t// Make int32 vector with both numbers\n\t__m128i iv = _mm_cvtsi32_si128( mul );\n\tiv = _mm_insert_epi32( iv, div, 1 );\n\t// Convert both numbers to FP64\n\t__m128d v = _mm_cvtepi32_pd( iv );\n\t// Compute mul / div\n\tv = _mm_div_sd( v, _mm_unpackhi_pd( v, v ) );\n\t// Square root\n\tv = _mm_sqrt_sd( v, v );\n\t// 4-th root\n\tv = _mm_sqrt_sd( v, v );\n\t// Invert the value\n\tv = _mm_div_sd( _mm_set_sd( 1.0 ), v );\n\t// Downcast to FP32, and return the result\n\t__m128 f32 = _mm_cvtsd_ss( _mm_setzero_ps(), v );\n\treturn _mm_cvtss_f32( f32 );\n}"
  },
  {
    "path": "Whisper/Utils/miscUtils.h",
    "content": "#pragma once\n\n#define CHECK( hr ) { const HRESULT __hr = ( hr ); if( FAILED( __hr ) ) return __hr; }\n#define CHECK_LOG( hr ) { const HRESULT __hr = ( hr ); if( FAILED( __hr ) ) { logErrorHr(__hr, u8\"%s failed\", #hr ); return __hr; } }\n\ninline void check( HRESULT hr )\n{\n\tif( SUCCEEDED( hr ) )\n\t\treturn;\n\tthrow hr;\n}\n\ninline __m128i __vectorcall load16( const int* rsi )\n{\n\treturn _mm_loadu_si128( ( const __m128i* )rsi );\n}\ninline __m128i __vectorcall load16( const uint32_t* rsi )\n{\n\treturn _mm_loadu_si128( ( const __m128i* )rsi );\n}\ninline __m128i __vectorcall load( const std::array<uint32_t, 4>& arr )\n{\n\treturn load16( arr.data() );\n}\ninline void __vectorcall store16( void* rdi, __m128i v )\n{\n\t_mm_storeu_si128( ( __m128i* )rdi, v );\n}\ninline void __vectorcall store12( void* rdi, __m128i v )\n{\n\t_mm_storel_epi64( ( __m128i* )rdi, v );\n\t( (int*)rdi )[ 2 ] = _mm_extract_epi32( v, 2 );\n}\ninline void __vectorcall store( std::array<uint32_t, 4>& arr, __m128i v )\n{\n\tstore16( arr.data(), v );\n}\ninline bool __vectorcall vectorEqual( __m128i a, __m128i b )\n{\n\t__m128i xx = _mm_xor_si128( a, b );\n\treturn (bool)_mm_testz_si128( xx, xx );\n}\n\ninline __m128i __vectorcall setLow_size( size_t low )\n{\n\treturn _mm_cvtsi64_si128( (int64_t)low );\n}\ninline __m128i __vectorcall setr_size( size_t low, size_t high )\n{\n\t__m128i v = setLow_size( low );\n\tv = _mm_insert_epi64( v, (int64_t)high, 1 );\n\treturn v;\n}\ninline __m128i __vectorcall setHigh_size( size_t high )\n{\n\t__m128i v = _mm_setzero_si128();\n\tv = _mm_insert_epi64( v, (int64_t)high, 1 );\n\treturn v;\n}\n\nvoid setCurrentThreadName( const char* name );\n\ninline HRESULT getLastHr()\n{\n\treturn HRESULT_FROM_WIN32( GetLastError() );\n}\n\n// Scale time in seconds from unsigned 64 bit rational number ( mul / div ) into 100-nanosecond ticks\n// These 100-nanosecond ticks are used in NTFS, FILETIME, .NET standard library, media foundation, and quite a few other places\ninline uint64_t makeTime( uint64_t mul, uint64_t div )\n{\n\tmul *= 10'000'000;\n\tmul += ( ( div / 2 ) - 1 );\n\treturn mul / div;\n}\n\ntemplate<class E>\ninline size_t vectorMemoryUse( const std::vector<E>& vec )\n{\n\treturn sizeof( E ) * vec.capacity();\n}\n\n// The formula is pow( mul / div, -0.25 )\nfloat computeScaling( int mul, int div );"
  },
  {
    "path": "Whisper/Utils/parallelFor.cpp",
    "content": "#include \"stdafx.h\"\n#include \"parallelFor.h\"\n\nnamespace\n{\n\tclass alignas( 64 ) ParallelForContext\n\t{\n\t\tvolatile long threadIndex;\n\t\tvolatile HRESULT status;\n\n\t\talignas( 64 ) void* const context;\n\t\tconst Whisper::pfnParallelForCallback pfn;\n\n\t\tstatic void __stdcall callbackStatic( PTP_CALLBACK_INSTANCE Instance, PVOID pv, PTP_WORK Work );\n\n\tpublic:\n\n\t\tParallelForContext( void* ctx, Whisper::pfnParallelForCallback pfn );\n\n\t\tPTP_WORK createWork();\n\n\t\tHRESULT getStatus() const;\n\t};\n\n\tParallelForContext::ParallelForContext( void* ctx, Whisper::pfnParallelForCallback callback ) :\n\t\tthreadIndex( 1 ),\n\t\tstatus( S_FALSE ),\n\t\tcontext( ctx ),\n\t\tpfn( callback )\n\t{ }\n\n\tPTP_WORK ParallelForContext::createWork()\n\t{\n\t\treturn CreateThreadpoolWork( &callbackStatic, this, nullptr );\n\t}\n\n\tvoid __stdcall ParallelForContext::callbackStatic( PTP_CALLBACK_INSTANCE Instance, PVOID pv, PTP_WORK Work )\n\t{\n\t\tParallelForContext& context = *(ParallelForContext*)pv;\n\t\tint ith = InterlockedIncrement( &context.threadIndex );\n\t\tith--;\n\t\tconst HRESULT hr = context.pfn( ith, context.context );\n\t\tif( SUCCEEDED( hr ) )\n\t\t\treturn;\n\t\tInterlockedCompareExchange( &context.status, hr, S_FALSE );\n\t}\n\n\tHRESULT ParallelForContext::getStatus() const\n\t{\n\t\tconst HRESULT hr = status;\n\t\tif( SUCCEEDED( hr ) )\n\t\t\treturn S_OK;\n\t\treturn hr;\n\t}\n}\n\nnamespace Whisper\n{\n\tHRESULT parallelFor( pfnParallelForCallback pfn, int threadsCount, void* ctx )\n\t{\n\t\tif( threadsCount < 1 )\n\t\t\treturn E_BOUNDS;\n\t\tif( threadsCount == 1 )\n\t\t\treturn pfn( 0, ctx );\n\n\t\tParallelForContext context{ ctx, pfn };\n\n\t\tPTP_WORK const pw = context.createWork();\n\t\tif( nullptr == pw )\n\t\t\treturn getLastHr();\n\n\t\tfor( int i = 1; i < threadsCount; i++ )\n\t\t\tSubmitThreadpoolWork( pw );\n\n\t\tconst HRESULT hr0 = pfn( 0, ctx );\n\n\t\tWaitForThreadpoolWorkCallbacks( pw, FALSE );\n\t\tCloseThreadpoolWork( pw );\n\n\t\tif( FAILED( hr0 ) )\n\t\t\treturn hr0;\n\t\treturn context.getStatus();\n\t}\n}\n\nusing namespace Whisper;\n\nThreadPoolWork::~ThreadPoolWork()\n{\n\tif( nullptr != work )\n\t{\n\t\tCloseThreadpoolWork( work );\n\t\twork = nullptr;\n\t}\n}\n\nHRESULT ThreadPoolWork::create()\n{\n\tif( nullptr == work )\n\t{\n\t\twork = CreateThreadpoolWork( &callbackStatic, this, nullptr );\n\t\tif( nullptr != work )\n\t\t\treturn S_OK;\n\t\treturn getLastHr();\n\t}\n\treturn HRESULT_FROM_WIN32( ERROR_ALREADY_INITIALIZED );\n}\n\nHRESULT ThreadPoolWork::parallelFor( int threadsCount ) noexcept\n{\n\tif( nullptr != work )\n\t{\n\t\tif( threadsCount <= 1 )\n\t\t\treturn threadPoolCallback( 0 );\n\n\t\tthreadIndex = 1;\n\t\tstatus = S_FALSE;\n\t\tfor( int i = 1; i < threadsCount; i++ )\n\t\t\tSubmitThreadpoolWork( work );\n\n\t\tconst HRESULT hr0 = threadPoolCallback( 0 );\n\n\t\tWaitForThreadpoolWorkCallbacks( work, FALSE );\n\n\t\tif( FAILED( hr0 ) )\n\t\t\treturn hr0;\n\t\tif( SUCCEEDED( status ) )\n\t\t\treturn S_OK;\n\t\treturn status;\n\t}\n\n\treturn OLE_E_BLANK;\n}\n\nvoid __stdcall ThreadPoolWork::callbackStatic( PTP_CALLBACK_INSTANCE Instance, PVOID pv, PTP_WORK Work )\n{\n\tThreadPoolWork* tpw = (ThreadPoolWork*)pv;\n\tint ith = InterlockedIncrement( &tpw->threadIndex );\n\tith--;\n\tconst HRESULT hr = tpw->threadPoolCallback( ith );\n\tif( SUCCEEDED( hr ) )\n\t\treturn;\n\tInterlockedCompareExchange( &tpw->status, hr, S_FALSE );\n}"
  },
  {
    "path": "Whisper/Utils/parallelFor.h",
    "content": "#pragma once\n\nnamespace Whisper\n{\n\t// A callback to offload to the thread pool\n\tusing pfnParallelForCallback = HRESULT( * )( int ith, void* ctx ) noexcept;\n\n\t// A simple parallel for implementation; Windows includes a decent thread pool since Vista (2006)\n\tHRESULT parallelFor( pfnParallelForCallback pfn, int threadsCount, void* ctx );\n\n\t// Use this version when you wanna use the thread pool repeatedly, for the same work.\n\t// This class caches native work handle, saving a couple of WinAPI calls.\n\tclass alignas( 64 ) ThreadPoolWork\n\t{\n\t\tPTP_WORK work = nullptr;\n\n\t\t// We want these volatile fields in another cache line from the rest of the data of this class.\n\t\t// threadIndex field is concurrently modified by different CPU cores, and these cache coherency protocols are slow.\n\t\t// OTOH, work and callback fields of this class only change when created / destroyed, that cache line is shared by CPU cores without any performance penalty.\n\t\talignas( 64 ) volatile long threadIndex = 0;\n\t\tvolatile HRESULT status = E_UNEXPECTED;\n\n\t\tstatic void __stdcall callbackStatic( PTP_CALLBACK_INSTANCE Instance, PVOID pv, PTP_WORK Work );\n\n\tprotected:\n\t\tvirtual HRESULT threadPoolCallback( int ith ) noexcept = 0;\n\n\tpublic:\n\t\tThreadPoolWork() = default;\n\t\tThreadPoolWork( const ThreadPoolWork& ) = delete;\n\n\t\t~ThreadPoolWork();\n\n\t\tHRESULT create();\n\n\t\tHRESULT parallelFor( int threadsCount ) noexcept;\n\t};\n}"
  },
  {
    "path": "Whisper/Whisper/ContextImpl.capture.cpp",
    "content": "﻿#include \"stdafx.h\"\n#include \"ContextImpl.h\"\n#include \"../API/iMediaFoundation.cl.h\"\n#include \"../MF/AudioBuffer.h\"\n#include \"../MF/mfUtils.h\"\n#include <mfidl.h>\n#include <mfapi.h>\n#include <mfreadwrite.h>\n#include \"voiceActivityDetection.h\"\n\nnamespace\n{\n\tusing namespace Whisper;\n\n\tclass TranscribeBuffer : public ComLight::ObjectRoot<iAudioBuffer>\n\t{\n\t\t// ==== iAudioBuffer ====\n\t\tuint32_t COMLIGHTCALL countSamples() const override final\n\t\t{\n\t\t\treturn (uint32_t)pcm.mono.size();\n\t\t}\n\t\tconst float* COMLIGHTCALL getPcmMono() const override final\n\t\t{\n\t\t\tif( !pcm.mono.empty() )\n\t\t\t\treturn pcm.mono.data();\n\t\t\treturn nullptr;\n\t\t}\n\t\tconst float* COMLIGHTCALL getPcmStereo() const override final\n\t\t{\n\t\t\tif( !pcm.stereo.empty() )\n\t\t\t\treturn pcm.stereo.data();\n\t\t\treturn nullptr;\n\t\t}\n\t\tHRESULT COMLIGHTCALL getTime( int64_t& rdi ) const override final\n\t\t{\n\t\t\trdi = MFllMulDiv( currentOffset, 10'000'000, SAMPLE_RATE, 0 );\n\t\t\treturn S_OK;\n\t\t}\n\tpublic:\n\t\tAudioBuffer pcm;\n\t\tint64_t currentOffset = 0;\n\t};\n\n\tclass TranscribeBufferObj : public ComLight::Object<TranscribeBuffer>\n\t{\n\t\tuint32_t Release() override final\n\t\t{\n\t\t\treturn RefCounter::implRelease();\n\t\t}\n\t};\n\n\t// Same data as in the Whisper.sCaptureParams public structure, the durations are scaled from FP32 seconds into uint32_t samples at 16 kHz\n\tstruct CaptureParams\n\t{\n\t\tuint32_t minDuration, maxDuration, dropStartSilence, pauseDuration;\n\t\tuint32_t flags;\n\n\t\tCaptureParams( const sCaptureParams& cp )\n\t\t{\n\t\t\t// Convert these floats from seconds to samples\n\t\t\t__m128 floats = _mm_loadu_ps( &cp.minDuration );\n\t\t\tfloats = _mm_mul_ps( floats, _mm_set1_ps( (float)SAMPLE_RATE ) );\n\t\t\tfloats = _mm_round_ps( floats, _MM_FROUND_NINT );\n\t\t\t__m128i ints = _mm_cvtps_epi32( floats );\n\t\t\tstore16( &minDuration, ints );\n\n\t\t\tflags = cp.flags;\n\t\t}\n\t};\n\n\tclass Capture\n\t{\n\t\tCComPtr<IMFSourceReader> reader;\n\t\tconst CaptureParams captureParams;\n\t\tconst sCaptureCallbacks callbacks;\n\t\t// Count of channels delivered from the source reader\n\t\tuint8_t readerChannels = 0;\n\t\tvolatile char stateFlags = 0;\n\n\t\tPTP_WORK work = nullptr;\n\t\tvolatile HRESULT workStatus = S_OK;\n\n\t\tTranscribeBufferObj buffer;\n\t\tCComAutoCriticalSection critSec;\n\t\tAudioBuffer pcm;\n\t\tAudioBuffer::pfnAppendSamples pfnAppendSamples = nullptr;\n\t\tint64_t pcmStartTime = 0;\n\t\tint64_t nextSampleTime = 0;\n\t\tVAD vad;\n\t\tsFullParams fullParams;\n\t\tProfileCollection& profiler;\n\t\tiContext* const whisperContext;\n\n\t\t// Set the state bit, and if needed notify user with the callback.\n\t\tHRESULT setStateFlag( eCaptureStatus newBit ) noexcept\n\t\t{\n\t\t\tconst uint8_t bit = (uint8_t)newBit;\n\t\t\tconst uint8_t oldVal = (uint8_t)InterlockedOr8( &stateFlags, (char)bit );\n\t\t\tif( nullptr == callbacks.captureStatus )\n\t\t\t\treturn S_OK;\t// no callbacks\n\t\t\tif( 0 != ( oldVal & bit ) )\n\t\t\t\treturn S_OK;\t// The bit was already set\n\t\t\treturn callbacks.captureStatus( callbacks.pv, (eCaptureStatus)( oldVal | bit ) );\n\t\t}\n\n\t\t// Clear the state bit, and if needed notify user with the callback\n\t\tHRESULT clearStateFlag( eCaptureStatus clearBit ) noexcept\n\t\t{\n\t\t\tconst uint8_t bit = (uint8_t)clearBit;\n\t\t\tconst uint8_t mask = ~bit;\n\t\t\tconst uint8_t oldVal = (uint8_t)InterlockedAnd8( &stateFlags, (char)mask );\n\t\t\tif( nullptr == callbacks.captureStatus )\n\t\t\t\treturn S_OK;\t// no callbacks\n\t\t\tif( 0 == ( oldVal & bit ) )\n\t\t\t\treturn S_OK;\t// The bit wasn't there\n\t\t\treturn callbacks.captureStatus( callbacks.pv, (eCaptureStatus)( oldVal & mask ) );\n\t\t}\n\n\t\tbool hasStateFlag( eCaptureStatus testBit ) const\n\t\t{\n\t\t\tconst uint8_t bit = (uint8_t)testBit;\n\t\t\treturn 0 != ( (uint8_t)stateFlags & bit );\n\t\t}\n\n\t\tHRESULT workCallback();\n\t\tstatic void __stdcall callbackStatic( PTP_CALLBACK_INSTANCE Instance, PVOID pv, PTP_WORK Work );\n\n\t\tHRESULT readSample( bool discard );\n\n\t\t// Run voice detection on the data in pcm.mono vector.\n\t\t// When not detected, return 0. When detected, return last frame index where it is detected.\n\t\tsize_t detectVoice();\n\n\t\tHRESULT postPoolWork()\n\t\t{\n\t\t\tassert( workStatus == S_OK );\n\t\t\tCHECK( setStateFlag( eCaptureStatus::Transcribing ) );\n\n\t\t\tworkStatus = S_FALSE;\n\t\t\tbuffer.currentOffset = pcmStartTime;\n\t\t\tpcm.swap( buffer.pcm );\n\t\t\tSubmitThreadpoolWork( work );\n\t\t\tpcmStartTime = nextSampleTime;\n\t\t\tpcm.clear();\n\t\t\tvad.clear();\n\t\t\treturn S_OK;\n\t\t}\n\n\tpublic:\n\t\tCapture( const sCaptureCallbacks& cb, const iAudioCapture* ac, const sFullParams& sfp, iContext* wc, ProfileCollection& pc ) :\n\t\t\tcallbacks( cb ),\n\t\t\tcaptureParams( ac->getParams() ),\n\t\t\tfullParams( sfp ), whisperContext( wc ), profiler( pc )\n\t\t{\n\t\t}\n\n\t\t~Capture()\n\t\t{\n\t\t\tif( workStatus == S_FALSE && nullptr != work )\n\t\t\t\tWaitForThreadpoolWorkCallbacks( work, FALSE );\n\n\t\t\tif( nullptr != work )\n\t\t\t{\n\t\t\t\tCloseThreadpoolWork( work );\n\t\t\t\twork = nullptr;\n\t\t\t}\n\t\t}\n\n\t\tHRESULT startup( const iAudioCapture* ac );\n\n\t\tHRESULT checkCancel() noexcept\n\t\t{\n\t\t\tif( nullptr == callbacks.shouldCancel )\n\t\t\t\treturn S_OK;\n\t\t\treturn callbacks.shouldCancel( callbacks.pv );\n\t\t}\n\n\t\tHRESULT run();\n\t};\n\n\tHRESULT Capture::startup( const iAudioCapture* ac )\n\t{\n\t\t// Initialize the MF source reader\n\t\tCHECK( ac->getReader( &reader ) );\n\t\twork = CreateThreadpoolWork( &callbackStatic, this, nullptr );\n\t\tif( nullptr == work )\n\t\t\treturn HRESULT_FROM_WIN32( GetLastError() );\n\n\t\t// Set up media type, and figure out sample handler\n\t\tCHECK( reader->SetStreamSelection( MF_SOURCE_READER_ALL_STREAMS, FALSE ) );\n\t\tCHECK( reader->SetStreamSelection( MF_SOURCE_READER_FIRST_AUDIO_STREAM, TRUE ) );\n\n\t\tCComPtr<IMFMediaType> mtNative;\n\t\tCHECK( reader->GetNativeMediaType( MF_SOURCE_READER_FIRST_AUDIO_STREAM, MF_SOURCE_READER_CURRENT_TYPE_INDEX, &mtNative ) );\n\t\tUINT32 numChannels;\n\t\tCHECK( mtNative->GetUINT32( MF_MT_AUDIO_NUM_CHANNELS, &numChannels ) );\n\n\t\tconst bool sourceMono = numChannels < 2;\n\t\tconst bool wantStereo = 0 != ( captureParams.flags & (uint32_t)eCaptureFlags::Stereo );\n\t\tpfnAppendSamples = AudioBuffer::appendSamplesFunc( sourceMono, wantStereo );\n\n\t\tCComPtr<IMFMediaType> mt;\n\t\tthis->readerChannels = ( !sourceMono && wantStereo ) ? 2 : 1;\n\t\tCHECK( createMediaType( !sourceMono, &mt ) );\n\t\tCHECK( reader->SetCurrentMediaType( MF_SOURCE_READER_FIRST_AUDIO_STREAM, nullptr, mt ) );\n\n\t\tCHECK( setStateFlag( eCaptureStatus::Listening ) );\n\t\treturn S_OK;\n\t}\n\n\t// This method is called in a loop until user stops the audio capture\n\tHRESULT Capture::run()\n\t{\n\t\tHRESULT hr;\n\t\tif( hasStateFlag( eCaptureStatus::Stalled ) )\n\t\t{\n\t\t\thr = workStatus;\n\t\t\tCHECK( hr );\n\t\t\tif( S_OK != hr )\n\t\t\t{\n\t\t\t\t// Still stalled, discard the upcoming sample\n\t\t\t\treturn readSample( true );\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// The postponed task has completed by now, no longer stalled\n\t\t\t\t// Move the current PCM buffer to the transcribe thread\n\t\t\t\tCHECK( clearStateFlag( eCaptureStatus::Stalled ) );\n\t\t\t\treturn postPoolWork();\n\t\t\t}\n\t\t}\n\n\t\tconst size_t oldSamples = pcm.mono.size();\n\t\tCHECK( readSample( false ) );\n\t\tconst size_t newSamples = pcm.mono.size();\n\n\t\tconst size_t lastVoiceFrame = detectVoice();\n\t\tif( lastVoiceFrame == 0 )\n\t\t{\n\t\t\t// No voice is detected in the entire buffered audio\n\t\t\tclearStateFlag( eCaptureStatus::Voice );\n\t\t\tif( newSamples < captureParams.dropStartSilence )\n\t\t\t\treturn S_OK;\n\n\t\t\tpcm.clear();\n\t\t\tvad.clear();\n\t\t\tpcmStartTime = nextSampleTime;\n\t\t\treturn S_OK;\n\t\t}\n\n\t\tconst bool newFrameVoice = lastVoiceFrame + captureParams.pauseDuration >= oldSamples;\n\n\t\tif( newFrameVoice )\n\t\t{\n\t\t\t// A voice is detected in the buffer, and it was fairly recently\n\t\t\tsetStateFlag( eCaptureStatus::Voice );\n\t\t\tif( newSamples < captureParams.maxDuration )\n\t\t\t\treturn S_OK;\t// While voice is continuously detected, we allow to grow the buffer up to `maxDuration` time\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// A voice is detected in the buffer, but it was a while ago\n\t\t\tclearStateFlag( eCaptureStatus::Voice );\n\t\t\tif( newSamples < captureParams.minDuration )\n\t\t\t\treturn S_OK;\t// When detected pause in the voice, we fire the transcribe task right away.\n\t\t}\n\n\t\t// Hopefully, we have enough captured PCM data to run the ASR model.\n\t\t// Check the background task status first.\n\t\thr = workStatus;\n\t\tCHECK( hr );\n\t\tif( hr == S_OK )\n\t\t{\n\t\t\t// S_OK workStatus means the previously posted transcribe job has completed successfully by now\n\t\t\treturn postPoolWork();\n\t\t}\n\n\t\t// S_FALSE means the previously posted transcribe job is still running\n\t\t// Allow the buffer to grow up to maxDuration length, before starting to drop the samples\n\t\tif( newSamples < captureParams.maxDuration )\n\t\t\treturn S_OK;\n\n\t\t// The previous task has not finished yet, but we don't want to grow the buffer even further.\n\t\t// We don't want concurrent transcribes here because not implemented, will simply crash.\n\t\t// Set the \"Stalled\" flag which causes capture to drop further samples\n\t\tsetStateFlag( eCaptureStatus::Stalled );\n\t\treturn S_OK;\n\t}\n\n\tHRESULT Capture::readSample( bool discard )\n\t{\n\t\twhile( true )\n\t\t{\n\t\t\tDWORD dwFlags = 0;\n\t\t\tCComPtr<IMFSample> sample;\n\n\t\t\t// Read the next sample\n\t\t\tHRESULT hr = reader->ReadSample( (DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM, 0, nullptr, &dwFlags, nullptr, &sample );\n\t\t\tif( FAILED( hr ) )\n\t\t\t{\n\t\t\t\tlogErrorHr( hr, u8\"IMFSourceReader.ReadSample\" );\n\t\t\t\treturn hr;\n\t\t\t}\n\n\t\t\tif( dwFlags & MF_SOURCE_READERF_CURRENTMEDIATYPECHANGED )\n\t\t\t{\n\t\t\t\tlogError( u8\"Media type changes ain’t supported by the library.\" );\n\t\t\t\treturn E_UNEXPECTED;\n\t\t\t}\n\n\t\t\tif( dwFlags & MF_SOURCE_READERF_ENDOFSTREAM )\n\t\t\t\treturn E_EOF;\n\n\t\t\tif( !sample )\n\t\t\t\tcontinue;\n\n\t\t\t// Get a pointer to the audio data in the sample.\n\t\t\tCComPtr<IMFMediaBuffer> buffer;\n\t\t\thr = sample->ConvertToContiguousBuffer( &buffer );\n\t\t\tif( FAILED( hr ) )\n\t\t\t\treturn hr;\n\n\t\t\tconst float* pAudioData = nullptr;\n\t\t\tDWORD cbBuffer;\n\t\t\thr = buffer->Lock( (BYTE**)&pAudioData, nullptr, &cbBuffer );\n\t\t\tif( FAILED( hr ) )\n\t\t\t\treturn hr;\n\n\t\t\ttry\n\t\t\t{\n\t\t\t\tassert( 0 == ( cbBuffer % sizeof( float ) ) );\n\t\t\t\tconst size_t countFloats = cbBuffer / sizeof( float );\n\t\t\t\tif( !discard )\n\t\t\t\t{\n\t\t\t\t\tconst size_t prevSize = pcm.mono.size();\n\t\t\t\t\t( pcm.*pfnAppendSamples )( pAudioData, countFloats );\n\t\t\t\t\tconst size_t newSize = pcm.mono.size();\n\t\t\t\t\tthis->nextSampleTime += ( newSize - prevSize );\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tthis->nextSampleTime += countFloats / readerChannels;\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch( const std::bad_alloc& )\n\t\t\t{\n\t\t\t\tbuffer->Unlock();\n\t\t\t\treturn E_OUTOFMEMORY;\n\t\t\t}\n\n\t\t\t// Unlock the buffer\n\t\t\thr = buffer->Unlock();\n\t\t\tif( FAILED( hr ) )\n\t\t\t\treturn hr;\n\n\t\t\treturn S_OK;\n\t\t}\n\t}\n\n\tHRESULT Capture::workCallback()\n\t{\n\t\tCHECK( whisperContext->runFull( fullParams, &buffer ) );\n\t\tCHECK( clearStateFlag( eCaptureStatus::Transcribing ) );\n\t\treturn S_OK;\n\t}\n\n\tvoid __stdcall Capture::callbackStatic( PTP_CALLBACK_INSTANCE Instance, PVOID pv, PTP_WORK Work )\n\t{\n\t\tCapture* pThis = (Capture*)pv;\n\t\tHRESULT status = E_UNEXPECTED;\n\t\ttry\n\t\t{\n\t\t\tstatus = pThis->workCallback();\n\t\t}\n\t\tcatch( HRESULT hr )\n\t\t{\n\t\t\tstatus = hr;\n\t\t}\n\t\tcatch( const std::bad_alloc& )\n\t\t{\n\t\t\tstatus = E_OUTOFMEMORY;\n\t\t}\n\t\tcatch( const std::exception& )\n\t\t{\n\t\t\tstatus = E_FAIL;\n\t\t}\n\t\tassert( S_OK == status || FAILED( status ) );\n\t\tpThis->workStatus = status;\n\t}\n\n\tsize_t Capture::detectVoice()\n\t{\n\t\tauto pf = profiler.cpuBlock( eCpuBlock::VAD );\n\t\treturn vad.detect( pcm.mono.data(), pcm.mono.size() );\n\t}\n}\n\nHRESULT COMLIGHTCALL ContextImpl::runCapture( const sFullParams& params, const sCaptureCallbacks& callbacks, const iAudioCapture* reader )\n{\n\tif( nullptr == reader )\n\t\treturn E_POINTER;\n\n\t// Validate a few things\n\t{\n\t\tconst auto& cp = reader->getParams();\n\t\tif( cp.minDuration < 0.125f || cp.minDuration > 30.0f )\n\t\t{\n\t\t\tlogError( u8\"%s parameter %g is out of range\", \"minDuration\", cp.minDuration );\n\t\t\treturn E_INVALIDARG;\n\t\t}\n\t\tif( cp.maxDuration < 0.125f || cp.maxDuration > 30.0f )\n\t\t{\n\t\t\tlogError( u8\"%s parameter %g is out of range\", \"maxDuration\", cp.maxDuration );\n\t\t\treturn E_INVALIDARG;\n\t\t}\n\t}\n\n\tauto profCompleteCpu = profiler.cpuBlock( eCpuBlock::RunComplete );\n\tCapture capture{ callbacks, reader, params, this, profiler };\n\tCHECK( capture.startup( reader ) );\n\n\twhile( true )\n\t{\n\t\tHRESULT hr = capture.checkCancel();\n\t\tCHECK( hr );\n\t\tif( hr != S_OK )\n\t\t\treturn S_OK;\n\t\tCHECK( capture.run() );\n\t}\n}"
  },
  {
    "path": "Whisper/Whisper/ContextImpl.cpp",
    "content": "#include \"stdafx.h\"\n#include \"ContextImpl.h\"\n#include \"Languages.h\"\n#include \"../Utils/Trace/tracing.h\"\nusing namespace Whisper;\n\nContextImpl::ContextImpl( const DirectCompute::Device& dev, const WhisperModel& modelData, iModel* modelPointer ) :\n\tdevice( dev ),\n\tmodel( modelData ),\n\tmodelPtr( modelPointer ),\n\tcontext( modelData, profiler ),\n\tprofiler( modelData )\n{ }\n\n#define WHISPER_CHUNK_SIZE  30\n\nHRESULT ContextImpl::encode( iSpectrogram& mel, int seek )\n{\n\tauto prof = profiler.cpuBlock( eCpuBlock::Encode );\n\t// whisper_encode\n\tusing namespace DirectCompute;\n\n\tsEncodeParams ep;\n\tep.n_ctx = ( exp_n_audio_ctx > 0 ) ? exp_n_audio_ctx : model.parameters.n_audio_ctx;\n\tep.n_mels = model.parameters.n_mels;\n\tep.mel_offset = seek;\n\tep.layersCount = model.parameters.n_audio_layer;\n\tep.n_state = model.parameters.n_audio_state;\n\tep.n_head = model.parameters.n_audio_head;\n\tep.n_audio_ctx = model.parameters.n_audio_ctx;\n\tep.n_text_state = model.parameters.n_text_state;\n\tep.n_text_layer = model.parameters.n_text_layer;\n\tep.n_text_ctx = model.parameters.n_text_ctx;\n\ttry\n\t{\n\t\tauto cur = context.encode( mel, ep );\n\t\tTracing::tensor( \"encode-out\", cur );\n\t\treturn S_OK;\n\t}\n\tcatch( HRESULT hr )\n\t{\n\t\treturn hr;\n\t}\n}\n\nHRESULT ContextImpl::decode( const int* tokens, size_t length, int n_past, int threads )\n{\n\t// whisper_decode\n\tusing namespace DirectCompute;\n\tsDecodeParams dp;\n\tdp.n_state = model.parameters.n_audio_state;\n\tdp.n_head = model.parameters.n_audio_head;\n\tdp.n_ctx = model.parameters.n_text_ctx;\n\tdp.n_past = n_past;\n\tdp.M = exp_n_audio_ctx > 0 ? exp_n_audio_ctx : model.parameters.n_audio_ctx;\n\tdp.n_text_layer = model.parameters.n_text_layer;\n\tdp.n_vocab = model.parameters.n_vocab;\n\n\ttry\n\t{\n\t\tcontext.decode( tokens, (int)length, dp, probs, threads );\n\t\treturn S_OK;\n\t}\n\tcatch( HRESULT hr )\n\t{\n\t\treturn hr;\n\t}\n}\n\n// the most basic sampling scheme - select the top token\nsTokenData ContextImpl::sampleBest( const float* probs, bool force_timestamp, bool is_initial )\n{\n\t// whisper_sample_best\n\tconst Vocabulary& vocab = model.shared->vocab;\n\tsTokenData result = { 0 };\n\n\tsize_t n_logits = vocab.size();\n\n\tprobs_id.clear();\n\tprobs_id.reserve( n_logits );\n\n\tfor( size_t i = 0; i < n_logits; i++ )\n\t\tprobs_id.emplace_back( probs[ i ], (int)i );\n\n\t{\n\t\tdouble sum_ts = 0.0;\n\t\tdouble max_ts = -1.0;\n\t\tdouble max_tx = -1.0;\n\n\t\tfor( int i = 0; i < vocab.token_beg; i++ )\n\t\t\tmax_tx = std::max( max_tx, probs_id[ i ].first );\n\n\t\tconst int i0 = is_initial ? vocab.token_beg + 101 : vocab.token_beg;\n\t\tconst int i1 = is_initial ? vocab.token_beg + 101 : (int)n_logits;\n\n\t\t// the initial timestamp cannot be larger than 100\n\t\t// ref: https://github.com/openai/whisper/blob/0b1ba3d46ebf7fe6f953acfd8cad62a4f851b49f/whisper/decoding.py#L426-L429\n\t\tif( is_initial )\n\t\t{\n\t\t\tfor( int i = i0; i < n_logits; i++ )\n\t\t\t\tprobs_id[ i ].first = -INFINITY;\n\t\t}\n\n\t\tfor( int i = vocab.token_beg; i < i1; i++ )\n\t\t{\n\t\t\tsum_ts += probs_id[ i ].first;\n\t\t\tif( probs_id[ i ].first > max_ts )\n\t\t\t{\n\t\t\t\tmax_ts = probs_id[ i ].first;\n\t\t\t\tresult.tid = probs_id[ i ].second;\n\t\t\t}\n\t\t}\n\n\t\t// if the probability sum of all timestamp tokens is higher than the max probability of the text tokens - sample a\n\t\t// timestamp token\n\t\tif( sum_ts > max_tx || force_timestamp )\n\t\t{\n\t\t\t// ref: https://github.com/openai/whisper/blob/0b1ba3d46ebf7fe6f953acfd8cad62a4f851b49f/whisper/decoding.py#L430-L438\n\t\t\tfor( int i = 0; i < vocab.token_beg; i++ )\n\t\t\t\tprobs_id[ i ].first = -INFINITY;\n\t\t}\n\n\t\tresult.pt = (float)( max_ts / ( sum_ts + 1e-10 ) );\n\t\tresult.ptsum = (float)sum_ts;\n\t}\n\n\t// find the top K tokens\n\tconst int top_k = 4;\n\n\tstd::partial_sort(\n\t\tprobs_id.begin(),\n\t\tprobs_id.begin() + top_k, probs_id.end(),\n\t\t[]( const std::pair<double, Vocabulary::id>& a, const std::pair<double, Vocabulary::id>& b ) {\n\t\t\treturn a.first > b.first;\n\t\t} );\n\n\tprobs_id.resize( top_k );\n\n\t//printf(\"\\n\");\n\t//for (int i = 0; i < (int) probs_id.size(); i++) {\n\t//    printf(\"%d: '%s' %f, %d\\n\", i, vocab.id_to_token.at(probs_id[i].second).c_str(), probs_id[i].first, probs_id[i].second);\n\t//}\n\n\tint res = 0;\n\twhile( ( probs_id[ res ].second == vocab.token_sot ||\n\t\tprobs_id[ res ].second == vocab.token_solm ||\n\t\tprobs_id[ res ].second == vocab.token_not ) &&\n\t\tres < (int)probs_id.size() - 1 )\n\t{\n\t\tres++;\n\t}\n\n\tresult.id = probs_id[ res ].second;\n\tresult.p = (float)probs_id[ res ].first;\n\n\treturn result;\n}\n\nsTokenData ContextImpl::sampleBest()\n{\n\tconst int n_vocab = model.shared->vocab.n_vocab;\n\treturn sampleBest( probs.data() + ( probs.size() - n_vocab ), false, false );\n}\n\nsTokenData ContextImpl::sampleTimestamp( bool initial )\n{\n\tconst int n_vocab = model.shared->vocab.n_vocab;\n\treturn sampleBest( probs.data() + ( probs.size() - n_vocab ), true, initial );\n}\n\n// a cost-function / heuristic that is high for text that takes longer to pronounce\n// Obviously, can be improved\nstatic float voice_length( const char* text )\n{\n\tif( nullptr == text )\n\t\treturn 0;\n\n\tfloat res = 0.0f;\n\twhile( true )\n\t{\n\t\tconst char c = *text;\n\t\tif( c == '\\0' )\n\t\t\treturn res;\n\t\ttext++;\n\n\t\t// Figure out the increment\n\t\tfloat inc;\n\t\tif( c >= '0' && c <= '9' )\n\t\t\tinc = 3.0f;\n\t\telse\n\t\t{\n\t\t\tswitch( c )\n\t\t\t{\n\t\t\tcase ' ': inc = 0.01f; break;\n\t\t\tcase ',': inc = 2.00f; break;\n\t\t\tcase '.':\n\t\t\tcase '!':\n\t\t\tcase '?':\n\t\t\t\tinc = 3.00f; break;\n\t\t\tdefault:\n\t\t\t\tinc = 1.0f;\n\t\t\t}\n\t\t}\n\n\t\tres += inc;\n\t}\n}\n\nstatic int timestamp_to_sample( int64_t t, int n_samples )\n{\n\treturn std::max( 0, std::min( (int)n_samples - 1, (int)( ( t * SAMPLE_RATE ) / 100 ) ) );\n}\n\nstatic int64_t sample_to_timestamp( int i_sample )\n{\n\treturn ( 100 * i_sample ) / SAMPLE_RATE;\n}\n\nvoid ContextImpl::expComputeTokenLevelTimestamps( int i_segment, float thold_pt, float thold_ptsum )\n{\n\t// whisper_exp_compute_token_level_timestamps\n\tconst Whisper::Vocabulary& vocab = model.shared->vocab;\n\n\tauto& segment = result_all[ i_segment ];\n\tauto& tokens = segment.tokens;\n\n\tconst int n_samples = energy.size();\n\n\tif( n_samples == 0 )\n\t{\n\t\tlogWarning( u8\"%s: no signal data available\", __func__ );\n\t\treturn;\n\t}\n\n\tconst int64_t t0 = segment.t0;\n\tconst int64_t t1 = segment.t1;\n\tconst int n = tokens.size();\n\n\tif( n == 0 )\n\t\treturn;\n\n\tif( n == 1 )\n\t{\n\t\ttokens[ 0 ].t0 = t0;\n\t\ttokens[ 0 ].t1 = t1;\n\t\treturn;\n\t}\n\n\tauto& t_beg = this->t_beg;\n\tauto& t_last = this->t_last;\n\tauto& tid_last = this->tid_last;\n\n\tfor( int j = 0; j < n; ++j )\n\t{\n\t\tauto& token = tokens[ j ];\n\n\t\tif( j == 0 )\n\t\t{\n\t\t\tif( token.id == vocab.token_beg )\n\t\t\t{\n\t\t\t\ttokens[ j ].t0 = t0;\n\t\t\t\ttokens[ j ].t1 = t0;\n\t\t\t\ttokens[ j + 1 ].t0 = t0;\n\n\t\t\t\tt_beg = t0;\n\t\t\t\tt_last = t0;\n\t\t\t\ttid_last = vocab.token_beg;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\ttokens[ j ].t0 = t_last;\n\t\t\t}\n\t\t}\n\n\t\tconst int64_t tt = t_beg + 2 * ( token.tid - vocab.token_beg );\n\n\t\ttokens[ j ].id = token.id;\n\t\ttokens[ j ].tid = token.tid;\n\t\ttokens[ j ].p = token.p;\n\t\ttokens[ j ].pt = token.pt;\n\t\ttokens[ j ].ptsum = token.ptsum;\n\t\ttokens[ j ].vlen = voice_length( vocab.string( token.id ) );\n\n\t\tif( token.pt > thold_pt && token.ptsum > thold_ptsum && token.tid > tid_last && tt <= t1 )\n\t\t{\n\t\t\tif( j > 0 )\n\t\t\t\ttokens[ j - 1 ].t1 = tt;\n\t\t\ttokens[ j ].t0 = tt;\n\t\t\ttid_last = token.tid;\n\t\t}\n\t}\n\n\ttokens[ n - 2 ].t1 = t1;\n\ttokens[ n - 1 ].t0 = t1;\n\ttokens[ n - 1 ].t1 = t1;\n\tt_last = t1;\n\n\t// find intervals of tokens with unknown timestamps\n\t// fill the timestamps by proportionally splitting the interval based on the token voice lengths\n\t{\n\t\tint p0 = 0;\n\t\tint p1 = 0;\n\n\t\twhile( true )\n\t\t{\n\t\t\twhile( p1 < n && tokens[ p1 ].t1 < 0 )\n\t\t\t\tp1++;\n\n\t\t\tif( p1 >= n )\n\t\t\t\tp1--;\n\n\t\t\tif( p1 > p0 )\n\t\t\t{\n\t\t\t\tdouble psum = 0.0;\n\t\t\t\tfor( int j = p0; j <= p1; j++ )\n\t\t\t\t\tpsum += tokens[ j ].vlen;\n\n\t\t\t\t//printf(\"analyzing %d - %d, psum = %f\\n\", p0, p1, psum);\n\t\t\t\tconst double dt = tokens[ p1 ].t1 - tokens[ p0 ].t0;\n\n\t\t\t\t// split the time proportionally to the voice length\n\t\t\t\tfor( int j = p0 + 1; j <= p1; j++ )\n\t\t\t\t{\n\t\t\t\t\tconst double ct = tokens[ j - 1 ].t0 + dt * tokens[ j - 1 ].vlen / psum;\n\t\t\t\t\ttokens[ j - 1 ].t1 = ct;\n\t\t\t\t\ttokens[ j ].t0 = ct;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tp1++;\n\t\t\tp0 = p1;\n\t\t\tif( p1 >= n )\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\t// fix up (just in case)\n\tfor( int j = 0; j < n - 1; j++ )\n\t{\n\t\tif( tokens[ j ].t1 < 0 )\n\t\t\ttokens[ j + 1 ].t0 = tokens[ j ].t1;\n\n\t\tif( j > 0 )\n\t\t{\n\t\t\tif( tokens[ j - 1 ].t1 > tokens[ j ].t0 ) {\n\t\t\t\ttokens[ j ].t0 = tokens[ j - 1 ].t1;\n\t\t\t\ttokens[ j ].t1 = std::max( tokens[ j ].t0, tokens[ j ].t1 );\n\t\t\t}\n\t\t}\n\t}\n\n\t// VAD\n\t// expand or contract tokens based on voice activity\n\t{\n\t\tconstexpr int hw = SAMPLE_RATE / 8;\n\n\t\tfor( int j = 0; j < n; j++ )\n\t\t{\n\t\t\tif( tokens[ j ].id >= vocab.token_eot )\n\t\t\t\tcontinue;\n\n\t\t\tint s0 = timestamp_to_sample( tokens[ j ].t0, n_samples );\n\t\t\tint s1 = timestamp_to_sample( tokens[ j ].t1, n_samples );\n\n\t\t\tconst int ss0 = std::max( s0 - hw, 0 );\n\t\t\tconst int ss1 = std::min( s1 + hw, n_samples );\n\n\t\t\tconst int ns = ss1 - ss0;\n\n\t\t\tfloat sum = 0.0f;\n\t\t\tfor( int k = ss0; k < ss1; k++ )\n\t\t\t\tsum += this->energy[ k ];\n\n\t\t\tconst float thold = 0.5 * sum / ns;\n\n\t\t\t{\n\t\t\t\tint k = s0;\n\t\t\t\tif( this->energy[ k ] > thold && j > 0 )\n\t\t\t\t{\n\t\t\t\t\twhile( k > 0 && this->energy[ k ] > thold )\n\t\t\t\t\t\tk--;\n\t\t\t\t\ttokens[ j ].t0 = sample_to_timestamp( k );\n\t\t\t\t\tif( tokens[ j ].t0 < tokens[ j - 1 ].t1 )\n\t\t\t\t\t\ttokens[ j ].t0 = tokens[ j - 1 ].t1;\n\t\t\t\t\telse\n\t\t\t\t\t\ts0 = k;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\twhile( this->energy[ k ] < thold && k < s1 )\n\t\t\t\t\t\tk++;\n\t\t\t\t\ts0 = k;\n\t\t\t\t\ttokens[ j ].t0 = sample_to_timestamp( k );\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t{\n\t\t\t\tint k = s1;\n\t\t\t\tif( this->energy[ k ] > thold )\n\t\t\t\t{\n\t\t\t\t\twhile( k < n_samples - 1 && this->energy[ k ] > thold )\n\t\t\t\t\t\tk++;\n\t\t\t\t\ttokens[ j ].t1 = sample_to_timestamp( k );\n\t\t\t\t\tif( j < ns - 1 && tokens[ j ].t1 > tokens[ j + 1 ].t0 )\n\t\t\t\t\t\ttokens[ j ].t1 = tokens[ j + 1 ].t0;\n\t\t\t\t\telse\n\t\t\t\t\t\ts1 = k;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\twhile( this->energy[ k ] < thold && k > s0 )\n\t\t\t\t\t\tk--;\n\t\t\t\t\ts1 = k;\n\t\t\t\t\ttokens[ j ].t1 = sample_to_timestamp( k );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nstatic std::string to_timestamp( int64_t t, bool comma = false )\n{\n\tint64_t msec = t * 10;\n\tint64_t hr = msec / ( 1000 * 60 * 60 );\n\tmsec = msec - hr * ( 1000 * 60 * 60 );\n\tint64_t min = msec / ( 1000 * 60 );\n\tmsec = msec - min * ( 1000 * 60 );\n\tint64_t sec = msec / 1000;\n\tmsec = msec - sec * 1000;\n\n\tchar buf[ 32 ];\n\tsnprintf( buf, sizeof( buf ), \"%02d:%02d:%02d%s%03d\", (int)hr, (int)min, (int)sec, comma ? \",\" : \".\", (int)msec );\n\n\treturn std::string( buf );\n}\n\nclass ContextImpl::CurrentSpectrogramRaii\n{\n\tContextImpl* ctx;\npublic:\n\tCurrentSpectrogramRaii( ContextImpl* c, iSpectrogram& mel )\n\t{\n\t\tctx = c;\n\t\tc->currentSpectrogram = &mel;\n\t}\n\t~CurrentSpectrogramRaii()\n\t{\n\t\tctx->currentSpectrogram = nullptr;\n\t}\n};\n\nHRESULT COMLIGHTCALL ContextImpl::runFullImpl( const sFullParams& params, const sProgressSink& progress, iSpectrogram& mel )\n{\n\tauto ts = device.setForCurrentThread();\n\tconst Whisper::Vocabulary& vocab = model.shared->vocab;\n\n\t// Ported from whisper_full() function\n\tresult_all.clear();\n\tif( params.flag( eFullParamsFlags::SpeedupAudio ) )\n\t{\n\t\tlogError( u8\"GPU model doesn't implement the SpeedupAudio flag\" );\n\t\treturn E_NOTIMPL;\n\t}\n\n\tCurrentSpectrogramRaii _cs( this, mel );\n\tconst int seek_start = params.offset_ms / 10;\n\tconst int seek_end = seek_start + ( params.duration_ms == 0 ? (int)mel.getLength() : params.duration_ms / 10 );\n\n\t// if length of spectrogram is less than 1s (100 samples), then return\n\t// basically don't process anything that is less than 1s\n\t// see issue #39: https://github.com/ggerganov/whisper.cpp/issues/39\n\tif( seek_end < 100 + seek_start )\n\t\treturn S_FALSE;\n\n\t// the accumulated text context so far\n\tif( params.flag( eFullParamsFlags::NoContext ) )\n\t\tprompt_past.clear();\n\n\t// prepend the prompt tokens to the prompt_past\n\tif( params.prompt_tokens && params.prompt_n_tokens > 0 )\n\t{\n\t\t// parse tokens from the pointer\n\t\tfor( int i = 0; i < params.prompt_n_tokens; i++ )\n\t\t\tprompt_past.push_back( params.prompt_tokens[ i ] );\n\t\tstd::rotate( prompt_past.begin(), prompt_past.end() - params.prompt_n_tokens, prompt_past.end() );\n\t}\n\n\t// overwrite audio_ctx\n\texp_n_audio_ctx = params.audio_ctx;\n\n\t// these tokens determine the task that will be performed\n\tstd::vector<whisper_token> prompt_init = { vocab.token_sot };\n\tif( vocab.is_multilingual() )\n\t{\n\t\tint langId = lookupLanguageId( params.language );\n\t\tif( langId < 0 )\n\t\t{\n\t\t\tchar lang[ 5 ];\n\t\t\t*(uint32_t*)( &lang[ 0 ] ) = params.language;\n\t\t\tlang[ 4 ] = '\\0';\n\t\t\tlogError( u8\"%s: unknown language '%s'\", __func__, lang );\n\t\t\treturn E_INVALIDARG;\n\t\t}\n\n\t\tprompt_init.push_back( vocab.token_sot + 1 + langId );\n\t\tif( params.flag( eFullParamsFlags::Translate ) )\n\t\t\tprompt_init.push_back( vocab.token_translate );\n\t\telse\n\t\t\tprompt_init.push_back( vocab.token_transcribe );\n\t}\n\n\t// int progress_prev = 0;\n\t// int progress_step = 5;\n\n\tstd::vector<sTokenData> tokens_cur;\n\ttokens_cur.reserve( model.parameters.n_text_ctx );\n\tstd::vector<whisper_token> prompt;\n\tprompt.reserve( model.parameters.n_text_ctx );\n\n\t// main loop\n\tint seek = seek_start;\n\t// Start measuring \"Run\" profiler value, both CPU and GPU times\n\tauto prof = context.completeProfiler();\n\tbool stoppedPrematurely = false;\n\n\tif( params.flag( eFullParamsFlags::NoContext ) )\n\t{\n\t\tCHECK( context.clearState() );\n\t}\n\n\twhile( true )\n\t{\n\t\tif( nullptr != progress.pfn )\n\t\t{\n\t\t\tconst int pos = seek - seek_start;\n\t\t\tconst int total = seek_end - seek_start;\n\t\t\tconst double percentage = (double)pos / (double)total;\n\t\t\tauto cb = profiler.cpuBlock( eCpuBlock::Callbacks );\n\t\t\tCHECK( progress.pfn( percentage, this, progress.pv ) );\n\t\t}\n\n\t\tif( seek + 100 >= seek_end )\n\t\t\tbreak;\n\n\t\tif( nullptr != params.encoder_begin_callback )\n\t\t{\n\t\t\tauto cb = profiler.cpuBlock( eCpuBlock::Callbacks );\n\t\t\tHRESULT hr = params.encoder_begin_callback( this, params.encoder_begin_callback_user_data );\n\t\t\tif( FAILED( hr ) )\n\t\t\t\treturn hr;\n\t\t\tif( hr != S_OK )\n\t\t\t{\n\t\t\t\tstoppedPrematurely = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\t// encode audio features starting at offset seek\n\t\tCHECK( encode( mel, seek ) );\n\n\t\tint n_past = 0;\n\t\tprompt.clear();\n\n\t\t// if we have already generated some text, use it as a prompt to condition the next generation\n\t\tif( !prompt_past.empty() )\n\t\t{\n\t\t\tint n_take = std::min( std::min( params.n_max_text_ctx, model.parameters.n_text_ctx / 2 ), int( prompt_past.size() ) );\n\n\t\t\tprompt = { vocab.token_prev };\n\t\t\tprompt.insert( prompt.begin() + 1, prompt_past.end() - n_take, prompt_past.end() );\n\n\t\t\tprompt_past.clear();\n\t\t\tprompt_past.insert( prompt_past.end(), prompt.begin() + 1, prompt.end() );\n\t\t}\n\n\t\tprompt.insert( prompt.end(), prompt_init.begin(), prompt_init.end() );\n\n\t\tint seek_delta = 100 * WHISPER_CHUNK_SIZE;\n\n\t\t// print the prompt\n\t\t//printf(\"\\n\\n\");\n\t\t//for (int i = 0; i < prompt.size(); i++) {\n\t\t//    printf(\"%s: prompt[%d] = %s\\n\", __func__, i, ctx->vocab.id_to_token[prompt[i]].c_str());\n\t\t//}\n\t\t//printf(\"\\n\\n\");\n\n\t\t// the accumulated transcription in the current iteration\n\t\tint result_len = 0;\n\t\ttokens_cur.clear();\n\n\t\tbool failed = false;\n\t\tbool has_ts = false; // have we already sampled a non-beg timestamp token for the current segment?\n\n\t\t{\n\t\t\t// Measure \"Decode\" profiler value, both CPU and GPU times\n\t\t\tauto prof = context.decodeProfiler();\n\t\t\tfor( int i = 0, n_max = model.parameters.n_text_ctx / 2 - 4; i < n_max; i++ )\n\t\t\t{\n\t\t\t\tCHECK( decode( prompt.data(), prompt.size(), n_past, params.cpuThreads ) );\n\n\t\t\t\tn_past += (int)prompt.size();\n\t\t\t\tprompt.clear();\n\n\t\t\t\t// very basic greedy sampling strategy:\n\t\t\t\t//\n\t\t\t\t//   - always take the most probable token\n\t\t\t\t//\n\t\t\t\t// more sophisticated sampling strategies could be implemented here, but we keep it simple\n\t\t\t\t// feel free to experiment!\n\t\t\t\t//\n\t\t\t\t{\n\t\t\t\t\tauto p = profiler.cpuBlock( eCpuBlock::Sample );\n\t\t\t\t\tconst sTokenData token = ( i == 0 ) ? sampleTimestamp( true ) : sampleBest();\n\n\t\t\t\t\t// timestamp token - update sliding window\n\t\t\t\t\tif( token.id > vocab.token_beg )\n\t\t\t\t\t{\n\t\t\t\t\t\tconst int seek_delta_new = 2 * ( token.id - vocab.token_beg );\n\n\t\t\t\t\t\t// do not allow to go back in time\n\t\t\t\t\t\tif( has_ts && seek_delta > seek_delta_new && result_len < i )\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tseek_delta = seek_delta_new;\n\t\t\t\t\t\tresult_len = i + 1;\n\t\t\t\t\t\thas_ts = true;\n\t\t\t\t\t}\n\n\t\t\t\t\t// add it to the context\n\t\t\t\t\tprompt.push_back( token.id );\n\t\t\t\t\ttokens_cur.push_back( token );\n\n\t\t\t\t\t//{\n\t\t\t\t\t//    const auto tt = token.pt > 0.10 ? ctx->vocab.id_to_token[token.tid] : \"[?]\";\n\t\t\t\t\t//    printf(\"%s: %10s %6d %6.3f '%s'\\n\", __func__, tt.c_str(), token.id, token.pt, ctx->vocab.id_to_token[token.id].c_str());\n\t\t\t\t\t//}\n\n\t\t\t\t\t// end of segment\n\t\t\t\t\tif( token.id == vocab.token_eot ||                  // end of text token\n\t\t\t\t\t\t( params.max_tokens > 0 && i >= params.max_tokens ) || // max tokens per segment reached\n\t\t\t\t\t\t( has_ts && seek + seek_delta + 100 >= seek_end )     // end of audio reached\n\t\t\t\t\t\t)\n\t\t\t\t\t{\n\t\t\t\t\t\tif( result_len == 0 )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif( seek + seek_delta + 100 >= seek_end )\n\t\t\t\t\t\t\t\tresult_len = i + 1;\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tfailed = true;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif( params.flag( eFullParamsFlags::SingleSegment ) )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tresult_len = i + 1;\n\t\t\t\t\t\t\tseek_delta = 100 * WHISPER_CHUNK_SIZE;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// sometimes, the decoding can get stuck in a repetition loop\n\t\t\t\t// this is a simple strategy to avoid such cases - we simply flag the decoding as failed and advance\n\t\t\t\t// the sliding window by 1 second\n\t\t\t\tif( i == n_max - 1 && ( result_len == 0 || seek_delta < 100 * WHISPER_CHUNK_SIZE / 2 ) )\n\t\t\t\t{\n\t\t\t\t\tfailed = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif( failed )\n\t\t{\n\t\t\tlogError( u8\"%s: failed to generate timestamp token - skipping one second\", __func__ );\n\t\t\tseek += 100;\n\t\t\tcontinue;\n\t\t}\n\n\t\t// shrink down to result_len\n\t\ttokens_cur.resize( result_len );\n\n\t\tfor( const auto& r : tokens_cur )\n\t\t\tprompt_past.push_back( r.id );\n\n\t\t// store the text from this iteration\n\t\tif( !tokens_cur.empty() )\n\t\t{\n\t\t\tint i0 = 0;\n\t\t\tint t0 = seek + 2 * ( tokens_cur.front().tid - vocab.token_beg );\n\t\t\tstd::string text = \"\";\n\n\t\t\tfor( int i = 0; i < (int)tokens_cur.size(); i++ )\n\t\t\t{\n\t\t\t\t//printf(\"%s: %18s %6.3f %18s %6.3f\\n\", __func__,\n\t\t\t\t//        ctx->vocab.id_to_token[tokens_cur[i].id].c_str(), tokens_cur[i].p,\n\t\t\t\t//        ctx->vocab.id_to_token[tokens_cur[i].tid].c_str(), tokens_cur[i].pt);\n\t\t\t\tif( params.flag( eFullParamsFlags::PrintSpecial ) || tokens_cur[ i ].id < vocab.token_eot )\n\t\t\t\t\ttext += vocab.string( tokens_cur[ i ].id );\n\n\t\t\t\tif( tokens_cur[ i ].id > vocab.token_beg && !params.flag( eFullParamsFlags::SingleSegment ) )\n\t\t\t\t{\n\t\t\t\t\tconst int t1 = seek + 2 * ( tokens_cur[ i ].tid - vocab.token_beg );\n\t\t\t\t\tif( !text.empty() )\n\t\t\t\t\t{\n\t\t\t\t\t\tconst bool speedUp = params.flag( eFullParamsFlags::SpeedupAudio );\n\t\t\t\t\t\tconst int tt0 = speedUp ? 2 * t0 : t0;\n\t\t\t\t\t\tconst int tt1 = speedUp ? 2 * t1 : t1;\n\n\t\t\t\t\t\tif( params.flag( eFullParamsFlags::PrintRealtime ) )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif( params.flag( eFullParamsFlags::PrintTimestamps ) )\n\t\t\t\t\t\t\t\tlogDebug( u8\"[%s --> %s]  %s\", to_timestamp( tt0 ).c_str(), to_timestamp( tt1 ).c_str(), text.c_str() );\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tlogDebug( u8\"%s\", text.c_str() );\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tresult_all.push_back( { tt0, tt1, text, {} } );\n\t\t\t\t\t\tfor( int j = i0; j <= i; j++ )\n\t\t\t\t\t\t\tresult_all.back().tokens.push_back( tokens_cur[ j ] );\n\n\t\t\t\t\t\tint n_new = 1;\n\n\t\t\t\t\t\tif( params.flag( eFullParamsFlags::TokenTimestamps ) )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\texpComputeTokenLevelTimestamps( (int)result_all.size() - 1, params.thold_pt, params.thold_ptsum );\n\t\t\t\t\t\t\tif( params.max_len > 0 )\n\t\t\t\t\t\t\t\tn_new = wrapSegment( params.max_len );\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif( nullptr != params.new_segment_callback )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tauto cb = profiler.cpuBlock( eCpuBlock::Callbacks );\n\t\t\t\t\t\t\tHRESULT hr = params.new_segment_callback( this, n_new, params.new_segment_callback_user_data );\n\t\t\t\t\t\t\tif( FAILED( hr ) )\n\t\t\t\t\t\t\t\treturn hr;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\ttext = \"\";\n\t\t\t\t\twhile( i < (int)tokens_cur.size() && tokens_cur[ i ].id > vocab.token_beg )\n\t\t\t\t\t\ti++;\n\t\t\t\t\ti--;\n\t\t\t\t\tt0 = t1;\n\t\t\t\t\ti0 = i + 1;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif( !text.empty() )\n\t\t\t{\n\t\t\t\tconst int t1 = seek + seek_delta;\n\n\t\t\t\tconst bool speedUp = params.flag( eFullParamsFlags::SpeedupAudio );\n\t\t\t\tconst int tt0 = speedUp ? 2 * t0 : t0;\n\t\t\t\tconst int tt1 = speedUp ? 2 * t1 : t1;\n\n\t\t\t\tif( params.flag( eFullParamsFlags::PrintRealtime ) )\n\t\t\t\t{\n\t\t\t\t\tif( params.flag( eFullParamsFlags::PrintTimestamps ) )\n\t\t\t\t\t\tlogDebug( u8\"[%s --> %s]  %s\", to_timestamp( tt0 ).c_str(), to_timestamp( tt1 ).c_str(), text.c_str() );\n\t\t\t\t\telse\n\t\t\t\t\t\tlogDebug( u8\"%s\", text.c_str() );\n\t\t\t\t}\n\n\t\t\t\tresult_all.push_back( { tt0, tt1, text, {} } );\n\t\t\t\tfor( int j = i0; j < (int)tokens_cur.size(); j++ )\n\t\t\t\t\tresult_all.back().tokens.push_back( tokens_cur[ j ] );\n\n\t\t\t\tint n_new = 1;\n\t\t\t\tif( params.flag( eFullParamsFlags::TokenTimestamps ) )\n\t\t\t\t{\n\t\t\t\t\texpComputeTokenLevelTimestamps( (int)result_all.size() - 1, params.thold_pt, params.thold_ptsum );\n\t\t\t\t\tif( params.max_len > 0 )\n\t\t\t\t\t\tn_new = wrapSegment( params.max_len );\n\t\t\t\t}\n\t\t\t\tif( nullptr != params.new_segment_callback )\n\t\t\t\t{\n\t\t\t\t\tauto cb = profiler.cpuBlock( eCpuBlock::Callbacks );\n\t\t\t\t\tHRESULT hr = params.new_segment_callback( this, n_new, params.new_segment_callback_user_data );\n\t\t\t\t\tif( FAILED( hr ) )\n\t\t\t\t\t\treturn hr;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tseek += seek_delta;\n\t}\n\n\tif( nullptr != progress.pfn && !stoppedPrematurely )\n\t{\n\t\tauto cb = profiler.cpuBlock( eCpuBlock::Callbacks );\n\t\tCHECK( progress.pfn( 1.0, this, progress.pv ) );\n\t}\n\treturn S_OK;\n}"
  },
  {
    "path": "Whisper/Whisper/ContextImpl.diarize.cpp",
    "content": "#include \"stdafx.h\"\n#include \"ContextImpl.h\"\nusing namespace Whisper;\n\nnamespace\n{\n\t// Offset the timestamp with mediaTimeOffset to find the time relative to the start of the iSpectrogram buffer,\n\t// then scale from 100 nanosecond ticks into the Whisper's 10ms chunks, rounding down\n\tinline int64_t chunkOffset( int64_t time, int64_t mediaTimeOffset )\n\t{\n\t\ttime -= mediaTimeOffset;\n\t\treturn ( time * 100 ) / 10'000'000;\n\t}\n\n\t// Compute per-channel sum of std::absf( pcm ) in the specified buffer,\n\t// and return left / right numbers in the lower 2 lanes of the SSE vector\n\tinline __m128 __vectorcall computeChannelsEnergy( const std::vector<StereoSample>& sourceVector )\n\t{\n\t\t// Might be possible to implement way more sophisticated, and precise, version of this function.\n\t\t// For example, compute these 3 metrics with VAD code, and cluster the numbers somehow.\n\t\t// Not doing that currently; instead, replicating the simple version from the whisper.cpp original version.\n\n\t\tconst StereoSample* rsi = sourceVector.data();\n\t\tconst StereoSample* const rsiEnd = rsi + sourceVector.size();\n\t\tconst StereoSample* const rsiEndAligned = rsi + ( sourceVector.size() & ( ~(size_t)1 ) );\n\n\t\t// Move 0x7FFFFFFF to lowest lane of the int32 vector;\n\t\t// unlike float scalars or all vectors, integer scalar constants are in the instruction stream\n\t\t__m128i absMaskInt = _mm_cvtsi32_si128( (int)0x7FFFFFFFu );\n\t\t// Broadcast over the complete vector\n\t\tabsMaskInt = _mm_shuffle_epi32( absMaskInt, 0 );\n\t\t// Bitcast to FP32 vector, for _mm_and_ps instruction\n\t\tconst __m128 absMask = _mm_castsi128_ps( absMaskInt );\n\n\t\t__m128 acc = _mm_setzero_ps();\n\t\tfor( ; rsi < rsiEndAligned; rsi += 2 )\n\t\t{\n\t\t\t__m128 v = _mm_loadu_ps( (const float*)rsi );\n\t\t\tv = _mm_and_ps( v, absMask );\n\t\t\tacc = _mm_add_ps( acc, v );\n\t\t}\n\t\tif( rsi != rsiEnd )\n\t\t{\n\t\t\t__m128 v = _mm_castpd_ps( _mm_load_sd( (const double*)rsi ) );\n\t\t\tv = _mm_and_ps( v, absMask );\n\t\t\tacc = _mm_add_ps( acc, v );\n\t\t}\n\n\t\t// Return acc.xy + acc.zw\n\t\tacc = _mm_add_ps( acc, _mm_movehl_ps( acc, acc ) );\n\t\treturn acc;\n\t}\n\n\tinline eSpeakerChannel produceResult( const __m128 ev )\n\t{\n\t\t// Original code did following:\n\t\t// if( energy0 > 1.1 * energy1 ) speaker = \"(speaker 0)\"; else if( energy1 > 1.1 * energy0 ) speaker = \"(speaker 1)\"; else speaker = \"(speaker ?)\";\n\n\t\t// Flip left/right channels\n\t\t__m128 tmp = _mm_shuffle_ps( ev, ev, _MM_SHUFFLE( 3, 2, 0, 1 ) );\n\t\t// Multiply by the magic number\n\t\ttmp = _mm_mul_ps( tmp, _mm_set1_ps( 1.1f ) );\n\t\t// Compare for ev > tmp\n\t\ttmp = _mm_cmpgt_ps( ev, tmp );\n\t\tconst uint32_t mask = (uint32_t)_mm_movemask_ps( tmp ) & 0b11;\n\n\t\tassert( mask != 0b11 );\t// That would mean the following is true: ( ( left > right * 1.1 ) && ( right > left * 1.1 ) )\n\n\t\treturn (eSpeakerChannel)mask;\n\t}\n}\n\nHRESULT COMLIGHTCALL ContextImpl::detectSpeaker( const sTimeInterval& time, eSpeakerChannel& result ) const noexcept\n{\n\t// Ensure we have the spectrogram\n\tif( nullptr == currentSpectrogram )\n\t{\n\t\tlogError( u8\"Because the audio is streamed, iContext.detectSpeaker() method only works when called from the callbacks\" );\n\t\treturn OLE_E_BLANK;\n\t}\n\n\t// Load the timestamps\n\tint64_t begin = (int64_t)time.begin.ticks;\n\tint64_t end = (int64_t)time.end.ticks;\n\t// Offset + scale into chunks\n\tbegin = chunkOffset( begin, mediaTimeOffset );\n\tend = chunkOffset( end, mediaTimeOffset );\n\n\tint64_t len = end - begin;\n\tif( len <= 0 )\n\t{\n\t\tresult = eSpeakerChannel::Unsure;\n\t\treturn S_OK;\n\t}\n\n\t// Extract the slice of stereo PCM data\n\tHRESULT hr = currentSpectrogram->copyStereoPcm( (size_t)begin, (size_t)len, diarizeBuffer );\n\tif( hr == OLE_E_BLANK )\n\t{\n\t\tresult = eSpeakerChannel::NoStereoData;\n\t\treturn S_OK;\n\t}\n\tCHECK( hr );\n\n\tconst __m128 energyVec = computeChannelsEnergy( diarizeBuffer );\n\tresult = produceResult( energyVec );\n\treturn S_OK;\n}"
  },
  {
    "path": "Whisper/Whisper/ContextImpl.h",
    "content": "#pragma once\n#include \"../API/iContext.cl.h\"\n#include \"../ComLightLib/comLightServer.h\"\n#include \"WhisperContext.h\"\n#include \"Spectrogram.h\"\n#include \"TranscribeResult.h\"\n#include \"sTokenData.h\"\n#include \"../ML/Device.h\"\n\nnamespace Whisper\n{\n\tclass ContextImpl : public ComLight::ObjectRoot<iContext>\n\t{\n\t\tconst DirectCompute::Device& device;\n\t\tconst WhisperModel& model;\n\t\tComLight::CComPtr<iModel> modelPtr;\n\t\tDirectCompute::WhisperContext context;\n\t\tSpectrogram spectrogram;\n\t\tint64_t mediaTimeOffset = 0;\n\t\tiSpectrogram* currentSpectrogram = nullptr;\n\t\tclass CurrentSpectrogramRaii;\n\t\tProfileCollection profiler;\n\n\t\tHRESULT COMLIGHTCALL getModel( iModel** pp ) override final;\n\t\tHRESULT COMLIGHTCALL timingsPrint() override final;\n\t\tHRESULT COMLIGHTCALL timingsReset() override final;\n\t\tHRESULT COMLIGHTCALL fullDefaultParams( eSamplingStrategy strategy, sFullParams* rdi ) override final;\n\t\tHRESULT COMLIGHTCALL runFullImpl( const sFullParams& params, const sProgressSink& progress, iSpectrogram& mel );\n\t\tHRESULT COMLIGHTCALL runFull( const sFullParams& params, const iAudioBuffer* buffer ) override final;\n\t\tHRESULT COMLIGHTCALL runStreamed( const sFullParams& params, const sProgressSink& progress, const iAudioReader* reader ) override final;\n\t\tHRESULT COMLIGHTCALL runCapture( const sFullParams& params, const sCaptureCallbacks& callbacks, const iAudioCapture* reader ) override final;\n\n\t\tstruct Segment\n\t\t{\n\t\t\tint64_t t0;\n\t\t\tint64_t t1;\n\t\t\tmutable std::string text;\n\t\t\tstd::vector<sTokenData> tokens;\n\t\t\tsize_t memoryUsage() const;\n\t\t};\n\t\tstd::vector<Segment> result_all;\n\n\t\tstd::vector<whisper_token> prompt_past;\n\n\t\t// [EXPERIMENTAL] token-level timestamps data\n\t\tint64_t t_beg = 0;\n\t\tint64_t t_last = 0;\n\t\twhisper_token tid_last = 0;\n\t\tstd::vector<float> energy; // PCM signal energy\n\n\t\t// [EXPERIMENTAL] speed-up techniques\n\t\tint32_t exp_n_audio_ctx = 0; // 0 - use default\n\n\t\tHRESULT encode( iSpectrogram& mel, int seek );\n\t\tHRESULT decode( const int* tokens, size_t length, int n_past, int threads );\n\t\tsTokenData sampleBest( const float* probs, bool force_timestamp, bool is_initial );\n\t\tsTokenData sampleBest();\n\t\tsTokenData sampleTimestamp( bool initial );\n\t\tint wrapSegment( int max_len );\n\t\tvoid expComputeTokenLevelTimestamps( int i_segment, float thold_pt, float thold_ptsum );\n\n\t\tstd::vector<float> probs;\n\t\tstd::vector<std::pair<double, Vocabulary::id>> probs_id;\n\n\t\tmutable TranscribeResultStatic results;\n\n\t\tHRESULT COMLIGHTCALL makeResults( eResultFlags flags, TranscribeResult& res, bool moveStrings ) const noexcept;\n\n\t\tHRESULT COMLIGHTCALL getResults( eResultFlags flags, iTranscribeResult** pp ) const noexcept override final;\n\t\tHRESULT COMLIGHTCALL detectSpeaker( const sTimeInterval& time, eSpeakerChannel& result ) const noexcept override final;\n\n\t\tint defaultThreadsCount() const;\n\n\t\t__m128i getMemoryUse() const;\n\t\tmutable std::vector<StereoSample> diarizeBuffer;\n\n\tpublic:\n\n\t\tContextImpl( const DirectCompute::Device& dev, const WhisperModel& modelData, iModel* modelPointer );\n\t};\n}"
  },
  {
    "path": "Whisper/Whisper/ContextImpl.misc.cpp",
    "content": "﻿#include \"stdafx.h\"\n#include \"ContextImpl.h\"\n#include <mfapi.h>\n#include \"MelStreamer.h\"\n#include \"../API/iMediaFoundation.cl.h\"\n#include \"../Utils/Trace/tracing.h\"\nusing namespace Whisper;\n\nstatic int getCpuCoresCount()\n{\n\tDWORD bufferSize = 0;\n\tGetLogicalProcessorInformation( NULL, &bufferSize );\n\n\t// The SYSTEM_LOGICAL_PROCESSOR_INFORMATION structure has a uint64_t field\n\t// Ideally need to align by 8 bytes, and that's why uint64_t type for the storage\n\tstd::unique_ptr<uint64_t[]> buffer = std::make_unique<uint64_t[]>( ( bufferSize + 7 ) / 8 );\n\n\tSYSTEM_LOGICAL_PROCESSOR_INFORMATION* ptr = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION*)buffer.get();\n\tif( !GetLogicalProcessorInformation( ptr, &bufferSize ) )\n\t{\n\t\tHRESULT hr = getLastHr();\n\t\tlogWarningHr( hr, u8\"GetLogicalProcessorInformation\" );\n\t\treturn 0;\n\t}\n\n\tDWORD byteOffset = 0;\n\tint physicalCores = 0;\n\twhile( byteOffset < bufferSize )\n\t{\n\t\tif( ptr->Relationship == RelationProcessorCore )\n\t\t\tphysicalCores++;\n\t\tbyteOffset += sizeof( SYSTEM_LOGICAL_PROCESSOR_INFORMATION );\n\t\tptr++;\n\t}\n\treturn physicalCores;\n}\n\nint ContextImpl::defaultThreadsCount() const\n{\n#if BUILD_HYBRID_VERSION\n\tconst bool isHybrid = !model.shared->hybridTensors.layers.empty();\n#else\n\tconstexpr bool isHybrid = false;\n#endif\n\n\tSYSTEM_INFO si;\n\tGetSystemInfo( &si );\n\tconst int hardwareThreads = (int)si.dwNumberOfProcessors;\n\n\tif( !isHybrid )\n\t\treturn std::min( hardwareThreads, 4 );\n\n\t// It seems the CPU decoder in the hybrid context doesn’t scale well with count of hardware threads, but it does scale with count of physical cores.\n\tint cores = getCpuCoresCount();\n\tif( cores > 1 )\n\t\treturn cores;\n\n\treturn hardwareThreads;\n}\n\nHRESULT COMLIGHTCALL ContextImpl::fullDefaultParams( eSamplingStrategy strategy, sFullParams* rdi )\n{\n\t// whisper_full_default_params\n\tif( nullptr == rdi )\n\t\treturn E_POINTER;\n\tmemset( rdi, 0, sizeof( sFullParams ) );\n\n\trdi->strategy = strategy;\n\trdi->cpuThreads = defaultThreadsCount();\n\trdi->n_max_text_ctx = 16384;\n\trdi->flags = eFullParamsFlags::PrintProgress | eFullParamsFlags::PrintTimestamps;\n\trdi->thold_pt = 0.01f;\n\trdi->thold_ptsum = 0.01f;\n\trdi->language = makeLanguageKey( \"en\" );\n\n\tswitch( strategy )\n\t{\n\tcase eSamplingStrategy::Greedy:\n\t\trdi->beam_search.n_past = -1;\n\t\trdi->beam_search.beam_width = -1;\n\t\trdi->beam_search.n_best = -1;\n\t\tbreak;\n\tcase eSamplingStrategy::BeamSearch:\n\t\trdi->greedy.n_past = -1;\n\t\trdi->beam_search.beam_width = 10;\n\t\trdi->beam_search.n_best = 5;\n\t\tbreak;\n\tdefault:\n\t\tlogError( u8\"Unknown sampling strategy %i\", (int)strategy );\n\t\treturn E_INVALIDARG;\n\t}\n\treturn S_OK;\n}\n\nHRESULT COMLIGHTCALL ContextImpl::getModel( iModel** pp )\n{\n\tif( nullptr == pp )\n\t\treturn E_POINTER;\n\tif( !modelPtr )\n\t\treturn OLE_E_BLANK;\n\t*pp = modelPtr;\n\tmodelPtr->AddRef();\n\treturn S_OK;\n}\n\nsize_t ContextImpl::Segment::memoryUsage() const\n{\n\treturn text.capacity() + vectorMemoryUse( tokens );\n}\n\n__m128i ContextImpl::getMemoryUse() const\n{\n\t// Misc. system RAM\n\tsize_t cb = vectorMemoryUse( result_all );\n\tfor( const auto& r : result_all )\n\t\tcb += r.memoryUsage();\n\tcb += vectorMemoryUse( prompt_past );\n\tcb += vectorMemoryUse( energy );\n\tcb += vectorMemoryUse( probs );\n\tcb += vectorMemoryUse( probs_id );\n\tcb += vectorMemoryUse( results.segments );\n\tcb += vectorMemoryUse( results.tokens );\n\tcb += spectrogram.memoryUsage();\n\n\t__m128i res = setLow_size( cb );\n\t// Add all the VRAM in the temporary buffers\n\tres = _mm_add_epi64( res, context.getMemoryUse() );\n\treturn res;\n}\n\nnamespace\n{\n\tstruct PrintedSize\n\t{\n\t\tdouble val;\n\t\tconst char* unit;\n\t\tPrintedSize( int64_t cb )\n\t\t{\n\t\t\tif( cb < ( 1 << 10 ) )\n\t\t\t{\n\t\t\t\tval = (double)cb;\n\t\t\t\tunit = \"bytes\";\n\t\t\t}\n\t\t\telse if( cb < ( 1 << 20 ) )\n\t\t\t{\n\t\t\t\tval = (double)cb * ( 1.0 / ( 1 << 10 ) );\n\t\t\t\tunit = \"KB\";\n\t\t\t}\n\t\t\telse if( cb < ( 1 << 30 ) )\n\t\t\t{\n\t\t\t\tval = (double)cb * ( 1.0 / ( 1 << 20 ) );\n\t\t\t\tunit = \"MB\";\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tval = (double)cb * ( 1.0 / ( 1 << 30 ) );\n\t\t\t\tunit = \"GB\";\n\t\t\t}\n\t\t}\n\t};\n\n\tstatic void __declspec( noinline ) logMemoryUse( const char* what, __m128i cb )\n\t{\n\t\tPrintedSize sys{ _mm_cvtsi128_si64( cb ) };\n\t\tPrintedSize vram{ _mm_extract_epi64( cb, 1 ) };\n\t\tlogInfo( u8\"%s\\t%g %s RAM, %g %s VRAM\", what, sys.val, sys.unit, vram.val, vram.unit );\n\t}\n}\n\nHRESULT COMLIGHTCALL ContextImpl::timingsPrint()\n{\n\tprofiler.print();\n\n\tauto ts = device.setForCurrentThread();\n\tconst __m128i memModel = model.getMemoryUse();\n\tconst __m128i memContext = getMemoryUse();\n\tlogInfo( u8\"    Memory Usage\" );\n\tlogMemoryUse( \"Model\", memModel );\n\tlogMemoryUse( \"Context\", memContext );\n\tlogMemoryUse( \"Total\", _mm_add_epi64( memModel, memContext ) );\n\treturn S_OK;\n}\n\nHRESULT COMLIGHTCALL ContextImpl::timingsReset()\n{\n\tprofiler.reset();\n\treturn S_OK;\n}\n\nHRESULT COMLIGHTCALL ContextImpl::getResults( eResultFlags flags, iTranscribeResult** pp ) const noexcept\n{\n\tif( nullptr == pp )\n\t\treturn E_POINTER;\n\n\tif( flags & eResultFlags::NewObject )\n\t{\n\t\tComLight::CComPtr<ComLight::Object<TranscribeResult>> obj;\n\t\tCHECK( ComLight::Object<TranscribeResult>::create( obj ) );\n\t\tCHECK( makeResults( flags, *obj, true ) );\n\t\tobj.detach( pp );\n\t\treturn S_OK;\n\t}\n\telse\n\t{\n\t\tCHECK( makeResults( flags, results, false ) );\n\t\tiTranscribeResult* res = &results;\n\t\tres->AddRef();\n\t\t*pp = res;\n\t\treturn S_OK;\n\t}\n}\n\ninline int64_t scaleTime( int64_t wisperTicks )\n{\n\treturn MFllMulDiv( wisperTicks, 10'000'000, 100, 0 );\n}\n\nHRESULT COMLIGHTCALL ContextImpl::makeResults( eResultFlags flags, TranscribeResult& res, bool moveStrings ) const noexcept\n{\n\tconst size_t segments = result_all.size();\n\t// Resize both vectors\n\ttry\n\t{\n\t\tres.segments.resize( segments );\n\t\tif( flags & eResultFlags::Tokens )\n\t\t{\n\t\t\tsize_t tc = 0;\n\t\t\tfor( const auto& s : result_all )\n\t\t\t\ttc += s.tokens.size();\n\t\t\tres.tokens.resize( tc );\n\t\t}\n\t\telse\n\t\t\tres.tokens.clear();\n\n\t\tres.segmentsText.clear();\n\t\tif( moveStrings )\n\t\t\tres.segmentsText.resize( segments );\n\t}\n\tcatch( const std::bad_alloc& )\n\t{\n\t\treturn E_OUTOFMEMORY;\n\t}\n\n\tconst Whisper::Vocabulary& vocab = model.shared->vocab;\n\tconst Vocabulary::id tokenEot = vocab.token_eot;\n\n\tsize_t tokensSoFar = 0;\n\tfor( size_t i = 0; i < segments; i++ )\n\t{\n\t\tsSegment& rdi = res.segments[ i ];\n\t\tconst auto& rsi = result_all[ i ];\n\n\t\tif( moveStrings )\n\t\t{\n\t\t\tres.segmentsText[ i ].swap( rsi.text );\n\t\t\trdi.text = res.segmentsText[ i ].c_str();\n\t\t}\n\t\telse\n\t\t\trdi.text = rsi.text.c_str();\n\n\t\tif( flags & eResultFlags::Timestamps )\n\t\t{\n\t\t\t// Offset the time relative to the start of the media\n\t\t\trdi.time.begin = scaleTime( rsi.t0 ) + mediaTimeOffset;\n\t\t\trdi.time.end = scaleTime( rsi.t1 ) + mediaTimeOffset;\n\t\t}\n\t\telse\n\t\t\tstore16( &rdi.time, _mm_setzero_si128() );\n\n\t\trdi.firstToken = (uint32_t)tokensSoFar;\n\t\tconst size_t tc = rsi.tokens.size();\n\t\trdi.countTokens = (uint32_t)tc;\n\n\t\tif( flags & eResultFlags::Tokens )\n\t\t{\n\t\t\tfor( size_t i = 0; i < tc; i++ )\n\t\t\t{\n\t\t\t\tsToken& rdi = res.tokens[ tokensSoFar + i ];\n\t\t\t\tconst auto& src = rsi.tokens[ i ];\n\t\t\t\trdi.text = vocab.string( src.id );\n\n\t\t\t\tif( flags & eResultFlags::Timestamps )\n\t\t\t\t{\n\t\t\t\t\t// Offset the time relative to the start of the media\n\t\t\t\t\trdi.time.begin = scaleTime( src.t0 ) + mediaTimeOffset;\n\t\t\t\t\trdi.time.end = scaleTime( src.t1 ) + mediaTimeOffset;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tstore16( &rdi.time, _mm_setzero_si128() );\n\n\t\t\t\t// Copy 4 floats with unaligned load and store instructions\n\t\t\t\t_mm_storeu_ps( &rdi.probability, _mm_loadu_ps( &src.p ) );\n\n\t\t\t\trdi.id = src.id;\n\n\t\t\t\tuint32_t flags = 0;\n\t\t\t\tif( src.id >= tokenEot )\n\t\t\t\t\tflags |= (uint32_t)eTokenFlags::Special;\n\t\t\t\trdi.flags = (eTokenFlags)flags;\n\t\t\t}\n\t\t}\n\t\ttokensSoFar += tc;\n\t}\n\treturn S_OK;\n}\n\nint ContextImpl::wrapSegment( int max_len )\n{\n\t// whisper_wrap_segment\n\tauto segment = result_all.back();\n\tint res = 1;\n\tint acc = 0;\n\tstd::string text;\n\tconst Whisper::Vocabulary& vocab = model.shared->vocab;\n\tconst int tokenEot = vocab.token_eot;\n\n\tfor( int i = 0; i < (int)segment.tokens.size(); i++ )\n\t{\n\t\tconst auto& token = segment.tokens[ i ];\n\t\tif( token.id >= tokenEot )\n\t\t\tcontinue;\n\n\t\tconst char* txt = vocab.string( token.id );\n\t\tconst int cur = (int)strlen( txt );\n\n\t\tif( acc + cur > max_len && i > 0 )\n\t\t{\n\t\t\t// split here\n\t\t\tresult_all.back().text = std::move( text );\n\t\t\tresult_all.back().t1 = token.t0;\n\t\t\tresult_all.back().tokens.resize( i );\n\n\t\t\tresult_all.push_back( {} );\n\t\t\tresult_all.back().t0 = token.t0;\n\t\t\tresult_all.back().t1 = segment.t1;\n\n\t\t\t// add tokens [i, end] to the new segment\n\t\t\tresult_all.back().tokens.insert( result_all.back().tokens.end(), segment.tokens.begin() + i, segment.tokens.end() );\n\n\t\t\tacc = 0;\n\t\t\ttext = \"\";\n\n\t\t\tsegment = result_all.back();\n\t\t\ti = -1;\n\n\t\t\tres++;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tacc += cur;\n\t\t\ttext += txt;\n\t\t}\n\t}\n\n\tresult_all.back().text = std::move( text );\n\treturn res;\n}\n\nHRESULT COMLIGHTCALL ContextImpl::runFull( const sFullParams& params, const iAudioBuffer* buffer )\n{\n#if SAVE_DEBUG_TRACE\n\tTracing::vector( \"runFull.pcm.in\", buffer->getPcmMono(), buffer->countSamples() );\n#endif\n\tCHECK( buffer->getTime( mediaTimeOffset ) );\n\n\tauto profCompleteCpu = profiler.cpuBlock( eCpuBlock::RunComplete );\n\t{\n\t\tauto p = profiler.cpuBlock( eCpuBlock::Spectrogram );\n\t\tCHECK( spectrogram.pcmToMel( buffer, model.shared->filters, params.cpuThreads ) );\n\t}\n\n\tif( params.flag( eFullParamsFlags::TokenTimestamps ) )\n\t{\n\t\tt_beg = 0;\n\t\tt_last = 0;\n\t\ttid_last = 0;\n\t\tcomputeSignalEnergy( energy, buffer, 32 );\n\t}\n\n\ttry\n\t{\n\t\tsProgressSink progressSink{ nullptr, nullptr };\n\t\treturn runFullImpl( params, progressSink, spectrogram );\n\t}\n\tcatch( HRESULT hr )\n\t{\n\t\treturn hr;\n\t}\n}\n\nHRESULT COMLIGHTCALL ContextImpl::runStreamed( const sFullParams& params, const sProgressSink& progress, const iAudioReader* reader )\n{\n\tif( params.flag( eFullParamsFlags::TokenTimestamps ) )\n\t{\n\t\tlogError( u8\"eFullParamsFlags.TokenTimestamps flag is not supported in streaming mode\" );\n\t\treturn E_NOTIMPL;\n\t}\n\n\tmediaTimeOffset = 0;\n\tauto profCompleteCpu = profiler.cpuBlock( eCpuBlock::RunComplete );\n\n\ttry\n\t{\n\t\tif( params.cpuThreads > 1 )\n\t\t{\n\t\t\tMelStreamerThread mel{ model.shared->filters, profiler, reader, params.cpuThreads };\n\t\t\treturn runFullImpl( params, progress, mel );\n\t\t}\n\t\telse\n\t\t{\n\t\t\tMelStreamerSimple mel{ model.shared->filters, profiler, reader };\n\t\t\treturn runFullImpl( params, progress, mel );\n\t\t}\n\t}\n\tcatch( HRESULT hr )\n\t{\n\t\treturn hr;\n\t}\n}"
  },
  {
    "path": "Whisper/Whisper/DecoderInputBuffers.cpp",
    "content": "#include \"stdafx.h\"\n#include \"DecoderInputBuffers.h\"\n#include \"../D3D/createBuffer.h\"\n#include \"../D3D/MappedResource.h\"\nusing namespace DirectCompute;\n\nvoid DecoderInputBuffers::resize( uint32_t size )\n{\n\tif( 0 == size )\n\t\tthrow E_INVALIDARG;\n\n\tif( size <= m_capacity )\n\t{\n\t\tm_size = size;\n\t\treturn;\n\t}\n\n\tembd = nullptr;\n\n\t// Round up by 256, mostly for lulz\n\tconst uint32_t newCapacity = ( size + 0xFFu ) & ( ~( 0xFFu ) );\n\tconst size_t totalBytes = (size_t)4 * newCapacity;\n\n\tcheck( createBuffer( eBufferUse::Dynamic, totalBytes, &embd, nullptr, nullptr ) );\n\n\tm_capacity = newCapacity;\n\tm_size = size;\n}\n\nnamespace\n{\n\tstatic Tensor createView( ID3D11Buffer* buffer, uint32_t length )\n\t{\n\t\tTensor res;\n\n\t\tTensorGpuViews& views = res;\n\t\tcheck( views.create( buffer, DXGI_FORMAT_R32_UINT, length, false ) );\n\n\t\tres.ne = { length, 1, 1, 1 };\n\t\tres.setDenseStrides();\n\t\treturn res;\n\t}\n}\n\nTensor DecoderInputBuffers::embedding( const int* rsi ) const\n{\n\tif( nullptr == embd || m_size == 0 )\n\t\tthrow OLE_E_BLANK;\n\n\t// Upload the data\n\t{\n\t\tMappedResource mapped;\n\t\tcheck( mapped.map( embd, false ) );\n\t\tint* const rdi = (int*)mapped.data();\n\t\tmemcpy( rdi, rsi, m_size * (size_t)4 );\n\t}\n\n\treturn createView( embd, m_size );\n}\n\nvoid DecoderInputBuffers::clear()\n{\n\tembd = nullptr;\n\tm_size = 0;\n\tm_capacity = 0;\n}\n\nHRESULT DecoderInputBuffers::zeroMemory() const\n{\n\tif( nullptr == embd || m_size == 0 )\n\t\treturn S_FALSE;\n\n\tMappedResource mapped;\n\tCHECK( mapped.map( embd, false ) );\n\t__stosd( (DWORD*)mapped.data(), 0, m_capacity );\n\treturn S_OK;\n}"
  },
  {
    "path": "Whisper/Whisper/DecoderInputBuffers.h",
    "content": "#pragma once\n#include \"../ML/Tensor.h\"\n\nnamespace DirectCompute\n{\n\t// A dynamic buffer\n\tclass DecoderInputBuffers\n\t{\n\t\tCComPtr<ID3D11Buffer> embd;\n\t\tuint32_t m_size = 0;\n\t\tuint32_t m_capacity = 0;\n\n\tpublic:\n\n\t\tvoid resize( uint32_t size );\n\n\t\t// Create 1D tensor with R32_UINT elements, upload the source data\n\t\tTensor embedding( const int* rsi ) const;\n\n\t\tvoid clear();\n\n\t\t__m128i getMemoryUse() const\n\t\t{\n\t\t\tsize_t i = m_capacity;\n\t\t\ti *= sizeof( uint32_t );\n\t\t\treturn _mm_set_epi64x( (int64_t)i, 0 );\n\t\t}\n\n\t\tHRESULT zeroMemory() const;\n\t};\n}"
  },
  {
    "path": "Whisper/Whisper/DecoderResultBuffer.cpp",
    "content": "#include \"stdafx.h\"\n#include \"DecoderResultBuffer.h\"\n#include \"../D3D/MappedResource.h\"\nusing namespace DirectCompute;\n\nvoid DecoderResultBuffer::copyFromVram( const Tensor& rsi )\n{\n\tID3D11ShaderResourceView* srv = rsi;\n\tif( nullptr == srv )\n\t\tthrow OLE_E_BLANK;\n\tif( !rsi.isContinuous() )\n\t\tthrow E_INVALIDARG;\n\n\tconst uint32_t len = rsi.countElements();\n\tif( len > m_capacity )\n\t{\n\t\tbuffer = nullptr;\n\t\tCD3D11_BUFFER_DESC desc{ len * 4, 0, D3D11_USAGE_STAGING, D3D11_CPU_ACCESS_READ };\n\t\tcheck( device()->CreateBuffer( &desc, nullptr, &buffer ) );\n\t\tm_capacity = len;\n\t}\n\n\tCComPtr<ID3D11Resource> source;\n\tsrv->GetResource( &source );\n\t// Coordinates of a box are in bytes for buffers\n\tD3D11_BOX box;\n\tstore16( &box, _mm_setr_epi32( 0, 0, 0, (int)( len * 4 ) ) );\n\t*(uint64_t*)&box.bottom = 0x100000001ull;\n\tcontext()->CopySubresourceRegion( buffer, 0, 0, 0, 0, source, 0, &box );\n\tm_size = len;\n}\n\nvoid DecoderResultBuffer::copyToVector( std::vector<float>& vec ) const\n{\n\tvec.resize( m_size );\n\tif( vec.empty() )\n\t\tthrow OLE_E_BLANK;\n\n\tMappedResource mapped;\n\tcheck( mapped.map( buffer, true ) );\n\tmemcpy( vec.data(), mapped.data(), (size_t)4 * m_size );\n}\n\nvoid DecoderResultBuffer::clear()\n{\n\tbuffer = nullptr;\n\tm_size = m_capacity = 0;\n}"
  },
  {
    "path": "Whisper/Whisper/DecoderResultBuffer.h",
    "content": "#pragma once\n#include \"../ML/Tensor.h\"\n\nnamespace DirectCompute\n{\n\tclass DecoderResultBuffer\n\t{\n\t\tCComPtr<ID3D11Buffer> buffer;\n\t\tuint32_t m_size = 0;\n\t\tuint32_t m_capacity = 0;\n\n\tpublic:\n\n\t\tvoid copyFromVram( const Tensor& rsi );\n\n\t\tvoid copyToVector( std::vector<float>& vec ) const;\n\n\t\tuint32_t size() const\n\t\t{\n\t\t\treturn m_size;\n\t\t}\n\t\tvoid clear();\n\n\t\t__m128i getMemoryUse() const\n\t\t{\n\t\t\treturn bufferMemoryUsage( buffer );\n\t\t}\n\t};\n}"
  },
  {
    "path": "Whisper/Whisper/KeyValueBuffers.cpp",
    "content": "#include \"stdafx.h\"\n#include \"KeyValueBuffers.h\"\n#include \"../D3D/createBuffer.h\"\n#include \"../ML/mlUtils.h\"\nusing namespace DirectCompute;\n\nvoid AttentionBuffer::resize( uint32_t size )\n{\n\tif( size <= m_size )\n\t\treturn;\n\n\tbuffer = nullptr;\n\tcheck( createBuffer( eBufferUse::ReadWrite, (size_t)2 * size, &buffer, nullptr, nullptr ) );\n\tm_size = size;\n}\n\nTensor AttentionBuffer::view( uint32_t length, uint32_t offset ) const\n{\n\tif( length + offset > m_size )\n\t\tthrow E_BOUNDS;\n\tif( 0 == length )\n\t\tthrow E_INVALIDARG;\n\n\tCComPtr<ID3D11ShaderResourceView> srv;\n\tCComPtr<ID3D11UnorderedAccessView> uav;\n\n\tCD3D11_SHADER_RESOURCE_VIEW_DESC srvDesc{ D3D11_SRV_DIMENSION_BUFFER, DXGI_FORMAT_R16_FLOAT, offset, length };\n\tcheck( device()->CreateShaderResourceView( buffer, &srvDesc, &srv ) );\n\n\tCD3D11_UNORDERED_ACCESS_VIEW_DESC uavDesc{ D3D11_UAV_DIMENSION_BUFFER, DXGI_FORMAT_R16_FLOAT, offset, length };\n\tcheck( device()->CreateUnorderedAccessView( buffer, &uavDesc, &uav ) );\n\n\tTensorShape shape;\n\tshape.ne = { length, 1, 1, 1 };\n\tshape.setDenseStrides();\n\treturn Tensor( shape, srv, uav );\n}\n\nvoid KeyValueBuffers::resize( uint32_t size )\n{\n\tkeys.resize( size );\n\tvalues.resize( size );\n}\n\nHRESULT AttentionBuffer::zeroMemory() const\n{\n\tif( 0 == m_size )\n\t\treturn S_FALSE;\n\n\tCComPtr<ID3D11UnorderedAccessView> uav;\n\tCD3D11_UNORDERED_ACCESS_VIEW_DESC uavDesc{ D3D11_UAV_DIMENSION_BUFFER, DXGI_FORMAT_R16_FLOAT, 0, m_size };\n\tcheck( device()->CreateUnorderedAccessView( buffer, &uavDesc, &uav ) );\n\n\ttry\n\t{\n\t\tDirectCompute::zeroMemory( uav, m_size );\n\t\treturn S_OK;\n\t}\n\tcatch( HRESULT hr )\n\t{\n\t\treturn hr;\n\t}\n}\n\nHRESULT KeyValueBuffers::zeroMemory() const\n{\n\tCHECK( keys.zeroMemory() );\n\tCHECK( values.zeroMemory() );\n\treturn S_OK;\n}"
  },
  {
    "path": "Whisper/Whisper/KeyValueBuffers.h",
    "content": "#pragma once\n#include \"../ML/Tensor.h\"\n\nnamespace DirectCompute\n{\n\t// FP16 buffer for self-attention and cross-attention layers\n\tclass AttentionBuffer\n\t{\n\t\tCComPtr<ID3D11Buffer> buffer;\n\t\tuint32_t m_size = 0;\n\n\tpublic:\n\t\t// Create buffer for the specified count of elements\n\t\tvoid resize( uint32_t size );\n\n\t\t// Create an 1D tensor which references a slice of that buffer\n\t\tTensor view( uint32_t length, uint32_t offset ) const;\n\n\t\tvoid clear()\n\t\t{\n\t\t\tbuffer = nullptr;\n\t\t\tm_size = 0;\n\t\t}\n\n\t\tID3D11Buffer* getBuffer() const { return buffer; }\n\n\t\tuint32_t getSize() const { return m_size; }\n\n\t\tHRESULT zeroMemory() const;\n\t};\n\n\tstruct KeyValueBuffers\n\t{\n\t\tAttentionBuffer keys, values;\n\n\t\tvoid resize( uint32_t size );\n\n\t\tvoid clear()\n\t\t{\n\t\t\tkeys.clear();\n\t\t\tvalues.clear();\n\t\t}\n\n\t\t__m128i getMemoryUse() const\n\t\t{\n\t\t\tsize_t i = keys.getSize();\n\t\t\ti += values.getSize();\n\t\t\ti *= sizeof( uint16_t );\n\t\t\treturn setHigh_size( (int64_t)i );\t// They both are in VRAM\n\t\t}\n\n\t\tHRESULT zeroMemory() const;\n\t};\n}"
  },
  {
    "path": "Whisper/Whisper/Languages.cpp",
    "content": "#include \"stdafx.h\"\n#include \"Languages.h\"\n#include <atlcoll.h>\n#include \"../API/iContext.cl.h\"\n\nnamespace\n{\n\t// These structures are compiled into the DLL, in read only data section\n\tusing Lang = Whisper::sLanguageEntry;\n\n\tstatic const Lang s_languageData[] =\n\t{\n#include \"languageCodez.inl\"\n\t};\n\n\tusing Whisper::makeLanguageKey;\n\n\t// Values for the hash map\n\tstruct sLanguage\n\t{\n\t\tint id;\n\t\tconst char* name;\n\t};\n\n\tclass LanguageIDs\n\t{\n\t\tCAtlMap<uint32_t, sLanguage> map;\n\n\t\tvoid add( const char* code, int id, const char* name )\n\t\t{\n\t\t\tassert( strlen( code ) <= 4 );\n\t\t\tconst uint16_t key = makeLanguageKey( code );\n\t\t\tmap.SetAt( key, sLanguage{ id, name } );\n\t\t}\n\n\tpublic:\n\n\t\tLanguageIDs() :\n\t\t\tmap( 103u, 0.75f, 0.25f, 2.25f, 99 )\n\t\t{\n\t\t\tfor( const Lang& e : s_languageData )\n\t\t\t\tmap.SetAt( e.key, sLanguage{ e.id, e.name } );\n\t\t};\n\n\t\tint lookupId( const char* code ) const\n\t\t{\n\t\t\tconst uint32_t key = makeLanguageKey( code );\n\t\t\tauto p = map.Lookup( key );\n\t\t\treturn ( nullptr != p ) ? p->m_value.id : -1;\n\t\t}\n\n\t\tint lookupKey( uint32_t key ) const\n\t\t{\n\t\t\tauto p = map.Lookup( key );\n\t\t\treturn ( nullptr != p ) ? p->m_value.id : -1;\n\t\t}\n\n\t\tconst char* lookupName( const char* code ) const\n\t\t{\n\t\t\tconst uint32_t key = makeLanguageKey( code );\n\t\t\tauto p = map.Lookup( key );\n\t\t\treturn ( nullptr != p ) ? p->m_value.name : nullptr;\n\t\t}\n\t};\n\n\tstatic const LanguageIDs g_table;\n}\n\nnamespace Whisper\n{\n\tint lookupLanguageId( const char* code )\n\t{\n\t\treturn g_table.lookupId( code );\n\t}\n\tint lookupLanguageId( uint32_t key )\n\t{\n\t\treturn g_table.lookupKey( key );\n\t}\n\tconst char* lookupLanguageName( const char* code )\n\t{\n\t\treturn g_table.lookupName( code );\n\t}\n\tint COMLIGHTCALL getLanguageId( const char* lang )\n\t{\n\t\treturn lookupLanguageId( lang );\n\t}\n\n\tuint32_t COMLIGHTCALL findLanguageKeyW( const wchar_t* lang )\n\t{\n\t\tuint32_t key = 0;\n\t\tuint32_t shift = 0;\n\t\tfor( size_t i = 0; i < 4; i++, lang++, shift += 8 )\n\t\t{\n\t\t\tconst wchar_t c = *lang;\n\t\t\tif( c == L'\\0' )\n\t\t\t\tbreak;\n\t\t\tif( c >= 0x80 )\n\t\t\t\treturn UINT_MAX;\n\t\t\tuint32_t u32 = (uint8_t)c;\n\t\t\tu32 = u32 << shift;\n\t\t\tkey |= u32;\n\t\t}\n\t\tif( g_table.lookupKey( key ) >= 0 )\n\t\t\treturn key;\n\t\treturn UINT_MAX;\n\t}\n\n\tuint32_t COMLIGHTCALL findLanguageKeyA( const char* lang )\n\t{\n\t\tconst uint32_t key = makeLanguageKey( lang );\n\t\tif( g_table.lookupKey( key ) >= 0 )\n\t\t\treturn key;\n\t\treturn UINT_MAX;\n\t}\n\n\tHRESULT COMLIGHTCALL getSupportedLanguages( sLanguageList& rdi )\n\t{\n\t\trdi.length = sizeof( s_languageData ) / sizeof( s_languageData[ 0 ] );\n\t\trdi.pointer = s_languageData;\n\t\treturn S_OK;\n\t}\n}"
  },
  {
    "path": "Whisper/Whisper/Languages.h",
    "content": "#pragma once\n#include \"../../ComLightLib/comLightCommon.h\"\n\nnamespace Whisper\n{\n\tint lookupLanguageId( const char* code );\n\tint lookupLanguageId( uint32_t key );\n\n\tconst char* lookupLanguageName( const char* code );\n\n\tint COMLIGHTCALL getLanguageId( const char* lang );\n}"
  },
  {
    "path": "Whisper/Whisper/MelInputTensor.cpp",
    "content": "#include \"stdafx.h\"\n#include \"MelInputTensor.h\"\n#include \"../D3D/MappedResource.h\"\n#include \"../D3D/createBuffer.h\"\n#include <mfapi.h>\t// MFCopyImage\nusing namespace DirectCompute;\n\nHRESULT MelInputTensor::create( Whisper::iSpectrogram& spectrogram, const sEncodeParams& encParams )\n{\n\t// Ported from the initial portion of whisper_encode() function\n\tconst size_t ne0 = encParams.n_ctx * 2;\n\tconst size_t ne1 = encParams.n_mels;\n\tconst size_t totalElts = ne0 * ne1;\n\tconst size_t totalBytes = totalElts * 4;\n\n\tif( capacity < (uint32_t)totalElts )\n\t{\n\t\t// The old buffer is too small: drop the old one, and create a larger buffer with SRV\n\t\tbuffer = nullptr;\n\t\tTensorGpuViews::clear();\n\n\t\tCHECK( createBuffer( eBufferUse::Dynamic, totalBytes, &buffer, nullptr, nullptr ) );\n\t\tCHECK( TensorGpuViews::create( buffer, DXGI_FORMAT_R32_FLOAT, totalElts, false ) );\n\n\t\tcapacity = (uint32_t)totalElts;\n\t}\n\n\t// Upload data to VRAM using D3D11_MAP_WRITE_DISCARD, that's why we made a dynamic buffer\n\t{\n\t\t// Ported from whisper_encode() function\n\t\tMappedResource mapped;\n\t\tCHECK( mapped.map( buffer, false ) );\n\t\tfloat* const dst = (float*)mapped.data();\n\t\tmemset( dst, 0, totalBytes );\n\n\t\tconst size_t n_len = spectrogram.getLength();\n\t\tconst size_t i0 = std::min( (size_t)encParams.mel_offset, n_len );\n\t\tconst size_t i1 = std::min( (size_t)encParams.mel_offset + 2 * encParams.n_ctx, n_len );\n\n\t\t// Whisper::MelBufferRaii sourceBuffer{ spectrogram, i0, i1 - i0 };\n\t\tconstexpr DWORD n_mel = Whisper::N_MEL;\n\t\tconst size_t rowBytes = ( i1 - i0 ) * 4;\n\t\t/*\n\t\tfor( size_t j = 0; j < n_mel; j++ )\n\t\t{\n\t\t\tfloat* rdi = dst + j * 2 * encParams.n_ctx;\n\t\t\tconst float* rsi = sourceBuffer[ j ];\n\t\t\tmemcpy( rdi, rsi, rowBytes );\n\t\t} */\n\n\t\tWhisper::MelBufferRaii sourceBuffer;\n\t\tCHECK( sourceBuffer.make( spectrogram, i0, i1 - i0 ) );\n\t\tCHECK( MFCopyImage(\n\t\t\t(BYTE*)dst, (LONG)( 2 * encParams.n_ctx * sizeof( float ) ),\n\t\t\tsourceBuffer.bytePtr(), sourceBuffer.strideBytes(),\n\t\t\t(DWORD)rowBytes, n_mel ) );\n\t}\n\n\t// Shape the tensor\n\tne = { 2 * encParams.n_ctx, encParams.n_mels, 1, 1 };\n\tTensorShape::setDenseStrides();\n\treturn S_OK;\n}"
  },
  {
    "path": "Whisper/Whisper/MelInputTensor.h",
    "content": "#pragma once\n#include \"../ML/TensorEx.h\"\n#include \"sEncodeParams.h\"\n#include \"iSpectrogram.h\"\n\nnamespace DirectCompute\n{\n\t// Input tensor in VRAM, in a dynamic FP32 buffer\n\tclass MelInputTensor : public TensorEx\n\t{\n\t\tuint32_t capacity;\n\n\tpublic:\n\n\t\tHRESULT create( Whisper::iSpectrogram& spectrogram, const sEncodeParams& encParams );\n\n\t\t__m128i getMemoryUse() const\n\t\t{\n\t\t\treturn setHigh_size( (size_t)capacity * 4 );\n\t\t}\n\t};\n}"
  },
  {
    "path": "Whisper/Whisper/MelStreamer.cpp",
    "content": "#include \"stdafx.h\"\n#include \"MelStreamer.h\"\n#include \"../Utils/parallelFor.h\"\nusing namespace Whisper;\n\nMelStreamer::MelStreamer( const Filters& filters, ProfileCollection& prof, const iAudioReader* iar ) :\n\treader( iar ),\n\tmelContext( filters ),\n\tprofiler( prof )\n{ }\n\nvoid MelStreamer::dropOldChunks( size_t off )\n{\n\tconst bool stereo = reader.outputsStereo();\n\tfor( size_t i = streamStartOffset; i < off; i++ )\n\t{\n\t\tqueuePcmMono.pop_front();\n\t\tqueueMel.pop_front();\n\t\tif( stereo )\n\t\t\tqueuePcmStereo.pop_front();\n\t}\n\tstreamStartOffset = off;\n}\n\nHRESULT MelStreamer::ensurePcmChunks( size_t len )\n{\n\tif( readerEof )\n\t\treturn queuePcmMono.empty() ? E_EOF : S_FALSE;\n\n\tconst bool loadStereo = reader.outputsStereo();\n\n\tconst size_t neededChunks = len + FFT_SIZE / FFT_STEP;\n\twhile( true )\n\t{\n\t\tif( queuePcmMono.size() >= neededChunks )\n\t\t\treturn S_OK;\n\n\t\tPcmMonoChunk& mono = queuePcmMono.emplace_back();\n\t\tPcmStereoChunk* stereo = loadStereo ? &queuePcmStereo.emplace_back() : nullptr;\n\t\tHRESULT hr = reader.readChunk( mono, stereo );\n\t\tif( SUCCEEDED( hr ) )\n\t\t\tcontinue;\n\n\t\tqueuePcmMono.pop_back();\n\t\tif( loadStereo )\n\t\t\tqueuePcmStereo.pop_back();\n\n\t\tif( hr == E_EOF )\n\t\t{\n\t\t\treaderEof = true;\n\t\t\treturn S_FALSE;\n\t\t}\n\n\t\treturn hr;\n\t}\n}\n\nsize_t MelStreamer::serializePcm( size_t startOffset )\n{\n\tconst ptrdiff_t chunks = (ptrdiff_t)queuePcmMono.size() - (ptrdiff_t)startOffset;\n\tassert( chunks > 0 );\n\n\ttempPcm.resize( chunks * FFT_STEP );\n\tfloat* rdi = tempPcm.data();\n\n\tfor( auto it = queuePcmMono.begin() + startOffset; it != queuePcmMono.end(); it++ )\n\t{\n\t\tmemcpy( rdi, it->mono.data(), FFT_STEP * 4 );\n\t\trdi += FFT_STEP;\n\t}\n\treturn chunks;\n}\n\nnamespace\n{\n\t__forceinline __m128 transpose4x80( __m128 vmax, const float* c0, const float* c1, const float* c2, const float* c3, float* rdi, size_t stride )\n\t{\n\t\tconst float* const c0End = c0 + 80;\n\t\tfor( ; c0 < c0End; c0 += 4, c1 += 4, c2 += 4, c3 += 4, rdi += stride * 4 )\n\t\t{\n\t\t\t__m128 r0 = _mm_loadu_ps( c0 );\n\t\t\t__m128 r1 = _mm_loadu_ps( c1 );\n\t\t\t__m128 r2 = _mm_loadu_ps( c2 );\n\t\t\t__m128 r3 = _mm_loadu_ps( c3 );\n\n\t\t\t__m128 ax01 = _mm_max_ps( r0, r1 );\n\t\t\t__m128 ax02 = _mm_max_ps( r2, r3 );\n\t\t\t__m128 ax = _mm_max_ps( ax01, ax02 );\n\t\t\tvmax = _mm_max_ps( vmax, ax );\n\n\t\t\t_MM_TRANSPOSE4_PS( r0, r1, r2, r3 );\n\n\t\t\t_mm_storeu_ps( rdi, r0 );\n\t\t\t_mm_storeu_ps( rdi + stride, r1 );\n\t\t\t_mm_storeu_ps( rdi + stride * 2, r2 );\n\t\t\t_mm_storeu_ps( rdi + stride * 3, r3 );\n\t\t}\n\t\treturn vmax;\n\t}\n\n\t__forceinline __m128 transpose80( __m128 vmax, const float* c0, float* rdi, size_t stride )\n\t{\n\t\tconst float* const c0End = c0 + 80;\n\t\tfor( ; c0 < c0End; c0 += 4, rdi += stride * 4 )\n\t\t{\n\t\t\t__m128 r0 = _mm_loadu_ps( c0 );\n\t\t\tvmax = _mm_max_ps( vmax, r0 );\n\n\t\t\t_mm_store_ss( rdi, r0 );\n\t\t\t*(int*)( rdi + stride ) = _mm_extract_ps( r0, 1 );\n\t\t\t*(int*)( rdi + stride * 2 ) = _mm_extract_ps( r0, 2 );\n\t\t\t*(int*)( rdi + stride * 3 ) = _mm_extract_ps( r0, 3 );\n\t\t}\n\t\treturn vmax;\n\t}\n\n\t__forceinline float horizontalMaximum( __m128 v )\n\t{\n\t\tv = _mm_max_ps( v, _mm_movehl_ps( v, v ) );\n\t\tv = _mm_max_ss( v, _mm_movehdup_ps( v ) );\n\t\treturn _mm_cvtss_f32( v );\n\t}\n}\n\nvoid MelStreamer::makeTransposedBuffer( size_t off, size_t len )\n{\n\t// Resize the output\n\tassert( len <= queueMel.size() );\n\toutputMel.resize( len * N_MEL );\t// N_MEL = 80\n\n\t// First pass, copy transposed MEL data, and compute the maximum\n\tconst size_t lengthAligned = ( len / 4 ) * 4;\n\t__m128 vMax = _mm_set1_ps( 1e-20f );\n\tfloat* rdi = outputMel.data();\n\n\tsize_t i;\n\tfor( i = 0; i < lengthAligned; i += 4, rdi += 4 )\n\t{\n\t\tvMax = transpose4x80( vMax,\n\t\t\tqueueMel[ i ].data(),\n\t\t\tqueueMel[ i + 1 ].data(),\n\t\t\tqueueMel[ i + 2 ].data(),\n\t\t\tqueueMel[ i + 3 ].data(),\n\t\t\trdi, len );\n\t}\n\tfor( ; i < len; i++, rdi++ )\n\t\tvMax = transpose80( vMax, queueMel[ i ].data(), rdi, len );\n\n\t// Second pass, clamping and normalization\n\tfloat mmax;\n\tconst size_t bufferEnd = off + len;\n\tif( lastBufferEnd != bufferEnd )\n\t{\n\t\t// Store maximum value in this class, along with the end sample index\n\t\tmmax = horizontalMaximum( vMax );\n\t\tlastBufferEnd = bufferEnd;\n\t\tlastBufferMax = mmax;\n\t}\n\telse\n\t{\n\t\t// We're probably at the and of the stream, the caller asked for a smalled slice of the samples with the same end as the last time.\n\t\t// Discard the computed maximum value, and instead use the number stored in this class\n\t\tmmax = lastBufferMax;\n\t}\n\n\tmmax -= 8.0f;\n\tvMax = _mm_set1_ps( mmax );\n\n\trdi = outputMel.data();\n\tfloat* const rdiEnd = rdi + outputMel.size();\n\tconst __m128 add = _mm_set1_ps( 4 );\n\tconst __m128 mul = _mm_set1_ps( 1.0f / 4.0f );\n\tfor( ; rdi < rdiEnd; rdi += 4 )\n\t{\n\t\t__m128 v = _mm_loadu_ps( rdi );\n\t\tv = _mm_max_ps( v, vMax );\n\t\tv = _mm_add_ps( v, add );\n\t\tv = _mm_mul_ps( v, mul );\n\t\t_mm_storeu_ps( rdi, v );\n\t}\n}\n\nHRESULT MelStreamerSimple::makeBuffer( size_t off, size_t len, const float** buffer, size_t& stride ) noexcept\n{\n\tif( off < streamStartOffset )\n\t{\n\t\tlogError( u8\"MelStreamer doesn't support backwards seeks\" );\n\t\treturn E_UNEXPECTED;\n\t}\n\n\tif( off > streamStartOffset )\n\t{\n\t\t// The model wants to advance forward, drop now irrelevant chunks of data\n\t\tdropOldChunks( off );\n\t}\n\n\t// Compute all these MEL chunks\n\tconst size_t availableMel = queueMel.size();\n\tif( availableMel < len )\n\t{\n\t\tCHECK( ensurePcmChunks( len ) );\n\n\t\tconst size_t pcmChunks = serializePcm( availableMel );\n\t\tconst size_t missingMelChunks = len - availableMel;\n\t\tsize_t i;\n\t\tconst size_t loop1 = std::min( missingMelChunks, pcmChunks );\n\t\t{\n\t\t\tauto profilerBlock = profiler.cpuBlock( eCpuBlock::Spectrogram );\n\t\t\tfor( i = 0; i < loop1; i++ )\n\t\t\t{\n\t\t\t\t// if( readerEof && i + 1 == loop1 ) __debugbreak();\n\t\t\t\tauto& arr = queueMel.emplace_back();\n\t\t\t\tconst float* sourcePcm = tempPcm.data() + i * FFT_STEP;\n\t\t\t\tsize_t availableChunks = pcmChunks - i;\n\t\t\t\tsize_t availableFloats = availableChunks * FFT_STEP;\n\t\t\t\tmelContext.fft( arr, sourcePcm, availableFloats );\n\t\t\t}\n\t\t}\n\t\tfor( ; i < missingMelChunks; i++ )\n\t\t{\n\t\t\tassert( readerEof );\n\t\t\tauto& arr = queueMel.emplace_back();\n\t\t\tmemset( arr.data(), 0, N_MEL * 4 );\n\t\t}\n\t}\n\n\t// Produce the result\n\tmakeTransposedBuffer( off, len );\n\tstride = len;\n\t*buffer = outputMel.data();\n\treturn S_OK;\n}\n\nMelStreamerThread::MelStreamerThread( const Filters& filters, ProfileCollection& profiler, const iAudioReader* iar, int countThreads ) :\n\tMelStreamer( filters, profiler, iar ),\n\tworkerThreads( countThreads )\n{\n\tif( workerThreads > 1 )\n\t{\n\t\tcheck( ThreadPoolWork::create() );\n\t\tmelContextsWorkers.reserve( workerThreads - 1 );\n\t\tfor( int i = 1; i < workerThreads; i++ )\n\t\t\tmelContextsWorkers.emplace_back( filters );\n\t}\n\n\tInitializeConditionVariable( &wakeMain );\n\tInitializeConditionVariable( &wakeBackground );\n\tthreadStatus = eThreadStatus::NotStarted;\n\tconst HANDLE h = CreateThread( nullptr, 0, &threadProcStatic, this, 0, nullptr );\n\tif( nullptr == h )\n\t\tthrow HRESULT_FROM_WIN32( GetLastError() );\n\tthreadHandle.Attach( h );\n}\n\nusing Lock = CComCritSecLock<CComAutoCriticalSection>;\n\nconstexpr ptrdiff_t prebufferChunks = 3000 * 2;\nconstexpr ptrdiff_t chunksPerWakeup = 512;\nconstexpr ptrdiff_t minChunksPerThread = 64;\n\nHRESULT MelStreamerThread::threadMain()\n{\n\tpendingChunks.reserve( chunksPerWakeup );\n\n\tEnterCriticalSection( &m_cs.m_sec );\n\tthreadStatus = eThreadStatus::Working;\n\n\twhile( true )\n\t{\n\t\tif( shuttingDown )\n\t\t{\n\t\t\tLeaveCriticalSection( &m_cs.m_sec );\n\t\t\treturn S_FALSE;\n\t\t}\n\n\t\t// Count of available MEL chunks\n\t\tconst ptrdiff_t availableMel = queueMel.size();\n\t\tif( availableMel >= prebufferChunks )\n\t\t{\n\t\t\tthreadStatus = eThreadStatus::Idle;\n\t\t\tSleepConditionVariableCS( &wakeBackground, &m_cs.m_sec, INFINITE );\n\t\t\tthreadStatus = eThreadStatus::Working;\n\t\t\tcontinue;\n\t\t}\n\t\t// Count of MEL chunks remaining in the whole stream\n\t\t// availableMel of them are already on the queue\n\t\tconst ptrdiff_t remainingMel = (ptrdiff_t)getLength() - (ptrdiff_t)streamStartOffset;\n\t\tLeaveCriticalSection( &m_cs.m_sec );\n\n\t\tconst ptrdiff_t missingChunks = prebufferChunks - availableMel;\n\t\tptrdiff_t chunks = std::min( missingChunks, chunksPerWakeup );\n\t\tchunks = std::min( chunks, remainingMel - availableMel );\n\t\tif( chunks <= 0 )\n\t\t\treturn S_OK; // This thread has produced all chunks of the stream\n\n\t\tCHECK( ensurePcmChunks( availableMel + chunks ) );\n\t\tconst size_t pcmChunks = serializePcm( availableMel );\n\t\tif( 0 == pcmChunks )\n\t\t\treturn S_OK;\n\n\t\tpendingChunks.clear();\n\n\t\tchunks = std::min( chunks, (ptrdiff_t)pcmChunks );\n\t\t{\n\t\t\tauto profilerBlock = profiler.cpuBlock( eCpuBlock::Spectrogram );\n\n\t\t\tif( this->workerThreads <= 1 || chunks < minChunksPerThread * 2 )\n\t\t\t{\n\t\t\t\t// Thread pool disabled with a setting, or not enough work for the thread pool\n\t\t\t\tfor( ptrdiff_t i = 0; i < chunks; i++ )\n\t\t\t\t{\n\t\t\t\t\tMelChunk& arr = pendingChunks.emplace_back();\n\t\t\t\t\tconst float* sourcePcm = tempPcm.data() + i * FFT_STEP;\n\t\t\t\t\tsize_t availableChunks = pcmChunks - i;\n\t\t\t\t\tsize_t availableFloats = availableChunks * FFT_STEP;\n\t\t\t\t\tmelContext.fft( arr, sourcePcm, availableFloats );\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// Use thread pool for these FFTs\n\t\t\t\tpendingChunks.resize( chunks );\n\t\t\t\tint nth = (int)( ( chunks + minChunksPerThread - 1 ) / minChunksPerThread );\n\t\t\t\tnth = std::min( nth, this->workerThreads );\n\t\t\t\tassert( nth > 1 );\n\t\t\t\tthis->fftChunks = (int)chunks;\n\t\t\t\tthis->fftThreads = nth;\n\t\t\t\tCHECK( ThreadPoolWork::parallelFor( nth ) );\n\t\t\t}\n\t\t}\n\n\t\tEnterCriticalSection( &m_cs.m_sec );\n\t\tif( shuttingDown )\n\t\t{\n\t\t\tLeaveCriticalSection( &m_cs.m_sec );\n\t\t\treturn S_FALSE;\n\t\t}\n\n\t\tfor( const auto& a : pendingChunks )\n\t\t\tqueueMel.push_back( a );\n\n\t\tLeaveCriticalSection( &m_cs.m_sec );\n\n\t\tWakeAllConditionVariable( &wakeMain );\n\t\tpendingChunks.clear();\n\n\t\tEnterCriticalSection( &m_cs.m_sec );\n\t}\n}\n\nHRESULT MelStreamerThread::threadPoolCallback( int ith ) noexcept\n{\n\tSpectrogramContext& ctx = ( 0 != ith ) ? melContextsWorkers[ ith - 1 ] : melContext;\n\n\t// Figure out the slice of the chunks to generate in this thread\n\tconst int nth = this->fftThreads;\n\tconst int chunks = this->fftChunks;\n\tconst int i0 = ( ith * chunks ) / nth;\n\tconst int i1 = ( ( ith + 1 ) * chunks ) / nth;\n\n\t// Run these FFTs\n\tconst size_t pcmChunks = tempPcm.size() / FFT_STEP;\n\tfor( int i = i0; i < i1; i++ )\n\t{\n\t\tMelChunk& arr = pendingChunks[ i ];\n\t\tconst float* sourcePcm = tempPcm.data() + i * FFT_STEP;\n\t\tsize_t availableChunks = pcmChunks - i;\n\t\tsize_t availableFloats = availableChunks * FFT_STEP;\n\t\tctx.fft( arr, sourcePcm, availableFloats );\n\t}\n\treturn S_OK;\n}\n\nHRESULT MelStreamerThread::run() noexcept\n{\n\tHRESULT status;\n\ttry\n\t{\n\t\tstatus = threadMain();\n\t}\n\tcatch( HRESULT hr )\n\t{\n\t\tstatus = hr;\n\t}\n\tcatch( const std::bad_alloc& )\n\t{\n\t\tstatus = E_OUTOFMEMORY;\n\t}\n\tcatch( const std::exception& )\n\t{\n\t\tstatus = E_FAIL;\n\t}\n\n\t{\n\t\tLock lk( m_cs );\n\t\tthreadStatus = SUCCEEDED( status ) ? eThreadStatus::Completed : eThreadStatus::Failed;\n\t}\n\n\t// Especially when things fail, we want to wake the main thread up, so it's aware of the situation.\n\tWakeAllConditionVariable( &wakeMain );\n\treturn status;\n}\n\nDWORD __stdcall MelStreamerThread::threadProcStatic( void* lpParameter )\n{\n\tsetCurrentThreadName( \"Whisper.dll MEL Streamer Thread\" );\n\tMelStreamerThread* p = (MelStreamerThread*)lpParameter;\n\treturn (DWORD)p->run();\n}\n\nHRESULT MelStreamerThread::makeBuffer( size_t off, size_t len, const float** buffer, size_t& stride ) noexcept\n{\n\tbool wakeThread = false;\n\n\t{\n\t\tLock lock( m_cs );\n\t\tif( off < streamStartOffset )\n\t\t{\n\t\t\tlogError( u8\"MelStreamer doesn't support backwards seeks\" );\n\t\t\treturn E_UNEXPECTED;\n\t\t}\n\n\t\tif( off > streamStartOffset )\n\t\t{\n\t\t\t// The model wants to advance forward, drop now irrelevant chunks of data\n\t\t\tdropOldChunks( off );\n\t\t\twakeThread = ( threadStatus == eThreadStatus::Working || threadStatus == eThreadStatus::Idle );\n\t\t}\n\n\t\twhile( true )\n\t\t{\n\t\t\tconst size_t availableMel = queueMel.size();\n\t\t\tif( availableMel >= len )\n\t\t\t\tbreak;\n\n\t\t\tconst eThreadStatus ts = threadStatus;\n\t\t\tif( ts == eThreadStatus::Working || ts == eThreadStatus::Idle )\n\t\t\t{\n\t\t\t\tWakeAllConditionVariable( &wakeBackground );\n\t\t\t\tSleepConditionVariableCS( &wakeMain, &m_cs.m_sec, INFINITE );\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif( ts == eThreadStatus::Failed )\n\t\t\t{\n\t\t\t\tDWORD code;\n\t\t\t\tif( GetExitCodeThread( threadHandle, &code ) )\n\t\t\t\t\treturn (HRESULT)code;\n\t\t\t\telse\n\t\t\t\t\treturn HRESULT_FROM_WIN32( GetLastError() );\n\t\t\t}\n\t\t\tassert( ts == eThreadStatus::Completed );\n\t\t\tbreak;\n\t\t}\n\n\t\tif( queueMel.size() < len )\n\t\t{\n\t\t\tassert( readerEof || threadStatus == eThreadStatus::Failed );\n\t\t\twhile( queueMel.size() < len )\n\t\t\t{\n\t\t\t\tauto& arr = queueMel.emplace_back();\n\t\t\t\tmemset( arr.data(), 0, N_MEL * 4 );\n\t\t\t}\n\t\t}\n\n\t\t// Produce the result\n\t\tmakeTransposedBuffer( off, len );\n\n\t}\t// Unlock the critical section\n\n\tstride = len;\n\t*buffer = outputMel.data();\n\tif( wakeThread )\n\t\tWakeAllConditionVariable( &wakeBackground );\n\treturn S_OK;\n}\n\nMelStreamerThread::~MelStreamerThread()\n{\n\tif( !threadHandle )\n\t\treturn;\n\n\t{\n\t\tLock lock( m_cs );\n\t\tif( threadStatus != eThreadStatus::Working )\n\t\t\treturn;\n\t\tshuttingDown = true;\n\t}\n\n\tDWORD res = WaitForSingleObject( threadHandle, 100 );\n\tif( res == WAIT_OBJECT_0 )\n\t\treturn;\n\t// TODO: log a warning\n}\n\nHRESULT MelStreamer::copyStereoPcm( size_t offset, size_t length, std::vector<StereoSample>& buffer ) const\n{\n\tif( queuePcmStereo.empty() )\n\t\treturn OLE_E_BLANK;\n\n\tif( offset < streamStartOffset )\n\t{\n\t\tlogError( u8\"MelStreamer doesn't support backwards seek\" );\n\t\treturn E_UNEXPECTED;\n\t}\n\n\t// Offset relative to the first chunk on the queue\n\tconst size_t off = offset - streamStartOffset;\n\tif( off >= queuePcmStereo.size() )\n\t\treturn E_BOUNDS;\n\n\t// Resize the output buffer\n\ttry\n\t{\n\t\tbuffer.resize( length * FFT_STEP );\n\t}\n\tcatch( const std::bad_alloc& )\n\t{\n\t\treturn E_OUTOFMEMORY;\n\t}\n\tStereoSample* rdi = buffer.data();\n\n\t// Copy PCM chunks from the queue\n\tconst size_t lengthToCopy = std::min( length, queuePcmStereo.size() - off );\n\tfor( size_t i = 0; i < lengthToCopy; i++, rdi += FFT_STEP )\n\t{\n\t\tconst float* rsi = queuePcmStereo[ i + off ].stereo.data();\n\t\tmemcpy( rdi, rsi, 8 * FFT_STEP );\n\t}\n\t// If needed, write zeros to the tail\n\tif( lengthToCopy == length )\n\t\treturn S_OK;\n\tmemset( rdi, 0, ( length - lengthToCopy ) * FFT_STEP );\n\treturn S_OK;\n}"
  },
  {
    "path": "Whisper/Whisper/MelStreamer.h",
    "content": "﻿#pragma once\n#include <deque>\n#include \"../MF/PcmReader.h\"\n#include \"melSpectrogram.h\"\n#include \"iSpectrogram.h\"\n#include <atlbase.h>\n#include \"../Utils/parallelFor.h\"\n#include \"../Utils/ProfileCollection.h\"\n#include \"../API/iMediaFoundation.cl.h\"\n\nnamespace Whisper\n{\n\t// Base class for both single- and multi-threaded MEL streamers\n\t// Used by iContext.runStreamed method\n\tclass MelStreamer : public iSpectrogram\n\t{\n\tprotected:\n\t\tPcmReader reader;\n\t\tstd::deque<PcmMonoChunk> queuePcmMono;\n\t\tusing MelChunk = std::array<float, N_MEL>;\n\t\tstd::deque<MelChunk> queueMel;\n\t\tsize_t streamStartOffset = 0;\n\t\tstd::vector<float> tempPcm;\n\t\tstd::vector<float> outputMel;\n\t\tSpectrogramContext melContext;\n\t\tbool readerEof = false;\n\t\tProfileCollection& profiler;\n\t\tstd::deque<PcmStereoChunk> queuePcmStereo;\n\n\t\t// If the streamStartOffset value is less than the argument,\n\t\t// remove ( off - streamStartOffset ) chunks from the start of all 3 queues, and advance streamStartOffset to the `off` argument\n\t\tvoid dropOldChunks( size_t off );\n\n\t\t// Ensure PCM queues have enough chunks to generate specified count of MEL chunks\n\t\t// At the end of the stream, the method delivers less chunks then requested and returns S_FALSE\n\t\tHRESULT ensurePcmChunks( size_t len );\n\n\t\t// Copy mono PCM chunks from the queue (starting at the specified element index) into the continuous tempPcm vector\n\t\t// Returns count of chunks copied there.\n\t\tsize_t serializePcm( size_t startOffset );\n\n\t\tsize_t lastBufferEnd = ~(size_t)0;\n\t\tfloat lastBufferMax = 0.0f;\n\t\tvoid makeTransposedBuffer( size_t off, size_t len );\n\n\t\tsize_t getLength() const noexcept override final { return reader.getLength(); }\n\n\t\tHRESULT copyStereoPcm( size_t offset, size_t length, std::vector<StereoSample>& buffer ) const override final;\n\n\tpublic:\n\t\tMelStreamer( const Filters& filters, ProfileCollection& profiler, const iAudioReader* reader );\n\t};\n\n\t// Single-threaded MEL streamer: runs these FFTs on-demand, from within makeBuffer() method\n\t// Used by iContext.runStreamed method when cpuThreads parameter is less than 2\n\tclass MelStreamerSimple : public MelStreamer\n\t{\n\t\tHRESULT makeBuffer( size_t offset, size_t length, const float** buffer, size_t& stride ) noexcept override final;\n\n\tpublic:\n\t\tMelStreamerSimple( const Filters& filters, ProfileCollection& profiler, const iAudioReader* reader ) :\n\t\t\tMelStreamer( filters, profiler, reader ) { }\n\t};\n\n\t// Multi threaded MEL streamers: runs FFT on a background thread ahead of time\n\t// The background thread tries to keep the queueMel full, this way the makeBuffer() method has very little to do\n\t// makeBuffer() only transposes the data, and does clamping + normalization, both steps are pretty fast\n\t// Used by iContext.runStreamed method when cpuThreads parameter is 2 or more\n\tclass MelStreamerThread : public MelStreamer,\n\t\tThreadPoolWork\n\t{\n\t\tHRESULT makeBuffer( size_t offset, size_t length, const float** buffer, size_t& stride ) noexcept override final;\n\n\t\tstatic DWORD __stdcall threadProcStatic( void* lpParameter );\n\t\tHRESULT run() noexcept;\n\t\tHRESULT threadMain();\n\n\t\tstd::vector<MelChunk> pendingChunks;\n\t\tint fftChunks = 0;\n\t\tint fftThreads = 0;\n\t\tstd::vector<SpectrogramContext> melContextsWorkers;\n\t\tCComAutoCriticalSection m_cs;\n\t\tCONDITION_VARIABLE wakeMain, wakeBackground;\n\t\tconst int workerThreads;\n\t\tenum struct eThreadStatus : uint8_t\n\t\t{\n\t\t\tNotStarted = 0,\n\t\t\tIdle,\n\t\t\tWorking,\n\t\t\tCompleted,\n\t\t\tFailed\n\t\t};\n\t\teThreadStatus threadStatus;\n\t\tbool shuttingDown = false;\n\t\tCHandle threadHandle;\n\n\t\tHRESULT threadPoolCallback( int ith ) noexcept override final;\n\n\tpublic:\n\n\t\tMelStreamerThread( const Filters& filters, ProfileCollection& profiler, const iAudioReader* reader, int countThreads );\n\n\t\t~MelStreamerThread();\n\t};\n}"
  },
  {
    "path": "Whisper/Whisper/ModelBuffers.clone.cpp",
    "content": "#include <stdafx.h>\n#include \"ModelBuffers.h\"\n#include \"../ML/mlUtils.h\"\n\nnamespace\n{\n\tusing namespace DirectCompute;\n\n\tHRESULT clone( Tensor& rdi, const Tensor& rsi )\n\t{\n\t\tCComPtr<ID3D11ShaderResourceView> srv;\n\t\tCHECK( cloneResourceView( rsi, &srv ) );\n\n\t\trdi = rsi;\n\t\trdi.setGpuViews( srv );\n\t\treturn S_OK;\n\t}\n\n\tHRESULT clone( TensorPair& rdi, const TensorPair& rsi )\n\t{\n\t\tCHECK( clone( rdi.w, rsi.w ) );\n\t\tCHECK( clone( rdi.b, rsi.b ) );\n\t\treturn S_OK;\n\t}\n\n\tHRESULT clone( LayerEncoder& rdi, const LayerEncoder& rsi )\n\t{\n\t\tCHECK( clone( rdi.attnLn0, rsi.attnLn0 ) );\n\t\tCHECK( clone( rdi.attnLn1, rsi.attnLn1 ) );\n\t\tCHECK( clone( rdi.attnQuery, rsi.attnQuery ) );\n\t\tCHECK( clone( rdi.attnKey, rsi.attnKey ) );\n\t\tCHECK( clone( rdi.attnValue, rsi.attnValue ) );\n\t\tCHECK( clone( rdi.mlpLn, rsi.mlpLn ) );\n\t\tCHECK( clone( rdi.mlp0, rsi.mlp0 ) );\n\t\tCHECK( clone( rdi.mlp1, rsi.mlp1 ) );\n\n\t\treturn S_OK;\n\t}\n\tHRESULT clone( LayerDecoder& rdi, const LayerDecoder& rsi )\n\t{\n\t\tCHECK( clone( rdi.attnLn0, rsi.attnLn0 ) );\n\t\tCHECK( clone( rdi.attnLn1, rsi.attnLn1 ) );\n\t\tCHECK( clone( rdi.attnQuery, rsi.attnQuery ) );\n\t\tCHECK( clone( rdi.attnKey, rsi.attnKey ) );\n\t\tCHECK( clone( rdi.attnValue, rsi.attnValue ) );\n\t\tCHECK( clone( rdi.crossAttnLn0, rsi.crossAttnLn0 ) );\n\t\tCHECK( clone( rdi.crossAttnLn1, rsi.crossAttnLn1 ) );\n\t\tCHECK( clone( rdi.crossAttnQuery, rsi.crossAttnQuery ) );\n\t\tCHECK( clone( rdi.crossAttnKey, rsi.crossAttnKey ) );\n\t\tCHECK( clone( rdi.crossAttnValue, rsi.crossAttnValue ) );\n\t\tCHECK( clone( rdi.mlpLn, rsi.mlpLn ) );\n\t\tCHECK( clone( rdi.mlp0, rsi.mlp0 ) );\n\t\tCHECK( clone( rdi.mlp1, rsi.mlp1 ) );\n\n\t\treturn S_OK;\n\t}\n\n\ttemplate<class E>\n\tHRESULT clone( std::vector<E>& rdi, const std::vector<E>& rsi )\n\t{\n\t\tconst size_t len = rsi.size();\n\t\ttry\n\t\t{\n\t\t\trdi.resize( len );\n\t\t}\n\t\tcatch( const std::bad_alloc& )\n\t\t{\n\t\t\treturn E_OUTOFMEMORY;\n\t\t}\n\n\t\tfor( size_t i = 0; i < len; i++ )\n\t\t\tCHECK( clone( rdi[ i ], rsi[ i ] ) );\n\n\t\treturn S_OK;\n\t}\n\n\tHRESULT clone( EncoderBuffers& rdi, const EncoderBuffers& rsi )\n\t{\n\t\tCHECK( clone( rdi.layers, rsi.layers ) );\n\t\tCHECK( clone( rdi.positionalEmbedding, rsi.positionalEmbedding ) );\n\t\tCHECK( clone( rdi.conv1, rsi.conv1 ) );\n\t\tCHECK( clone( rdi.conv2, rsi.conv2 ) );\n\t\tCHECK( clone( rdi.lnPost, rsi.lnPost ) );\n\t\treturn S_OK;\n\t}\n\n\tHRESULT clone( DecoderBuffers& rdi, const DecoderBuffers& rsi )\n\t{\n\t\tCHECK( clone( rdi.positionalEmbedding, rsi.positionalEmbedding ) );\n\t\tCHECK( clone( rdi.tokenEmbedding, rsi.tokenEmbedding ) );\n\t\tCHECK( clone( rdi.ln, rsi.ln ) );\n\t\tCHECK( clone( rdi.layers, rsi.layers ) );\n\n\t\treturn S_OK;\n\t}\n}\n\nHRESULT ModelBuffers::createClone( const ModelBuffers& rsi )\n{\n\tCHECK( clone( enc, rsi.enc ) );\n\tCHECK( clone( dec, rsi.dec ) );\n\n\treturn S_OK;\n}"
  },
  {
    "path": "Whisper/Whisper/ModelBuffers.cpp",
    "content": "#include \"stdafx.h\"\n#include \"ModelLoader.h\"\n\n\n#if BUILD_BOTH_VERSIONS\nnamespace DirectCompute\n{\n\tstatic ModelBuffers s_model;\n\tconst ModelBuffers& gpuModel = s_model;\n}\n\nusing namespace DirectCompute;\n\nModelLoader::ModelLoader( int encoderLayers, int decoderLayers ) :\n\tmodel( s_model )\n{\n\tif( encoderLayers <= 0 || decoderLayers <= 0 )\n\t\tthrow E_INVALIDARG;\n\tmodel.enc.layers.resize( (uint32_t)encoderLayers );\n\tmodel.dec.layers.resize( (uint32_t)decoderLayers );\n}\n\nvoid ModelLoader::add( const ggml_tensor* ggml, Tensor& gpu )\n{\n\tif( nullptr == ggml )\n\t\tthrow E_POINTER;\n\n\tauto res = map.try_emplace( ggml, &gpu );\n\tif( !res.second )\n\t\tthrow E_INVALIDARG;\n}\n\nTensor* ModelLoader::lookup( const ggml_tensor* ggml ) const\n{\n\tauto it = map.find( ggml );\n\tif( it == map.end() )\n\t\treturn nullptr;\n\treturn it->second;\n}\n\nbool ModelLoader::tryLoad( const ggml_tensor* ggml )\n{\n\tTensor* rdi = lookup( ggml );\n\tif( nullptr == rdi )\n\t\treturn false;\n\tHRESULT hr = rdi->create( *ggml, eBufferUse::Immutable, true );\n\tif( SUCCEEDED( hr ) )\n\t\treturn true;\n\tthrow hr;\n}\n#endif\n\n__m128i __declspec( noinline ) DirectCompute::TensorPair::getMemoryUse() const\n{\n\treturn _mm_add_epi64( w.getMemoryUse(), b.getMemoryUse() );\n}\n\n__m128i DirectCompute::LayerEncoder::getMemoryUse() const\n{\n\t__m128i v = attnLn0.getMemoryUse();\n\tv = _mm_add_epi64( v, attnLn1.getMemoryUse() );\n\tv = _mm_add_epi64( v, attnQuery.getMemoryUse() );\n\tv = _mm_add_epi64( v, attnKey.getMemoryUse() );\n\tv = _mm_add_epi64( v, attnValue.getMemoryUse() );\n\tv = _mm_add_epi64( v, mlpLn.getMemoryUse() );\n\tv = _mm_add_epi64( v, mlp0.getMemoryUse() );\n\tv = _mm_add_epi64( v, mlp1.getMemoryUse() );\n\treturn v;\n}\n\n__m128i DirectCompute::EncoderBuffers::getMemoryUse() const\n{\n\t__m128i v = _mm_cvtsi64_si128( vectorMemoryUse( layers ) );\n\tv = _mm_add_epi64( v, positionalEmbedding.getMemoryUse() );\n\tv = _mm_add_epi64( v, conv1.getMemoryUse() );\n\tv = _mm_add_epi64( v, conv2.getMemoryUse() );\n\tv = _mm_add_epi64( v, lnPost.getMemoryUse() );\n\tfor( const auto& layer : layers )\n\t\tv = _mm_add_epi64( v, layer.getMemoryUse() );\n\treturn v;\n}\n\n__m128i DirectCompute::LayerDecoder::getMemoryUse() const\n{\n\t__m128i v = attnLn0.getMemoryUse();\n\tv = _mm_add_epi64( v, attnLn1.getMemoryUse() );\n\tv = _mm_add_epi64( v, attnQuery.getMemoryUse() );\n\tv = _mm_add_epi64( v, attnKey.getMemoryUse() );\n\tv = _mm_add_epi64( v, attnValue.getMemoryUse() );\n\tv = _mm_add_epi64( v, crossAttnLn0.getMemoryUse() );\n\tv = _mm_add_epi64( v, crossAttnLn1.getMemoryUse() );\n\tv = _mm_add_epi64( v, crossAttnQuery.getMemoryUse() );\n\tv = _mm_add_epi64( v, crossAttnKey.getMemoryUse() );\n\tv = _mm_add_epi64( v, crossAttnValue.getMemoryUse() );\n\tv = _mm_add_epi64( v, mlpLn.getMemoryUse() );\n\tv = _mm_add_epi64( v, mlp0.getMemoryUse() );\n\tv = _mm_add_epi64( v, mlp1.getMemoryUse() );\n\treturn v;\n}\n\n__m128i DirectCompute::DecoderBuffers::getMemoryUse() const\n{\n\t__m128i v = _mm_cvtsi64_si128( vectorMemoryUse( layers ) );\n\tv = _mm_add_epi64( v, positionalEmbedding.getMemoryUse() );\n\tv = _mm_add_epi64( v, tokenEmbedding.getMemoryUse() );\n\tv = _mm_add_epi64( v, ln.getMemoryUse() );\n\tfor( const auto& layer : layers )\n\t\tv = _mm_add_epi64( v, layer.getMemoryUse() );\n\treturn v;\n}\n\n__m128i DirectCompute::ModelBuffers::getMemoryUse() const\n{\n\treturn _mm_add_epi64( enc.getMemoryUse(), dec.getMemoryUse() );\n}"
  },
  {
    "path": "Whisper/Whisper/ModelBuffers.h",
    "content": "#pragma once\n#include \"../ML/Tensor.h\"\n#include <vector>\n\nnamespace DirectCompute\n{\n\t// A pair of tensors containing weights and biases; apparently, both tensors are of the same shape.\n\tstruct TensorPair\n\t{\n\t\tTensor w, b;\n\n\t\t__m128i getMemoryUse() const;\n\t};\n\n\t// A set of tensors for one encoder's layer\n\tstruct LayerEncoder\n\t{\n\t\t// encoder.blocks.*.attn_ln\n\t\tTensorPair attnLn0;\n\t\t// encoder.blocks.*.attn.out\n\t\tTensorPair attnLn1;\n\t\t// encoder.blocks.*.attn.query\n\t\tTensorPair attnQuery;\n\t\t// encoder.blocks.*.attn.key\n\t\tTensor attnKey;\n\t\t// encoder.blocks.*.attn.value\n\t\tTensorPair attnValue;\n\t\t// encoder.blocks.*.mlp_ln\n\t\tTensorPair mlpLn;\n\t\t// encoder.blocks.*.mlp.0\n\t\tTensorPair mlp0;\n\t\t// encoder.blocks.*.mlp.2\n\t\tTensorPair mlp1;\n\n\t\t__m128i getMemoryUse() const;\n\t};\n\n\t// A set of tensors for the encoder\n\tstruct EncoderBuffers\n\t{\n\t\t// encoder.positional_embedding\n\t\tTensor positionalEmbedding;\n\t\t// encoder.conv1\n\t\tTensorPair conv1;\n\t\t// encoder.conv2\n\t\tTensorPair conv2;\n\t\t// encoder.ln_post\n\t\tTensorPair lnPost;\n\t\t// A vector of layers\n\t\tstd::vector<LayerEncoder> layers;\n\n\t\t__m128i getMemoryUse() const;\n\t};\n\n\t// A set of tensors for one decoder's layer\n\tstruct LayerDecoder\n\t{\n\t\t// decoder.blocks.*.attn_ln\n\t\tTensorPair attnLn0;\n\t\t// decoder.blocks.*.attn.out\n\t\tTensorPair attnLn1;\n\t\t// decoder.blocks.*.attn.query\n\t\tTensorPair attnQuery;\n\t\t// decoder.blocks.*.attn.key\n\t\tTensor attnKey;\n\t\t// decoder.blocks.*.attn.value\n\t\tTensorPair attnValue;\n\t\t// decoder.blocks.*.cross_attn_ln\n\t\tTensorPair crossAttnLn0;\n\t\t// decoder.blocks.*.cross_attn.out\n\t\tTensorPair crossAttnLn1;\n\t\t// decoder.blocks.*.cross_attn.query\n\t\tTensorPair crossAttnQuery;\n\t\t// decoder.blocks.*.cross_attn.key\n\t\tTensor crossAttnKey;\n\t\t// decoder.blocks.*.cross_attn.value\n\t\tTensorPair crossAttnValue;\n\t\t// decoder.blocks.*.mlp_ln\n\t\tTensorPair mlpLn;\n\t\t// decoder.blocks.*.mlp.0\n\t\tTensorPair mlp0;\n\t\t// decoder.blocks.*.mlp.2\n\t\tTensorPair mlp1;\n\n\t\t__m128i getMemoryUse() const;\n\t};\n\n\t// A set of tensors for the decoder\n\tstruct DecoderBuffers\n\t{\n\t\t// decoder.positional_embedding\n\t\tTensor positionalEmbedding;\n\t\t// decoder.token_embedding\n\t\tTensor tokenEmbedding;\n\t\t// decoder.ln\n\t\tTensorPair ln;\n\t\t// A vector of layers\n\t\tstd::vector<LayerDecoder> layers;\n\n\t\t__m128i getMemoryUse() const;\n\t};\n\n\t// A complete set of tensors for a model\n\tstruct ModelBuffers\n\t{\n\t\tEncoderBuffers enc;\n\t\tDecoderBuffers dec;\n\t\t__m128i getMemoryUse() const;\n\n\t\tHRESULT createClone( const ModelBuffers& rsi );\n\t};\n\n#if BUILD_BOTH_VERSIONS\n\textern const ModelBuffers& gpuModel;\n#endif\n}"
  },
  {
    "path": "Whisper/Whisper/ModelImpl.cpp",
    "content": "﻿#include \"stdafx.h\"\n#include \"ModelImpl.h\"\n#include \"ContextImpl.h\"\n#include <intrin.h>\n#include \"../Utils/ReadStream.h\"\n#include \"../modelFactory.h\"\nusing namespace Whisper;\n\nvoid ModelImpl::FinalRelease()\n{\n\tdevice.destroy();\n}\n\nHRESULT COMLIGHTCALL ModelImpl::createContext( iContext** pp )\n{\n\tauto ts = device.setForCurrentThread();\n\tComLight::CComPtr<ComLight::Object<ContextImpl>> obj;\n\n\tiModel* m = this;\n\tCHECK( ComLight::Object<ContextImpl>::create( obj, device, model, m ) );\n\n\tobj.detach( pp );\n\treturn S_OK;\n}\n\n\nHRESULT COMLIGHTCALL ModelImpl::tokenize( const char* text, pfnDecodedTokens pfn, void* pv )\n{\n\tstd::vector<int> tokens;\n\tCHECK( model.shared->vocab.tokenize( text, tokens ) );\n\n\tif( !tokens.empty() )\n\t\tpfn( tokens.data(), (int)tokens.size(), pv );\n\telse\n\t\tpfn( nullptr, 0, pv );\n\n\treturn S_OK;\n}\n\nHRESULT COMLIGHTCALL ModelImpl::clone( iModel** rdi )\n{\n\tif( !device.gpuInfo.cloneableModel() )\n\t{\n\t\tlogError( u8\"iModel.clone requires the Cloneable model flag\" );\n\t\treturn HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );\n\t}\n\n\tComLight::CComPtr<ComLight::Object<ModelImpl>> obj;\n\tCHECK( ComLight::Object<ModelImpl>::create( obj, *this ) );\n\tCHECK( obj->createClone( *this ) );\n\tobj.detach( rdi );\n\treturn S_OK;\n}\n\nHRESULT ModelImpl::createClone( const ModelImpl& source )\n{\n\tauto ts = device.setForCurrentThread();\n\tCHECK( device.createClone( source.device ) );\n\treturn model.createClone( source.model );\n}\n\nHRESULT ModelImpl::load( iReadStream* stm, bool hybrid, const sLoadModelCallbacks* callbacks )\n{\n\tauto ts = device.setForCurrentThread();\n\tCHECK( device.create( gpuFlags, adapter ) );\n\treturn model.load( stm, hybrid, callbacks );\n}\n\ninline bool hasSse41AndF16C()\n{\n\tint cpu_info[ 4 ];\n\t__cpuid( cpu_info, 1 );\n\n\t// https://en.wikipedia.org/wiki/CPUID EAX=1: Processor Info and Feature Bits\n\tconstexpr uint32_t sse41 = ( 1u << 19 );\n\tconstexpr uint32_t f16c = ( 1u << 29 );\n\n#ifdef __AVX__\n\tconstexpr uint32_t requiredBits = sse41 | f16c;\n#else\n\tconstexpr uint32_t requiredBits = sse41;\n#endif\n\n\tconst uint32_t ecx = (uint32_t)cpu_info[ 2 ];\n\treturn ( ecx & requiredBits ) == requiredBits;\n}\n\n// True when the current CPU is good enough to run the hybrid model\ninline bool hasAvxAndFma()\n{\n\t// AVX needs OS support to preserve the 32-bytes registers across context switches, CPU support alone ain't enough\n\t// Calling a kernel API to check that support\n\t// The magic number is from there: https://stackoverflow.com/a/35096938/126995\n\tif( 0 == ( GetEnabledXStateFeatures() & 4 ) )\n\t\treturn false;\n\n\t// FMA3 and F16C\n\tint cpuInfo[ 4 ];\n\t__cpuid( cpuInfo, 1 );\n\t// The magic numbers are from \"Feature Information\" table on Wikipedia:\n\t// https://en.wikipedia.org/wiki/CPUID#EAX=1:_Processor_Info_and_Feature_Bits \n\tconstexpr int requiredBits = ( 1 << 12 ) | ( 1 << 29 );\n\tif( requiredBits != ( cpuInfo[ 2 ] & requiredBits ) )\n\t\treturn false;\n\n\t// BMI1\n\t// https://en.wikipedia.org/wiki/CPUID#EAX=7,_ECX=0:_Extended_Features\n\t__cpuid( cpuInfo, 7 );\n\tif( 0 == ( cpuInfo[ 1 ] & ( 1 << 3 ) ) )\n\t\treturn false;\n\n\treturn true;\n}\n\nHRESULT __stdcall Whisper::loadGpuModel( const wchar_t* path, const sModelSetup& setup, const sLoadModelCallbacks* callbacks, iModel** pp )\n{\n\tif( nullptr == path || nullptr == pp )\n\t\treturn E_POINTER;\n\n\tconst bool hybrid = setup.impl == eModelImplementation::Hybrid;\n\tif( hybrid )\n\t{\n#if BUILD_HYBRID_VERSION\n\t\tif( !hasAvxAndFma() )\n\t\t{\n\t\t\tlogError( u8\"eModelImplementation.Hybrid model requires a CPU with AVX1, FMA3, F16C and BMI1 support\" );\n\t\t\treturn ERROR_HV_CPUID_FEATURE_VALIDATION;\n\t\t}\n#else\n\t\tlogError( u8\"This build of the DLL doesn’t implement eModelImplementation.Hybrid model\" );\n\t\treturn E_NOTIMPL;\n#endif\n\t}\n\telse if( !hasSse41AndF16C() )\n\t{\n\t\tlogError( u8\"eModelImplementation.GPU model requires a CPU with SSE 4.1 and F16C support\" );\n\t\treturn ERROR_HV_CPUID_FEATURE_VALIDATION;\n\t}\n\n\tComLight::Object<ReadStream> stream;\n\tHRESULT hr = stream.open( path );\n\tif( FAILED( hr ) )\n\t{\n\t\tlogError16( L\"Unable to open model binary file \\\"%s\\\"\", path );\n\t\treturn hr;\n\t}\n\n\tComLight::CComPtr<ComLight::Object<ModelImpl>> obj;\n\tCHECK( ComLight::Object<ModelImpl>::create( obj, setup ) );\n\thr = obj->load( &stream, hybrid, callbacks );\n\tif( FAILED( hr ) )\n\t{\n\t\tlogError16( L\"Error loading the model from \\\"%s\\\"\", path );\n\t\treturn hr;\n\t}\n\n\tobj.detach( pp );\n\tlogInfo16( L\"Loaded model from \\\"%s\\\" to VRAM\", path );\n\treturn S_OK;\n}"
  },
  {
    "path": "Whisper/Whisper/ModelImpl.h",
    "content": "#pragma once\n#include \"../API/iContext.cl.h\"\n#include \"../ComLightLib/comLightServer.h\"\n#include \"WhisperModel.h\"\n#include \"../ComLightLib/streams.h\"\n#include \"../ML/Device.h\"\n\nnamespace Whisper\n{\n\tusing ComLight::iReadStream;\n\n\tclass ModelImpl : public ComLight::ObjectRoot<iModel>\n\t{\n\t\tDirectCompute::Device device;\n\t\tWhisperModel model;\n\t\tconst uint32_t gpuFlags;\n\t\tconst std::wstring adapter;\n\n\t\tHRESULT COMLIGHTCALL createContext( iContext** pp ) override final;\n\n\t\tHRESULT COMLIGHTCALL tokenize( const char* text, pfnDecodedTokens pfn, void* pv ) override final;\n\n\t\tHRESULT COMLIGHTCALL getSpecialTokens( SpecialTokens& rdi ) override final\n\t\t{\n\t\t\tmodel.shared->vocab.getSpecialTokens( rdi );\n\t\t\treturn S_OK;\n\t\t}\n\n\t\tHRESULT COMLIGHTCALL isMultilingual() override final\n\t\t{\n\t\t\treturn model.shared->vocab.is_multilingual() ? S_OK : S_FALSE;\n\t\t}\n\n\t\tconst char* COMLIGHTCALL stringFromToken( whisper_token token ) override final\n\t\t{\n\t\t\treturn model.shared->vocab.string( token );\n\t\t}\n\n\t\tstatic inline std::wstring makeString( const wchar_t* p )\n\t\t{\n\t\t\tif( p == nullptr )\n\t\t\t\treturn std::wstring{};\n\t\t\telse\n\t\t\t\treturn std::wstring{ p };\n\t\t}\n\n\t\tHRESULT createClone( const ModelImpl& source );\n\t\tHRESULT COMLIGHTCALL clone( iModel** rdi ) override final;\n\n\tpublic:\n\t\tModelImpl( const sModelSetup& setup ) :\n\t\t\tgpuFlags( setup.flags ),\n\t\t\tadapter( makeString( setup.adapter ) )\n\t\t{ }\n\n\t\tModelImpl( const ModelImpl& source ) :\n\t\t\tgpuFlags( source.gpuFlags ),\n\t\t\tadapter( source.adapter )\n\t\t{ }\n\n\t\tvoid FinalRelease();\n\n\t\tHRESULT load( iReadStream* stm, bool hybrid, const sLoadModelCallbacks* callbacks );\n\t};\n}"
  },
  {
    "path": "Whisper/Whisper/ModelLoader.h",
    "content": "#pragma once\n#include \"ModelBuffers.h\"\n#include <map>\n\nnamespace DirectCompute\n{\n\tstruct ModelLoader\n\t{\n\t\tModelLoader( int encoderLayers, int decoderLayers );\n\n\t\tvoid add( const ggml_tensor* ggml, Tensor& gpu );\n\n\t\tvoid add( const ggml_tensor* w, const ggml_tensor* b, TensorPair& gpu )\n\t\t{\n\t\t\tadd( w, gpu.w );\n\t\t\tadd( b, gpu.b );\n\t\t}\n\n\t\tbool tryLoad( const ggml_tensor* ggml );\n\n\t\tModelBuffers& model;\n\n\tprivate:\n\n\t\tTensor* lookup( const ggml_tensor* ggml ) const;\n\n\t\tstd::map<const ggml_tensor*, Tensor*> map;\n\t};\n}"
  },
  {
    "path": "Whisper/Whisper/Spectrogram.cpp",
    "content": "#include \"stdafx.h\"\n#include \"Spectrogram.h\"\n#include <memory>\n#define _USE_MATH_DEFINES\n#include <math.h>\n#include \"../Utils/parallelFor.h\"\n#include \"../API/iMediaFoundation.cl.h\"\n#include \"../ML/testUtils.h\"\n#include \"melSpectrogram.h\"\nusing namespace Whisper;\n\nclass alignas( 64 ) Spectrogram::MelContext\n{\n\tconst float* const samples;\n\tconst size_t countSamples;\n\tSpectrogram& result;\n\tconst int n_threads;\n\tSpectrogramContext context;\n\npublic:\n\n\tMelContext( const float* rsi, size_t len, const Filters& f, Spectrogram& rdi, int countThreads ) :\n\t\tsamples( rsi ), countSamples( len ), result( rdi ), n_threads( countThreads ),\n\t\tcontext( f )\n\t{ }\n\n\tvoid run( int ith );\n\n\tstatic HRESULT workCallback( int ith, void* ctx ) noexcept;\n};\n\nvoid Spectrogram::MelContext::run( int ith )\n{\n\tstd::array<float, N_MEL> arr;\n\tfor( uint32_t i = ith; i < result.length; i += n_threads )\n\t{\n\t\tconst int offset = i * FFT_STEP;\n\t\tconst float* rsi = samples + offset;\n\t\tcontext.fft( arr, rsi, countSamples - offset );\n\n\t\tfor( size_t j = 0; j < N_MEL; j++ )\n\t\t\tresult.data[ j * result.length + i ] = arr[ j ];\n\t}\n}\n\nHRESULT Spectrogram::MelContext::workCallback( int ith, void* ctx ) noexcept\n{\n\tstd::vector<Spectrogram::MelContext>& contexts = *( std::vector<Spectrogram::MelContext>* )ctx;\n\ttry\n\t{\n\t\tcontexts.at( ith ).run( ith );\n\t\treturn S_OK;\n\t}\n\tcatch( const std::bad_alloc& )\n\t{\n\t\treturn E_OUTOFMEMORY;\n\t}\n\tcatch( const std::exception& )\n\t{\n\t\treturn E_FAIL;\n\t}\n}\n\nHRESULT Spectrogram::pcmToMel( const iAudioBuffer* buffer, const Filters& filters, int threads )\n{\n\tif( nullptr == buffer )\n\t\treturn E_POINTER;\n\tconst uint32_t countSamples = buffer->countSamples();\n\tif( 0 == countSamples )\n\t\treturn OLE_E_BLANK;\n\tconst float* const samples = buffer->getPcmMono();\n\n\tlength = ( countSamples ) / FFT_STEP;\n\tdata.resize( N_MEL * length );\n\n\tif( threads < 2 )\n\t{\n\t\tMelContext ctx{ samples, countSamples, filters, *this, 1 };\n\t\tctx.run( 0 );\n\t}\n\telse\n\t{\n\t\tstd::vector<MelContext> contexts;\n\t\tcontexts.reserve( threads );\n\t\tfor( int i = 0; i < threads; i++ )\n\t\t\tcontexts.emplace_back( MelContext{ samples, countSamples, filters, *this, (int)threads } );\n\t\tCHECK( parallelFor( &MelContext::workCallback, threads, &contexts ) );\n\t}\n\n\t// clamping and normalization\n\tdouble mmax = -1e20;\n\tfor( double f : data )\n\t\tmmax = std::max( mmax, f );\n\t//printf(\"%s: max = %f\\n\", __func__, mmax);\n\n\tmmax -= 8.0;\n\n\tfor( float& f : data )\n\t{\n\t\tif( f < mmax )\n\t\t\tf = (float)mmax;\n\t\tf = (float)( ( f + 4.0 ) / 4.0 );\n\t}\n\t// DirectCompute::dbgWriteBinaryFile( LR\"(C:\\Temp\\2remove\\ML\\mel-my.bin)\", data.data(), data.size() * 4 );\n\tconst float* const pcmStereo = buffer->getPcmStereo();\n\tif( nullptr != pcmStereo )\n\t{\n\t\ttry\n\t\t{\n\t\t\tstereo.resize( countSamples );\n\t\t}\n\t\tcatch( const std::bad_alloc& )\n\t\t{\n\t\t\treturn E_OUTOFMEMORY;\n\t\t}\n\t\tmemcpy( stereo.data(), pcmStereo, countSamples * 8 );\n\t}\n\telse\n\t\tstereo.clear();\n\n\treturn S_OK;\n}\n\nvoid Whisper::computeSignalEnergy( std::vector<float>& result, const iAudioBuffer* buffer, int n_samples_per_half_window )\n{\n\tconst size_t countSamples = buffer->countSamples();\n\tconst float* const samples = buffer->getPcmMono();\n\n\tconst int hw = n_samples_per_half_window;\n\tresult.resize( countSamples );\n\n\tfor( size_t i = 0; i < countSamples; i++ )\n\t{\n\t\tfloat sum = 0;\n\t\tfor( int j = -hw; j <= hw; j++ )\n\t\t\tif( i + j >= 0 && i + j < countSamples )\n\t\t\t\tsum += fabsf( samples[ i + j ] );\n\t\tresult[ i ] = sum / ( 2 * hw + 1 );\n\t}\n}\n\nHRESULT Spectrogram::copyStereoPcm( size_t offset, size_t length, std::vector<StereoSample>& buffer ) const\n{\n\tif( stereo.empty() )\n\t\treturn OLE_E_BLANK;\n\n\tlength *= FFT_STEP;\n\toffset *= FFT_STEP;\n\tif( offset >= stereo.size() )\n\t\treturn E_BOUNDS;\n\n\ttry\n\t{\n\t\tbuffer.resize( length );\n\t}\n\tcatch( const std::bad_alloc& )\n\t{\n\t\treturn E_OUTOFMEMORY;\n\t}\n\n\tconst size_t lengthToCopy = std::min( length, stereo.size() - offset );\n\tmemcpy( buffer.data(), &stereo[ offset ], lengthToCopy * 8 );\n\tif( lengthToCopy == length )\n\t\treturn S_OK;\n\n\tmemset( &buffer[ lengthToCopy ], 0, ( buffer.size() - lengthToCopy ) * 8 );\n\treturn S_OK;\n}"
  },
  {
    "path": "Whisper/Whisper/Spectrogram.h",
    "content": "#pragma once\n#include \"WhisperModel.h\"\n#include \"iSpectrogram.h\"\n#include \"audioConstants.h\"\n\nnamespace Whisper\n{\n\tstruct iAudioBuffer;\n\n\t// This implementation of iSpectrogram interface converts complete audio into MEL spectrogram\n\t// Used for unbuffered audio, and capture: iContext.runFull and runCapture methods.\n\tclass Spectrogram: public iSpectrogram\n\t{\n\t\tuint32_t length = 0;\n\t\tstatic constexpr uint32_t mel = N_MEL;\n\t\tstd::vector<float> data;\n\t\tstd::vector<StereoSample> stereo;\n\n\t\tHRESULT makeBuffer( size_t off, size_t len, const float** buffer, size_t& stride ) noexcept override final\n\t\t{\n\t\t\tif( off + len > length )\n\t\t\t\treturn E_BOUNDS;\n\t\t\t*buffer = &data[ off ];\n\t\t\tstride = length;\n\t\t\treturn S_OK;\n\t\t}\n\n\t\tclass MelContext;\n\n\t\tHRESULT copyStereoPcm( size_t offset, size_t length, std::vector<StereoSample>& buffer ) const override final;\n\n\tpublic:\n\t\tsize_t getLength() const noexcept override final\n\t\t{\n\t\t\treturn length;\n\t\t}\n\t\tHRESULT pcmToMel( const iAudioBuffer* buffer, const Filters& filters, int threads = 1 );\n\n\t\tsize_t memoryUsage() const\n\t\t{\n\t\t\treturn data.size() * 4;\n\t\t}\n\t};\n\n\t// average the fabs of the signal\n\tvoid computeSignalEnergy( std::vector<float>& result, const iAudioBuffer* buffer, int n_samples_per_half_window );\n}"
  },
  {
    "path": "Whisper/Whisper/TranscribeResult.h",
    "content": "#pragma once\n#include \"../API/iTranscribeResult.cl.h\"\n#include \"../ComLightLib/comLightServer.h\"\n\nnamespace Whisper\n{\n\tclass TranscribeResult : public ComLight::ObjectRoot<iTranscribeResult>\n\t{\n\t\tHRESULT COMLIGHTCALL getSize( sTranscribeLength& rdi ) const noexcept override final\n\t\t{\n\t\t\trdi.countSegments = (uint32_t)segments.size();\n\t\t\trdi.countTokens = (uint32_t)tokens.size();\n\t\t\treturn S_OK;\n\t\t}\n\t\tconst sSegment* COMLIGHTCALL getSegments() const noexcept override final\n\t\t{\n\t\t\tif( !segments.empty() )\n\t\t\t\treturn segments.data();\n\t\t\treturn nullptr;\n\t\t}\n\t\tconst sToken* COMLIGHTCALL getTokens() const noexcept override final\n\t\t{\n\t\t\tif( !tokens.empty() )\n\t\t\t\treturn tokens.data();\n\t\t\treturn nullptr;\n\t\t}\n\n\tpublic:\n\t\tstd::vector<sSegment> segments;\n\t\tstd::vector<sToken> tokens;\n\t\tstd::vector<std::string> segmentsText;\n\t};\n\n\tclass TranscribeResultStatic : public ComLight::Object<TranscribeResult>\n\t{\n\t\tuint32_t COMLIGHTCALL Release() override final\n\t\t{\n\t\t\t// When the ref.counter reaches zero, Object.Release() method calls `delete this`.\n\t\t\t// We don't want that for the aggregated object.\n\t\t\t// Instead we only decrement the ref.counter, but do not delete the object even when the counter reaches zero.\n\t\t\treturn RefCounter::implRelease();\n\t\t}\n\t};\n}"
  },
  {
    "path": "Whisper/Whisper/Vocabulary.cpp",
    "content": "#include \"stdafx.h\"\n#include \"Vocabulary.h\"\n#include \"loaderUtils.h\"\n#include <regex>\nusing ComLight::iReadStream;\nusing namespace Whisper;\n\nVocabulary::Vocabulary() :\n\tidFromToken( 17u, 0.75f, 0.25f, 1.5f, 1024 )\n{ }\n\nvoid Vocabulary::addExtra( int index, const char* format, int i )\n{\n\tconst int len = std::snprintf( nullptr, 0, format, i );\n\tconst size_t offset = stringData.size();\n\tstringData.resize( offset + len + 1 );\n\tchar* const rdi = stringData.data() + offset;\n\tstd::snprintf( rdi, len + 1, format, i );\n\trdi[ len ] = '\\0';\n\ttokens[ index ] = reinterpret_cast<const char*>( offset );\n}\n\nvoid Vocabulary::completeBuild()\n{\n\tstringData.shrink_to_fit();\n\n\t// Replace offsets with char pointers\n\tconst size_t dataLength = stringData.size();\n\tfor( auto& s : tokens )\n\t{\n\t\t// The reason this hack works - on Windows, lower 2GB of address space is reserved to the kernel.\n\t\t// That's why the strings from the read only section of this DLL like \"[_PREV_]\" are guaranteed to have their addresses much larger than the size of the data buffer\n\t\tconst size_t ri = reinterpret_cast<size_t>( s );\n\t\tif( ri < dataLength )\n\t\t\ts = stringData.data() + ri;\n\t}\n\n\t// Build hash map to lookup the tokens\n\tconst size_t tokensCount = tokens.size();\n\tfor( size_t i = 0; i < tokensCount; i++ )\n\t\tidFromToken.SetAt( tokens[ i ], (int)i );\n\tidFromToken.Rehash();\n\n\t// Log success message\n\tint64_t cb = stringData.size();\n\tcb += tokens.size() * sizeof( void* );\n\n\tcb += sizeof( void* ) * idFromToken.GetHashTableSize();\n\tcb += ( sizeof( THashMap::CPair ) + 16 ) * idFromToken.GetCount();\n\n\tconstexpr double mulKb = 1.0 / ( 1 << 10 );\n\tlogDebug( u8\"Loaded vocabulary, %zu strings, %.1f kb RAM\", tokens.size(), mulKb * cb );\n}\n\nint Vocabulary::findId( const char* token ) const\n{\n\tauto p = idFromToken.Lookup( token );\n\tif( nullptr != p )\n\t\treturn p->m_value;\n\telse\n\t\treturn -1;\n}\n\nHRESULT Vocabulary::load( ComLight::iReadStream* stm, int lengthInHeader )\n{\n\tif( lengthInHeader <= 0 )\n\t\treturn E_INVALIDARG;\n\n\ttokens.clear();\n\tstringData.clear();\n\n\tint countWords = 0;\n\tCHECK( readStruct( stm, countWords ) );\n\tif( countWords <= 0 )\n\t\treturn E_INVALIDARG;\n\n\tconst size_t count = (uint32_t)countWords;\n\tconst size_t actualCount = std::max( count, (size_t)lengthInHeader );\n\ttokens.resize( actualCount );\n\n\tfor( int i = 0; i < count; i++ )\n\t{\n\t\tint countChars = 0;\n\t\tCHECK( readStruct( stm, countChars ) );\n\t\tif( countChars < 0 )\n\t\t{\n\t\t\tlogError( u8\"Vocabulary.load failed: string length is negative\" );\n\t\t\treturn E_INVALIDARG;\n\t\t}\n\t\tif( countChars == 0 )\n\t\t{\n\t\t\t// This happens with `ggml-large.bin` and `ggml-large-v1.bin` models.\n\t\t\t// A bug in the model maybe?\n\t\t\ttokens[ i ] = \"\";\n\t\t\tcontinue;\n\t\t}\n\t\tconst size_t len = (size_t)countChars;\n\n\t\tconst size_t offset = stringData.size();\n\t\tstringData.resize( offset + len + 1 );\n\n\t\tCHECK( readBytes( stm, &stringData[ offset ], len ) );\n\t\t*stringData.rbegin() = '\\0';\n\n\t\ttokens[ i ] = reinterpret_cast<const char*>( offset );\n\t}\n\n\tn_vocab = lengthInHeader;\n\n\tif( is_multilingual() )\n\t{\n\t\ttoken_eot++;\n\t\ttoken_sot++;\n\t\ttoken_prev++;\n\t\ttoken_solm++;\n\t\ttoken_not++;\n\t\ttoken_beg++;\n\t};\n\n\tif( countWords < lengthInHeader )\n\t{\n\t\tfor( int i = countWords; i < lengthInHeader; i++ )\n\t\t{\n\t\t\tif( i > token_beg )\n\t\t\t\taddExtra( i, \"[_TT_%i]\", i - token_beg );\n\t\t\telse if( i == token_eot )\n\t\t\t\ttokens[ i ] = \"[_EOT_]\";\n\t\t\telse if( i == token_sot )\n\t\t\t\ttokens[ i ] = \"[_SOT_]\";\n\t\t\telse if( i == token_prev )\n\t\t\t\ttokens[ i ] = \"[_PREV_]\";\n\t\t\telse if( i == token_not )\n\t\t\t\ttokens[ i ] = \"[_NOT_]\";\n\t\t\telse if( i == token_beg )\n\t\t\t\ttokens[ i ] = \"[_BEG_]\";\n\t\t\telse\n\t\t\t\taddExtra( i, \"[_extra_token_%i]\", i );\n\t\t}\n\t}\n\n\tcompleteBuild();\n\treturn S_OK;\n}\n\nvoid Vocabulary::getSpecialTokens( SpecialTokens& rdi ) const\n{\n\trdi.TranscriptionEnd = token_eot;\n\trdi.TranscriptionStart = token_sot;\n\trdi.PreviousWord = token_prev;\n\trdi.SentenceStart = token_solm;\n\trdi.Not = token_not;\n\trdi.TranscriptionBegin = token_beg;\n\trdi.TaskTranslate = token_translate;\n\trdi.TaskTranscribe = token_transcribe;\n}\n\n// https://github.com/ggerganov/whisper.cpp/blob/v1.2.1/whisper.cpp#L2451\nHRESULT Vocabulary::tokenize( const std::string& text, std::vector<id>& tokens ) const\n{\n\tstd::vector<std::string> words;\n\n\t// first split the text into words\n\t{\n\t\tstd::string str = text;\n\t\tstd::string pat = R\"('s|'t|'re|'ve|'m|'ll|'d| ?[[:alpha:]]+| ?[[:digit:]]+| ?[^\\s[:alpha:][:digit:]]+|\\s+(?!\\S)|\\s+)\";\n\t\tstd::regex re( pat );\n\t\tstd::smatch m;\n\n\t\twhile( std::regex_search( str, m, re ) )\n\t\t{\n\t\t\tfor( auto x : m )\n\t\t\t\twords.push_back( x );\n\t\t\tstr = m.suffix();\n\t\t}\n\t}\n\n\t// find the longest tokens that form the words:\n\ttokens.clear();\n\tfor( const auto& word : words )\n\t{\n\t\tif( word.empty() )\n\t\t\tcontinue;\n\n\t\tint i = 0;\n\t\tint n = (int)word.size();\n\t\twhile( i < n )\n\t\t{\n\t\t\tint j = n;\n\t\t\twhile( j > i )\n\t\t\t{\n\t\t\t\tconst int it = findId( word.substr( i, j - i ) );\n\t\t\t\tif( it >= 0 )\n\t\t\t\t{\n\t\t\t\t\ttokens.push_back( it );\n\t\t\t\t\ti = j;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tj--;\n\t\t\t}\n\t\t\tif( i == n )\n\t\t\t\tbreak;\n\n\t\t\tif( j == i )\n\t\t\t{\n\t\t\t\tconst auto sub = word.substr( i, 1 );\n\t\t\t\tconst int it = findId( sub );\n\t\t\t\tif( it >= 0 )\n\t\t\t\t{\n\t\t\t\t\ttokens.push_back( it );\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tlogError( u8\"Unknown token \\\"%s\\\"\", sub.c_str() );\n\t\t\t\t\treturn E_INVALIDARG;\n\t\t\t\t}\n\t\t\t\ti++;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn S_OK;\n}"
  },
  {
    "path": "Whisper/Whisper/Vocabulary.h",
    "content": "#pragma once\n#include \"../../ComLightLib/streams.h\"\n#include \"../API/SpecialTokens.h\"\n#include \"../Utils/MurmurHash3.h\"\n\nnamespace Whisper\n{\n\tclass Vocabulary\n\t{\n\t\tstd::vector<const char*> tokens;\n\t\tstd::vector<char> stringData;\n\t\tusing THashMap = CAtlMap<const char*, int, StringPtrTraits>;\n\t\tTHashMap idFromToken;\n\n\t\tvoid addExtra( int index, const char* format, int i );\n\n\t\tvoid completeBuild();\n\tpublic:\n\t\tVocabulary();\n\n\t\tint n_vocab = 51864;\n\n\t\tHRESULT load( ComLight::iReadStream* stm, int lengthInHeader );\n\n\t\tusing id = int;\n\n\t\tid token_eot = 50256;\n\t\tid token_sot = 50257;\n\t\tid token_prev = 50360;\n\t\tid token_solm = 50361; // ??\n\t\tid token_not = 50362; // no timestamps\n\t\tid token_beg = 50363;\n\n\t\t// available tasks\n\t\tstatic const id token_translate = 50358;\n\t\tstatic const id token_transcribe = 50359;\n\n\t\tbool is_multilingual() const\n\t\t{\n\t\t\treturn n_vocab == 51865;\n\t\t}\n\n\t\tconst char* string( int id ) const\n\t\t{\n\t\t\tif( id >= 0 && id < (int)tokens.size() )\n\t\t\t\treturn tokens[ id ];\n\t\t\treturn nullptr;\n\t\t}\n\n\t\tint findId( const char* token ) const;\n\t\tint findId( const std::string& token ) const\n\t\t{\n\t\t\treturn findId( token.c_str() );\n\t\t}\n\n\t\tsize_t size() const\n\t\t{\n\t\t\treturn tokens.size();\n\t\t}\n\n\t\tvoid getSpecialTokens( SpecialTokens& rdi ) const;\n\n\t\tsize_t getMemoryUse() const\n\t\t{\n\t\t\treturn vectorMemoryUse( tokens ) + vectorMemoryUse( stringData );\n\t\t}\n\n\t\tHRESULT tokenize( const std::string& text, std::vector<id>& tokens ) const;\n\t};\n}"
  },
  {
    "path": "Whisper/Whisper/WhisperContext.cpp",
    "content": "#include \"stdafx.h\"\n#include \"WhisperContext.h\"\n#include \"ModelBuffers.h\"\n#include <optional>\n#include \"../Utils/Trace/tracing.h\"\n#include \"../D3D/RenderDoc/renderDoc.h\"\n#include \"../ML/testUtils.h\"\nusing namespace DirectCompute;\n\nnamespace\n{\n\t// True to measure GPU time of individual shaders which run during the encode step of the algorithm\n\tconstexpr bool profileEncodeShaders = true;\n\t// True to measure GPU time of individual shaders which run during the decode step of the algorithm\n\tconstexpr bool profileDecodeShaders = true;\n\n\tLPCTSTR traceFileNative = LR\"(C:\\Temp\\2remove\\Whisper\\gpu.bin)\";\n\tLPCTSTR traceFileHybrid = LR\"(C:\\Temp\\2remove\\Whisper\\hybrid.bin)\";\n\n\tTensorsArena::sArenaConfigs defaultArenaConfigs()\n\t{\n\t\tTensorsArena::sArenaConfigs res = {};\n\t\treturn res;\n\t}\n}\n\nWhisperContext::Arenas::Arenas() :\n\touter( defaultArenaConfigs() ), layer( defaultArenaConfigs() )\n{ }\n\nTensor WhisperContext::DecoderLayerPool::tensor( eDataType type, const std::array<uint32_t, 4>& ne )\n{\n\tassert( type == eDataType::FP32 );\n\treturn result.tensor( eDataType::FP32, ne, &DirectCompute::defaultNewCapacity );\n}\n\nclass WhisperContext::ArenaRaii\n{\n\tWhisperContext& context;\n\tiTensorArena* prevCurrent;\n\npublic:\n\n\tArenaRaii( WhisperContext& ctx, iTensorArena& ta ) :\n\t\tcontext( ctx )\n\t{\n\t\tprevCurrent = ctx.currentArena;\n\t\tctx.currentArena = &ta;\n\t}\n\n\t~ArenaRaii()\n\t{\n\t\tcontext.currentArena->reset();\n\t\tcontext.currentArena = prevCurrent;\n\t}\n};\n\nWhisperContext::WhisperContext( const Whisper::WhisperModel& wm, Whisper::ProfileCollection& pc ) :\n\tMlContext( pc ),\n\tgpuModel( wm.tensors )\n{\n#if BUILD_HYBRID_VERSION\n\tif( !wm.shared->hybridTensors.layers.empty() )\n\t{\n\t\thybridContext = std::make_unique<HybridContext>( wm );\n\t\tcheck( hybridContext->create() );\n#if SAVE_DEBUG_TRACE\n\t\tTracing::traceCreate( traceFileHybrid );\n#endif\n\t}\n\telse\n#endif\n\t{\n#if SAVE_DEBUG_TRACE\n\t\tTracing::traceCreate( traceFileNative );\n#endif\n\t}\n}\n\n#if BUILD_BOTH_VERSIONS\nnamespace\n{\n\tthread_local WhisperContext* ts_context = nullptr;\n\tconst ModelBuffers& getGlobalModel()\n\t{\n\t\treturn gpuModel;\n\t}\n}\n\n/*\nWhisperContext::WhisperContext() :\n\tgpuModel( getGlobalModel() ),\n{\n\tif( nullptr != ts_context )\n\t\tthrow HRESULT_FROM_WIN32( ERROR_ALREADY_INITIALIZED );\n\tts_context = this;\n}*/\n\nWhisperContext::~WhisperContext()\n{\n\tTracing::traceClose();\n\tif( ts_context == nullptr )\n\t\treturn;\n\tassert( ts_context == this );\n\tts_context = nullptr;\n}\n\nWhisperContext& WhisperContext::current()\n{\n\tWhisperContext* c = ts_context;\n\tif( nullptr == c )\n\t\tthrow OLE_E_BLANK;\n\treturn *c;\n}\n#else\nWhisperContext& WhisperContext::current()\n{\n\tthrow E_NOTIMPL;\n}\n#endif\n\nTensor WhisperContext::createTensor( eDataType type, const std::array<uint32_t, 4>& ne )\n{\n\t// return MlContext::createTensor( type, ne );\n\n\tiTensorArena* const ca = currentArena;\n\tif( nullptr != ca )\n\t\treturn ca->tensor( type, ne );\n\telse\n\t\treturn MlContext::createTensor( type, ne );\n}\n\nvoid WhisperContext::fmaRepeat( Tensor& cur, const TensorPair& that )\n{\n\tMlContext::fmaRepeat( cur, that.w, that.b );\n}\n\nTensor WhisperContext::convolutionAndGelu( const Tensor& mel, uint32_t n_ctx )\n{\n\tconst EncoderBuffers& model = gpuModel.enc;\n\tTensor cur = conv_1d_1s( model.conv1.w, mel );\n\tTracing::tensor( \"enc.conv1\", cur );\n\taddRepeatGelu( cur, model.conv1.b );\n\tTracing::tensor( \"enc.temp1\", cur );\n\n\tcur = conv_1d_2s( model.conv2.w, cur );\n\taddRepeatGelu( cur, model.conv2.b );\n\n\tconst Tensor& posEmbed = model.positionalEmbedding;\n\tconst uint32_t peStride = posEmbed.ne[ 0 ];\n\tconstexpr uint32_t peOffset = 0;\n\n\tTensor e_pe = view2d( posEmbed, posEmbed.ne[ 0 ], n_ctx, peStride, peOffset );\n\tcur = add( e_pe, transpose( cur ) );\n\treturn cur;\n}\n\nTensor WhisperContext::encodeLayer( const Tensor& source, size_t index, uint32_t n_state, uint32_t n_head, uint32_t n_ctx )\n{\n\tauto prof = profiler.block( eProfilerBlock::EncodeLayer );\n\tArenaRaii arenaRaii{ *this, arenas.layer };\n\n\tconst LayerEncoder& layer = gpuModel.enc.layers[ index ];\n\t// norm\n\tTensor cur = norm( source );\n\tif( 0 == index )\n\t\tTracing::tensor( \"enc-norm\", cur );\n\tfmaRepeat( cur, layer.attnLn0 );\n\n\t// self-attention\n\tTensor Qcur;\n\tTensor reshaped;\n\tif( gpuInfo().useReshapedMatMul() )\n\t{\n\t\tconst uint16_t tag = profiler.setNextTag( \"enc.layer.1\" );\n\t\treshaped = reshapePanels( cur );\n\t\tprofiler.setNextTag( tag );\n\t\tQcur = mulMatTiledEx( layer.attnQuery.w, reshaped );\n\t}\n\telse\n\t{\n\t\tprofiler.setNextTag( \"enc.layer.1\" );\n\t\tQcur = mulMat( layer.attnQuery.w, cur );\n\t}\n\n\tif( 0 == index )\n\t\tTracing::tensor( \"enc-Qcur\", Qcur );\n\taddRepeat( Qcur, layer.attnQuery.b );\n\n\t// note: no bias for Key\n\tTensor Kcur;\n\tif( gpuInfo().useReshapedMatMul() )\n\t{\n\t\t// Already reshaped by the previous `if`\n\t\tprofiler.setNextTag( \"enc.layer.2\" );\n\t\tKcur = mulMatTiledEx( layer.attnKey, reshaped );\n\t}\n\telse\n\t{\n\t\tprofiler.setNextTag( \"enc.layer.2\" );\n\t\tKcur = mulMat( layer.attnKey, cur );\n\t}\n\n\tif( 0 == index )\n\t\tTracing::tensor( \"enc-Kcur\", Kcur );\n\n\tTensor Vcur;\n\tif( gpuInfo().useReshapedMatMul() )\n\t{\n\t\t// Already reshaped by the previous `if`\n\t\tprofiler.setNextTag( \"enc.layer.3\" );\n\t\tVcur = mulMatTiledEx( layer.attnValue.w, reshaped );\n\t}\n\telse\n\t{\n\t\tprofiler.setNextTag( \"enc.layer.3\" );\n\t\tVcur = mulMat( layer.attnValue.w, cur );\n\t}\n\n\tif( 0 == index )\n\t\tTracing::tensor( \"enc-Vcur\", Vcur );\n\taddRepeat( Vcur, layer.attnValue.b );\n\n\t// ------\n\tTensor Q = permute( copy( Qcur, eDataType::FP16, { n_state / n_head, n_head, n_ctx } ), 0, 2, 1, 3 );\n\tTensor K = permute( copy( Kcur, eDataType::FP16, { n_state / n_head, n_head, n_ctx } ), 0, 2, 1, 3 );\n\tTensor V = copy( permute( Vcur.reshape3d( n_state / n_head, n_head, n_ctx ), 1, 2, 0, 3 ), eDataType::FP16, { n_ctx, n_state / n_head, n_head } );\n\tTensor KQV = flashAttention( Q, K, V, false );\n\tif( 0 == index )\n\t\tTracing::tensor( \"enc-KQV\", KQV );\n\tTensor KQV_merged = permute( KQV, 0, 2, 1, 3 );\n\tcopyInPlace( cur, KQV_merged, eDataType::FP32, { n_state, n_ctx } );\n\n\t// projection\n\tif( gpuInfo().useReshapedMatMul() )\n\t{\n\t\tconst uint16_t tag = profiler.setNextTag( \"enc.layer.4\" );\n\t\tcur = reshapePanels( cur );\n\t\tprofiler.setNextTag( tag );\n\t\tcur = mulMatTiledEx( layer.attnLn1.w, cur );\n\t}\n\telse\n\t{\n\t\tprofiler.setNextTag( \"enc.layer.4\" );\n\t\tcur = mulMat( layer.attnLn1.w, cur );\n\t}\n\n\t// add the input\n\taddRepeatEx( cur, layer.attnLn1.b, source );\n\n\t// feed-forward network\n\tTensor inpFF = cur;\n\n\tcur = norm( inpFF );\n\tfmaRepeat( cur, layer.mlpLn );\n\n\t// fully connected\n\tif( gpuInfo().useReshapedMatMul() )\n\t{\n\t\tconst uint16_t tag = profiler.setNextTag( \"enc.layer.5\" );\n\t\tcur = reshapePanels( cur );\n\t\tprofiler.setNextTag( tag );\n\t\tcur = mulMatTiledEx( layer.mlp0.w, cur );\n\t}\n\telse\n\t{\n\t\tprofiler.setNextTag( \"enc.layer.5\" );\n\t\tcur = mulMat( layer.mlp0.w, cur );\n\t}\n\taddRepeatGelu( cur, layer.mlp0.b );\n\n\t// projection\n\tif( gpuInfo().useReshapedMatMul() )\n\t{\n\t\tconst uint16_t tag = profiler.setNextTag( \"enc.layer.6\" );\n\t\tcur = reshapePanels( cur );\n\t\tprofiler.setNextTag( tag );\n\t\tcur = mulMatTiledEx( layer.mlp1.w, cur );\n\t}\n\telse\n\t{\n\t\tprofiler.setNextTag( \"enc.layer.6\" );\n\t\tcur = mulMat( layer.mlp1.w, cur );\n\t}\n\n\t// output from this layer\n\taddRepeatEx( cur, layer.mlp1.b, inpFF );\n\treturn cur;\n}\n\nvoid WhisperContext::createKeyValueBuffers( const sEncodeParams& encParams )\n{\n\t{\n\t\tconst uint32_t n_audio_ctx = encParams.n_audio_ctx;\n\t\tconst uint32_t n_mem = encParams.n_text_layer * encParams.n_audio_ctx;\n\t\tconst uint32_t n_elements = encParams.n_text_state * n_mem;\n\t\tkvCross.resize( n_elements );\n\t}\n\n#if BUILD_HYBRID_VERSION\n\tif( !hybridContext )\n#endif\n\t{\n\t\tconst uint32_t n_mem = encParams.n_text_layer * encParams.n_text_ctx;\n\t\tconst uint32_t n_elements = encParams.n_text_state * n_mem;\n\t\tkv.resize( n_elements );\n\t}\n}\n\nTensor WhisperContext::encode( Whisper::iSpectrogram& spectrogram, const sEncodeParams& encParams )\n{\n\tauto prof = profiler.block( eProfilerBlock::Encode );\n\tCaptureRaii renderdocCapture;\n\tprofiler.profileShaders = profileEncodeShaders;\n\n\tcreateKeyValueBuffers( encParams );\n\t// Upload the source\n\tcheck( melInput.create( spectrogram, encParams ) );\n\tTracing::tensor( \"enc.input\", melInput );\n\n\tArenaRaii arenaRaii{ *this, arenas.outer };\n\n\t// Initial few steps\n\tTensor cur = convolutionAndGelu( melInput, encParams.n_ctx );\n\n\t// Process all these layers\n\t{\n\t\tconst size_t layersCount = encParams.layersCount;\n\t\tfor( size_t i = 0; i < layersCount; i++ )\n\t\t{\n\t\t\tTracing::tensor( { \"enc.layer[ %i ].in\", i }, cur );\n\t\t\tcur = encodeLayer( cur, i, encParams.n_state, encParams.n_head, encParams.n_ctx );\n\t\t}\n\t}\n\tTracing::tensor( \"enc.layers\", cur );\n\n\t// A few last steps\n\t{\n\t\tcur = norm( cur );\n\t\t// cur = ln_f_g*cur + ln_f_b\n\t\tfmaRepeat( cur, gpuModel.enc.lnPost );\n\t}\n\n\t// pre-compute cross-attention buffers\n\t{\n\t\tTensor reshaped;\n\t\tif( gpuInfo().useReshapedMatMul() )\n\t\t{\n\t\t\tif( cur.ne[ 1 ] != 1 )\n\t\t\t{\n\t\t\t\tprofiler.setNextTag( \"enc.cross\" );\n\t\t\t\treshaped = reshapePanels( cur );\n\t\t\t}\n\t\t\telse\n\t\t\t\treshaped = cur;\n\t\t}\n\n\t\tconst size_t layersCount = encParams.n_text_layer;\n\t\tconst uint32_t stride = encParams.n_state * encParams.n_ctx;\n\t\tconst float finalScaling = computeScaling( (int)encParams.n_state, (int)encParams.n_head );\n\t\tfor( size_t i = 0; i < layersCount; i++ )\n\t\t{\n\t\t\tconst LayerDecoder& layer = gpuModel.dec.layers[ i ];\n\t\t\tTensor Kcross, Vcross;\n\t\t\tif( gpuInfo().useReshapedMatMul() )\n\t\t\t\tKcross = mulMatEx( layer.crossAttnKey, reshaped, \"enc.cross.1\" );\n\t\t\telse\n\t\t\t{\n\t\t\t\tprofiler.setNextTag( \"enc.cross.1\" );\n\t\t\t\tKcross = mulMat( layer.crossAttnKey, cur );\n\t\t\t}\n\t\t\tscale( Kcross, finalScaling );\n\n\t\t\tif( gpuInfo().useReshapedMatMul() )\n\t\t\t\tVcross = mulMatEx( layer.crossAttnValue.w, reshaped, \"enc.cross.2\" );\n\t\t\telse\n\t\t\t{\n\t\t\t\tprofiler.setNextTag( \"enc.cross.2\" );\n\t\t\t\tVcross = mulMat( layer.crossAttnValue.w, cur );\n\t\t\t}\n\t\t\taddRepeat( Vcross, layer.crossAttnValue.b );\n\n\t\t\tTensor k = kvCross.keys.view( stride, stride * (uint32_t)i );\n\t\t\tcopyImpl( Kcross, k, Kcross.getType() == eDataType::FP32 );\n\n\t\t\tTensor v = kvCross.values.view( stride, stride * (uint32_t)i );\n\t\t\tcopyImpl( Vcross, v, Vcross.getType() == eDataType::FP32 );\n\t\t}\n\t}\n\n#if BUILD_HYBRID_VERSION\n\tif( hybridContext )\n\t{\n\t\t// When running hybrid model, download cross-attention buffers from VRAM to system RAM\n\t\tcheck( hybridContext->downloadKeyValues( kvCross ) );\n\t}\n#endif\n\treturn cur;\n}\n\nstruct WhisperContext::sLayerDecParams\n{\n\tuint32_t n_state, n_head, N;\n\tuint32_t n_ctx, n_past, M;\n};\n\nTensor WhisperContext::decodeLayer( const Tensor& inpL, size_t il, const sLayerDecParams& ldp )\n{\n\tauto prof = profiler.block( eProfilerBlock::DecodeLayer );\n\tconst auto& layer = gpuModel.dec.layers[ il ];\n\tstd::optional<ArenaRaii> arenaRaii{ std::in_place, *this, arenas.layer };\n\tif( 0 == il ) Tracing::tensor( \"dec-inpL\", inpL );\n\n\t// norm\n\tTensor cur = norm( inpL );\n\tfmaRepeat( cur, layer.attnLn0 );\n\tif( 0 == il ) Tracing::tensor( \"dec-norm\", cur );\n\n\t// self-attention\n\t{\n\t\tprofiler.setNextTag( \"dec.layer.1\" );\n\t\tTensor Qcur = mulMat( layer.attnQuery.w, cur );\n\t\tif( 0 == il ) Tracing::tensor( \"dec-Qcur-0\", Qcur );\n\t\tconst float scaling = computeScaling( (int)ldp.n_state, (int)ldp.n_head );\n\t\taddRepeatScale( Qcur, layer.attnQuery.b, scaling );\n\t\tif( 0 == il ) Tracing::tensor( \"dec-Qcur-1\", Qcur );\n\n\t\t// note: no bias for Key\n\t\tprofiler.setNextTag( \"dec.layer.2\" );\n\t\tTensor Kcur = mulMat( layer.attnKey, cur );\n\t\tscale( Kcur, scaling );\n\t\tif( 0 == il ) Tracing::tensor( \"dec-Kcur\", Kcur );\n\n\t\tprofiler.setNextTag( \"dec.layer.3\" );\n\t\tTensor Vcur = mulMat( layer.attnValue.w, cur );\n\t\taddRepeat( Vcur, layer.attnValue.b );\n\t\tif( 0 == il ) Tracing::tensor( \"dec-Vcur\", Vcur );\n\n\t\t// store key and value to memory\n\t\t{\n\t\t\tconst uint32_t len = ldp.N * ldp.n_state;\n\t\t\tconst uint32_t off = ldp.n_state * ( (uint32_t)il * ldp.n_ctx + ldp.n_past );\n\t\t\tTensor k = kv.keys.view( len, off );\n\t\t\tTensor v = kv.values.view( len, off );\n\t\t\tcopyImpl( Kcur, k, true );\n\t\t\tcopyImpl( Vcur, v, true );\n\t\t}\n\n\t\t// ------\n\t\tTensor Q = permute( copy( Qcur, eDataType::FP32, { ldp.n_state / ldp.n_head, ldp.n_head, ldp.N } ), 0, 2, 1, 3 );\n\t\tTensor K = permute( kv.keys.view( ( ldp.n_past + ldp.N ) * ldp.n_state, (uint32_t)il * ldp.n_ctx * ldp.n_state )\n\t\t\t.reshape3d( ldp.n_state / ldp.n_head, ldp.n_head, ldp.n_past + ldp.N ),\n\t\t\t0, 2, 1, 3 );\n\t\tprofiler.setNextTag( \"dec.layer.4\" );\n\t\tTensor KQ = mulMat( K, Q );\n\t\tif( 0 == il ) Tracing::tensor( \"dec-KQ-0\", KQ );\n\t\tdiagMaskInf( KQ, ldp.n_past );\n\t\tif( 0 == il ) Tracing::tensor( \"dec-KQ-1\", KQ );\n\t\tprofiler.setNextTag( \"decLayer.1\" );\n\t\tsoftMax( KQ );\n\t\tif( 0 == il ) Tracing::tensor( \"dec-KQ-2\", KQ );\n\n\t\tTensor V_trans = permute(\n\t\t\tkv.values\n\t\t\t.view( ( ldp.n_past + ldp.N ) * ldp.n_state, (uint32_t)il * ldp.n_ctx * ldp.n_state )\n\t\t\t.reshape3d( ldp.n_state / ldp.n_head, ldp.n_head, ldp.n_past + ldp.N ),\n\t\t\t1, 2, 0, 3 );\n\n\t\tprofiler.setNextTag( \"dec.layer.5\" );\n\t\tTensor KQV = mulMat( V_trans, KQ );\n\t\tif( 0 == il ) Tracing::tensor( \"dec-KQV\", KQV );\n\n\t\tTensor KQV_merged = permute( KQV, 0, 2, 1, 3 );\n\t\tcopyInPlace( cur, KQV_merged, eDataType::FP32, { ldp.n_state, ldp.N } );\n\t}\n\n\t{\n\t\tprofiler.setNextTag( \"dec.layer.6\" );\n\t\tcur = mulMat( layer.attnLn1.w, cur );\n\t}\n\t// add the input\n\taddRepeatEx( cur, layer.attnLn1.b, inpL );\n\tTensor inpCA = cur;\n\n\t// norm\n\t{\n\t\tcur = norm( inpCA );\n\t\tfmaRepeat( cur, layer.crossAttnLn0 );\n\t}\n\n\t// cross-attention\n\t{\n\t\tprofiler.setNextTag( \"dec.layer.7\" );\n\t\tTensor Qcur = mulMat( layer.crossAttnQuery.w, cur );\n\t\taddRepeatScale( Qcur, layer.crossAttnQuery.b, computeScaling( (int)ldp.n_state, (int)ldp.n_head ) );\n\n\t\t// Kcross is already scaled\n\t\tconst uint32_t len = ldp.M * ldp.n_state;\n\t\tconst uint32_t off = (uint32_t)il * len;\n\t\tTensor Kcross = kvCross.keys.view( len, off ).reshape3d( ldp.n_state / ldp.n_head, ldp.n_head, ldp.M );\n\t\tTensor Vcross = kvCross.values.view( len, off ).reshape3d( ldp.n_state / ldp.n_head, ldp.n_head, ldp.M );\n\n\t\t// ------\n\t\tTensor Q = permute( copy( Qcur, eDataType::FP32, { ldp.n_state / ldp.n_head, ldp.n_head, ldp.N } ), 0, 2, 1, 3 );\n\t\tTensor K = permute( Kcross, 0, 2, 1, 3 );\n\t\tprofiler.setNextTag( \"dec.layer.8\" );\n\t\tTensor KQ = mulMat( K, Q );\n\t\tprofiler.setNextTag( \"decLayer.2\" );\n\t\tsoftMax( KQ );\n\t\tTensor V_trans = permute( Vcross, 1, 2, 0, 3 );\n\t\tprofiler.setNextTag( \"dec.layer.9\" );\n\t\tTensor KQV = mulMat( V_trans, KQ );\n\t\tif( 0 == il ) Tracing::tensor( \"dec-KQV\", KQV );\n\t\tTensor KQV_merged = permute( KQV, 0, 2, 1, 3 );\n\n\t\tcopyInPlace( cur, KQV_merged, eDataType::FP32, { ldp.n_state, ldp.N } );\n\t}\n\n\t// projection\n\t{\n\t\tprofiler.setNextTag( \"dec.layer.10\" );\n\t\tcur = mulMat( layer.crossAttnLn1.w, cur );\n\t}\n\t// add the input\n\taddRepeatEx( cur, layer.crossAttnLn1.b, inpCA );\n\tTensor inpFF = cur;\n\n\t// feed-forward network\n\t{\n\t\t// norm\n\t\tcur = norm( inpFF );\n\t\tfmaRepeat( cur, layer.mlpLn );\n\n\t\tif( gpuInfo().useReshapedMatMul() )\n\t\t\tcur = mulMatEx( layer.mlp0.w, cur, \"dec.layer.11\" );\n\t\telse\n\t\t{\n\t\t\tprofiler.setNextTag( \"dec.layer.11\" );\n\t\t\tcur = mulMat( layer.mlp0.w, cur );\n\t\t}\n\n\t\taddRepeatGelu( cur, layer.mlp0.b );\n\n\t\t// projection\n\t\tif( gpuInfo().useReshapedMatMul() )\n\t\t{\n\t\t\tif( cur.ne[ 1 ] != 1 )\n\t\t\t{\n\t\t\t\tconst uint16_t tag = profiler.setNextTag( \"dec.layer.12\" );\n\t\t\t\tcur = reshapePanels( cur );\n\n\t\t\t\t// The mulMatTiledEx() line creates a layer output tensor. We have a special pool for such tensors so they survive the destruction of the arena.\n\t\t\t\tarenaRaii.emplace( *this, decPool );\n\t\t\t\tprofiler.setNextTag( tag );\n\t\t\t\tcur = mulMatTiledEx( layer.mlp1.w, cur );\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// The mulMatByRowTiledEx() line creates a layer output tensor. We have a special pool for such tensors so they survive the destruction of the arena.\n\t\t\t\tarenaRaii.emplace( *this, decPool );\n\t\t\t\tprofiler.setNextTag( \"dec.layer.12\" );\n\t\t\t\tcur = mulMatByRowTiledEx( layer.mlp1.w, cur );\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// The mulMat() line creates a layer output tensor. We have a special pool for such tensors so they survive the destruction of the arena.\n\t\t\tarenaRaii.emplace( *this, decPool );\n\t\t\tprofiler.setNextTag( \"dec.layer.12\" );\n\t\t\tcur = mulMat( layer.mlp1.w, cur );\n\t\t}\n\t}\n\t// output from this layer\n\taddRepeatEx( cur, layer.mlp1.b, inpFF );\n\treturn cur;\n}\n\nvoid WhisperContext::decode( const int* tokens, const int n_tokens, const sDecodeParams& decParams, std::vector<float>& probs, int threads )\n{\n\tauto cppp = profiler.cpuBlock( Whisper::eCpuBlock::DecodeStep );\n\n#if BUILD_HYBRID_VERSION\n\tif( hybridContext )\n\t{\n\t\tHybridContext::sDecParams sdp;\n\t\tsdp.n_threads = threads;\n\t\tsdp.M = decParams.M;\n\t\tcheck( hybridContext->decode( tokens, n_tokens, decParams.n_past, sdp, probs ) );\n\t\treturn;\n\t}\n#endif\n\n\tauto prof = profiler.block( eProfilerBlock::DecodeStep );\n\tCaptureRaii renderdocCapture;\n\tprofiler.profileShaders = profileDecodeShaders;\n\tArenaRaii arenaRaii{ *this, arenas.outer };\n\n\tassert( n_tokens > 0 );\n\tconst uint32_t N = (uint32_t)n_tokens;\n\tdecoderInput.resize( N );\n\n\tTensor embd = decoderInput.embedding( tokens );\n\tTensor cur = addRows( gpuModel.dec.tokenEmbedding, gpuModel.dec.positionalEmbedding, embd, decParams.n_past );\n\tTracing::tensor( \"dec-rows\", cur );\n\n\t{\n\t\tsLayerDecParams ldp;\n\t\tldp.n_state = decParams.n_state;\n\t\tldp.n_head = decParams.n_head;\n\t\tldp.N = N;\n\t\tldp.n_ctx = decParams.n_ctx;\n\t\tldp.n_past = decParams.n_past;\n\t\tldp.M = decParams.M;\n#if 1\n\t\tfor( size_t i = 0; i < decParams.n_text_layer; i++ )\n\t\t\tcur = decodeLayer( cur, i, ldp );\n#else\n\t\tdbgDecodeTest = decodeLayer( cur, 0, ldp );\n\t\treturn;\n#endif\n\t}\n\n\t// norm\n\tcur = norm( cur );\n\tfmaRepeat( cur, gpuModel.dec.ln );\n\n\tprofiler.setNextTag( \"dec.logits\" );\n\tcur = mulMat( gpuModel.dec.tokenEmbedding, cur );\n\n\t// logits -> probs\n\tprofiler.setNextTag( \"dec.probs\" );\n\tsoftMax( cur );\n\n\tdecoderOutput.copyFromVram( cur );\n\tassert( decoderOutput.size() == N * decParams.n_vocab );\n\n\tdecoderOutput.copyToVector( probs );\n\tTracing::vector( \"probs\", probs );\n}\n\n__m128i WhisperContext::Arenas::getMemoryUse() const\n{\n\t__m128i res = outer.getMemoryUse();\n\tres = _mm_add_epi64( res, layer.getMemoryUse() );\n\treturn res;\n}\n\n__m128i WhisperContext::DecoderLayerPool::getMemoryUse() const\n{\n\tsize_t cb = result.getCapacity() * 4;\n\t__m128i res = _mm_setzero_si128();\n\treturn _mm_insert_epi64( res, (int64_t)cb, 1 );\n}\n\n__m128i WhisperContext::getMemoryUse() const\n{\n\t__m128i res = MlContext::getMemoryUse();\n\tres = _mm_add_epi64( res, arenas.getMemoryUse() );\n\tres = _mm_add_epi64( res, decPool.getMemoryUse() );\n\tres = _mm_add_epi64( res, melInput.getMemoryUse() );\n\tres = _mm_add_epi64( res, kv.getMemoryUse() );\n\tres = _mm_add_epi64( res, kvCross.getMemoryUse() );\n\tres = _mm_add_epi64( res, decoderInput.getMemoryUse() );\n\tres = _mm_add_epi64( res, decoderOutput.getMemoryUse() );\n\treturn res;\n}\n\nHRESULT WhisperContext::clearState()\n{\n\t// CHECK( kv.zeroMemory( cb ) );\n\t// CHECK( kvCross.zeroMemory( cb ) );\n\t// The above code doesn't work for some reason.\n\t// Ideally need to debug, but destroying and re-creating these two buffers is not a huge deal. Unlike the buffers in the pools, only a few megabytes of VRAM.\n\tkv.clear();\n\tkvCross.clear();\n\n\tCHECK( arenas.outer.zeroMemory() );\n\tCHECK( arenas.layer.zeroMemory() );\n\tCHECK( decPool.zeroMemory() );\n\tCHECK( decoderInput.zeroMemory() );\n\tdecoderOutput.clear();\n\treturn S_OK;\n}"
  },
  {
    "path": "Whisper/Whisper/WhisperContext.h",
    "content": "#pragma once\n#include \"../ML/MlContext.h\"\n#include \"MelInputTensor.h\"\n#include \"KeyValueBuffers.h\"\n#include \"sEncodeParams.h\"\n#include \"DecoderInputBuffers.h\"\n#include \"DecoderResultBuffer.h\"\n#include \"../ML/TensorsArena.h\"\n#include \"iSpectrogram.h\"\n#include \"../Hybrid/HybridContext.h\"\n#include <memory>\n#include \"WhisperModel.h\"\n#include <tuple>\n#include <optional>\n\nnamespace DirectCompute\n{\n\tstruct TensorPair;\n\tstruct ModelBuffers;\n\n\tclass WhisperContext : public MlContext\n\t{\n\t\tstruct Arenas\n\t\t{\n\t\t\tTensorsArena outer;\n\t\t\tTensorsArena layer;\n\t\t\tArenas();\n\t\t\t__m128i getMemoryUse() const;\n\t\t};\n\n\t\tiTensorArena* currentArena = nullptr;\n\t\tArenas arenas;\n\n\t\t// Specialized tensor arena for decoder layer outputs, with just a single tensor\n\t\tclass DecoderLayerPool : public iTensorArena\n\t\t{\n\t\t\tPooledTensor result;\n\t\tpublic:\n\t\t\tTensor tensor( eDataType type, const std::array<uint32_t, 4>& ne ) override final;\n\t\t\tvoid reset() override final { }\n\t\t\t__m128i getMemoryUse() const;\n\t\t\tvoid clear()\n\t\t\t{\n\t\t\t\tresult.clear();\n\t\t\t}\n\t\t\tHRESULT zeroMemory()\n\t\t\t{\n\t\t\t\treturn result.zeroMemory();\n\t\t\t}\n\t\t};\n\n\t\tDecoderLayerPool decPool;\n\n\t\tclass ArenaRaii;\n\n\t\tMelInputTensor melInput;\n\t\tKeyValueBuffers kv, kvCross;\n\t\tDecoderInputBuffers decoderInput;\n\t\tDecoderResultBuffer decoderOutput;\n\t\tconst ModelBuffers& gpuModel;\n#if BUILD_HYBRID_VERSION\n\t\tstd::unique_ptr<HybridContext> hybridContext;\n#endif\n\t\tstruct sWhisperMel\n\t\t{\n\t\t\tuint32_t n_len;\n\t\t\tuint32_t n_mel;\n\t\t\tconst std::vector<float>& data;\n\t\t};\n\n\t\tvoid createKeyValueBuffers( const sEncodeParams& encParams );\n\t\t// Encoder methods\n\t\tTensor convolutionAndGelu( const Tensor& mel, uint32_t n_ctx );\n\t\tTensor encodeLayer( const Tensor& source, size_t index, uint32_t n_state, uint32_t n_head, uint32_t n_ctx );\n\n\t\tstruct sLayerDecParams;\n\n\t\t// Decoder methods\n\t\tTensor decodeLayer( const Tensor& source, size_t index, const sLayerDecParams& ldp );\n\n\t\t// cur = add( mul( repeat( that.w, cur ), cur ), repeat( that.b, cur ) );\n\t\tvoid fmaRepeat( Tensor& cur, const TensorPair& that );\n\n\t\tTensor createTensor( eDataType type, const std::array<uint32_t, 4>& ne ) override final;\n\n\tpublic:\n#if BUILD_BOTH_VERSIONS\n\t\tWhisperContext();\n\t\t~WhisperContext();\n#else\n\t\t~WhisperContext() = default;\n#endif\n\t\tWhisperContext( const Whisper::WhisperModel& wm, Whisper::ProfileCollection& pc );\n\t\tWhisperContext( const WhisperContext& ) = delete;\n\n\t\tTensor encode( Whisper::iSpectrogram& spectrogram, const sEncodeParams& encParams );\n\n\t\tvoid decode( const int* tokens, const int n_tokens, const sDecodeParams& decParams, std::vector<float>& probs, int threads );\n\n\t\tstatic WhisperContext& current();\n\n\t\t// Create a RAII object which measures both CPU and GPU time for the complete runFull() method\n\t\tdecltype( auto ) completeProfiler()\n\t\t{\n\t\t\treturn std::make_tuple(\n\t\t\t\tprofiler.cpuBlock( Whisper::eCpuBlock::Run ),\n\t\t\t\tprofiler.block( eProfilerBlock::Run ) );\n\t\t}\n\n\t\t// Create a RAII object which measures CPU and optionally GPU time for the loop which calls decode() method\n\t\tdecltype( auto ) decodeProfiler()\n\t\t{\n#if BUILD_HYBRID_VERSION\n\t\t\tif( hybridContext )\n\t\t\t\treturn std::make_tuple(\n\t\t\t\t\tprofiler.cpuBlock( Whisper::eCpuBlock::Decode ),\n\t\t\t\t\tstd::optional<GpuProfiler::BlockRaii>{} );\n\t\t\telse\n\t\t\t\treturn std::make_tuple(\n\t\t\t\t\tprofiler.cpuBlock( Whisper::eCpuBlock::Decode ),\n\t\t\t\t\tstd::optional<GpuProfiler::BlockRaii>{ std::in_place, profiler.block( eProfilerBlock::Decode ) } );\n#else\n\t\t\treturn std::make_tuple(\n\t\t\t\tprofiler.cpuBlock( Whisper::eCpuBlock::Decode ),\n\t\t\t\tprofiler.block( eProfilerBlock::Decode ) );\n#endif\n\t\t}\n\n\t\t__m128i getMemoryUse() const;\n\n\t\tHRESULT clearState();\n\t};\n}"
  },
  {
    "path": "Whisper/Whisper/WhisperModel.cpp",
    "content": "#include \"stdafx.h\"\n#include \"WhisperModel.h\"\n#include \"loaderUtils.h\"\n#include \"../D3D/createBuffer.h\"\n#include <atlcoll.h>\n#include <atlstr.h>\n#include \"../Utils/GpuProfilerSimple.h\"\n#include \"../Utils/CpuProfiler.h\"\n#include \"../CPU/HybridLoader.h\"\n#include \"../ML/Reshaper.h\"\nusing namespace Whisper;\nusing namespace DirectCompute;\n\nnamespace\n{\n\tstruct ParamsAndMelHeader\n\t{\n\t\tsModelParams mp;\n\t\tuint32_t n_mel = 0, n_fft = 0;\n\t};\n\n\tenum struct ePostProcessing : uint8_t\n\t{\n\t\tNone = 0,\n\t\tMakePanels = 1\n\t};\n\n\tstruct PendingTensor\n\t{\n\t\tDirectCompute::Tensor* dest = nullptr;\n\t\tePostProcessing postProcessing = ePostProcessing::None;\n\n\t\tPendingTensor() = default;\n\t\tPendingTensor( const PendingTensor& ) = default;\n\t\tPendingTensor( DirectCompute::Tensor& tensor, ePostProcessing pp = ePostProcessing::None ) :\n\t\t\tdest( &tensor ), postProcessing( pp ) { }\n\n\t\t// If you wonder why not reshape them after all tensors are loaded, doing that on the fly is faster because CPU and GPU work in parallel\n\t\t// In the current version, CPU reads data for a next tensor, while in the meantime GPU reshapes a previously loaded tensor.\n\t\tHRESULT postProcess( Reshaper& rs, eDataType dt )\n\t\t{\n\t\t\tswitch( postProcessing )\n\t\t\t{\n\t\t\tcase ePostProcessing::None:\n\t\t\t\treturn S_OK;\n\t\t\tcase ePostProcessing::MakePanels:\n\t\t\t\tif( gpuInfo().useReshapedMatMul() )\n\t\t\t\t{\n\t\t\t\t\t// GpuInfo structure says we should use that new method\n\t\t\t\t\treturn rs.makePanels( *dest, dt );\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t// The feature ain't enabled on the current user's GPU\n\t\t\t\t\treturn S_OK;\n\t\t\t\t}\n\t\t\tdefault:\n\t\t\t\treturn E_UNEXPECTED;\n\t\t\t}\n\t\t}\n\t};\n\n\tvoid populateEncodeTensorsMap( CAtlMap<CStringA, PendingTensor>& map, int layersEnc, DirectCompute::ModelBuffers& tensors )\n\t{\n\t\ttensors.enc.layers.resize( layersEnc );\n\n\t\tCStringA tempString;\n\t\t// Encoder tensors\n\t\tauto& enc = tensors.enc;\n\n\t\tmap[ \"encoder.positional_embedding\" ] = enc.positionalEmbedding;\n\t\tmap[ \"encoder.conv1.weight\" ] = enc.conv1.w;\n\t\tmap[ \"encoder.conv1.bias\" ] = enc.conv1.b;\n\n\t\tmap[ \"encoder.conv2.weight\" ] = enc.conv2.w;\n\t\tmap[ \"encoder.conv2.bias\" ] = enc.conv2.b;\n\n\t\tmap[ \"encoder.ln_post.weight\" ] = enc.lnPost.w;\n\t\tmap[ \"encoder.ln_post.bias\" ] = enc.lnPost.b;\n\n\t\tauto add = [ & ]( const char* name, int i, DirectCompute::Tensor& t, ePostProcessing pp = ePostProcessing::None )\n\t\t{\n\t\t\ttempString.Format( \"encoder.blocks.%i.%s\", i, name );\n\t\t\tmap[ tempString ] = PendingTensor{ t, pp };\n\t\t};\n\t\tauto add2 = [ & ]( const char* name, int i, DirectCompute::TensorPair& t, ePostProcessing ppWeight = ePostProcessing::None, ePostProcessing ppBias = ePostProcessing::None )\n\t\t{\n\t\t\ttempString.Format( \"encoder.blocks.%i.%s.weight\", i, name );\n\t\t\tmap[ tempString ] = PendingTensor{ t.w, ppWeight };\n\t\t\ttempString.Format( \"encoder.blocks.%i.%s.bias\", i, name );\n\t\t\tmap[ tempString ] = PendingTensor{ t.b, ppBias };\n\t\t};\n\n\t\tfor( int i = 0; i < layersEnc; i++ )\n\t\t{\n\t\t\tauto& gpu = enc.layers[ i ];\n\t\t\tadd2( \"mlp_ln\", i, gpu.mlpLn );\n\t\t\tadd2( \"mlp.0\", i, gpu.mlp0, ePostProcessing::MakePanels );\n\t\t\tadd2( \"mlp.2\", i, gpu.mlp1, ePostProcessing::MakePanels );\n\t\t\tadd2( \"attn_ln\", i, gpu.attnLn0 );\n\t\t\tadd2( \"attn.query\", i, gpu.attnQuery, ePostProcessing::MakePanels );\n\t\t\tadd( \"attn.key.weight\", i, gpu.attnKey, ePostProcessing::MakePanels );\n\t\t\tadd2( \"attn.value\", i, gpu.attnValue, ePostProcessing::MakePanels );\n\t\t\tadd2( \"attn.out\", i, gpu.attnLn1, ePostProcessing::MakePanels );\n\t\t}\n\t}\n\n\tvoid populateDecodeTensorsMap( CAtlMap<CStringA, PendingTensor>& map, int layersDec, DirectCompute::ModelBuffers& tensors, bool hybrid )\n\t{\n\t\ttensors.dec.layers.resize( layersDec );\n\t\tCStringA tempString;\n\t\t// Decoder tensors\n\n\t\tauto& dec = tensors.dec;\n\t\tif( !hybrid )\n\t\t{\n\t\t\tmap[ \"decoder.positional_embedding\" ] = dec.positionalEmbedding;\n\t\t\tmap[ \"decoder.token_embedding.weight\" ] = dec.tokenEmbedding;\n\t\t\tmap[ \"decoder.ln.weight\" ] = dec.ln.w;\n\t\t\tmap[ \"decoder.ln.bias\" ] = dec.ln.b;\n\t\t}\n\n\t\tauto add = [ & ]( const char* name, int i, DirectCompute::Tensor& t, ePostProcessing pp = ePostProcessing::None )\n\t\t{\n\t\t\ttempString.Format( \"decoder.blocks.%i.%s\", i, name );\n\t\t\tmap[ tempString ] = PendingTensor{ t, pp };\n\t\t};\n\t\tauto add2 = [ & ]( const char* name, int i, DirectCompute::TensorPair& t, ePostProcessing ppWeight = ePostProcessing::None, ePostProcessing ppBias = ePostProcessing::None )\n\t\t{\n\t\t\ttempString.Format( \"decoder.blocks.%i.%s.weight\", i, name );\n\t\t\tmap[ tempString ] = PendingTensor{ t.w, ppWeight };\n\t\t\ttempString.Format( \"decoder.blocks.%i.%s.bias\", i, name );\n\t\t\tmap[ tempString ] = PendingTensor{ t.b, ppBias };\n\t\t};\n\n\t\tfor( int i = 0; i < layersDec; i++ )\n\t\t{\n\t\t\tauto& gpu = dec.layers[ i ];\n\t\t\tadd( \"cross_attn.key.weight\", i, gpu.crossAttnKey, ePostProcessing::MakePanels );\n\t\t\tadd2( \"cross_attn.value\", i, gpu.crossAttnValue, ePostProcessing::MakePanels );\n\t\t\tif( hybrid )\n\t\t\t\tcontinue;\n\n\t\t\tadd2( \"mlp_ln\", i, gpu.mlpLn );\n\t\t\tadd2( \"mlp.0\", i, gpu.mlp0, ePostProcessing::MakePanels );\n\t\t\tadd2( \"mlp.2\", i, gpu.mlp1, ePostProcessing::MakePanels );\n\t\t\tadd2( \"attn_ln\", i, gpu.attnLn0 );\n\t\t\tadd2( \"attn.query\", i, gpu.attnQuery );\n\t\t\tadd( \"attn.key.weight\", i, gpu.attnKey );\n\t\t\tadd2( \"attn.value\", i, gpu.attnValue );\n\t\t\tadd2( \"attn.out\", i, gpu.attnLn1 );\n\t\t\tadd2( \"cross_attn_ln\", i, gpu.crossAttnLn0 );\n\t\t\tadd2( \"cross_attn.query\", i, gpu.crossAttnQuery );\n\t\t\tadd2( \"cross_attn.out\", i, gpu.crossAttnLn1 );\n\t\t}\n\t}\n\n\tvoid populateTensorsMap( CAtlMap<CStringA, PendingTensor>& map, int layersEnc, int layersDec, DirectCompute::ModelBuffers& tensors, bool hybrid )\n\t{\n\t\tpopulateEncodeTensorsMap( map, layersEnc, tensors );\n\t\tpopulateDecodeTensorsMap( map, layersDec, tensors, hybrid );\n\t}\n\n\tstruct sTensorHeader\n\t{\n\t\tint n_dims, length, ftype;\n\t};\n\n\t// compare signed int32 lanes for a <= b\n\tinline __m128i cmple( __m128i a, __m128i b )\n\t{\n\t\t__m128i i = _mm_min_epi32( a, b );\n\t\treturn _mm_cmpeq_epi32( a, i );\n\t}\n\n\tinline bool allPositive( const std::array<int, 4>& ne )\n\t{\n\t\tconst __m128i v = _mm_loadu_si128( ( const __m128i* )ne.data() );\n\t\tconst __m128i le = cmple( v, _mm_setzero_si128() );\n\t\treturn (bool)_mm_testz_si128( le, le );\n\t}\n\n\tinline const char* cstr( const CStringA& s ) { return s; }\n}\n\nclass WhisperModel::CallbacksImpl : public CpuCompute::iLoaderProgressSink\n{\n\tsLoadModelCallbacks lmcb;\n\tint64_t fileSize;\n\n\tHRESULT gotBytes( int64_t cb ) override final\n\t{\n\t\tif( nullptr != lmcb.cancel )\n\t\t{\n\t\t\tHRESULT hr = lmcb.cancel( lmcb.pv );\n\t\t\tCHECK( hr );\n\t\t\tif( S_OK != hr )\n\t\t\t\treturn HRESULT_FROM_WIN32( ERROR_CANCELLED );\n\t\t}\n\n\t\tif( nullptr != lmcb.progress )\n\t\t{\n\t\t\tpostponedBytes -= cb;\n\t\t\tassert( postponedBytes >= 0 );\n\t\t\tint64_t pos = fileSize - postponedBytes;\n\t\t\tconst double progressVal = (double)pos / (double)fileSize;\n\t\t\tHRESULT hr = lmcb.progress( progressVal, lmcb.pv );\n\t\t\tCHECK( hr );\n\t\t}\n\t\treturn S_OK;\n\t}\npublic:\n\tint64_t postponedBytes;\n\n\tCallbacksImpl()\n\t{\n\t\tlmcb.progress = nullptr;\n\t\tlmcb.cancel = nullptr;\n\t\tlmcb.pv = nullptr;\n\t\tfileSize = 0;\n\t\tpostponedBytes = 0;\n\t}\n\n\tHRESULT initialize( ComLight::iReadStream* stm, const sLoadModelCallbacks* rsi )\n\t{\n\t\tif( nullptr == rsi )\n\t\t\treturn S_OK;\n\t\tlmcb = *rsi;\n\t\tif( nullptr != lmcb.progress )\n\t\t\tCHECK( stm->getLength( fileSize ) );\n\t\treturn S_OK;\n\t}\n\n\tHRESULT call( ComLight::iReadStream* stm )\n\t{\n\t\tif( nullptr != lmcb.cancel )\n\t\t{\n\t\t\tHRESULT hr = lmcb.cancel( lmcb.pv );\n\t\t\tCHECK( hr );\n\t\t\tif( S_OK != hr )\n\t\t\t\treturn HRESULT_FROM_WIN32( ERROR_CANCELLED );\n\t\t}\n\n\t\tif( nullptr != lmcb.progress )\n\t\t{\n\t\t\tint64_t pos;\n\t\t\tCHECK( stm->getPosition( pos ) );\n\t\t\tpos -= postponedBytes;\n\t\t\tconst double progressVal = (double)pos / (double)fileSize;\n\t\t\tHRESULT hr = lmcb.progress( progressVal, lmcb.pv );\n\t\t\tCHECK( hr );\n\t\t}\n\t\treturn S_OK;\n\t}\n};\n\nHRESULT WhisperModel::loadGpu( ComLight::iReadStream* stm, CallbacksImpl& callbacks )\n{\n\tCAtlMap<CStringA, PendingTensor> map;\n\tpopulateTensorsMap( map, parameters.n_audio_layer, parameters.n_text_layer, tensors, false );\n\n\tDirectCompute::Reshaper reshape;\n\n\tstd::vector<uint8_t> bytesVector;\n\tsize_t countLoaded = 0;\n\tCStringA name;\n\tint64_t cb = 0;\n\twhile( true )\n\t{\n\t\tCHECK( callbacks.call( stm ) );\n\n\t\tsTensorHeader header;\n\t\tHRESULT hr = readStruct( stm, header );\n\t\tif( hr == E_EOF )\n\t\t\tbreak;\n\t\tif( FAILED( hr ) )\n\t\t\treturn hr;\n\t\tif( header.n_dims < 1 || header.n_dims>3 )\n\t\t\treturn E_INVALIDARG;\n\n\t\tstd::array<int, 4> ne = { 1, 1, 1, 1 };\n\t\tCHECK( readBytes( stm, ne.data(), header.n_dims * 4 ) );\n\t\tif( !allPositive( ne ) )\n\t\t\treturn E_INVALIDARG;\n\n\t\tchar* nameBuffer = name.GetBufferSetLength( header.length );\n\t\thr = readBytes( stm, nameBuffer, header.length );\n\t\tname.ReleaseBuffer();\n\t\tif( FAILED( hr ) )\n\t\t\treturn hr;\n\n\t\tauto p = map.Lookup( name );\n\t\tif( nullptr == p )\n\t\t{\n\t\t\tlogError( u8\"%s: unknown tensor '%s' in model file\", __func__, cstr( name ) );\n\t\t\treturn E_INVALIDARG;\n\t\t}\n\n\t\tDirectCompute::eDataType dt;\n\t\tsize_t cbElement;\n\t\tif( header.ftype == 0 )\n\t\t{\n\t\t\tdt = DirectCompute::eDataType::FP32;\n\t\t\tcbElement = 4;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tdt = DirectCompute::eDataType::FP16;\n\t\t\tcbElement = 2;\n\t\t}\n\n\t\tconst size_t totalElts = (size_t)(uint32_t)ne[ 0 ] * (uint32_t)ne[ 1 ] * (uint32_t)ne[ 2 ];\n\t\tif( totalElts * cbElement > UINT_MAX )\n\t\t\treturn DISP_E_OVERFLOW;\n\n\t\ttry\n\t\t{\n\t\t\tbytesVector.resize( cbElement * totalElts );\n\t\t}\n\t\tcatch( const std::bad_alloc& )\n\t\t{\n\t\t\treturn E_OUTOFMEMORY;\n\t\t}\n\t\tCHECK( readBytes( stm, bytesVector.data(), bytesVector.size() ) );\n\t\tcb += bytesVector.size();\n\t\tCHECK( p->m_value.dest->createImmutable( dt, ne, bytesVector.data() ) );\n\t\tCHECK( p->m_value.postProcess( reshape, dt ) );\n\t\tcountLoaded++;\n\t}\n\n\tif( countLoaded != map.GetCount() )\n\t{\n\t\tlogError( u8\"Not all tensors loaded from model file - expected %zu, got %zu\", map.GetCount(), countLoaded );\n\t\treturn E_INVALIDARG;\n\t}\n\n\tconstexpr double mulMb = 1.0 / ( 1 << 20 );\n\tlogDebug( u8\"Loaded %zu GPU tensors, %g MB VRAM\", countLoaded, mulMb * cb );\n\treturn S_OK;\n}\n\n#if BUILD_HYBRID_VERSION\nHRESULT WhisperModel::loadHybrid( ComLight::iReadStream* stm, CallbacksImpl& callbacks )\n{\n\tCAtlMap<CStringA, PendingTensor> map;\n\tpopulateTensorsMap( map, parameters.n_audio_layer, parameters.n_text_layer, tensors, true );\n\tDirectCompute::Reshaper reshape;\n\tCpuCompute::HybridLoader loader( shared->hybridTensors, parameters.n_text_layer );\n\n\tstd::vector<uint8_t> bytesVector;\n\tsize_t countLoaded = 0;\n\tCStringA name;\n\tint64_t cb = 0;\n\twhile( true )\n\t{\n\t\tCHECK( callbacks.call( stm ) );\n\n\t\tsTensorHeader header;\n\t\tHRESULT hr = readStruct( stm, header );\n\t\tif( hr == E_EOF )\n\t\t\tbreak;\n\t\tif( FAILED( hr ) )\n\t\t\treturn hr;\n\t\tif( header.n_dims < 1 || header.n_dims > 3 )\n\t\t\treturn E_INVALIDARG;\n\n\t\tstd::array<int, 4> ne = { 1, 1, 1, 1 };\n\t\tCHECK( readBytes( stm, ne.data(), header.n_dims * 4 ) );\n\t\tif( !allPositive( ne ) )\n\t\t\treturn E_INVALIDARG;\n\n\t\tchar* nameBuffer = name.GetBufferSetLength( header.length );\n\t\thr = readBytes( stm, nameBuffer, header.length );\n\t\tname.ReleaseBuffer();\n\t\tif( FAILED( hr ) )\n\t\t\treturn hr;\n\n\t\tauto p = map.Lookup( name );\n\t\tif( nullptr == p )\n\t\t{\n\t\t\tHRESULT hr = loader.setupTensor( name, header.n_dims, header.ftype, ne, stm, callbacks.postponedBytes );\n\t\t\tif( hr == S_OK )\n\t\t\t\tcontinue;\n\t\t\tlogError( u8\"%s: unknown tensor '%s' in model file\", __func__, cstr( name ) );\n\t\t\treturn E_INVALIDARG;\n\t\t}\n\n\t\tDirectCompute::eDataType dt;\n\t\tsize_t cbElement;\n\t\tif( header.ftype == 0 )\n\t\t{\n\t\t\tdt = DirectCompute::eDataType::FP32;\n\t\t\tcbElement = 4;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tdt = DirectCompute::eDataType::FP16;\n\t\t\tcbElement = 2;\n\t\t}\n\n\t\tconst size_t totalElts = (size_t)(uint32_t)ne[ 0 ] * (uint32_t)ne[ 1 ] * (uint32_t)ne[ 2 ];\n\t\tif( totalElts * cbElement > UINT_MAX )\n\t\t\treturn DISP_E_OVERFLOW;\n\n\t\ttry\n\t\t{\n\t\t\tbytesVector.resize( cbElement * totalElts );\n\t\t}\n\t\tcatch( const std::bad_alloc& )\n\t\t{\n\t\t\treturn E_OUTOFMEMORY;\n\t\t}\n\t\tCHECK( readBytes( stm, bytesVector.data(), bytesVector.size() ) );\n\t\tCHECK( p->m_value.dest->createImmutable( dt, ne, bytesVector.data() ) );\n\t\tCHECK( p->m_value.postProcess( reshape, dt ) );\n\t\tcountLoaded++;\n\t\tcb += bytesVector.size();\n\t}\n\n\tif( countLoaded != map.GetCount() )\n\t{\n\t\tlogError( u8\"Not all tensors loaded from model file - expected %zu, got %zu\", map.GetCount(), countLoaded );\n\t\treturn E_INVALIDARG;\n\t}\n\n\tconstexpr double mulMb = 1.0 / ( 1 << 20 );\n\tlogDebug( u8\"Loaded %zu GPU tensors, %g MB VRAM\", countLoaded, mulMb * cb );\n\n\tCHECK( loader.completeLoad( stm, callbacks ) );\n\treturn S_OK;\n}\n#endif\n\nHRESULT WhisperModel::load( ComLight::iReadStream* stm, bool hybrid, const sLoadModelCallbacks* callbacks )\n{\n\tCpuProfiler cpuPerf;\n\tCallbacksImpl cb;\n\tCHECK( cb.initialize( stm, callbacks ) );\n\t// verify magic\n\t{\n\t\tuint32_t magic;\n\t\tCHECK( readStruct( stm, magic ) );\n\t\tif( magic != 0x67676d6c )\n\t\t{\n\t\t\tlogError( u8\"Invalid model file, bad magic\" );\n\t\t\treturn E_INVALIDARG;\n\t\t}\n\t}\n\n\tshared = std::make_shared<ModelShared>();\n\n\t// hparams and MEL filters\n\t{\n\t\tParamsAndMelHeader pmh;\n\t\tCHECK( readStruct( stm, pmh ) );\n\t\tparameters = pmh.mp;\n\t\tassert( parameters.n_text_state == parameters.n_audio_state );\n\n\t\tshared->filters.n_mel = pmh.n_mel;\n\t\tshared->filters.n_fft = pmh.n_fft;\n\t\tconst size_t len = (size_t)pmh.n_mel * pmh.n_fft;\n\t\tshared->filters.data.resize( len );\n\t\tCHECK( readBytes( stm, shared->filters.data.data(), len * 4 ) );\n\n\t\tconst int64_t cb = len * 4;\n\t\tconstexpr double mulKb = 1.0 / ( 1 << 10 );\n\t\tlogDebug( u8\"Loaded MEL filters, %.1f kb RAM\", mulKb * cb );\n\t}\n\tCHECK( cb.call( stm ) );\n\n\t// Vocabulary\n\tCHECK( shared->vocab.load( stm, parameters.n_vocab ) );\n\tCHECK( cb.call( stm ) );\n\n\tDirectCompute::GpuProfilerSimple gpuProfiler;\n\tCHECK( gpuProfiler.create() );\n\n\tif( hybrid )\n\t{\n#if BUILD_HYBRID_VERSION\n\t\tCHECK( loadHybrid( stm, cb ) )\n#else\n\t\treturn E_NOTIMPL;\n#endif\n\t}\n\telse\n\t\tCHECK( loadGpu( stm, cb ) );\n\n\tCHECK( gpuProfiler.time( loadTimeGpu ) );\n\tloadTimeCpu = cpuPerf.elapsed();\n\treturn S_OK;\n}\n\nHRESULT Whisper::WhisperModel::createClone( const WhisperModel& rsi )\n{\n\tparameters = rsi.parameters;\n\tshared = rsi.shared;\n\tCHECK( tensors.createClone( rsi.tensors ) );\n\treturn S_OK;\n}\n\n__m128i Whisper::WhisperModel::getMemoryUse() const\n{\n\tsize_t cb = shared->vocab.getMemoryUse();\n\tcb += vectorMemoryUse( shared->filters.data );\n\t__m128i v = _mm_cvtsi64_si128( (int64_t)cb );\n\tv = _mm_add_epi64( v, tensors.getMemoryUse() );\n\treturn v;\n}"
  },
  {
    "path": "Whisper/Whisper/WhisperModel.h",
    "content": "﻿#pragma once\n#include <memory>\n#include \"Vocabulary.h\"\n#include \"ModelBuffers.h\"\n#include \"../../ComLightLib/streams.h\"\n#include \"../CPU/DecoderTensors.h\"\n#include \"../API/sLoadModelCallbacks.h\"\n#include \"sModelParams.h\"\n\nnamespace Whisper\n{\n\tstruct Filters\n\t{\n\t\tuint32_t n_mel;\n\t\tuint32_t n_fft;\n\t\tstd::vector<float> data;\n\t};\n\n\tstruct ModelShared\n\t{\n\t\tVocabulary vocab;\n\t\tFilters filters;\n#if BUILD_HYBRID_VERSION\n\t\tCpuCompute::DecoderTensors hybridTensors;\n#endif\n\t};\n\n\t// The complete model, as loaded from a GGML binary file.\n\t// The entire model is immutable, and can be safely used from multiple threads in parallel.\n\t// The tensors are uploaded to VRAM and don’t stay in system memory, everything else is in the system RAM.\n\tstruct WhisperModel\n\t{\n\t\tsModelParams parameters;\n\t\tstd::shared_ptr<ModelShared> shared;\n\t\tDirectCompute::ModelBuffers tensors;\n\n\t\tHRESULT load( ComLight::iReadStream* stm, bool hybrid, const sLoadModelCallbacks* callbacks );\n\t\tHRESULT createClone( const WhisperModel& rsi );\n\n\t\t// A vector of 2 uint64_t values, both numbers are 100 nanosecond ticks:\n\t\t// 0. The time it took to load the model, measured on CPU\n\t\t// 1. The time it took to upload all these tensors to VRAM, measured on GPU\n\t\t__m128i getLoadTimes() const\n\t\t{\n\t\t\tstatic_assert( offsetof( WhisperModel, loadTimeCpu ) + 8 == offsetof( WhisperModel, loadTimeGpu ) );\n\t\t\treturn _mm_loadu_si128( ( const __m128i* )( &loadTimeCpu ) );\n\t\t}\n\n\t\t__m128i getMemoryUse() const;\n\n\tprivate:\n\t\tuint64_t loadTimeCpu = 0;\n\t\tuint64_t loadTimeGpu = 0;\n\n\t\tclass CallbacksImpl;\n\n\t\tHRESULT loadGpu( ComLight::iReadStream* stm, CallbacksImpl& callbacks );\n\t\tHRESULT loadHybrid( ComLight::iReadStream* stm, CallbacksImpl& callbacks );\n\t};\n}"
  },
  {
    "path": "Whisper/Whisper/audioConstants.h",
    "content": "#pragma once\n#include <stdint.h>\n\nnamespace Whisper\n{\n\t// WHISPER_SAMPLE_RATE, 16 kHz\n\tconstexpr uint32_t SAMPLE_RATE = 16000;\n\t// WHISPER_N_FFT, 25 milliseconds\n\tconstexpr uint32_t FFT_SIZE = 400;\n\t// WHISPER_HOP_LENGTH, 10 milliseconds\n\tconstexpr uint32_t FFT_STEP = 160;\n\t// WHISPER_N_MEL\n\tconstexpr uint32_t N_MEL = 80;\n}"
  },
  {
    "path": "Whisper/Whisper/iSpectrogram.h",
    "content": "#pragma once\n#include \"audioConstants.h\"\n\nnamespace Whisper\n{\n\tstruct alignas( 8 ) StereoSample\n\t{\n\t\tfloat left, right;\n\t};\n\n\t__interface iSpectrogram\n\t{\n\t\t// Make a buffer with length * N_MEL floats, starting at the specified offset\n\t\t// An implementation of this interface may visualize the spectrogram, making pieces on demand\n\t\tHRESULT makeBuffer( size_t offset, size_t length, const float** buffer, size_t& stride );\n\n\t\t// Apparently, the length unit is 160 input samples = 10 milliseconds of audio\n\t\tsize_t getLength() const;\n\n\t\t// If the source data is stereo, copy the specified slice of the data into the provided vector\n\t\tHRESULT copyStereoPcm( size_t offset, size_t length, std::vector<StereoSample>& buffer ) const;\n\t};\n\n\t// RAII class to deal with iSpectrogram's makeBuffer method.\n\t// Throws exceptions when things fail.\n\tclass MelBufferRaii\n\t{\n\t\tconst float* pointer;\n\t\tsize_t stride;\n\tpublic:\n\n\t\tHRESULT make( iSpectrogram& mel, size_t off, size_t len )\n\t\t{\n\t\t\treturn mel.makeBuffer( off, len, &pointer, stride );\n\t\t}\n\n\t\tconst float* operator[]( size_t idx ) const\n\t\t{\n\t\t\tassert( idx < N_MEL );\n\t\t\treturn pointer + idx * stride;\n\t\t}\n\n\t\tconst BYTE* bytePtr() const { return (const BYTE*)pointer; }\n\t\tLONG strideBytes() const { return (LONG)stride * 4; }\n\t};\n}"
  },
  {
    "path": "Whisper/Whisper/languageCodez.inl",
    "content": "// This file is generated by a tool, from the `languageCodez.tsv` file in this repository\nLang{ 0x6661, 68, \"afrikaans\" },\nLang{ 0x7173, 58, \"albanian\" },\nLang{ 0x6D61, 75, \"amharic\" },\nLang{ 0x7261, 13, \"arabic\" },\nLang{ 0x7968, 53, \"armenian\" },\nLang{ 0x7361, 91, \"assamese\" },\nLang{ 0x7A61, 45, \"azerbaijani\" },\nLang{ 0x6162, 96, \"bashkir\" },\nLang{ 0x7565, 51, \"basque\" },\nLang{ 0x6562, 71, \"belarusian\" },\nLang{ 0x6E62, 43, \"bengali\" },\nLang{ 0x7362, 56, \"bosnian\" },\nLang{ 0x7262, 50, \"breton\" },\nLang{ 0x6762, 33, \"bulgarian\" },\nLang{ 0x6163, 11, \"catalan\" },\nLang{ 0x687A, 1, \"chinese\" },\nLang{ 0x7268, 32, \"croatian\" },\nLang{ 0x7363, 24, \"czech\" },\nLang{ 0x6164, 26, \"danish\" },\nLang{ 0x6C6E, 12, \"dutch\" },\nLang{ 0x6E65, 0, \"english\" },\nLang{ 0x7465, 48, \"estonian\" },\nLang{ 0x6F66, 79, \"faroese\" },\nLang{ 0x6966, 18, \"finnish\" },\nLang{ 0x7266, 6, \"french\" },\nLang{ 0x6C67, 60, \"galician\" },\nLang{ 0x616B, 70, \"georgian\" },\nLang{ 0x6564, 2, \"german\" },\nLang{ 0x6C65, 22, \"greek\" },\nLang{ 0x7567, 74, \"gujarati\" },\nLang{ 0x7468, 80, \"haitian creole\" },\nLang{ 0x6168, 95, \"hausa\" },\nLang{ 0x776168, 93, \"hawaiian\" },\nLang{ 0x7769, 20, \"hebrew\" },\nLang{ 0x6968, 17, \"hindi\" },\nLang{ 0x7568, 27, \"hungarian\" },\nLang{ 0x7369, 52, \"icelandic\" },\nLang{ 0x6469, 16, \"indonesian\" },\nLang{ 0x7469, 15, \"italian\" },\nLang{ 0x616A, 7, \"japanese\" },\nLang{ 0x776A, 97, \"javanese\" },\nLang{ 0x6E6B, 47, \"kannada\" },\nLang{ 0x6B6B, 57, \"kazakh\" },\nLang{ 0x6D6B, 64, \"khmer\" },\nLang{ 0x6F6B, 5, \"korean\" },\nLang{ 0x6F6C, 77, \"lao\" },\nLang{ 0x616C, 35, \"latin\" },\nLang{ 0x766C, 42, \"latvian\" },\nLang{ 0x6E6C, 94, \"lingala\" },\nLang{ 0x746C, 34, \"lithuanian\" },\nLang{ 0x626C, 86, \"luxembourgish\" },\nLang{ 0x6B6D, 49, \"macedonian\" },\nLang{ 0x676D, 90, \"malagasy\" },\nLang{ 0x736D, 23, \"malay\" },\nLang{ 0x6C6D, 37, \"malayalam\" },\nLang{ 0x746D, 84, \"maltese\" },\nLang{ 0x696D, 36, \"maori\" },\nLang{ 0x726D, 61, \"marathi\" },\nLang{ 0x6E6D, 55, \"mongolian\" },\nLang{ 0x796D, 87, \"myanmar\" },\nLang{ 0x656E, 54, \"nepali\" },\nLang{ 0x6F6E, 29, \"norwegian\" },\nLang{ 0x6E6E, 83, \"nynorsk\" },\nLang{ 0x636F, 69, \"occitan\" },\nLang{ 0x7370, 81, \"pashto\" },\nLang{ 0x6166, 41, \"persian\" },\nLang{ 0x6C70, 10, \"polish\" },\nLang{ 0x7470, 8, \"portuguese\" },\nLang{ 0x6170, 62, \"punjabi\" },\nLang{ 0x6F72, 25, \"romanian\" },\nLang{ 0x7572, 4, \"russian\" },\nLang{ 0x6173, 85, \"sanskrit\" },\nLang{ 0x7273, 44, \"serbian\" },\nLang{ 0x6E73, 65, \"shona\" },\nLang{ 0x6473, 73, \"sindhi\" },\nLang{ 0x6973, 63, \"sinhala\" },\nLang{ 0x6B73, 39, \"slovak\" },\nLang{ 0x6C73, 46, \"slovenian\" },\nLang{ 0x6F73, 67, \"somali\" },\nLang{ 0x7365, 3, \"spanish\" },\nLang{ 0x7573, 98, \"sundanese\" },\nLang{ 0x7773, 59, \"swahili\" },\nLang{ 0x7673, 14, \"swedish\" },\nLang{ 0x6C74, 89, \"tagalog\" },\nLang{ 0x6774, 72, \"tajik\" },\nLang{ 0x6174, 28, \"tamil\" },\nLang{ 0x7474, 92, \"tatar\" },\nLang{ 0x6574, 40, \"telugu\" },\nLang{ 0x6874, 30, \"thai\" },\nLang{ 0x6F62, 88, \"tibetan\" },\nLang{ 0x7274, 9, \"turkish\" },\nLang{ 0x6B74, 82, \"turkmen\" },\nLang{ 0x6B75, 21, \"ukrainian\" },\nLang{ 0x7275, 31, \"urdu\" },\nLang{ 0x7A75, 78, \"uzbek\" },\nLang{ 0x6976, 19, \"vietnamese\" },\nLang{ 0x7963, 38, \"welsh\" },\nLang{ 0x6979, 76, \"yiddish\" },\nLang{ 0x6F79, 66, \"yoruba\" },\n"
  },
  {
    "path": "Whisper/Whisper/languageCodez.tsv",
    "content": "﻿en\t0\tenglish\nzh\t1\tchinese\nde\t2\tgerman\nes\t3\tspanish\nru\t4\trussian\nko\t5\tkorean\nfr\t6\tfrench\nja\t7\tjapanese\npt\t8\tportuguese\ntr\t9\tturkish\npl\t10\tpolish\nca\t11\tcatalan\nnl\t12\tdutch\nar\t13\tarabic\nsv\t14\tswedish\nit\t15\titalian\nid\t16\tindonesian\nhi\t17\thindi\nfi\t18\tfinnish\nvi\t19\tvietnamese\niw\t20\thebrew\nuk\t21\tukrainian\nel\t22\tgreek\nms\t23\tmalay\ncs\t24\tczech\nro\t25\tromanian\nda\t26\tdanish\nhu\t27\thungarian\nta\t28\ttamil\nno\t29\tnorwegian\nth\t30\tthai\nur\t31\turdu\nhr\t32\tcroatian\nbg\t33\tbulgarian\nlt\t34\tlithuanian\nla\t35\tlatin\nmi\t36\tmaori\nml\t37\tmalayalam\ncy\t38\twelsh\nsk\t39\tslovak\nte\t40\ttelugu\nfa\t41\tpersian\nlv\t42\tlatvian\nbn\t43\tbengali\nsr\t44\tserbian\naz\t45\tazerbaijani\nsl\t46\tslovenian\nkn\t47\tkannada\net\t48\testonian\nmk\t49\tmacedonian\nbr\t50\tbreton\neu\t51\tbasque\nis\t52\ticelandic\nhy\t53\tarmenian\nne\t54\tnepali\nmn\t55\tmongolian\nbs\t56\tbosnian\nkk\t57\tkazakh\nsq\t58\talbanian\nsw\t59\tswahili\ngl\t60\tgalician\nmr\t61\tmarathi\npa\t62\tpunjabi\nsi\t63\tsinhala\nkm\t64\tkhmer\nsn\t65\tshona\nyo\t66\tyoruba\nso\t67\tsomali\naf\t68\tafrikaans\noc\t69\toccitan\nka\t70\tgeorgian\nbe\t71\tbelarusian\ntg\t72\ttajik\nsd\t73\tsindhi\ngu\t74\tgujarati\nam\t75\tamharic\nyi\t76\tyiddish\nlo\t77\tlao\nuz\t78\tuzbek\nfo\t79\tfaroese\nht\t80\thaitian creole\nps\t81\tpashto\ntk\t82\tturkmen\nnn\t83\tnynorsk\nmt\t84\tmaltese\nsa\t85\tsanskrit\nlb\t86\tluxembourgish\nmy\t87\tmyanmar\nbo\t88\ttibetan\ntl\t89\ttagalog\nmg\t90\tmalagasy\nas\t91\tassamese\ntt\t92\ttatar\nhaw\t93\thawaiian\nln\t94\tlingala\nha\t95\thausa\nba\t96\tbashkir\njw\t97\tjavanese\nsu\t98\tsundanese"
  },
  {
    "path": "Whisper/Whisper/loaderUtils.h",
    "content": "#pragma once\n#include \"../../ComLightLib/streams.h\"\n\nnamespace Whisper\n{\n\tinline HRESULT readBytes( ComLight::iReadStream* stm, void* rdi, size_t cb )\n\t{\n\t\tif( cb > INT_MAX )\n\t\t\treturn DISP_E_OVERFLOW;\n\t\tif( cb == 0 )\n\t\t\treturn S_FALSE;\n\t\tint n;\n\t\tCHECK( stm->read( rdi, (int)cb, n ) );\n\t\tif( n != (int)cb )\n\t\t\treturn E_EOF;\n\t\treturn S_OK;\n\t}\n\n\ttemplate<typename T>\n\tinline HRESULT readStruct( ComLight::iReadStream* stm, T& dest )\n\t{\n\t\treturn readBytes( stm, &dest, sizeof( T ) );\n\t}\n}"
  },
  {
    "path": "Whisper/Whisper/melSpectrogram.cpp",
    "content": "#include \"stdafx.h\"\n#include <cmath>\n#include \"melSpectrogram.h\"\n\nnamespace Whisper\n{\n\tHanningWindow::HanningWindow()\n\t{\n\t\tfor( int i = 0; i < FFT_SIZE; i++ )\n\t\t{\n\t\t\t// TODO [low]: use XMVectorCos instead\n\t\t\thann[ i ] = (float)( 0.5 * ( 1.0 - std::cos( ( 2.0 * M_PI * i ) / ( FFT_SIZE ) ) ) );\n\t\t}\n\t}\n\tconst HanningWindow s_hanning;\n}\n\nnamespace\n{\n\tusing namespace Whisper;\n\n\tuint32_t tempVectorSizeRecursion( uint32_t len )\n\t{\n\t\t// out.resize( in.size() * 2 );\n\t\tconst uint32_t res = len * 2;\n\t\tif( len == 1 )\n\t\t\treturn res;\n\t\tif( len % 2 == 1 )\n\t\t\treturn res;\t// dft\n\n\t\tconst uint32_t even = ( len + 1 ) / 2;\n\t\tconst uint32_t odd = len / 2;\n\t\tconst uint32_t evenFft = tempVectorSizeRecursion( even );\n\t\tconst uint32_t oddFft = tempVectorSizeRecursion( odd );\n\t\treturn res + even + odd + evenFft + oddFft;\n\t}\n\n\t// 6000\n\t// const uint32_t tempBufferSize = FFT_SIZE + tempVectorSizeRecursion( FFT_SIZE );\n\tconstexpr uint32_t tempBufferSize = 6000;\n\n\t// [ a, b, c, d ], [ e, f, g, h ] => [ a+b+c+d, e+f+g+h ]\n\tinline  __m128 hadd2( __m128 low, __m128 high )\n\t{\n\t\t// [ a, e, b, f ]\n\t\t__m128 a = _mm_unpacklo_ps( low, high );\n\t\t// [ c, g, d, h ]\n\t\t__m128 b = _mm_unpackhi_ps( low, high );\n\t\t// [ a+c, e+g, b+d, f+h ]\n\t\t__m128 r = _mm_add_ps( a, b );\n\t\t// [ b+d, f+h, b+d, f+h ]\n\t\t__m128 tmp = _mm_movehl_ps( r, r );\n\t\t// [ a+c+b+d, e+g+f+h ]\n\t\treturn _mm_add_ps( r, tmp );\n\t}\n\n\tinline __m128 load2( const float* rsi )\n\t{\n\t\treturn _mm_castpd_ps( _mm_load_sd( (const double*)rsi ) );\n\t}\n\tinline void store2( float* rdi, __m128 vec )\n\t{\n\t\t_mm_store_sd( (double*)rdi, _mm_castps_pd( vec ) );\n\t}\n\tinline __m128 loadFloat3( const float* rsi )\n\t{\n\t\t__m128 f = load2( rsi );\n\t\tf = _mm_insert_ps( f, _mm_load_ss( rsi + 2 ), 0x20 );\n\t\treturn f;\n\t}\n\tinline __m128 loadPartial( const float* rsi, size_t rem )\n\t{\n\t\tassert( rem > 0 && rem < 4 );\n\t\tswitch( rem )\n\t\t{\n\t\tcase 1:\n\t\t\treturn _mm_load_ss( rsi );\n\t\tcase 2:\n\t\t\treturn load2( rsi );\n\t\tcase 3:\n\t\t\treturn loadFloat3( rsi );\n\t\t}\n\t\treturn _mm_setzero_ps();\n\t}\n\n\t// naive Discrete Fourier Transform\n\t// input is real-valued\n\t// output is complex-valued\n\tinline void dft( const float* rsi, size_t len, float* rdi )\n\t{\n\t\tconst size_t lenAligned = len & ( ~(size_t)3 );\n\t\tconst size_t remainder = len % 4;\n\n\t\tconst double mulScalarBase = ( 2.0 * M_PI ) / (double)(int)len;\n\n\t\tconst __m128 nvInitial = _mm_setr_ps( 0, 1, 2, 3 );\n\t\tconst __m128 nvInc = _mm_set1_ps( 4 );\n\n\t\tfor( size_t k = 0; k < len; k++ )\n\t\t{\n#if 1\n\t\t\tconst __m128 mul = _mm_set1_ps( (float)( mulScalarBase * (int)k ) );\n\n\t\t\t__m128 nv = nvInitial;\n\t\t\t__m128 cosine = _mm_setzero_ps();\n\t\t\t__m128 sine = _mm_setzero_ps();\n\t\t\tfor( size_t n = 0; n < lenAligned; n += 4 )\n\t\t\t{\n\t\t\t\tconst __m128 angles = _mm_mul_ps( nv, mul );\n\t\t\t\tnv = _mm_add_ps( nv, nvInc );\n\t\t\t\t__m128 s, c;\n\t\t\t\t// That library function from Windows SDK is way faster than std::sinf/cosf\n\t\t\t\t// Especially because we use the version which computes 4 angles at once\n\t\t\t\t// Source codes there: https://github.com/microsoft/DirectXMath/blob/dec2022/Inc/DirectXMathVector.inl#L4456-L4512\n\t\t\t\tDirectX::XMVectorSinCos( &s, &c, angles );\n\n\t\t\t\t// Multiply sin/cos by 4 source values\n\t\t\t\tconst __m128 source = _mm_loadu_ps( &rsi[ n ] );\n\t\t\t\tc = _mm_mul_ps( c, source );\n\t\t\t\ts = _mm_mul_ps( s, source );\n\n\t\t\t\t// Accumulate in 2 vectors\n\t\t\t\tcosine = _mm_add_ps( cosine, c );\n\t\t\t\tsine = _mm_sub_ps( sine, s );\n\t\t\t}\n\n\t\t\t// Handle the remainder; debugger shows it's always 1, BTW\n\t\t\tif( 0 != remainder )\n\t\t\t{\n\t\t\t\tconst __m128 angles = _mm_mul_ps( nv, mul );\n\t\t\t\t__m128 s, c;\n\t\t\t\tDirectX::XMVectorSinCos( &s, &c, angles );\n\t\t\t\t// loadPartial sets unused lanes to 0..\n\t\t\t\tconst __m128 source = loadPartial( &rsi[ lenAligned ], remainder );\n\t\t\t\t// x * 0.0 == 0.0 ..\n\t\t\t\tc = _mm_mul_ps( c, source );\n\t\t\t\ts = _mm_mul_ps( s, source );\n\t\t\t\t// .. that's why it's fine to accumulate the complete vectors.\n\t\t\t\t// Adding or subtracting zero doesn't change the accumulator\n\t\t\t\tcosine = _mm_add_ps( cosine, c );\n\t\t\t\tsine = _mm_sub_ps( sine, s );\n\t\t\t}\n\n\t\t\t// Reduce 2*4 accumulators -> 2 scalars in a single vector\n\t\t\tconst __m128 res = hadd2( cosine, sine );\n\t\t\t// Store 2 floats, with 1 instruction\n\t\t\tstore2( &rdi[ k * 2 ], res );\n#else\n\t\t\t// Original scalar version here\n\t\t\tfloat re = 0;\n\t\t\tfloat im = 0;\n\t\t\tfor( int n = 0; n < len; n++ )\n\t\t\t{\n\t\t\t\tfloat angle = (float)( 2 * M_PI * (int)k * n / len );\n\t\t\t\tre += (float)( rsi[ n ] * std::cosf( angle ) );\n\t\t\t\tim -= (float)( rsi[ n ] * std::sinf( angle ) );\n\t\t\t}\n\n\t\t\trdi[ k * 2 + 0 ] = re;\n\t\t\trdi[ k * 2 + 1 ] = im;\n#endif\n\t\t}\n\t}\n\n\tinline void splitEvenOdd( const float* rsi, size_t len, float* rdiEven, float* rdiOdd )\n\t{\n\t\tconst float* const rsiEndAligned = rsi + ( len & ( ~(size_t)7 ) );\n\t\tconst size_t rem = len % 8;\n\n\t\tfor( ; rsi < rsiEndAligned; rsi += 8, rdiEven += 4, rdiOdd += 4 )\n\t\t{\n\t\t\tconst __m128 v1 = _mm_loadu_ps( rsi );\n\t\t\tconst __m128 v2 = _mm_loadu_ps( rsi + 4 );\n\t\t\tconst __m128 e = _mm_shuffle_ps( v1, v2, _MM_SHUFFLE( 2, 0, 2, 0 ) );\n\t\t\tconst __m128 o = _mm_shuffle_ps( v1, v2, _MM_SHUFFLE( 3, 1, 3, 1 ) );\n\t\t\t_mm_storeu_ps( rdiEven, e );\n\t\t\t_mm_storeu_ps( rdiOdd, o );\n\t\t}\n\n#pragma loop( no_vector )\n\t\tfor( size_t i = 0; i < rem; i++, rsi++ )\n\t\t{\n\t\t\tif( i % 2 == 0 )\n\t\t\t{\n\t\t\t\t*rdiEven = *rsi;\n\t\t\t\trdiEven++;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t*rdiOdd = *rsi;\n\t\t\t\trdiOdd++;\n\t\t\t}\n\t\t}\n\t}\n\tinline __m128 set2( float f )\n\t{\n\t\t__m128 v = _mm_set_ss( f );\n\t\treturn _mm_moveldup_ps( v );\n\t}\n\t// [ x, y ] => [ x, y, x, y ]\n\tinline __m128 dup2( __m128 x )\n\t{\n\t\t__m128d v = _mm_castps_pd( x );\n\t\tv = _mm_movedup_pd( v );\n\t\treturn _mm_castpd_ps( v );\n\t}\n\tinline __m128 load2dup( const float* rsi )\n\t{\n\t\treturn _mm_castpd_ps( _mm_loaddup_pd( (const double*)rsi ) );\n\t}\n\tinline void store2high( float* rdi, __m128 vec )\n\t{\n\t\t_mm_storeh_pd( (double*)rdi, _mm_castps_pd( vec ) );\n\t}\n}\n\nusing namespace Whisper;\n\nSpectrogramContext::SpectrogramContext( const Filters& flt ) :\n\tfilters( flt )\n{\n\tassert( tempBufferSize == FFT_SIZE + tempVectorSizeRecursion( FFT_SIZE ) );\n\ttempBuffer = std::make_unique<float[]>( tempBufferSize );\n}\n\n// Cooley-Tukey FFT\n// poor man's implementation - use something better\n// input is real-valued\n// output is complex-valued\nfloat* SpectrogramContext::fftRecursion( float* temp, const float* const rsi, const size_t len )\n{\n\tfloat* const out = temp;\n\ttemp += len * 2;\n\tif( len == 1 )\n\t{\n\t\tout[ 0 ] = rsi[ 0 ];\n\t\tout[ 1 ] = 0;\n\t\treturn temp;\n\t}\n\n\tif( len % 2 == 1 )\n\t{\n\t\tdft( rsi, len, out );\n\t\treturn temp;\n\t}\n\n\tconst size_t lenEven = ( len + 1 ) / 2;\n\tconst size_t lenOdd = len / 2;\n\tfloat* const even = temp;\n\ttemp += lenEven;\n\n\tfloat* const odd = temp;\n\ttemp += lenOdd;\n\tsplitEvenOdd( rsi, len, even, odd );\n\n\tconst float* const evenFft = temp;\n\ttemp = fftRecursion( temp, even, lenEven );\n\n\tconst float* const oddFft = temp;\n\ttemp = fftRecursion( temp, odd, lenOdd );\n\n\tconst size_t N = len;\n\tconst __m128 maskNegateHigh = _mm_setr_ps( 0, 0, -0.0f, -0.0f );\n\tfor( size_t k = 0; k < N / 2; k++ )\n\t{\n\t\tconst float theta = (float)( 2 * M_PI * (double)(int)k / N );\n\n\t\t/*\n\t\tconst float re = std::cosf( theta );\n\t\tconst float im = -std::sinf( theta );\n\n\t\tfloat re_odd = oddFft[ 2 * k + 0 ];\n\t\tfloat im_odd = oddFft[ 2 * k + 1 ];\n\n\t\tout[ 2 * k + 0 ] = evenFft[ 2 * k + 0 ] + re * re_odd - im * im_odd;\n\t\tout[ 2 * k + 1 ] = evenFft[ 2 * k + 1 ] + re * im_odd + im * re_odd;\n\n\t\tout[ 2 * ( k + N / 2 ) + 0 ] = evenFft[ 2 * k + 0 ] - re * re_odd + im * im_odd;\n\t\tout[ 2 * ( k + N / 2 ) + 1 ] = evenFft[ 2 * k + 1 ] - re * im_odd - im * re_odd;\n\t\t*/\n\t\tfloat sine, cosine;\n\t\tDirectX::XMScalarSinCos( &sine, &cosine, theta );\n\t\tconst __m128 re = _mm_set_ss( cosine );\n\t\tconst __m128 im = _mm_set_ss( sine );\n\t\t__m128 reIm = _mm_shuffle_ps( re, im, _MM_SHUFFLE( 0, 0, 0, 0 ) );\n\t\t// [ re, re, im, im ]\n\t\treIm = _mm_xor_ps( reIm, maskNegateHigh );\n\n\t\t// [ re_odd, im_odd ]\n\t\t__m128 odd = load2( oddFft + 2 * k );\n\t\t// [ re_odd, im_odd, im_odd, re_odd ]\n\t\todd = _mm_shuffle_ps( odd, odd, _MM_SHUFFLE( 0, 1, 1, 0 ) );\n\n\t\t// re_odd * re, im_odd * re, im_odd * im, re_odd * im ]\n\t\tconst __m128 products4 = _mm_mul_ps( reIm, odd );\n\n\t\t// re_odd * re, im_odd * re, re_odd * re, im_odd * re\n\t\t__m128 prod1 = dup2( products4 );\n\t\t// im_odd * im, re_odd * im, im_odd * im, re_odd * im\n\t\t__m128 prod2 = _mm_movehl_ps( products4, products4 );\n\n\t\t// re_odd * re, im_odd * re, -re_odd * re, -im_odd * re\n\t\tprod1 = _mm_xor_ps( prod1, maskNegateHigh );\n\t\t// im_odd * im, re_odd * im, -im_odd * im, -re_odd * im\n\t\tprod2 = _mm_xor_ps( prod2, maskNegateHigh );\n\n\t\tconst __m128 even = load2dup( evenFft + 2 * k );\n\t\t__m128 res;\n\t\tres = _mm_add_ps( even, prod1 );\n\t\tres = _mm_addsub_ps( res, prod2 );\n\t\tstore2( out + 2 * k, res );\n\t\tstore2high( out + 2 * ( k + N / 2 ), res );\n\t}\n\n\treturn temp;\n}\n\nvoid SpectrogramContext::fft( std::array<float, N_MEL>& rdi, const float* pcm, size_t length )\n{\n\tassert( length > 0 );\n\tlength = std::min( length, (size_t)FFT_SIZE );\n\n\tfloat* const temp = tempBuffer.get();\n\t// Apply Hanning window\n\tfor( size_t i = 0; i < length; i++ )\n\t\ttemp[ i ] = pcm[ i ] * s_hanning[ i ];\n\tif( length < FFT_SIZE )\n\t\tmemset( temp + length, 0, ( FFT_SIZE - length ) * 4 );\n\n\tfloat* const fftOut = temp + FFT_SIZE;\n\tfloat* bufferEnd = fftRecursion( fftOut, temp, FFT_SIZE );\n\tassert( bufferEnd == tempBuffer.get() + tempBufferSize );\n\n\t// for( size_t j = 0; j < FFT_SIZE; j++ )\n\t//\tfft_out[ j ] = ( fft_out[ 2 * j + 0 ] * fft_out[ 2 * j + 0 ] + fft_out[ 2 * j + 1 ] * fft_out[ 2 * j + 1 ] );\n\tfor( size_t j = 0; j < 4; j++ )\n\t{\n\t\t__m128 tmp = load2( fftOut + 2 * j );\n\t\ttmp = _mm_mul_ps( tmp, tmp );\n\t\ttmp = _mm_add_ss( tmp, _mm_movehdup_ps( tmp ) );\n\t\t_mm_store_ss( fftOut + j, tmp );\n\t}\n\tfor( size_t j = 4; j < FFT_SIZE; j += 4 )\n\t{\n\t\t__m128 low = _mm_loadu_ps( fftOut + 2 * j );\n\t\t__m128 high = _mm_loadu_ps( fftOut + 2 * j + 4 );\n\t\tlow = _mm_mul_ps( low, low );\n\t\thigh = _mm_mul_ps( high, high );\n\t\t__m128 res = _mm_hadd_ps( low, high );\n\t\t_mm_storeu_ps( fftOut + j, res );\n\t}\n\n\t// for( size_t j = 1; j < FFT_SIZE / 2; j++ )\n\t// \tfftOut[ j ] += fftOut[ FFT_SIZE - j ];\n\tfor( size_t j = 1; j < 4; j++ )\n\t\tfftOut[ j ] += fftOut[ FFT_SIZE - j ];\n\tfor( size_t j = 4; j < FFT_SIZE / 2; j += 4 )\n\t{\n\t\t__m128 curr = _mm_loadu_ps( fftOut + j );\n\t\t// Too bad _mm_loadr_ps requires alignment\n\t\t__m128 high = _mm_loadu_ps( fftOut + ( FFT_SIZE - 3 ) - j );\n\t\thigh = _mm_shuffle_ps( high, high, _MM_SHUFFLE( 0, 1, 2, 3 ) );\n\t\tcurr = _mm_add_ps( curr, high );\n\t\t_mm_storeu_ps( fftOut + j, curr );\n\t}\n\n\tconstexpr size_t n_fft = 1 + ( FFT_SIZE / 2 );\n\n\t// mel spectrogram\n\tfor( size_t j = 0; j < N_MEL; j++ )\n\t{\n\t\tdouble sum = 0.0;\n\t\tfor( size_t k = 0; k < n_fft; k++ )\n\t\t\tsum += fftOut[ k ] * filters.data[ j * n_fft + k ];\n\t\tif( sum < 1e-10 )\n\t\t\tsum = 1e-10;\n\t\tsum = log10( sum );\n\t\trdi[ j ] = (float)sum;\n\t}\n\n\t/*\n\tconst float* ptr = rdi.data();\n\tconst float* const ptrEnd = ptr + rdi.size();\n\tstatic_assert( 0 == N_MEL % 4 );\n\t__m128 ax = _mm_loadu_ps( ptr );\n\tfor( ptr += 4; ptr < ptrEnd; ptr += 4 )\n\t\tax = _mm_max_ps( ax, _mm_loadu_ps( ptr ) );\n\tax = _mm_max_ps( ax, _mm_movehl_ps( ax, ax ) );\n\tax = _mm_max_ss( ax, _mm_movehdup_ps( ax ) );\n\treturn _mm_cvtss_f32( ax );\n\t*/\n}"
  },
  {
    "path": "Whisper/Whisper/melSpectrogram.h",
    "content": "#pragma once\n#include \"audioConstants.h\"\n#include \"WhisperModel.h\"\n#include <memory>\n\nnamespace Whisper\n{\n\tclass HanningWindow\n\t{\n\t\tstd::array<float, FFT_SIZE> hann;\n\tpublic:\n\t\tHanningWindow();\n\n\t\tfloat operator[]( size_t i ) const\n\t\t{\n\t\t\treturn hann[ i ];\n\t\t}\n\t};\n\n\textern const HanningWindow s_hanning;\n\n\tclass SpectrogramContext\n\t{\n\t\tconst Filters& filters;\n\t\tstatic float* fftRecursion( float* temp, const float* const rsi, const size_t len );\n\t\tstd::unique_ptr<float[]> tempBuffer;\n\n\tpublic:\n\t\tSpectrogramContext( const Filters& flt );\n\n\t\t// First step of the MEL algorithm, and recursively compute the FFT\n\t\tvoid fft( std::array<float, N_MEL>& rdi, const float* pcm, size_t length );\n\t};\n}"
  },
  {
    "path": "Whisper/Whisper/sEncodeParams.h",
    "content": "#pragma once\n#include <stdint.h>\n\nnamespace DirectCompute\n{\n\tstruct sEncodeParams\n\t{\n\t\tuint32_t n_ctx, n_mels, mel_offset;\n\t\tuint32_t layersCount, n_state, n_head;\n\t\tuint32_t n_audio_ctx, n_text_state, n_text_layer, n_text_ctx;\n\t};\n\n\tstruct sDecodeParams\n\t{\n\t\tuint32_t n_state, n_head;\n\t\tuint32_t n_ctx, n_past, M;\n\t\tuint32_t n_text_layer;\n\t\tuint32_t n_vocab;\n\t};\n}"
  },
  {
    "path": "Whisper/Whisper/sModelParams.h",
    "content": "#pragma once\nnamespace Whisper\n{\n\t// default hparams (Whisper tiny)\n\tstruct sModelParams\n\t{\n\t\tint n_vocab = 51864;\n\t\tint n_audio_ctx = 1500;\n\t\tint n_audio_state = 384;\n\t\tint n_audio_head = 6;\n\t\tint n_audio_layer = 4;\n\t\tint n_text_ctx = 448;\n\t\tint n_text_state = 384;\n\t\tint n_text_head = 6;\n\t\tint n_text_layer = 4;\n\t\tint n_mels = 80;\n\t\tint f16 = 1;\n\t};\n}"
  },
  {
    "path": "Whisper/Whisper/sTokenData.h",
    "content": "#pragma once\n#include <stdint.h>\n\nnamespace Whisper\n{\n\tusing whisper_token = int;\n\n\tstruct sTokenData\n\t{\n\t\twhisper_token id;  // token id\n\t\twhisper_token tid; // forced timestamp token id\n\t\tfloat p;           // probability of the token\n\t\tfloat pt;          // probability of the timestamp token\n\t\tfloat ptsum;       // sum of probabilities of all timestamp tokens\n\t\tfloat vlen;        // voice length of the token\n\t\t// token-level timestamp data\n\t\t// do not use if you haven't computed token-level timestamps\n\t\tint64_t t0;        // start time of the token\n\t\tint64_t t1;        //   end time of the token\n\t};\n\n\n}"
  },
  {
    "path": "Whisper/Whisper/voiceActivityDetection.cpp",
    "content": "#include \"stdafx.h\"\n#include \"voiceActivityDetection.h\"\nusing namespace Whisper;\n\n// Initially ported (poorly) from there https://github.com/panmasuo/voice-activity-detection MIT license\n// The code is based on that article:\n// https://www.researchgate.net/publication/255667085_A_simple_but_efficient_real-time_voice_activity_detection_algorithm\n\ninline VAD::Feature VAD::defaultPrimaryThresholds()\n{\n\tFeature f;\n\tf.energy = 40;\n\tf.F = 185;\n\tf.SFM = 5;\n\treturn f;\n}\n\nVAD::VAD() :\n\tprimThresh( defaultPrimaryThresholds() )\n{\n\tfft_signal = std::make_unique<cplx[]>( FFT_POINTS );\n}\n\ninline void VAD::fft( cplx* buf, cplx* out, size_t n, size_t step )\n{\n\tif( step < n )\n\t{\n\t\tfft( out, buf, n, step * 2 );\n\t\tfft( out + step, buf + step, n, step * 2 );\n\n\t\tfor( size_t i = 0; i < n; i += 2 * step )\n\t\t{\n\t\t\t// cplx t = cexp(-I * M_PI * i / n) * out[i + step];\n\t\t\t// using namespace std::complex_literals;\n\t\t\tconst float mul = (float)M_PI * (float)(int)i / (float)(int)n;\n\t\t\t// cplx t0 = std::exp( -1.0if * mul ) * out[ i + step ];\n\t\t\tfloat sine, cosine;\n\t\t\tDirectX::XMScalarSinCos( &sine, &cosine, -mul );\n\t\t\tconst cplx exponent{ cosine, sine };\n\t\t\tcplx t = exponent * out[ i + step ];\n\n\t\t\tbuf[ i / 2 ] = out[ i ] + t;\n\t\t\tbuf[ ( i + n ) / 2 ] = out[ i ] - t;\n\t\t}\n\t}\n}\n\nvoid VAD::fft() const\n{\n\tcplx out[ FFT_POINTS ];\n\tmemcpy( &out[ 0 ], fft_signal.get(), FFT_POINTS * sizeof( cplx ) );\n\n\tfft( fft_signal.get(), out, FFT_POINTS, 1 );\n}\n\nconstexpr float mulInt16FromFloat = 32768.0;\n\ninline float squareRoot( float x )\n{\n\t__m128 v = _mm_set_ss( x );\n\tv = _mm_sqrt_ss( v );\n\treturn _mm_cvtss_f32( v );\n}\n\nfloat VAD::computeEnergy( const float* rsi )\n{\n\t// calculate_energy\n\tdouble sum = 0;\n\tfor( size_t i = 0; i < FFT_POINTS; i++ )\n\t{\n\t\tfloat f = rsi[ i ];\n\t\tf *= mulInt16FromFloat;\n\t\tf *= f;\n\t\tsum += f;\n\t}\n\treturn squareRoot( (float)( sum * ( 1.0 / FFT_POINTS ) ) );\n}\n\nfloat VAD::computeDominant( const cplx* spectrum )\n{\n\t// calculate_dominant, reworked heavily\n\tfloat maxMagSquared = 0;\n\tint maxFreq = 0;\n\n\tfor( int i = 0; i < FFT_POINTS / 2; i++ )\n\t{\n\t\tconst float real = (float)spectrum[ i ].real();\n\t\tconst float imag = (float)spectrum[ i ].imag();\n\t\tfloat sq = real * real + imag * imag;\n\t\tif( sq <= maxMagSquared )\n\t\t\tcontinue;\n\t\tmaxMagSquared = sq;\n\t\tmaxFreq = i;\n\t}\n\treturn (float)maxFreq * FFT_STEP;\n}\n\nfloat VAD::computreSpectralFlatnessMeasure( const cplx* spectrum )\n{\n\t// calculate_sfm\n\tdouble sum_ari = 0;\n\tdouble sum_geo = 0;\n\tfor( size_t i = 0; i < FFT_POINTS; i++ )\n\t{\n\t\t// sig = cabsf( spectrum[ i ] );\n\t\tfloat sig = std::abs( spectrum[ i ] );\n\t\tsum_ari += sig;\n\t\tsum_geo += std::log( sig );\n\t}\n\tsum_ari = sum_ari / FFT_POINTS;\n\tsum_geo = std::exp( sum_geo / FFT_POINTS );\n\treturn -10.0f * std::log10f( (float)( sum_geo / sum_ari ) );\n}\n\nvoid VAD::clear()\n{\n\tmemset( &state, 0, sizeof( State ) );\n\tstate.currThresh = primThresh;\n}\n\nsize_t VAD::detect( const float* rsi, size_t length )\n{\n\t// The cryptic numbers in the comments are from section 3 \"Proposed VAD Algorithm\" of the article, on page 2550, on the right\n\tconst size_t frames = length / FFT_POINTS;\n\tif( frames <= 0 )\n\t{\n\t\tclear();\n\t\treturn 0;\n\t}\n\n\t// Load detection state from the field\n\tFeature currThresh = state.currThresh;\n\tFeature minFeature = state.minFeature;\n\tFeature curr = state.curr;\n\n\tsize_t lastSpeech = state.lastSpeech;\n\tfloat silenceRun = state.silenceRun;\n\tsize_t i = state.i;\n\n\t// Run the loop just on the [ state.i .. frames ] slice of the input PCM\n\trsi += i * FFT_POINTS;\n\tfor( ; i < frames; i++, rsi += FFT_POINTS )\n\t{\n\t\t// 3-2 calculate FFT\n\t\tfor( size_t j = 0; j < FFT_POINTS; j++ )\n\t\t{\n\t\t\tconst float re = rsi[ j ] * mulInt16FromFloat;\n\t\t\tfft_signal[ j ] = { re, 0.0f };\n\t\t}\n\t\tfft();\n\n\t\t// 3-1 + 3-2 calculate features\n\t\tcurr.energy = computeEnergy( rsi );\n\t\tcurr.F = computeDominant( fft_signal.get() );\n\t\tcurr.SFM = computreSpectralFlatnessMeasure( fft_signal.get() );\n\n\t\t// 3-3 calculate minimum value for first 30 frames\n\t\tif( i == 0 )\n\t\t\tminFeature = curr;\n\t\telse if( i < 30 )\n\t\t{\n\t\t\tminFeature.energy = std::min( minFeature.energy, curr.energy );\n\t\t\tminFeature.F = std::min( minFeature.F, curr.F );\n\t\t\tminFeature.SFM = std::min( minFeature.SFM, curr.SFM );\n\t\t}\n\n\t\t// 3-4 set thresholds\n\t\tcurrThresh.energy = primThresh.energy * std::log10f( minFeature.energy );\n\n\t\t// 3-5 calculate decision\n\t\tuint8_t counter = 0;\n\t\tif( ( curr.energy - minFeature.energy ) >= currThresh.energy )\n\t\t\tcounter = 1;\n\t\tif( ( curr.F - minFeature.F ) >= currThresh.F )\n\t\t\tcounter++;\n\t\tif( ( curr.SFM - minFeature.SFM ) >= currThresh.SFM )\n\t\t\tcounter++;\n\n\t\tif( counter > 1 )\n\t\t{\n\t\t\t// 3-6 If counter > 1 mark the current frame as speech\n\t\t\tlastSpeech = ( i + 1 ) * FFT_POINTS;\n\t\t\tsilenceRun = 0.0f;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tsilenceRun += 1.0f;\n\t\t\t// 3-7 If current frame is marked as silence, update the energy minimum value\n\t\t\tminFeature.energy = ( ( silenceRun * minFeature.energy ) + curr.energy ) / ( silenceRun + 1 );\n\t\t}\n\n\t\t// 3-8\n\t\tcurrThresh.energy = primThresh.energy * std::log10f( minFeature.energy );\n\t}\n\n\t// Store the updated detection state back into that field\n\tstate.currThresh = currThresh;\n\tstate.minFeature = minFeature;\n\tstate.curr = curr;\n\tstate.lastSpeech = (uint32_t)lastSpeech;\n\tstate.silenceRun = silenceRun;\n\tstate.i = (uint32_t)i;\n\n\treturn lastSpeech;\n}"
  },
  {
    "path": "Whisper/Whisper/voiceActivityDetection.h",
    "content": "#pragma once\n#include <complex>\n#include <memory>\n#include \"audioConstants.h\"\n\nnamespace Whisper\n{\n\tclass VAD\n\t{\n\t\tusing cplx = std::complex<float>;\n\t\tstd::unique_ptr<cplx[]> fft_signal;\n\n\t\tstruct Feature\n\t\t{\n\t\t\tfloat energy;\n\t\t\tfloat F;\n\t\t\tfloat SFM;\n\t\t};\n\t\tconst Feature primThresh;\n\t\tstatic Feature defaultPrimaryThresholds();\n\n\t\tstruct State\n\t\t{\n\t\t\tFeature currThresh;\n\t\t\tFeature minFeature;\n\t\t\tFeature curr;\n\n\t\t\tuint32_t lastSpeech;\n\t\t\tfloat silenceRun;\n\t\t\tuint32_t i;\n\t\t};\n\t\tState state;\n\n\t\tstatic inline void fft( cplx* buf, cplx* out, size_t n, size_t step );\n\t\tvoid fft() const;\n\n\t\tstatic float computeEnergy( const float* rsi );\n\t\tstatic float computeDominant( const cplx* spectrum );\n\t\tstatic float computreSpectralFlatnessMeasure( const cplx* spectrum );\n\n\tpublic:\n\n\t\tVAD();\n\n\t\t// When no speech is detected, returns 0\n\t\t// When speech is detected, returns sample position for the end of the speech\n\t\tsize_t detect( const float* rsi, size_t length );\n\n\t\tvoid clear();\n\n\t\tstatic constexpr uint32_t FFT_POINTS = 256;\n\t\tstatic constexpr float FFT_STEP = (float)SAMPLE_RATE / (float)FFT_POINTS;\n\t};\n}"
  },
  {
    "path": "Whisper/Whisper.vcxproj",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project DefaultTargets=\"Build\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <ItemGroup Label=\"ProjectConfigurations\">\n    <ProjectConfiguration Include=\"Debug|x64\">\n      <Configuration>Debug</Configuration>\n      <Platform>x64</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Release|x64\">\n      <Configuration>Release</Configuration>\n      <Platform>x64</Platform>\n    </ProjectConfiguration>\n  </ItemGroup>\n  <PropertyGroup Label=\"Globals\">\n    <VCProjectVersion>16.0</VCProjectVersion>\n    <Keyword>Win32Proj</Keyword>\n    <ProjectGuid>{701df8c8-e4a5-43ec-9c6b-747bbf4d8e71}</ProjectGuid>\n    <RootNamespace>Whisper</RootNamespace>\n    <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>\n  </PropertyGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.Default.props\" />\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\" Label=\"Configuration\">\n    <ConfigurationType>DynamicLibrary</ConfigurationType>\n    <UseDebugLibraries>true</UseDebugLibraries>\n    <PlatformToolset>v143</PlatformToolset>\n    <CharacterSet>Unicode</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\" Label=\"Configuration\">\n    <ConfigurationType>DynamicLibrary</ConfigurationType>\n    <UseDebugLibraries>false</UseDebugLibraries>\n    <PlatformToolset>v143</PlatformToolset>\n    <WholeProgramOptimization>true</WholeProgramOptimization>\n    <CharacterSet>Unicode</CharacterSet>\n  </PropertyGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.props\" />\n  <ImportGroup Label=\"ExtensionSettings\">\n  </ImportGroup>\n  <ImportGroup Label=\"Shared\">\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <PropertyGroup Label=\"UserMacros\" />\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\n    <IncludePath>$(ProjectDir);$(IncludePath)</IncludePath>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <IncludePath>$(ProjectDir);$(IncludePath)</IncludePath>\n  </PropertyGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <SDLCheck>true</SDLCheck>\n      <PreprocessorDefinitions>_DEBUG;WHISPER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <ConformanceMode>true</ConformanceMode>\n      <LanguageStandard>stdcpp20</LanguageStandard>\n      <PrecompiledHeader>Use</PrecompiledHeader>\n      <MultiProcessorCompilation>true</MultiProcessorCompilation>\n      <EnableEnhancedInstructionSet>AdvancedVectorExtensions</EnableEnhancedInstructionSet>\n    </ClCompile>\n    <Link>\n      <SubSystem>Windows</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n      <EnableUAC>false</EnableUAC>\n      <ModuleDefinitionFile>whisper.def</ModuleDefinitionFile>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <FunctionLevelLinking>true</FunctionLevelLinking>\n      <IntrinsicFunctions>true</IntrinsicFunctions>\n      <SDLCheck>true</SDLCheck>\n      <PreprocessorDefinitions>NDEBUG;WHISPER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <ConformanceMode>true</ConformanceMode>\n      <LanguageStandard>stdcpp20</LanguageStandard>\n      <PrecompiledHeader>Use</PrecompiledHeader>\n      <MultiProcessorCompilation>true</MultiProcessorCompilation>\n      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>\n      <EnableEnhancedInstructionSet>AdvancedVectorExtensions</EnableEnhancedInstructionSet>\n    </ClCompile>\n    <Link>\n      <SubSystem>Windows</SubSystem>\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\n      <OptimizeReferences>true</OptimizeReferences>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n      <EnableUAC>false</EnableUAC>\n      <ModuleDefinitionFile>whisper.def</ModuleDefinitionFile>\n      <LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemGroup>\n    <ProjectReference Include=\"..\\ComLightLib\\ComLightLib.vcxproj\">\n      <Project>{52f486e7-830c-45d8-be47-e76b5aab2772}</Project>\n    </ProjectReference>\n  </ItemGroup>\n  <ItemGroup>\n    <ClCompile Include=\"CPU\\BufferAllocator.cpp\" />\n    <ClCompile Include=\"CPU\\DecoderTensors.cpp\" />\n    <ClCompile Include=\"CPU\\mulMatImpl.avx2.cpp\">\n      <EnableEnhancedInstructionSet Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">AdvancedVectorExtensions2</EnableEnhancedInstructionSet>\n      <EnableEnhancedInstructionSet Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">AdvancedVectorExtensions2</EnableEnhancedInstructionSet>\n    </ClCompile>\n    <ClCompile Include=\"CPU\\mulMatImpl.panel.cpp\">\n      <EnableEnhancedInstructionSet Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">AdvancedVectorExtensions</EnableEnhancedInstructionSet>\n      <EnableEnhancedInstructionSet Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">AdvancedVectorExtensions</EnableEnhancedInstructionSet>\n    </ClCompile>\n    <ClCompile Include=\"CPU\\TensorCpu.cpp\">\n      <EnableEnhancedInstructionSet Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">AdvancedVectorExtensions</EnableEnhancedInstructionSet>\n      <EnableEnhancedInstructionSet Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">AdvancedVectorExtensions</EnableEnhancedInstructionSet>\n    </ClCompile>\n    <ClCompile Include=\"CPU\\HybridLoader.cpp\" />\n    <ClCompile Include=\"D3D\\createDevice.cpp\" />\n    <ClCompile Include=\"D3D\\listGPUs.cpp\" />\n    <ClCompile Include=\"ML\\Device.cpp\" />\n    <ClCompile Include=\"ML\\DbgNanTest.cpp\" />\n    <ClCompile Include=\"Utils\\MurmurHash3.cpp\" />\n    <ClCompile Include=\"Utils\\DelayExecution.cpp\" />\n    <ClCompile Include=\"Hybrid\\HybridContext.cpp\" />\n    <ClCompile Include=\"CPU\\ParallelForRunner.cpp\">\n      <EnableEnhancedInstructionSet Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">AdvancedVectorExtensions</EnableEnhancedInstructionSet>\n      <EnableEnhancedInstructionSet Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">AdvancedVectorExtensions</EnableEnhancedInstructionSet>\n    </ClCompile>\n    <ClCompile Include=\"CPU\\LargeBuffer.cpp\" />\n    <ClCompile Include=\"CPU\\simdUtils.cpp\">\n      <EnableEnhancedInstructionSet Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">AdvancedVectorExtensions</EnableEnhancedInstructionSet>\n      <EnableEnhancedInstructionSet Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">AdvancedVectorExtensions</EnableEnhancedInstructionSet>\n    </ClCompile>\n    <ClCompile Include=\"CPU\\mulMat.cpp\">\n      <EnableEnhancedInstructionSet Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">AdvancedVectorExtensions</EnableEnhancedInstructionSet>\n      <EnableEnhancedInstructionSet Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">AdvancedVectorExtensions</EnableEnhancedInstructionSet>\n    </ClCompile>\n    <ClCompile Include=\"CPU\\MlContextCpu.cpp\">\n      <EnableEnhancedInstructionSet Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">AdvancedVectorExtensions</EnableEnhancedInstructionSet>\n      <EnableEnhancedInstructionSet Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">AdvancedVectorExtensions</EnableEnhancedInstructionSet>\n    </ClCompile>\n    <ClCompile Include=\"CPU\\KvTensorsCpu.cpp\" />\n    <ClCompile Include=\"Hybrid\\KeyValueDownloader.cpp\" />\n    <ClCompile Include=\"CPU\\mulMatImpl.cpp\">\n      <EnableEnhancedInstructionSet Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">AdvancedVectorExtensions</EnableEnhancedInstructionSet>\n      <EnableEnhancedInstructionSet Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">AdvancedVectorExtensions</EnableEnhancedInstructionSet>\n    </ClCompile>\n    <ClCompile Include=\"ML\\Reshaper.cpp\" />\n    <ClCompile Include=\"Utils\\Logger.cpp\" />\n    <ClCompile Include=\"MF\\AudioCapture.cpp\" />\n    <ClCompile Include=\"Utils\\LZ4\\lz4.c\">\n      <PrecompiledHeader>NotUsing</PrecompiledHeader>\n    </ClCompile>\n    <ClCompile Include=\"Utils\\miscUtils.cpp\" />\n    <ClCompile Include=\"Whisper\\ContextImpl.diarize.cpp\" />\n    <ClCompile Include=\"Whisper\\ModelBuffers.clone.cpp\" />\n    <ClCompile Include=\"Whisper\\voiceActivityDetection.cpp\" />\n    <ClCompile Include=\"Whisper\\ContextImpl.capture.cpp\" />\n    <ClCompile Include=\"Whisper\\MelStreamer.cpp\" />\n    <ClCompile Include=\"Whisper\\melSpectrogram.cpp\" />\n    <ClCompile Include=\"modelFactory.cpp\" />\n    <ClCompile Include=\"MF\\AudioBuffer.cpp\" />\n    <ClCompile Include=\"MF\\PcmReader.cpp\" />\n    <ClCompile Include=\"Utils\\Trace\\tracing.cpp\" />\n    <ClCompile Include=\"Utils\\Trace\\TraceStructures.cpp\" />\n    <ClCompile Include=\"Utils\\Trace\\TraceWriter.cpp\" />\n    <ClCompile Include=\"source.compat\\convertThings.cpp\" />\n    <ClCompile Include=\"D3D\\shaderNames.cpp\" />\n    <ClCompile Include=\"MF\\mfUtils.cpp\" />\n    <ClCompile Include=\"MF\\loadAudioFile.cpp\" />\n    <ClCompile Include=\"MF\\MediaFoundation.cpp\" />\n    <ClCompile Include=\"MF\\mfStartup.cpp\" />\n    <ClCompile Include=\"source.compat\\ggmlMsvc.c\">\n      <PrecompiledHeader>NotUsing</PrecompiledHeader>\n      <EnableEnhancedInstructionSet>AdvancedVectorExtensions</EnableEnhancedInstructionSet>\n    </ClCompile>\n    <ClCompile Include=\"Whisper\\ContextImpl.misc.cpp\" />\n    <ClCompile Include=\"Utils\\ProfileCollection.cpp\" />\n    <ClCompile Include=\"Utils\\CpuProfiler.cpp\" />\n    <ClCompile Include=\"D3D\\enums.cpp\" />\n    <ClCompile Include=\"Utils\\GpuProfiler.cpp\" />\n    <ClCompile Include=\"ML\\TensorsArena.cpp\" />\n    <ClCompile Include=\"Whisper\\Languages.cpp\" />\n    <ClCompile Include=\"Whisper\\ContextImpl.cpp\" />\n    <ClCompile Include=\"Whisper\\ModelImpl.cpp\" />\n    <ClCompile Include=\"Utils\\parallelFor.cpp\" />\n    <ClCompile Include=\"Whisper\\Spectrogram.cpp\" />\n    <ClCompile Include=\"Whisper\\WhisperModel.cpp\" />\n    <ClCompile Include=\"Whisper\\Vocabulary.cpp\" />\n    <ClCompile Include=\"Whisper\\DecoderResultBuffer.cpp\" />\n    <ClCompile Include=\"Whisper\\DecoderInputBuffers.cpp\" />\n    <ClCompile Include=\"ML\\mlUtils.cpp\" />\n    <ClCompile Include=\"Whisper\\KeyValueBuffers.cpp\" />\n    <ClCompile Include=\"D3D\\Binder.cpp\" />\n    <ClCompile Include=\"ML\\LookupTables.cpp\" />\n    <ClCompile Include=\"ML\\LookupTablesData.cpp\" />\n    <ClCompile Include=\"ML\\Context.ops.cpp\" />\n    <ClCompile Include=\"ML\\MlContext.dbg.cpp\" />\n    <ClCompile Include=\"ML\\MlContext.cpp\" />\n    <ClCompile Include=\"ML\\ConstantBuffer.cpp\" />\n    <ClCompile Include=\"D3D\\createBuffer.cpp\" />\n    <ClCompile Include=\"D3D\\MappedResource.cpp\" />\n    <ClCompile Include=\"Whisper\\WhisperContext.cpp\" />\n    <ClCompile Include=\"Whisper\\ModelBuffers.cpp\" />\n    <ClCompile Include=\"D3D\\shaders.cpp\" />\n    <ClCompile Include=\"ML\\TensorShape.cpp\" />\n    <ClCompile Include=\"DllMain.cpp\" />\n    <ClCompile Include=\"D3D\\downloadBuffer.cpp\" />\n    <ClCompile Include=\"D3D\\RenderDoc\\renderDoc.cpp\" />\n    <ClCompile Include=\"Whisper\\MelInputTensor.cpp\" />\n    <ClCompile Include=\"source\\ggml.c\">\n      <ExcludedFromBuild Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">true</ExcludedFromBuild>\n      <ExcludedFromBuild Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">true</ExcludedFromBuild>\n    </ClCompile>\n    <ClCompile Include=\"source\\whisper.cpp\">\n      <ExcludedFromBuild Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">true</ExcludedFromBuild>\n      <ExcludedFromBuild Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">true</ExcludedFromBuild>\n    </ClCompile>\n    <ClCompile Include=\"ML\\TempBuffers.cpp\" />\n    <ClCompile Include=\"ML\\tensorOpsTests.cpp\" />\n    <ClCompile Include=\"stdafx.cpp\">\n      <PrecompiledHeader Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">Create</PrecompiledHeader>\n      <PrecompiledHeader Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">Create</PrecompiledHeader>\n    </ClCompile>\n    <ClCompile Include=\"ML\\testUtils.cpp\" />\n    <ClCompile Include=\"ML\\Tensor.cpp\" />\n    <ClCompile Include=\"ML\\TensorGpuViews.cpp\" />\n    <ClCompile Include=\"ML\\TensorEx.cpp\" />\n    <ClCompile Include=\"whisperCom.cpp\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ClInclude Include=\"API\\iContext.h\" />\n    <ClInclude Include=\"API\\iMediaFoundation.h\" />\n    <ClInclude Include=\"API\\iTranscribeResult.h\" />\n    <ClInclude Include=\"API\\loggerApi.h\" />\n    <ClInclude Include=\"API\\MfStructs.h\" />\n    <ClInclude Include=\"API\\iContext.cl.h\" />\n    <ClInclude Include=\"API\\iMediaFoundation.cl.h\" />\n    <ClInclude Include=\"API\\iTranscribeResult.cl.h\" />\n    <ClInclude Include=\"API\\sLanguageList.h\" />\n    <ClInclude Include=\"API\\sLoadModelCallbacks.h\" />\n    <ClInclude Include=\"API\\sModelSetup.h\" />\n    <ClInclude Include=\"API\\SpecialTokens.h\" />\n    <ClInclude Include=\"API\\sFullParams.h\" />\n    <ClInclude Include=\"API\\whisperComLight.h\" />\n    <ClInclude Include=\"API\\whisperWindows.h\" />\n    <ClInclude Include=\"CPU\\BufferAllocator.h\" />\n    <ClInclude Include=\"CPU\\mulMatUtils.hpp\" />\n    <ClInclude Include=\"CPU\\Tensor.h\" />\n    <ClInclude Include=\"CPU\\DecoderTensors.h\" />\n    <ClInclude Include=\"CPU\\HybridLoader.h\" />\n    <ClInclude Include=\"D3D\\createDevice.h\" />\n    <ClInclude Include=\"D3D\\listGPUs.h\" />\n    <ClInclude Include=\"D3D\\sGpuInfo.h\" />\n    <ClInclude Include=\"ML\\Device.h\" />\n    <ClInclude Include=\"ML\\DbgNanTest.h\" />\n    <ClInclude Include=\"ML\\mlUtils.h\" />\n    <ClInclude Include=\"Utils\\MurmurHash3.h\" />\n    <ClInclude Include=\"Utils\\DelayExecution.h\" />\n    <ClInclude Include=\"Hybrid\\HybridContext.h\" />\n    <ClInclude Include=\"CPU\\ParallelForRunner.h\" />\n    <ClInclude Include=\"CPU\\LargeBuffer.h\" />\n    <ClInclude Include=\"CPU\\simdUtils.h\" />\n    <ClInclude Include=\"CPU\\MlContext.h\" />\n    <ClInclude Include=\"CPU\\KvTensors.h\" />\n    <ClInclude Include=\"Hybrid\\KeyValueDownloader.h\" />\n    <ClInclude Include=\"ML\\reshapedMultiply.h\" />\n    <ClInclude Include=\"ML\\testUtilsC.h\" />\n    <ClInclude Include=\"CPU\\mulMat.h\" />\n    <ClInclude Include=\"CPU\\mulMatImpl.h\" />\n    <ClInclude Include=\"ML\\Reshaper.h\" />\n    <ClInclude Include=\"Utils\\Logger.h\" />\n    <ClInclude Include=\"MF\\AudioCapture.h\" />\n    <ClInclude Include=\"Utils\\LZ4\\lz4.h\" />\n    <ClInclude Include=\"Whisper\\sModelParams.h\" />\n    <ClInclude Include=\"Whisper\\voiceActivityDetection.h\" />\n    <ClInclude Include=\"Whisper\\MelStreamer.h\" />\n    <ClInclude Include=\"Whisper\\melSpectrogram.h\" />\n    <ClInclude Include=\"modelFactory.h\" />\n    <ClInclude Include=\"MF\\AudioBuffer.h\" />\n    <ClInclude Include=\"MF\\PcmReader.h\" />\n    <ClInclude Include=\"Utils\\miscUtils.h\" />\n    <ClInclude Include=\"Utils\\Trace\\tracing.h\" />\n    <ClInclude Include=\"Utils\\Trace\\TraceStructures.h\" />\n    <ClInclude Include=\"Utils\\Trace\\TraceWriter.h\" />\n    <ClInclude Include=\"source.compat\\convertThings.h\" />\n    <ClInclude Include=\"MF\\mfUtils.h\" />\n    <ClInclude Include=\"MF\\loadAudioFile.h\" />\n    <ClInclude Include=\"MF\\mfStartup.h\" />\n    <ClInclude Include=\"API\\TranscribeStructs.h\" />\n    <ClInclude Include=\"resource.h\" />\n    <ClInclude Include=\"Utils\\ReadStream.h\" />\n    <ClInclude Include=\"Whisper\\audioConstants.h\" />\n    <ClInclude Include=\"Whisper\\iSpectrogram.h\" />\n    <ClInclude Include=\"Whisper\\sTokenData.h\" />\n    <ClInclude Include=\"Whisper\\TranscribeResult.h\" />\n    <ClInclude Include=\"Utils\\ProfileCollection.h\" />\n    <ClInclude Include=\"Utils\\CpuProfiler.h\" />\n    <ClInclude Include=\"Utils\\GpuProfiler.h\" />\n    <ClInclude Include=\"ML\\TensorsArena.h\" />\n    <ClInclude Include=\"Utils\\GpuProfilerSimple.h\" />\n    <ClInclude Include=\"Whisper\\Languages.h\" />\n    <ClInclude Include=\"Whisper\\ContextImpl.h\" />\n    <ClInclude Include=\"Whisper\\ModelImpl.h\" />\n    <ClInclude Include=\"Utils\\parallelFor.h\" />\n    <ClInclude Include=\"Whisper\\Spectrogram.h\" />\n    <ClInclude Include=\"Whisper\\loaderUtils.h\" />\n    <ClInclude Include=\"Whisper\\WhisperModel.h\" />\n    <ClInclude Include=\"Whisper\\Vocabulary.h\" />\n    <ClInclude Include=\"Whisper\\DecoderResultBuffer.h\" />\n    <ClInclude Include=\"Whisper\\DecoderInputBuffers.h\" />\n    <ClInclude Include=\"Whisper\\KeyValueBuffers.h\" />\n    <ClInclude Include=\"D3D\\Binder.h\" />\n    <ClInclude Include=\"ML\\LookupTables.h\" />\n    <ClInclude Include=\"ML\\LookupTablesData.h\" />\n    <ClInclude Include=\"ML\\MlContext.h\" />\n    <ClInclude Include=\"ML\\ConstantBuffer.h\" />\n    <ClInclude Include=\"D3D\\device.h\" />\n    <ClInclude Include=\"D3D\\createBuffer.h\" />\n    <ClInclude Include=\"D3D\\enums.h\" />\n    <ClInclude Include=\"D3D\\MappedResource.h\" />\n    <ClInclude Include=\"Whisper\\sEncodeParams.h\" />\n    <ClInclude Include=\"Whisper\\WhisperContext.h\" />\n    <ClInclude Include=\"Whisper\\ModelBuffers.h\" />\n    <ClInclude Include=\"Whisper\\ModelLoader.h\" />\n    <ClInclude Include=\"D3D\\RenderDoc\\renderdoc_app.h\" />\n    <ClInclude Include=\"D3D\\shaderNames.h\" />\n    <ClInclude Include=\"D3D\\shaders.h\" />\n    <ClInclude Include=\"D3D\\downloadBuffer.h\" />\n    <ClInclude Include=\"D3D\\RenderDoc\\renderDoc.h\" />\n    <ClInclude Include=\"Whisper\\MelInputTensor.h\" />\n    <ClInclude Include=\"ML\\TensorShape.h\" />\n    <ClInclude Include=\"source\\ggml.h\" />\n    <ClInclude Include=\"source\\whisper.h\" />\n    <ClInclude Include=\"ML\\TempBuffers.h\" />\n    <ClInclude Include=\"ML\\tensorOpsTests.h\" />\n    <ClInclude Include=\"stdafx.h\" />\n    <ClInclude Include=\"ML\\testUtils.h\" />\n    <ClInclude Include=\"ML\\Tensor.h\" />\n    <ClInclude Include=\"ML\\TensorGpuViews.h\" />\n    <ClInclude Include=\"ML\\TensorEx.h\" />\n  </ItemGroup>\n  <ItemGroup>\n    <None Include=\"D3D\\shaderData-Debug.inl\" />\n    <None Include=\"D3D\\shaderData-Release.inl\" />\n    <None Include=\"CPU\\mulMat.kernel.hpp\" />\n    <None Include=\"ML\\LookupTablesData.inl\" />\n    <None Include=\"source\\LICENSE\" />\n    <None Include=\"whisper.def\" />\n    <None Include=\"Whisper\\languageCodez.inl\" />\n    <None Include=\"Whisper\\languageCodez.tsv\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Natvis Include=\"misc.natvis\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ResourceCompile Include=\"Resource.rc\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Text Include=\"API\\Readme.txt\" />\n    <Text Include=\"CPU\\Readme.txt\" />\n    <Text Include=\"Hybrid\\Readme.txt\" />\n    <Text Include=\"Readme.txt\" />\n    <Text Include=\"source.compat\\Readme.txt\" />\n    <Text Include=\"source\\Readme.txt\" />\n  </ItemGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.targets\" />\n  <ImportGroup Label=\"ExtensionTargets\">\n  </ImportGroup>\n</Project>"
  },
  {
    "path": "Whisper/Whisper.vcxproj.filters",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <ItemGroup>\n    <ClCompile Include=\"DllMain.cpp\" />\n    <ClCompile Include=\"whisperCom.cpp\" />\n    <ClCompile Include=\"source\\ggml.c\" />\n    <ClCompile Include=\"source\\whisper.cpp\" />\n    <ClCompile Include=\"ML\\ConstantBuffer.cpp\" />\n    <ClCompile Include=\"D3D\\MappedResource.cpp\" />\n    <ClCompile Include=\"D3D\\shaders.cpp\" />\n    <ClCompile Include=\"D3D\\createBuffer.cpp\" />\n    <ClCompile Include=\"ML\\TempBuffers.cpp\" />\n    <ClCompile Include=\"ML\\MlContext.cpp\" />\n    <ClCompile Include=\"ML\\tensorOpsTests.cpp\" />\n    <ClCompile Include=\"D3D\\Binder.cpp\" />\n    <ClCompile Include=\"stdafx.cpp\" />\n    <ClCompile Include=\"ML\\testUtils.cpp\" />\n    <ClCompile Include=\"D3D\\downloadBuffer.cpp\" />\n    <ClCompile Include=\"D3D\\RenderDoc\\renderDoc.cpp\" />\n    <ClCompile Include=\"Whisper\\MelInputTensor.cpp\" />\n    <ClCompile Include=\"Whisper\\ModelBuffers.cpp\" />\n    <ClCompile Include=\"ML\\Context.ops.cpp\" />\n    <ClCompile Include=\"ML\\TensorShape.cpp\" />\n    <ClCompile Include=\"ML\\Tensor.cpp\" />\n    <ClCompile Include=\"ML\\TensorGpuViews.cpp\" />\n    <ClCompile Include=\"ML\\TensorEx.cpp\" />\n    <ClCompile Include=\"Whisper\\WhisperContext.cpp\" />\n    <ClCompile Include=\"ML\\MlContext.dbg.cpp\" />\n    <ClCompile Include=\"ML\\LookupTablesData.cpp\" />\n    <ClCompile Include=\"ML\\LookupTables.cpp\" />\n    <ClCompile Include=\"Whisper\\KeyValueBuffers.cpp\" />\n    <ClCompile Include=\"ML\\mlUtils.cpp\" />\n    <ClCompile Include=\"Whisper\\DecoderInputBuffers.cpp\" />\n    <ClCompile Include=\"Whisper\\DecoderResultBuffer.cpp\" />\n    <ClCompile Include=\"Whisper\\Vocabulary.cpp\" />\n    <ClCompile Include=\"Whisper\\WhisperModel.cpp\" />\n    <ClCompile Include=\"Whisper\\Spectrogram.cpp\" />\n    <ClCompile Include=\"Utils\\parallelFor.cpp\" />\n    <ClCompile Include=\"Whisper\\ModelImpl.cpp\" />\n    <ClCompile Include=\"Whisper\\ContextImpl.cpp\" />\n    <ClCompile Include=\"Whisper\\Languages.cpp\" />\n    <ClCompile Include=\"ML\\TensorsArena.cpp\" />\n    <ClCompile Include=\"D3D\\enums.cpp\" />\n    <ClCompile Include=\"Utils\\GpuProfiler.cpp\" />\n    <ClCompile Include=\"Utils\\CpuProfiler.cpp\" />\n    <ClCompile Include=\"Utils\\ProfileCollection.cpp\" />\n    <ClCompile Include=\"D3D\\shaderNames.cpp\" />\n    <ClCompile Include=\"MF\\mfStartup.cpp\" />\n    <ClCompile Include=\"MF\\MediaFoundation.cpp\" />\n    <ClCompile Include=\"MF\\loadAudioFile.cpp\" />\n    <ClCompile Include=\"MF\\mfUtils.cpp\" />\n    <ClCompile Include=\"source.compat\\convertThings.cpp\" />\n    <ClCompile Include=\"source.compat\\ggmlMsvc.c\" />\n    <ClCompile Include=\"Whisper\\ContextImpl.misc.cpp\" />\n    <ClCompile Include=\"Utils\\Trace\\TraceWriter.cpp\" />\n    <ClCompile Include=\"Utils\\Trace\\TraceStructures.cpp\" />\n    <ClCompile Include=\"Utils\\Trace\\tracing.cpp\" />\n    <ClCompile Include=\"MF\\AudioBuffer.cpp\" />\n    <ClCompile Include=\"modelFactory.cpp\" />\n    <ClCompile Include=\"MF\\PcmReader.cpp\" />\n    <ClCompile Include=\"Whisper\\melSpectrogram.cpp\" />\n    <ClCompile Include=\"Whisper\\MelStreamer.cpp\" />\n    <ClCompile Include=\"Utils\\miscUtils.cpp\" />\n    <ClCompile Include=\"MF\\AudioCapture.cpp\" />\n    <ClCompile Include=\"Utils\\Logger.cpp\" />\n    <ClCompile Include=\"Whisper\\ContextImpl.capture.cpp\" />\n    <ClCompile Include=\"Whisper\\voiceActivityDetection.cpp\" />\n    <ClCompile Include=\"CPU\\LargeBuffer.cpp\" />\n    <ClCompile Include=\"CPU\\ParallelForRunner.cpp\" />\n    <ClCompile Include=\"CPU\\simdUtils.cpp\" />\n    <ClCompile Include=\"CPU\\mulMat.cpp\" />\n    <ClCompile Include=\"CPU\\TensorCpu.cpp\" />\n    <ClCompile Include=\"CPU\\MlContextCpu.cpp\" />\n    <ClCompile Include=\"CPU\\BufferAllocator.cpp\" />\n    <ClCompile Include=\"CPU\\HybridLoader.cpp\" />\n    <ClCompile Include=\"CPU\\DecoderTensors.cpp\" />\n    <ClCompile Include=\"Hybrid\\HybridContext.cpp\" />\n    <ClCompile Include=\"CPU\\KvTensorsCpu.cpp\" />\n    <ClCompile Include=\"Hybrid\\KeyValueDownloader.cpp\" />\n    <ClCompile Include=\"CPU\\mulMatImpl.cpp\" />\n    <ClCompile Include=\"CPU\\mulMatImpl.avx2.cpp\" />\n    <ClCompile Include=\"CPU\\mulMatImpl.panel.cpp\" />\n    <ClCompile Include=\"ML\\Reshaper.cpp\" />\n    <ClCompile Include=\"Utils\\DelayExecution.cpp\" />\n    <ClCompile Include=\"Whisper\\ContextImpl.diarize.cpp\" />\n    <ClCompile Include=\"D3D\\listGPUs.cpp\" />\n    <ClCompile Include=\"Utils\\LZ4\\lz4.c\" />\n    <ClCompile Include=\"D3D\\createDevice.cpp\" />\n    <ClCompile Include=\"ML\\DbgNanTest.cpp\" />\n    <ClCompile Include=\"ML\\Device.cpp\" />\n    <ClCompile Include=\"Whisper\\ModelBuffers.clone.cpp\" />\n    <ClCompile Include=\"Utils\\MurmurHash3.cpp\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ClInclude Include=\"source\\ggml.h\" />\n    <ClInclude Include=\"source\\whisper.h\" />\n    <ClInclude Include=\"API\\iContext.cl.h\" />\n    <ClInclude Include=\"API\\sFullParams.h\" />\n    <ClInclude Include=\"D3D\\device.h\" />\n    <ClInclude Include=\"ML\\ConstantBuffer.h\" />\n    <ClInclude Include=\"D3D\\MappedResource.h\" />\n    <ClInclude Include=\"D3D\\shaderNames.h\" />\n    <ClInclude Include=\"D3D\\shaders.h\" />\n    <ClInclude Include=\"D3D\\createBuffer.h\" />\n    <ClInclude Include=\"ML\\TempBuffers.h\" />\n    <ClInclude Include=\"ML\\MlContext.h\" />\n    <ClInclude Include=\"ML\\tensorOpsTests.h\" />\n    <ClInclude Include=\"D3D\\Binder.h\" />\n    <ClInclude Include=\"stdafx.h\" />\n    <ClInclude Include=\"ML\\testUtils.h\" />\n    <ClInclude Include=\"D3D\\downloadBuffer.h\" />\n    <ClInclude Include=\"D3D\\RenderDoc\\renderdoc_app.h\" />\n    <ClInclude Include=\"D3D\\RenderDoc\\renderDoc.h\" />\n    <ClInclude Include=\"Whisper\\MelInputTensor.h\" />\n    <ClInclude Include=\"Whisper\\ModelBuffers.h\" />\n    <ClInclude Include=\"Whisper\\ModelLoader.h\" />\n    <ClInclude Include=\"ML\\TensorShape.h\" />\n    <ClInclude Include=\"ML\\Tensor.h\" />\n    <ClInclude Include=\"ML\\TensorGpuViews.h\" />\n    <ClInclude Include=\"D3D\\enums.h\" />\n    <ClInclude Include=\"ML\\TensorEx.h\" />\n    <ClInclude Include=\"Whisper\\WhisperContext.h\" />\n    <ClInclude Include=\"ML\\LookupTablesData.h\" />\n    <ClInclude Include=\"ML\\LookupTables.h\" />\n    <ClInclude Include=\"Whisper\\sEncodeParams.h\" />\n    <ClInclude Include=\"Whisper\\KeyValueBuffers.h\" />\n    <ClInclude Include=\"Whisper\\DecoderInputBuffers.h\" />\n    <ClInclude Include=\"Whisper\\DecoderResultBuffer.h\" />\n    <ClInclude Include=\"Whisper\\Vocabulary.h\" />\n    <ClInclude Include=\"Whisper\\WhisperModel.h\" />\n    <ClInclude Include=\"Whisper\\loaderUtils.h\" />\n    <ClInclude Include=\"Whisper\\Spectrogram.h\" />\n    <ClInclude Include=\"Utils\\parallelFor.h\" />\n    <ClInclude Include=\"Whisper\\ModelImpl.h\" />\n    <ClInclude Include=\"Whisper\\ContextImpl.h\" />\n    <ClInclude Include=\"Whisper\\Languages.h\" />\n    <ClInclude Include=\"ML\\TensorsArena.h\" />\n    <ClInclude Include=\"Utils\\GpuProfiler.h\" />\n    <ClInclude Include=\"Utils\\GpuProfilerSimple.h\" />\n    <ClInclude Include=\"Utils\\CpuProfiler.h\" />\n    <ClInclude Include=\"Utils\\ProfileCollection.h\" />\n    <ClInclude Include=\"MF\\mfStartup.h\" />\n    <ClInclude Include=\"API\\iMediaFoundation.cl.h\" />\n    <ClInclude Include=\"MF\\loadAudioFile.h\" />\n    <ClInclude Include=\"MF\\mfUtils.h\" />\n    <ClInclude Include=\"API\\TranscribeStructs.h\" />\n    <ClInclude Include=\"API\\iTranscribeResult.cl.h\" />\n    <ClInclude Include=\"Whisper\\TranscribeResult.h\" />\n    <ClInclude Include=\"Utils\\ReadStream.h\" />\n    <ClInclude Include=\"API\\SpecialTokens.h\" />\n    <ClInclude Include=\"Whisper\\sTokenData.h\" />\n    <ClInclude Include=\"resource.h\" />\n    <ClInclude Include=\"source.compat\\convertThings.h\" />\n    <ClInclude Include=\"Utils\\Trace\\TraceWriter.h\" />\n    <ClInclude Include=\"Utils\\Trace\\TraceStructures.h\" />\n    <ClInclude Include=\"Utils\\Trace\\tracing.h\" />\n    <ClInclude Include=\"Utils\\miscUtils.h\" />\n    <ClInclude Include=\"MF\\AudioBuffer.h\" />\n    <ClInclude Include=\"modelFactory.h\" />\n    <ClInclude Include=\"Whisper\\iSpectrogram.h\" />\n    <ClInclude Include=\"MF\\PcmReader.h\" />\n    <ClInclude Include=\"Whisper\\audioConstants.h\" />\n    <ClInclude Include=\"Whisper\\melSpectrogram.h\" />\n    <ClInclude Include=\"Whisper\\MelStreamer.h\" />\n    <ClInclude Include=\"API\\MfStructs.h\" />\n    <ClInclude Include=\"MF\\AudioCapture.h\" />\n    <ClInclude Include=\"API\\loggerApi.h\" />\n    <ClInclude Include=\"Utils\\Logger.h\" />\n    <ClInclude Include=\"Whisper\\voiceActivityDetection.h\" />\n    <ClInclude Include=\"CPU\\LargeBuffer.h\" />\n    <ClInclude Include=\"API\\iContext.h\" />\n    <ClInclude Include=\"API\\iMediaFoundation.h\" />\n    <ClInclude Include=\"API\\iTranscribeResult.h\" />\n    <ClInclude Include=\"API\\whisperComLight.h\" />\n    <ClInclude Include=\"API\\whisperWindows.h\" />\n    <ClInclude Include=\"API\\sLanguageList.h\" />\n    <ClInclude Include=\"CPU\\ParallelForRunner.h\" />\n    <ClInclude Include=\"CPU\\simdUtils.h\" />\n    <ClInclude Include=\"ML\\testUtilsC.h\" />\n    <ClInclude Include=\"CPU\\mulMat.h\" />\n    <ClInclude Include=\"CPU\\Tensor.h\" />\n    <ClInclude Include=\"CPU\\MlContext.h\" />\n    <ClInclude Include=\"CPU\\BufferAllocator.h\" />\n    <ClInclude Include=\"CPU\\DecoderTensors.h\" />\n    <ClInclude Include=\"CPU\\HybridLoader.h\" />\n    <ClInclude Include=\"Whisper\\sModelParams.h\" />\n    <ClInclude Include=\"Hybrid\\HybridContext.h\" />\n    <ClInclude Include=\"CPU\\KvTensors.h\" />\n    <ClInclude Include=\"Hybrid\\KeyValueDownloader.h\" />\n    <ClInclude Include=\"CPU\\mulMatUtils.hpp\" />\n    <ClInclude Include=\"CPU\\mulMatImpl.h\" />\n    <ClInclude Include=\"API\\sLoadModelCallbacks.h\" />\n    <ClInclude Include=\"ML\\Reshaper.h\" />\n    <ClInclude Include=\"ML\\reshapedMultiply.h\" />\n    <ClInclude Include=\"Utils\\DelayExecution.h\" />\n    <ClInclude Include=\"ML\\mlUtils.h\" />\n    <ClInclude Include=\"API\\sModelSetup.h\" />\n    <ClInclude Include=\"D3D\\listGPUs.h\" />\n    <ClInclude Include=\"Utils\\LZ4\\lz4.h\" />\n    <ClInclude Include=\"D3D\\sGpuInfo.h\" />\n    <ClInclude Include=\"D3D\\createDevice.h\" />\n    <ClInclude Include=\"ML\\DbgNanTest.h\" />\n    <ClInclude Include=\"ML\\Device.h\" />\n    <ClInclude Include=\"Utils\\MurmurHash3.h\" />\n  </ItemGroup>\n  <ItemGroup>\n    <None Include=\"whisper.def\" />\n    <None Include=\"D3D\\shaderData-Debug.inl\" />\n    <None Include=\"D3D\\shaderData-Release.inl\" />\n    <None Include=\"Whisper\\languageCodez.inl\" />\n    <None Include=\"Whisper\\languageCodez.tsv\" />\n    <None Include=\"CPU\\mulMat.kernel.hpp\" />\n    <None Include=\"source\\LICENSE\" />\n    <None Include=\"ML\\LookupTablesData.inl\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Natvis Include=\"misc.natvis\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ResourceCompile Include=\"Resource.rc\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Text Include=\"Readme.txt\" />\n    <Text Include=\"API\\Readme.txt\" />\n    <Text Include=\"source.compat\\Readme.txt\" />\n    <Text Include=\"source\\Readme.txt\" />\n    <Text Include=\"Hybrid\\Readme.txt\" />\n    <Text Include=\"CPU\\Readme.txt\" />\n  </ItemGroup>\n</Project>"
  },
  {
    "path": "Whisper/misc.natvis",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<AutoVisualizer xmlns=\"http://schemas.microsoft.com/vstudio/debugger/natvis/2010\">\n\t<!-- This file is only for Visual Studio debugger, it doesn't effect the binary in any way.\n\tMore info: https://learn.microsoft.com/en-us/visualstudio/debugger/create-custom-views-of-native-objects -->\n\n\t<Type Name=\"__m128i\">\n\t\t<DisplayString>[ { m128i_i32[ 0 ] }, { m128i_i32[ 1 ] }, { m128i_i32[ 2 ] }, { m128i_i32[ 3 ] } ]</DisplayString>\n\t</Type>\n\t<Type Name=\"__m128\">\n\t\t<DisplayString>[ { m128_f32[ 0 ] }, { m128_f32[ 1 ] }, { m128_f32[ 2 ] }, { m128_f32[ 3 ] } ]</DisplayString>\n\t</Type>\n\t<Type Name=\"__m256\">\n\t\t<DisplayString>[ { m256_f32[ 0 ] }, { m256_f32[ 1 ] }, { m256_f32[ 2 ] }, { m256_f32[ 3 ] }, { m256_f32[ 4 ] }, { m256_f32[ 5 ] }, { m256_f32[ 6 ] }, { m256_f32[ 7 ] } ]</DisplayString>\n\t</Type>\n\t<Type Name=\"__m256i\">\n\t\t<DisplayString>[ { m256i_i32[ 0 ] }, { m256i_i32[ 1 ] }, { m256i_i32[ 2 ] }, { m256i_i32[ 3 ] }, { m256i_i32[ 4 ] }, { m256i_i32[ 5 ] }, { m256i_i32[ 6 ] }, { m256i_i32[ 7 ] } ]</DisplayString>\n\t</Type>\n\n\t<Type Name=\"std::array&lt;*,4&gt;\">\n\t\t<DisplayString>[ { _Elems[ 0 ] }, { _Elems[ 1 ] }, { _Elems[ 2 ] }, { _Elems[ 3 ] } ]</DisplayString>\n\t</Type>\n\t<Type Name=\"std::array&lt;*,3&gt;\">\n\t\t<DisplayString>[ { _Elems[ 0 ] }, { _Elems[ 1 ] }, { _Elems[ 2 ] } ]</DisplayString>\n\t</Type>\n\t<Type Name=\"std::array&lt;*,2&gt;\">\n\t\t<DisplayString>[ { _Elems[ 0 ] }, { _Elems[ 1 ] } ]</DisplayString>\n\t</Type>\n\t<Type Name=\"DirectCompute::Tensor\">\n\t\t<DisplayString>Size { ne }, strides { nb }</DisplayString>\n\t</Type>\n\t<Type Name=\"CpuCompute::Tensor\">\n\t\t<DisplayString Condition=\"m_type==DirectCompute::eDataType::FP16\">FP16 { ne }, strides { nb }</DisplayString>\n\t\t<DisplayString Condition=\"m_type==DirectCompute::eDataType::FP32\">FP32 { ne }, strides { nb }</DisplayString>\n\t\t<DisplayString Condition=\"m_type==DirectCompute::eDataType::U32\">U32 { ne }, strides { nb }</DisplayString>\n\t\t<Expand>\n\t\t\t<ArrayItems Condition=\"m_type==DirectCompute::eDataType::FP16\">\n\t\t\t\t<Size>ne._Elems[ 0 ] * ne._Elems[ 1 ] * ne._Elems[ 2 ] * ne._Elems[ 3 ]</Size>\n\t\t\t\t<ValuePointer>(uint16_t*)m_data</ValuePointer>\n\t\t\t</ArrayItems>\n\t\t\t<ArrayItems Condition=\"m_type==DirectCompute::eDataType::FP32\">\n\t\t\t\t<Size>ne._Elems[ 0 ] * ne._Elems[ 1 ] * ne._Elems[ 2 ] * ne._Elems[ 3 ]</Size>\n\t\t\t\t<ValuePointer>(float*)m_data</ValuePointer>\n\t\t\t</ArrayItems>\n\t\t\t<ArrayItems Condition=\"m_type==DirectCompute::eDataType::U32\">\n\t\t\t\t<Size>ne._Elems[ 0 ] * ne._Elems[ 1 ] * ne._Elems[ 2 ] * ne._Elems[ 3 ]</Size>\n\t\t\t\t<ValuePointer>(uint32_t*)m_data</ValuePointer>\n\t\t\t</ArrayItems>\n\t\t</Expand>\n\t</Type>\n</AutoVisualizer>"
  },
  {
    "path": "Whisper/modelFactory.cpp",
    "content": "﻿#include \"stdafx.h\"\n#include \"modelFactory.h\"\n#include \"API/iContext.cl.h\"\n\nHRESULT COMLIGHTCALL Whisper::loadModel( const wchar_t* path, const sModelSetup& setup, const sLoadModelCallbacks* callbacks, iModel** pp )\n{\n\tswitch( setup.impl )\n\t{\n\tcase eModelImplementation::GPU:\n\tcase eModelImplementation::Hybrid:\n\t\treturn loadGpuModel( path, setup, callbacks, pp );\n\tcase eModelImplementation::Reference:\n\t\tif( 0 != setup.flags )\n\t\t\tlogWarning( u8\"The reference model doesn’t currently use any flags, argument ignored\" );\n\t\treturn loadReferenceCpuModel( path, pp );\n\t}\n\n\tlogError( u8\"Unknown model implementation 0x%X\", (int)setup.impl );\n\treturn E_INVALIDARG;\n}"
  },
  {
    "path": "Whisper/modelFactory.h",
    "content": "#pragma once\n#include \"API/sLoadModelCallbacks.h\"\n#include \"API/sModelSetup.h\"\n\nnamespace Whisper\n{\n\tstruct iModel;\n\n\tHRESULT __stdcall loadGpuModel( const wchar_t* path, const sModelSetup& setup, const sLoadModelCallbacks* callbacks, iModel** pp );\n\n\tHRESULT __stdcall loadReferenceCpuModel( const wchar_t* path, iModel** pp );\n}"
  },
  {
    "path": "Whisper/resource.h",
    "content": "//{{NO_DEPENDENCIES}}\n// Microsoft Visual C++ generated include file.\n// Used by Resource.rc\n\n// Next default values for new objects\n// \n#ifdef APSTUDIO_INVOKED\n#ifndef APSTUDIO_READONLY_SYMBOLS\n#define _APS_NEXT_RESOURCE_VALUE        101\n#define _APS_NEXT_COMMAND_VALUE         40001\n#define _APS_NEXT_CONTROL_VALUE         1001\n#define _APS_NEXT_SYMED_VALUE           101\n#endif\n#endif\n"
  },
  {
    "path": "Whisper/source/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2022 Georgi Gerganov\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "Whisper/source/Readme.txt",
    "content": "﻿The code in this folder is dropped by the linker’s dead code elimination optimization pass, unless you change BUILD_BOTH_VERSIONS macro in stdafx.h"
  },
  {
    "path": "Whisper/source/ggml.c",
    "content": "#include \"ggml.h\"\n\n#if defined(_MSC_VER) || defined(__MINGW32__)\n#include <malloc.h> // using malloc.h with MSC/MINGW\n#elif !defined(__FreeBSD__)\n#include <alloca.h>\n#endif\n\n#include <assert.h>\n#include <time.h>\n#include <math.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <stdio.h>\n\n// if C99 - static_assert is noop\n// ref: https://stackoverflow.com/a/53923785/4039976\n#ifndef static_assert\n#define static_assert(cond, msg) struct global_scope_noop_trick\n#endif\n\n#if defined _MSC_VER || defined(__MINGW32__)\n\n#if !defined(__MINGW32__)\n#include <Windows.h>\n#else\n// ref: https://github.com/ggerganov/whisper.cpp/issues/168\n#include <windows.h>\n#include <errno.h>\n#endif\n\ntypedef volatile LONG atomic_int;\ntypedef atomic_int atomic_bool;\n\nstatic void atomic_store(atomic_int* ptr, LONG val) {\n    InterlockedExchange(ptr, val);\n}\nstatic LONG atomic_load(atomic_int* ptr) {\n    return InterlockedCompareExchange(ptr, 0, 0);\n}\nstatic LONG atomic_fetch_add(atomic_int* ptr, LONG inc) {\n    return InterlockedExchangeAdd(ptr, inc);\n}\nstatic LONG atomic_fetch_sub(atomic_int* ptr, LONG dec) {\n    return atomic_fetch_add(ptr, -(dec));\n}\n\ntypedef HANDLE pthread_t;\n\ntypedef DWORD thread_ret_t;\nstatic int pthread_create(pthread_t* out, void* unused, thread_ret_t(*func)(void*), void* arg) {\n    HANDLE handle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) func, arg, 0, NULL);\n    if (handle == NULL)\n    {\n        return EAGAIN;\n    }\n\n    *out = handle;\n    return 0;\n}\n\nstatic int pthread_join(pthread_t thread, void* unused) {\n    return (int) WaitForSingleObject(thread, INFINITE);\n}\n\nstatic int sched_yield (void) {\n    Sleep (0);\n    return 0;\n}\n#else\n#include <pthread.h>\n#include <stdatomic.h>\n\ntypedef void* thread_ret_t;\n#endif\n\n#ifdef __HAIKU__\n#define static_assert(cond, msg) _Static_assert(cond, msg)\n#endif\n\n#define GGML_DEBUG 0\n#define GGML_GELU_FP16\n\n#if UINTPTR_MAX == 0xFFFFFFFF\n    #define GGML_MEM_ALIGN 4\n#else\n    #define GGML_MEM_ALIGN 16\n#endif\n\n#define MAX(a, b) ((a) > (b) ? (a) : (b))\n#define MIN(a, b) ((a) < (b) ? (a) : (b))\n\n#define UNUSED(x) (void)(x)\n#define SWAP(x, y, T) do { T SWAP = x; x = y; y = SWAP; } while (0)\n\n#define GGML_ASSERT(x) \\\n    do { \\\n        if (!(x)) { \\\n            logError( u8\"GGML_ASSERT: %s:%d: %s\", __FILE__, __LINE__, #x); \\\n            abort(); \\\n        } \\\n    } while (0)\n\n#ifdef GGML_USE_ACCELERATE\n#include <Accelerate/Accelerate.h>\n#elif GGML_USE_OPENBLAS\n#include <cblas.h>\n#endif\n\n// floating point type used to accumulate sums\ntypedef double ggml_float;\n\n// 16-bit float\n// on Arm, we use __fp16\n// on x86, we use uint16_t\n#ifdef __ARM_NEON\n\n// if YCM cannot find <arm_neon.h>, make a symbolic link to it, for example:\n//\n//   $ ln -sfn /Library/Developer/CommandLineTools/usr/lib/clang/13.1.6/include/arm_neon.h ./src/\n//\n#include <arm_neon.h>\n\nfloat ggml_fp16_to_fp32(ggml_fp16_t x) {\n    return x;\n}\n\nggml_fp16_t ggml_fp32_to_fp16(float x) {\n    return x;\n}\n\n#define GGML_FP16_TO_FP32(x) (x)\n#define GGML_FP32_TO_FP16(x) (x)\n\n#else\n\n#ifdef __wasm_simd128__\n#include <wasm_simd128.h>\n#else\n#ifdef __POWER9_VECTOR__\n#include <altivec.h>\n#undef bool\n#define bool _Bool\n#else\n#include <immintrin.h>\n#endif\n#endif\n\n#ifdef __F16C__\nfloat ggml_fp16_to_fp32(ggml_fp16_t h) {\n    return _cvtsh_ss(h);\n}\nggml_fp16_t ggml_fp32_to_fp16(float f) {\n    return _cvtss_sh(f, 0);\n}\n\n#define GGML_FP16_TO_FP32(x) _cvtsh_ss(x)\n#define GGML_FP32_TO_FP16(x) _cvtss_sh(x, 0)\n\n#else\n\n// FP16 <-> FP32\n// ref: https://github.com/Maratyszcza/FP16\n\nstatic inline float fp32_from_bits(uint32_t w) {\n    union {\n        uint32_t as_bits;\n        float as_value;\n    } fp32;\n    fp32.as_bits = w;\n    return fp32.as_value;\n}\n\nstatic inline uint32_t fp32_to_bits(float f) {\n\tunion {\n\t\tfloat as_value;\n\t\tuint32_t as_bits;\n\t} fp32;\n\tfp32.as_value = f;\n\treturn fp32.as_bits;\n}\n\nfloat ggml_fp16_to_fp32(ggml_fp16_t h) {\n    const uint32_t w = (uint32_t) h << 16;\n    const uint32_t sign = w & UINT32_C(0x80000000);\n    const uint32_t two_w = w + w;\n\n    const uint32_t exp_offset = UINT32_C(0xE0) << 23;\n#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) || defined(__GNUC__) && !defined(__STRICT_ANSI__)\n    const float exp_scale = 0x1.0p-112f;\n#else\n    const float exp_scale = fp32_from_bits(UINT32_C(0x7800000));\n#endif\n    const float normalized_value = fp32_from_bits((two_w >> 4) + exp_offset) * exp_scale;\n\n    const uint32_t magic_mask = UINT32_C(126) << 23;\n    const float magic_bias = 0.5f;\n    const float denormalized_value = fp32_from_bits((two_w >> 17) | magic_mask) - magic_bias;\n\n    const uint32_t denormalized_cutoff = UINT32_C(1) << 27;\n    const uint32_t result = sign |\n        (two_w < denormalized_cutoff ? fp32_to_bits(denormalized_value) : fp32_to_bits(normalized_value));\n    return fp32_from_bits(result);\n}\n\nggml_fp16_t ggml_fp32_to_fp16(float f) {\n#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) || defined(__GNUC__) && !defined(__STRICT_ANSI__)\n    const float scale_to_inf = 0x1.0p+112f;\n    const float scale_to_zero = 0x1.0p-110f;\n#else\n    const float scale_to_inf = fp32_from_bits(UINT32_C(0x77800000));\n    const float scale_to_zero = fp32_from_bits(UINT32_C(0x08800000));\n#endif\n    float base = (fabsf(f) * scale_to_inf) * scale_to_zero;\n\n    const uint32_t w = fp32_to_bits(f);\n    const uint32_t shl1_w = w + w;\n    const uint32_t sign = w & UINT32_C(0x80000000);\n    uint32_t bias = shl1_w & UINT32_C(0xFF000000);\n    if (bias < UINT32_C(0x71000000)) {\n        bias = UINT32_C(0x71000000);\n    }\n\n    base = fp32_from_bits((bias >> 1) + UINT32_C(0x07800000)) + base;\n    const uint32_t bits = fp32_to_bits(base);\n    const uint32_t exp_bits = (bits >> 13) & UINT32_C(0x00007C00);\n    const uint32_t mantissa_bits = bits & UINT32_C(0x00000FFF);\n    const uint32_t nonsign = exp_bits + mantissa_bits;\n    return (sign >> 16) | (shl1_w > UINT32_C(0xFF000000) ? UINT16_C(0x7E00) : nonsign);\n}\n\n#define GGML_FP16_TO_FP32(x) ggml_fp16_to_fp32(x)\n#define GGML_FP32_TO_FP16(x) ggml_fp32_to_fp16(x)\n\n#endif // __F16C__\n\n#endif // __ARM_NEON\n\n//\n// global data\n//\n\n// precomputed gelu table for f16 (128 KB)\nstatic ggml_fp16_t table_gelu_f16[1 << 16];\n\n// precomputed exp table for f16 (128 KB)\nstatic ggml_fp16_t table_exp_f16[1 << 16];\n\n//\n// timing\n//\n\n#if defined(_MSC_VER) || defined(__MINGW32__)\nstatic int64_t timer_freq;\nvoid ggml_time_init(void) {\n    LARGE_INTEGER frequency;\n    QueryPerformanceFrequency(&frequency);\n    timer_freq = frequency.QuadPart;\n}\nint64_t ggml_time_ms(void) {\n    LARGE_INTEGER t;\n    QueryPerformanceCounter(&t);\n    return (t.QuadPart * 1000) / timer_freq;\n}\nint64_t ggml_time_us(void) {\n    LARGE_INTEGER t;\n    QueryPerformanceCounter(&t);\n    return (t.QuadPart * 1000000) / timer_freq;\n}\n#else\nvoid ggml_time_init(void) {}\nint64_t ggml_time_ms(void) {\n    struct timespec ts;\n    clock_gettime(CLOCK_MONOTONIC, &ts);\n    return (int64_t)ts.tv_sec*1000 + (int64_t)ts.tv_nsec/1000000;\n}\n\nint64_t ggml_time_us(void) {\n    struct timespec ts;\n    clock_gettime(CLOCK_MONOTONIC, &ts);\n    return (int64_t)ts.tv_sec*1000000 + (int64_t)ts.tv_nsec/1000;\n}\n#endif\n\nint64_t ggml_cycles(void) {\n    return clock();\n}\n\nint64_t ggml_cycles_per_ms(void) {\n    return CLOCKS_PER_SEC/1000;\n}\n\n#ifdef GGML_PERF\n#define ggml_perf_time_ms()       ggml_time_ms()\n#define ggml_perf_time_us()       ggml_time_us()\n#define ggml_perf_cycles()        ggml_cycles()\n#define ggml_perf_cycles_per_ms() ggml_cycles_per_ms()\n#else\n#define ggml_perf_time_ms()       0\n#define ggml_perf_time_us()       0\n#define ggml_perf_cycles()        0\n#define ggml_perf_cycles_per_ms() 0\n#endif\n\n//\n// cache line\n//\n\n#if defined(__cpp_lib_hardware_interference_size)\n#define CACHE_LINE_SIZE hardware_destructive_interference_size\n#else\n#define CACHE_LINE_SIZE 64\n#endif\n\nstatic const size_t CACHE_LINE_SIZE_F32 = CACHE_LINE_SIZE/sizeof(float);\n\n//\n// simd mappings\n//\n\n// we define a common set of C macros which map to specific intrinsics based on the current architecture\n// we then implement the fundamental computation operations below using only these macros\n// adding support for new architectures requires to define the corresponding SIMD macros\n//\n// GGML_F32_STEP / GGML_F16_STEP\n//   number of elements to process in a single step\n//\n// GGML_F32_EPR / GGML_F16_EPR\n//   number of elements to fit in a single register\n//\n\n#if defined(__ARM_NEON) && defined(__ARM_FEATURE_FMA)\n\n#define GGML_SIMD\n\n// F32 NEON\n\n#define GGML_F32_STEP 16\n#define GGML_F32_EPR  4\n\n#define GGML_F32x4              float32x4_t\n#define GGML_F32x4_ZERO         vdupq_n_f32(0.0f)\n#define GGML_F32x4_SET1(x)      vdupq_n_f32(x)\n#define GGML_F32x4_LOAD         vld1q_f32\n#define GGML_F32x4_STORE        vst1q_f32\n#define GGML_F32x4_FMA(a, b, c) vfmaq_f32(a, b, c)\n#define GGML_F32x4_ADD          vaddq_f32\n#define GGML_F32x4_MUL          vmulq_f32\n#if defined(__ARM_FEATURE_QRDMX)\n    #define GGML_F32x4_REDUCE_ONE(x) vaddvq_f32(x)\n#else\n    #define GGML_F32x4_REDUCE_ONE(x) \\\n    (vgetq_lane_f32(x, 0) +          \\\n     vgetq_lane_f32(x, 1) +          \\\n     vgetq_lane_f32(x, 2) +          \\\n     vgetq_lane_f32(x, 3))\n#endif\n#define GGML_F32x4_REDUCE(res, x)              \\\n{                                              \\\n    for (int i = 0; i < GGML_F32_ARR/2; ++i) { \\\n        x[2*i] = vaddq_f32(x[2*i], x[2*i+1]);  \\\n    }                                          \\\n    for (int i = 0; i < GGML_F32_ARR/4; ++i) { \\\n        x[4*i] = vaddq_f32(x[4*i], x[4*i+2]);  \\\n    }                                          \\\n    for (int i = 0; i < GGML_F32_ARR/8; ++i) { \\\n        x[8*i] = vaddq_f32(x[8*i], x[8*i+4]);  \\\n    }                                          \\\n    res = GGML_F32x4_REDUCE_ONE(x[0]);         \\\n}\n\n#define GGML_F32_VEC        GGML_F32x4\n#define GGML_F32_VEC_ZERO   GGML_F32x4_ZERO\n#define GGML_F32_VEC_SET1   GGML_F32x4_SET1\n#define GGML_F32_VEC_LOAD   GGML_F32x4_LOAD\n#define GGML_F32_VEC_STORE  GGML_F32x4_STORE\n#define GGML_F32_VEC_FMA    GGML_F32x4_FMA\n#define GGML_F32_VEC_ADD    GGML_F32x4_ADD\n#define GGML_F32_VEC_MUL    GGML_F32x4_MUL\n#define GGML_F32_VEC_REDUCE GGML_F32x4_REDUCE\n\n// F16 NEON\n\n#if defined(__ARM_FEATURE_FP16_VECTOR_ARITHMETIC)\n    #define GGML_F16_STEP 32\n    #define GGML_F16_EPR  8\n\n    #define GGML_F16x8              float16x8_t\n    #define GGML_F16x8_ZERO         vdupq_n_f16(0.0f)\n    #define GGML_F16x8_SET1(x)      vdupq_n_f16(x)\n    #define GGML_F16x8_LOAD         vld1q_f16\n    #define GGML_F16x8_STORE        vst1q_f16\n    #define GGML_F16x8_FMA(a, b, c) vfmaq_f16(a, b, c)\n    #define GGML_F16x8_ADD          vaddq_f16\n    #define GGML_F16x8_MUL          vmulq_f16\n    #define GGML_F16x8_REDUCE(res, x)                             \\\n    {                                                             \\\n        for (int i = 0; i < GGML_F16_ARR/2; ++i) {                \\\n            x[2*i] = vaddq_f16(x[2*i], x[2*i+1]);                 \\\n        }                                                         \\\n        for (int i = 0; i < GGML_F16_ARR/4; ++i) {                \\\n            x[4*i] = vaddq_f16(x[4*i], x[4*i+2]);                 \\\n        }                                                         \\\n        for (int i = 0; i < GGML_F16_ARR/8; ++i) {                \\\n            x[8*i] = vaddq_f16(x[8*i], x[8*i+4]);                 \\\n        }                                                         \\\n        const float32x4_t t0 = vcvt_f32_f16(vget_low_f16 (x[0])); \\\n        const float32x4_t t1 = vcvt_f32_f16(vget_high_f16(x[0])); \\\n        res = vaddvq_f32(vaddq_f32(t0, t1));                      \\\n    }\n\n    #define GGML_F16_VEC        GGML_F16x8\n    #define GGML_F16_VEC_ZERO   GGML_F16x8_ZERO\n    #define GGML_F16_VEC_SET1   GGML_F16x8_SET1\n    #define GGML_F16_VEC_LOAD   GGML_F16x8_LOAD\n    #define GGML_F16_VEC_STORE  GGML_F16x8_STORE\n    #define GGML_F16_VEC_FMA    GGML_F16x8_FMA\n    #define GGML_F16_VEC_ADD    GGML_F16x8_ADD\n    #define GGML_F16_VEC_MUL    GGML_F16x8_MUL\n    #define GGML_F16_VEC_REDUCE GGML_F16x8_REDUCE\n#else\n    // if FP16 vector arithmetic is not supported, we use FP32 instead\n    // and take advantage of the vcvt_ functions to convert to/from FP16\n\n    #define GGML_F16_STEP 16\n    #define GGML_F16_EPR  4\n\n    #define GGML_F32Cx4              float32x4_t\n    #define GGML_F32Cx4_ZERO         vdupq_n_f32(0.0f)\n    #define GGML_F32Cx4_SET1(x)      vdupq_n_f32(x)\n    #define GGML_F32Cx4_LOAD(x)      vcvt_f32_f16(vld1_f16(x))\n    #define GGML_F32Cx4_STORE(x, y)  vst1_f16(x, vcvt_f16_f32(y))\n    #define GGML_F32Cx4_FMA(a, b, c) vfmaq_f32(a, b, c)\n    #define GGML_F32Cx4_ADD          vaddq_f32\n    #define GGML_F32Cx4_MUL          vmulq_f32\n    #define GGML_F32Cx4_REDUCE       GGML_F32x4_REDUCE\n\n    #define GGML_F16_VEC        GGML_F32Cx4\n    #define GGML_F16_VEC_ZERO   GGML_F32Cx4_ZERO\n    #define GGML_F16_VEC_SET1   GGML_F32Cx4_SET1\n    #define GGML_F16_VEC_LOAD   GGML_F32Cx4_LOAD\n    #define GGML_F16_VEC_STORE  GGML_F32Cx4_STORE\n    #define GGML_F16_VEC_FMA    GGML_F32Cx4_FMA\n    #define GGML_F16_VEC_ADD    GGML_F32Cx4_ADD\n    #define GGML_F16_VEC_MUL    GGML_F32Cx4_MUL\n    #define GGML_F16_VEC_REDUCE GGML_F32Cx4_REDUCE\n#endif\n\n#elif defined(__AVX__)\n\n#define GGML_SIMD\n\n// F32 AVX\n\n#define GGML_F32_STEP 32\n#define GGML_F32_EPR  8\n\n#define GGML_F32x8         __m256\n#define GGML_F32x8_ZERO    _mm256_setzero_ps()\n#define GGML_F32x8_SET1(x) _mm256_set1_ps(x)\n#define GGML_F32x8_LOAD    _mm256_loadu_ps\n#define GGML_F32x8_STORE   _mm256_storeu_ps\n#if defined(__FMA__)\n    #define GGML_F32x8_FMA(a, b, c) _mm256_fmadd_ps(b, c, a)\n#else\n    #define GGML_F32x8_FMA(a, b, c) _mm256_add_ps(_mm256_mul_ps(b, c), a)\n#endif\n#define GGML_F32x8_ADD     _mm256_add_ps\n#define GGML_F32x8_MUL     _mm256_mul_ps\n#define GGML_F32x8_REDUCE(res, x)                                 \\\n{                                                                 \\\n    for (int i = 0; i < GGML_F32_ARR/2; ++i) {                    \\\n        x[2*i] = _mm256_add_ps(x[2*i], x[2*i+1]);                 \\\n    }                                                             \\\n    for (int i = 0; i < GGML_F32_ARR/4; ++i) {                    \\\n        x[4*i] = _mm256_add_ps(x[4*i], x[4*i+2]);                 \\\n    }                                                             \\\n    for (int i = 0; i < GGML_F32_ARR/8; ++i) {                    \\\n        x[8*i] = _mm256_add_ps(x[8*i], x[8*i+4]);                 \\\n    }                                                             \\\n    const __m128 t0 = _mm_add_ps(_mm256_castps256_ps128(x[0]),    \\\n                                 _mm256_extractf128_ps(x[0], 1)); \\\n    const __m128 t1 = _mm_hadd_ps(t0, t0);                        \\\n    res = _mm_cvtss_f32(_mm_hadd_ps(t1, t1));                     \\\n}\n// TODO: is this optimal ?\n\n#define GGML_F32_VEC        GGML_F32x8\n#define GGML_F32_VEC_ZERO   GGML_F32x8_ZERO\n#define GGML_F32_VEC_SET1   GGML_F32x8_SET1\n#define GGML_F32_VEC_LOAD   GGML_F32x8_LOAD\n#define GGML_F32_VEC_STORE  GGML_F32x8_STORE\n#define GGML_F32_VEC_FMA    GGML_F32x8_FMA\n#define GGML_F32_VEC_ADD    GGML_F32x8_ADD\n#define GGML_F32_VEC_MUL    GGML_F32x8_MUL\n#define GGML_F32_VEC_REDUCE GGML_F32x8_REDUCE\n\n// F16 AVX\n\n#define GGML_F16_STEP 32\n#define GGML_F16_EPR  8\n\n// F16 arithmetic is not supported by AVX, so we use F32 instead\n// we take advantage of the _mm256_cvt intrinsics to convert F16 <-> F32\n\n#define GGML_F32Cx8             __m256\n#define GGML_F32Cx8_ZERO        _mm256_setzero_ps()\n#define GGML_F32Cx8_SET1(x)     _mm256_set1_ps(x)\n#define GGML_F32Cx8_LOAD(x)     _mm256_cvtph_ps(_mm_loadu_si128((__m128i *)(x)))\n#define GGML_F32Cx8_STORE(x, y) _mm_storeu_si128((__m128i *)(x), _mm256_cvtps_ph(y, 0))\n#define GGML_F32Cx8_FMA         GGML_F32x8_FMA\n#define GGML_F32Cx8_ADD         _mm256_add_ps\n#define GGML_F32Cx8_MUL         _mm256_mul_ps\n#define GGML_F32Cx8_REDUCE      GGML_F32x8_REDUCE\n\n#define GGML_F16_VEC        GGML_F32Cx8\n#define GGML_F16_VEC_ZERO   GGML_F32Cx8_ZERO\n#define GGML_F16_VEC_SET1   GGML_F32Cx8_SET1\n#define GGML_F16_VEC_LOAD   GGML_F32Cx8_LOAD\n#define GGML_F16_VEC_STORE  GGML_F32Cx8_STORE\n#define GGML_F16_VEC_FMA    GGML_F32Cx8_FMA\n#define GGML_F16_VEC_ADD    GGML_F32Cx8_ADD\n#define GGML_F16_VEC_MUL    GGML_F32Cx8_MUL\n#define GGML_F16_VEC_REDUCE GGML_F32Cx8_REDUCE\n\n#elif defined(__POWER9_VECTOR__)\n\n// TODO: uncomment this when it works\n//#define GGML_SIMD\n\n// F32 POWER9\n\n#define GGML_F32_STEP 32\n#define GGML_F32_EPR  8\n\n// TODO: not tested !!\n#define GGML_F32x4         __vector float\n#define GGML_F32x4_ZERO    (__vector float){0.0f, 0.0f, 0.0f, 0.0f}\n#define GGML_F32x4_SET1(x) (__vector float){x, x, x, x}\n#define GGML_F32x4_LOAD    vec_vsx_ld\n#define GGML_F32x4_STORE   vec_vsx_st\n#define GGML_F32x4_FMA(a, b, c) vec_madd(b, c, a)\n#define GGML_F32x4_ADD     vec_add\n#define GGML_F32x4_MUL     vec_mul\n#define GGML_F32x4_REDUCE(res, x)              \\\n{                                              \\\n    for (int i = 0; i < GGML_F32_ARR/2; ++i) { \\\n        x[2*i] = vec_add(x[2*i], x[2*i+1]);    \\\n    }                                          \\\n    for (int i = 0; i < GGML_F32_ARR/4; ++i) { \\\n        x[4*i] = vec_add(x[4*i], x[4*i+2]);    \\\n    }                                          \\\n    for (int i = 0; i < GGML_F32_ARR/8; ++i) { \\\n        x[8*i] = vec_add(x[8*i], x[8*i+4]);    \\\n    }                                          \\\n    res = vec_extract(x[0], 0) +               \\\n          vec_extract(x[0], 1) +               \\\n          vec_extract(x[0], 2) +               \\\n          vec_extract(x[0], 3);                \\\n}\n\n#define GGML_F32_VEC        GGML_F32x4\n#define GGML_F32_VEC_ZERO   GGML_F32x4_ZERO\n#define GGML_F32_VEC_SET1   GGML_F32x4_SET1\n#define GGML_F32_VEC_LOAD   GGML_F32x4_LOAD\n#define GGML_F32_VEC_STORE  GGML_F32x4_STORE\n#define GGML_F32_VEC_FMA    GGML_F32x4_FMA\n#define GGML_F32_VEC_ADD    GGML_F32x4_ADD\n#define GGML_F32_VEC_MUL    GGML_F32x4_MUL\n#define GGML_F32_VEC_REDUCE GGML_F32x4_REDUCE\n\n// F16 POWER9\n// TODO: implement here\n// ...\n\n#elif defined(__wasm_simd128__)\n\n#define GGML_SIMD\n\n// F32 WASM\n\n#define GGML_F32_STEP 16\n#define GGML_F32_EPR  4\n\n#define GGML_F32x4              v128_t\n#define GGML_F32x4_ZERO         wasm_f32x4_splat(0.0f)\n#define GGML_F32x4_SET1(x)      wasm_f32x4_splat(x)\n#define GGML_F32x4_LOAD         wasm_v128_load\n#define GGML_F32x4_STORE        wasm_v128_store\n#define GGML_F32x4_FMA(a, b, c) wasm_f32x4_add(wasm_f32x4_mul(b, c), a)\n#define GGML_F32x4_ADD          wasm_f32x4_add\n#define GGML_F32x4_MUL          wasm_f32x4_mul\n#define GGML_F32x4_REDUCE(res, x)                  \\\n{                                                  \\\n    for (int i = 0; i < GGML_F32_ARR/2; ++i) {     \\\n        x[2*i] = wasm_f32x4_add(x[2*i], x[2*i+1]); \\\n    }                                              \\\n    for (int i = 0; i < GGML_F32_ARR/4; ++i) {     \\\n        x[4*i] = wasm_f32x4_add(x[4*i], x[4*i+2]); \\\n    }                                              \\\n    for (int i = 0; i < GGML_F32_ARR/8; ++i) {     \\\n        x[8*i] = wasm_f32x4_add(x[8*i], x[8*i+4]); \\\n    }                                              \\\n    res = wasm_f32x4_extract_lane(x[0], 0) +       \\\n          wasm_f32x4_extract_lane(x[0], 1) +       \\\n          wasm_f32x4_extract_lane(x[0], 2) +       \\\n          wasm_f32x4_extract_lane(x[0], 3);        \\\n}\n\n#define GGML_F32_VEC        GGML_F32x4\n#define GGML_F32_VEC_ZERO   GGML_F32x4_ZERO\n#define GGML_F32_VEC_SET1   GGML_F32x4_SET1\n#define GGML_F32_VEC_LOAD   GGML_F32x4_LOAD\n#define GGML_F32_VEC_STORE  GGML_F32x4_STORE\n#define GGML_F32_VEC_FMA    GGML_F32x4_FMA\n#define GGML_F32_VEC_ADD    GGML_F32x4_ADD\n#define GGML_F32_VEC_MUL    GGML_F32x4_MUL\n#define GGML_F32_VEC_REDUCE GGML_F32x4_REDUCE\n\n// F16 WASM\n\n#define GGML_F16_STEP 16\n#define GGML_F16_EPR  4\n\ninline static v128_t __wasm_f16x4_load(const ggml_fp16_t * p) {\n    float tmp[4];\n\n    tmp[0] = GGML_FP16_TO_FP32(p[0]);\n    tmp[1] = GGML_FP16_TO_FP32(p[1]);\n    tmp[2] = GGML_FP16_TO_FP32(p[2]);\n    tmp[3] = GGML_FP16_TO_FP32(p[3]);\n\n    return wasm_v128_load(tmp);\n}\n\ninline static void __wasm_f16x4_store(ggml_fp16_t * p, v128_t x) {\n    float tmp[4];\n\n    wasm_v128_store(tmp, x);\n\n    p[0] = GGML_FP32_TO_FP16(tmp[0]);\n    p[1] = GGML_FP32_TO_FP16(tmp[1]);\n    p[2] = GGML_FP32_TO_FP16(tmp[2]);\n    p[3] = GGML_FP32_TO_FP16(tmp[3]);\n}\n\n#define GGML_F16x4             v128_t\n#define GGML_F16x4_ZERO        wasm_f32x4_splat(0.0f)\n#define GGML_F16x4_SET1(x)     wasm_f32x4_splat(x)\n#define GGML_F16x4_LOAD(x)     __wasm_f16x4_load(x)\n#define GGML_F16x4_STORE(x, y) __wasm_f16x4_store(x, y)\n#define GGML_F16x4_FMA         GGML_F32x4_FMA\n#define GGML_F16x4_ADD         wasm_f32x4_add\n#define GGML_F16x4_MUL         wasm_f32x4_mul\n#define GGML_F16x4_REDUCE(res, x)                  \\\n{                                                  \\\n    for (int i = 0; i < GGML_F16_ARR/2; ++i) {     \\\n        x[2*i] = wasm_f32x4_add(x[2*i], x[2*i+1]); \\\n    }                                              \\\n    for (int i = 0; i < GGML_F16_ARR/4; ++i) {     \\\n        x[4*i] = wasm_f32x4_add(x[4*i], x[4*i+2]); \\\n    }                                              \\\n    for (int i = 0; i < GGML_F16_ARR/8; ++i) {     \\\n        x[8*i] = wasm_f32x4_add(x[8*i], x[8*i+4]); \\\n    }                                              \\\n    res = wasm_f32x4_extract_lane(x[0], 0) +       \\\n          wasm_f32x4_extract_lane(x[0], 1) +       \\\n          wasm_f32x4_extract_lane(x[0], 2) +       \\\n          wasm_f32x4_extract_lane(x[0], 3);        \\\n}\n\n#define GGML_F16_VEC        GGML_F16x4\n#define GGML_F16_VEC_ZERO   GGML_F16x4_ZERO\n#define GGML_F16_VEC_SET1   GGML_F16x4_SET1\n#define GGML_F16_VEC_LOAD   GGML_F16x4_LOAD\n#define GGML_F16_VEC_STORE  GGML_F16x4_STORE\n#define GGML_F16_VEC_FMA    GGML_F16x4_FMA\n#define GGML_F16_VEC_ADD    GGML_F16x4_ADD\n#define GGML_F16_VEC_MUL    GGML_F16x4_MUL\n#define GGML_F16_VEC_REDUCE GGML_F16x4_REDUCE\n\n#endif\n\n// GGML_F32_ARR / GGML_F16_ARR\n//   number of registers to use per step\n#ifdef GGML_SIMD\n#define GGML_F32_ARR (GGML_F32_STEP/GGML_F32_EPR)\n#define GGML_F16_ARR (GGML_F16_STEP/GGML_F16_EPR)\n#endif\n\n//\n// fundamental operations\n//\n\ninline static void ggml_vec_set_i8(const int n, int8_t * x, const int8_t v) { for (int i = 0; i < n; ++i) x[i] = v; }\n\ninline static void ggml_vec_set_i16(const int n, int16_t * x, const int16_t v) { for (int i = 0; i < n; ++i) x[i] = v; }\n\ninline static void ggml_vec_set_i32(const int n, int32_t * x, const int32_t v) { for (int i = 0; i < n; ++i) x[i] = v; }\n\ninline static void ggml_vec_set_f16(const int n, ggml_fp16_t * x, const int32_t v) { for (int i = 0; i < n; ++i) x[i] = v; }\n\ninline static void ggml_vec_add_f32 (const int n, float * z, const float * x, const float * y) { for (int i = 0; i < n; ++i) z[i]  = x[i] + y[i]; }\ninline static void ggml_vec_acc_f32 (const int n, float * y, const float * x)                  { for (int i = 0; i < n; ++i) y[i] += x[i];        }\ninline static void ggml_vec_acc1_f32(const int n, float * y, const float   v)                  { for (int i = 0; i < n; ++i) y[i] += v;           }\ninline static void ggml_vec_sub_f32 (const int n, float * z, const float * x, const float * y) { for (int i = 0; i < n; ++i) z[i]  = x[i] - y[i]; }\ninline static void ggml_vec_set_f32 (const int n, float * x, const float   v)                  { for (int i = 0; i < n; ++i) x[i]  = v;           }\ninline static void ggml_vec_cpy_f32 (const int n, float * y, const float * x)                  { for (int i = 0; i < n; ++i) y[i]  = x[i];        }\ninline static void ggml_vec_neg_f32 (const int n, float * y, const float * x)                  { for (int i = 0; i < n; ++i) y[i]  = -x[i];       }\ninline static void ggml_vec_mul_f32 (const int n, float * z, const float * x, const float * y) { for (int i = 0; i < n; ++i) z[i]  = x[i]*y[i];   }\ninline static void ggml_vec_div_f32 (const int n, float * z, const float * x, const float * y) { for (int i = 0; i < n; ++i) z[i]  = x[i]/y[i];   }\n\ninline static void ggml_vec_dot_f32(const int n, float * restrict s, const float * restrict x, const float * restrict y) {\n    ggml_float sumf = 0.0;\n\n#ifdef GGML_SIMD\n    const int np = (n & ~(GGML_F32_STEP - 1));\n\n    GGML_F32_VEC sum[GGML_F32_ARR] = { GGML_F32_VEC_ZERO };\n\n    GGML_F32_VEC ax[GGML_F32_ARR];\n    GGML_F32_VEC ay[GGML_F32_ARR];\n\n    for (int i = 0; i < np; i += GGML_F32_STEP) {\n        for (int j = 0; j < GGML_F32_ARR; j++) {\n            ax[j] = GGML_F32_VEC_LOAD(x + i + j*GGML_F32_EPR);\n            ay[j] = GGML_F32_VEC_LOAD(y + i + j*GGML_F32_EPR);\n\n            sum[j] = GGML_F32_VEC_FMA(sum[j], ax[j], ay[j]);\n        }\n    }\n\n    // reduce sum0..sum3 to sum0\n    GGML_F32_VEC_REDUCE(sumf, sum);\n\n    // leftovers\n    for (int i = np; i < n; ++i) {\n        sumf += x[i]*y[i];\n    }\n#else\n    // scalar\n    for (int i = 0; i < n; ++i) {\n        sumf += x[i]*y[i];\n    }\n#endif\n\n    *s = sumf;\n}\n\ninline static void ggml_vec_dot_f16(const int n, float * restrict s, ggml_fp16_t * restrict x, ggml_fp16_t * restrict y) {\n    ggml_float sumf = 0.0;\n\n#if defined(GGML_SIMD)\n    const int np = (n & ~(GGML_F16_STEP - 1));\n\n    GGML_F16_VEC sum[GGML_F16_ARR] = { GGML_F16_VEC_ZERO };\n\n    GGML_F16_VEC ax[GGML_F16_ARR];\n    GGML_F16_VEC ay[GGML_F16_ARR];\n\n    for (int i = 0; i < np; i += GGML_F16_STEP) {\n        for (int j = 0; j < GGML_F16_ARR; j++) {\n            ax[j] = GGML_F16_VEC_LOAD(x + i + j*GGML_F16_EPR);\n            ay[j] = GGML_F16_VEC_LOAD(y + i + j*GGML_F16_EPR);\n\n            sum[j] = GGML_F16_VEC_FMA(sum[j], ax[j], ay[j]);\n        }\n    }\n\n    // reduce sum0..sum3 to sum0\n    GGML_F16_VEC_REDUCE(sumf, sum);\n\n    // leftovers\n    for (int i = np; i < n; ++i) {\n        sumf += GGML_FP16_TO_FP32(x[i])*GGML_FP16_TO_FP32(y[i]);\n    }\n#elif defined(__POWER9_VECTOR__)\n    // TODO: this is temporary because I cannot fit it in the GGML_SIMD pattern like all other architectures without\n    //       being able to test it. hoping someone with access to a POWER9 machine can help out here.\n    const int n32 = (n & ~31);\n\n    vector float sum0 = vec_splats (0.0f);\n\n    for (int i = 0; i < n32; i += 32) {\n        // Use vec_xl, not vec_ld, because x is sometimes unaligned.\n        vector unsigned short x0 = vec_xl(i * 2 +  0, x);\n        vector unsigned short x1 = vec_xl(i * 2 + 16, x);\n        vector unsigned short x2 = vec_xl(i * 2 + 32, x);\n        vector unsigned short x3 = vec_xl(i * 2 + 48, x);\n\n        vector unsigned short y0 = vec_xl(i * 2 +  0, y);\n        vector unsigned short y1 = vec_xl(i * 2 + 16, y);\n        vector unsigned short y2 = vec_xl(i * 2 + 32, y);\n        vector unsigned short y3 = vec_xl(i * 2 + 48, y);\n\n        vector float fx0l = vec_extract_fp32_from_shortl(x0);\n        vector float fx0h = vec_extract_fp32_from_shorth(x0);\n        vector float fx1l = vec_extract_fp32_from_shortl(x1);\n        vector float fx1h = vec_extract_fp32_from_shorth(x1);\n        vector float fx2l = vec_extract_fp32_from_shortl(x2);\n        vector float fx2h = vec_extract_fp32_from_shorth(x2);\n        vector float fx3l = vec_extract_fp32_from_shortl(x3);\n        vector float fx3h = vec_extract_fp32_from_shorth(x3);\n\n        vector float fy0l = vec_extract_fp32_from_shortl(y0);\n        vector float fy0h = vec_extract_fp32_from_shorth(y0);\n        vector float fy1l = vec_extract_fp32_from_shortl(y1);\n        vector float fy1h = vec_extract_fp32_from_shorth(y1);\n        vector float fy2l = vec_extract_fp32_from_shortl(y2);\n        vector float fy2h = vec_extract_fp32_from_shorth(y2);\n        vector float fy3l = vec_extract_fp32_from_shortl(y3);\n        vector float fy3h = vec_extract_fp32_from_shorth(y3);\n\n        sum0 = vec_add(sum0, vec_mul(fx0l, fy0l));\n        sum0 = vec_add(sum0, vec_mul(fx0h, fy0h));\n        sum0 = vec_add(sum0, vec_mul(fx1l, fy1l));\n        sum0 = vec_add(sum0, vec_mul(fx1h, fy1h));\n        sum0 = vec_add(sum0, vec_mul(fx2l, fy2l));\n        sum0 = vec_add(sum0, vec_mul(fx2h, fy2h));\n        sum0 = vec_add(sum0, vec_mul(fx3l, fy3l));\n        sum0 = vec_add(sum0, vec_mul(fx3h, fy3h));\n    }\n\n    sumf = vec_extract(sum0, 0) + vec_extract(sum0, 1)\n         + vec_extract(sum0, 2) + vec_extract(sum0, 3);\n\n    for (int i = n32; i < n; ++i) {\n        sumf += GGML_FP16_TO_FP32(x[i])*GGML_FP16_TO_FP32(y[i]);\n    }\n#else\n    for (int i = 0; i < n; ++i) {\n        sumf += GGML_FP16_TO_FP32(x[i])*GGML_FP16_TO_FP32(y[i]);\n    }\n#endif\n\n    *s = sumf;\n}\n\ninline static void ggml_vec_mad_f32(const int n, float * restrict y, const float * restrict x, const float v) {\n#if defined(GGML_SIMD)\n    const int np = (n & ~(GGML_F32_STEP - 1));\n\n    GGML_F32_VEC vx = GGML_F32_VEC_SET1(v);\n\n    GGML_F32_VEC ax[GGML_F32_ARR];\n    GGML_F32_VEC ay[GGML_F32_ARR];\n\n    for (int i = 0; i < np; i += GGML_F32_STEP) {\n        for (int j = 0; j < GGML_F32_ARR; j++) {\n            ax[j] = GGML_F32_VEC_LOAD(x + i + j*GGML_F32_EPR);\n            ay[j] = GGML_F32_VEC_LOAD(y + i + j*GGML_F32_EPR);\n            ay[j] = GGML_F32_VEC_FMA(ay[j], ax[j], vx);\n\n            GGML_F32_VEC_STORE(y + i + j*GGML_F32_EPR, ay[j]);\n        }\n    }\n\n    // leftovers\n    for (int i = np; i < n; ++i) {\n        y[i] += x[i]*v;\n    }\n#else\n    // scalar\n    for (int i = 0; i < n; ++i) {\n        y[i] += x[i]*v;\n    }\n#endif\n}\n\ninline static void ggml_vec_mad_f16(const int n, ggml_fp16_t * restrict y, ggml_fp16_t * restrict x, const float v) {\n#if defined(GGML_SIMD)\n    const int np = (n & ~(GGML_F16_STEP - 1));\n\n    GGML_F16_VEC vx = GGML_F16_VEC_SET1(v);\n\n    GGML_F16_VEC ax[GGML_F16_ARR];\n    GGML_F16_VEC ay[GGML_F16_ARR];\n\n    for (int i = 0; i < np; i += GGML_F16_STEP) {\n        for (int j = 0; j < GGML_F16_ARR; j++) {\n            ax[j] = GGML_F16_VEC_LOAD(x + i + j*GGML_F16_EPR);\n            ay[j] = GGML_F16_VEC_LOAD(y + i + j*GGML_F16_EPR);\n            ay[j] = GGML_F16_VEC_FMA(ay[j], ax[j], vx);\n\n            GGML_F16_VEC_STORE(y + i + j*GGML_F16_EPR, ay[j]);\n        }\n    }\n\n    // leftovers\n    for (int i = np; i < n; ++i) {\n        GGML_ASSERT(false);\n        y[i] = GGML_FP32_TO_FP16(GGML_FP16_TO_FP32(y[i]) + GGML_FP16_TO_FP32(x[i])*v);\n    }\n#elif defined(__POWER9_VECTOR__)\n    // TODO: this is temporary because I cannot fit it in the GGML_SIMD pattern like all other architectures without\n    //       being able to test it. hoping someone with access to a POWER9 machine can help out here.\n    const int n32 = (n & ~31);\n    for (int i = 0; i < n32; i += 32) {\n        // Use vec_xl, not vec_ld, because x is sometimes unaligned!\n        vector unsigned short x0 = vec_xl(i * 2 +  0, x);\n        vector unsigned short x1 = vec_xl(i * 2 + 16, x);\n        vector unsigned short x2 = vec_xl(i * 2 + 32, x);\n        vector unsigned short x3 = vec_xl(i * 2 + 48, x);\n\n        vector unsigned short y0 = vec_xl(i * 2 +  0, y);\n        vector unsigned short y1 = vec_xl(i * 2 + 16, y);\n        vector unsigned short y2 = vec_xl(i * 2 + 32, y);\n        vector unsigned short y3 = vec_xl(i * 2 + 48, y);\n\n        vector float v4 = vec_splats(v);\n\n        vector float fx0l = vec_extract_fp32_from_shortl(x0);\n        vector float fx0h = vec_extract_fp32_from_shorth(x0);\n        vector float fx1l = vec_extract_fp32_from_shortl(x1);\n        vector float fx1h = vec_extract_fp32_from_shorth(x1);\n        vector float fx2l = vec_extract_fp32_from_shortl(x2);\n        vector float fx2h = vec_extract_fp32_from_shorth(x2);\n        vector float fx3l = vec_extract_fp32_from_shortl(x3);\n        vector float fx3h = vec_extract_fp32_from_shorth(x3);\n\n        vector float fy0l = vec_extract_fp32_from_shortl(y0);\n        vector float fy0h = vec_extract_fp32_from_shorth(y0);\n        vector float fy1l = vec_extract_fp32_from_shortl(y1);\n        vector float fy1h = vec_extract_fp32_from_shorth(y1);\n        vector float fy2l = vec_extract_fp32_from_shortl(y2);\n        vector float fy2h = vec_extract_fp32_from_shorth(y2);\n        vector float fy3l = vec_extract_fp32_from_shortl(y3);\n        vector float fy3h = vec_extract_fp32_from_shorth(y3);\n\n        fy0l = vec_madd(fx0l, v4, fy0l);\n        fy0h = vec_madd(fx0h, v4, fy0h);\n        fy1l = vec_madd(fx1l, v4, fy1l);\n        fy1h = vec_madd(fx1h, v4, fy1h);\n        fy2l = vec_madd(fx2l, v4, fy2l);\n        fy2h = vec_madd(fx2h, v4, fy2h);\n        fy3l = vec_madd(fx3l, v4, fy3l);\n        fy3h = vec_madd(fx3h, v4, fy3h);\n\n        y0 = vec_pack_to_short_fp32(fy0h, fy0l);\n        y1 = vec_pack_to_short_fp32(fy1h, fy1l);\n        y2 = vec_pack_to_short_fp32(fy2h, fy2l);\n        y3 = vec_pack_to_short_fp32(fy3h, fy3l);\n\n        vec_xst(y0, i * 2 +  0, y);\n        vec_xst(y1, i * 2 + 16, y);\n        vec_xst(y2, i * 2 + 32, y);\n        vec_xst(y3, i * 2 + 48, y);\n    }\n\n    for (int i = n32; i < n; ++i) {\n        y[i] = GGML_FP32_TO_FP16(GGML_FP16_TO_FP32(y[i]) + GGML_FP16_TO_FP32(x[i])*v);\n    }\n#else\n    for (int i = 0; i < n; ++i) {\n        y[i] = GGML_FP32_TO_FP16(GGML_FP16_TO_FP32(y[i]) + GGML_FP16_TO_FP32(x[i])*v);\n    }\n#endif\n}\n\n//inline static void ggml_vec_scale_f32(const int n, float * y, const float   v) { for (int i = 0; i < n; ++i) y[i] *= v;          }\ninline static void ggml_vec_scale_f32(const int n, float * y, const float   v) {\n#if defined(GGML_SIMD)\n    const int np = (n & ~(GGML_F32_STEP - 1));\n\n    GGML_F32_VEC vx = GGML_F32_VEC_SET1(v);\n\n    GGML_F32_VEC ay[GGML_F32_ARR];\n\n    for (int i = 0; i < np; i += GGML_F32_STEP) {\n        for (int j = 0; j < GGML_F32_ARR; j++) {\n            ay[j] = GGML_F32_VEC_LOAD(y + i + j*GGML_F32_EPR);\n            ay[j] = GGML_F32_VEC_MUL(ay[j], vx);\n\n            GGML_F32_VEC_STORE(y + i + j*GGML_F32_EPR, ay[j]);\n        }\n    }\n\n    // leftovers\n    for (int i = np; i < n; ++i) {\n        y[i] *= v;\n    }\n#else\n    // scalar\n    for (int i = 0; i < n; ++i) {\n        y[i] *= v;\n    }\n#endif\n}\n\ninline static void ggml_vec_norm_f32 (const int n, float * s, const float * x) { ggml_vec_dot_f32(n, s, x, x); *s = sqrt(*s);   }\ninline static void ggml_vec_sqr_f32  (const int n, float * y, const float * x) { for (int i = 0; i < n; ++i) y[i] = x[i]*x[i];   }\ninline static void ggml_vec_sqrt_f32 (const int n, float * y, const float * x) { for (int i = 0; i < n; ++i) y[i] = sqrt(x[i]); }\ninline static void ggml_vec_abs_f32  (const int n, float * y, const float * x) { for (int i = 0; i < n; ++i) y[i] = fabsf(x[i]); }\ninline static void ggml_vec_sgn_f32  (const int n, float * y, const float * x) { for (int i = 0; i < n; ++i) y[i] = (x[i] > 0.f) ? 1.f : ((x[i] < 0.f) ? -1.f : 0.f); }\ninline static void ggml_vec_step_f32 (const int n, float * y, const float * x) { for (int i = 0; i < n; ++i) y[i] = (x[i] > 0.f) ? 1.f : 0.f; }\ninline static void ggml_vec_relu_f32 (const int n, float * y, const float * x) { for (int i = 0; i < n; ++i) y[i] = (x[i] > 0.f) ? x[i] : 0.f; }\n\nstatic const ggml_float GELU_COEF_A    = 0.044715;\nstatic const ggml_float SQRT_2_OVER_PI = 0.79788456080286535587989211986876;\n\ninline static float ggml_gelu_f32(float x) {\n    return 0.5*x*(1.0 + tanh(SQRT_2_OVER_PI*x*(1.0 + GELU_COEF_A*x*x)));\n}\n\ninline static void ggml_vec_gelu_f16(const int n, ggml_fp16_t * y, const ggml_fp16_t * x) {\n    const uint16_t * i16 = (const uint16_t *) x;\n    for (int i = 0; i < n; ++i) {\n        y[i] = table_gelu_f16[i16[i]];\n    }\n}\n\n#ifdef GGML_GELU_FP16\ninline static void ggml_vec_gelu_f32(const int n, float * y, const float * x) {\n    uint16_t t;\n    for (int i = 0; i < n; ++i) {\n        ggml_fp16_t fp16 = GGML_FP32_TO_FP16(x[i]);\n        memcpy(&t, &fp16, sizeof(uint16_t));\n        y[i] = GGML_FP16_TO_FP32(table_gelu_f16[t]);\n    }\n}\n#else\ninline static void ggml_vec_gelu_f32(const int n, float * y, const float * x) {\n    for (int i = 0; i < n; ++i) {\n        y[i] = ggml_gelu_f32(x[i]);\n    }\n}\n#endif\n\ninline static void ggml_vec_sum_f32     (const int n, float * s, const float * x) { ggml_float sum = 0.0; for (int i = 0; i < n; ++i) sum += x[i]; *s += sum; }\ninline static void ggml_vec_norm_inv_f32(const int n, float * s, const float * x) { ggml_vec_norm_f32(n, s, x); *s = 1./(*s); }\n\n//\n// logging\n//\n\n#if (GGML_DEBUG >= 1)\n#define GGML_PRINT_DEBUG(...) printf(__VA_ARGS__)\n#else\n#define GGML_PRINT_DEBUG(...)\n#endif\n\n#if (GGML_DEBUG >= 5)\n#define GGML_PRINT_DEBUG_5(...) printf(__VA_ARGS__)\n#else\n#define GGML_PRINT_DEBUG_5(...)\n#endif\n\n#if (GGML_DEBUG >= 10)\n#define GGML_PRINT_DEBUG_10(...) printf(__VA_ARGS__)\n#else\n#define GGML_PRINT_DEBUG_10(...)\n#endif\n\n#define GGML_PRINT(...) logDebug( __VA_ARGS__ )\n\n//\n// data types\n//\n\nstatic const size_t GGML_TYPE_SIZE[GGML_TYPE_COUNT] = {\n    sizeof(int8_t ),\n    sizeof(int16_t),\n    sizeof(int32_t),\n    sizeof(ggml_fp16_t),\n    sizeof(float  ),\n};\n\nstatic const char * GGML_OP_LABEL[GGML_OP_COUNT] = {\n    \"NONE\",\n\n    \"DUP\",\n    \"ADD\",\n    \"SUB\",\n    \"MUL\",\n    \"DIV\",\n    \"SQR\",\n    \"SQRT\",\n    \"SUM\",\n    \"MEAN\",\n    \"REPEAT\",\n    \"ABS\",\n    \"SGN\",\n    \"NEG\",\n    \"STEP\",\n    \"RELU\",\n    \"GELU\",\n    \"NORM\",\n\n    \"MUL_MAT\",\n\n    \"SCALE\",\n    \"CPY\",\n    \"RESHAPE\",\n    \"VIEW\",\n    \"PERMUTE\",\n    \"TRANSPOSE\",\n    \"GET_ROWS\",\n    \"DIAG_MASK_INF\",\n    \"SOFT_MAX\",\n    \"ROPE\",\n    \"CONV_1D_1S\",\n    \"CONV_1D_2S\",\n\n    \"FLASH_ATTN\",\n    \"FLASH_FF\",\n};\n\nstatic const char * GGML_OP_SYMBOL[GGML_OP_COUNT] = {\n    \"none\",\n\n    \"x\",\n    \"x+y\",\n    \"x-y\",\n    \"x*y\",\n    \"x/y\",\n    \"x^2\",\n    \"√x\",\n    \"Σx\",\n    \"Σx/n\",\n    \"repeat(x)\",\n    \"abs(x)\",\n    \"sgn(x)\",\n    \"-x\",\n    \"step(x)\",\n    \"relu(x)\",\n    \"gelu(x)\",\n    \"norm(x)\",\n\n    \"X*Y\",\n\n    \"x*v\",\n    \"x-\\\\>y\",\n    \"reshape(x)\",\n    \"view(x)\",\n    \"permute(x)\",\n    \"transpose(x)\",\n    \"get_rows(x)\",\n    \"diag_mask_inf(x)\",\n    \"soft_max(x)\",\n    \"rope(x)\",\n    \"conv_1d_1s(x)\",\n    \"conv_1d_2s(x)\",\n\n    \"flash_attn(x)\",\n    \"flash_ff(x)\",\n};\n\n//\n// ggml object\n//\n\nstruct ggml_object {\n    size_t offset;\n    size_t size;\n\n    struct ggml_object * next;\n\n    char padding[8];\n};\n\nstatic const size_t GGML_OBJECT_SIZE = sizeof(struct ggml_object);\n\nstatic_assert(sizeof(struct ggml_object)%GGML_MEM_ALIGN == 0, \"ggml_object size must be a multiple of GGML_MEM_ALIGN\");\nstatic_assert(sizeof(struct ggml_tensor)%GGML_MEM_ALIGN == 0, \"ggml_tensor size must be a multiple of GGML_MEM_ALIGN\");\n\n//\n// ggml context\n//\n\nstruct ggml_context {\n    size_t mem_size;\n    void * mem_buffer;\n    bool   mem_buffer_owned;\n\n    int n_objects;\n\n    struct ggml_object * objects_begin;\n    struct ggml_object * objects_end;\n};\n\nstruct ggml_context_container {\n    bool used;\n\n    struct ggml_context context;\n};\n\n//\n// compute types\n//\n\nenum ggml_task_type {\n    GGML_TASK_INIT = 0,\n    GGML_TASK_COMPUTE,\n    GGML_TASK_FINALIZE,\n};\n\nstruct ggml_compute_params {\n    enum ggml_task_type type;\n\n    int ith, nth;\n\n    // work buffer for all threads\n    size_t wsize;\n    void * wdata;\n};\n\n//\n// ggml state\n//\n\nstruct ggml_state {\n    struct ggml_context_container contexts[GGML_MAX_CONTEXTS];\n};\n\n// global state\nstatic struct ggml_state g_state;\nstatic atomic_int g_state_barrier = 0;\n\n// barrier via spin lock\ninline static void ggml_critical_section_start() {\n    int processing = atomic_fetch_add(&g_state_barrier, 1);\n\n    while (processing > 0) {\n        // wait for other threads to finish\n        atomic_fetch_sub(&g_state_barrier, 1);\n        sched_yield(); // TODO: reconsider this\n        processing = atomic_fetch_add(&g_state_barrier, 1);\n    }\n}\n\n// TODO: make this somehow automatically executed\n//       some sort of \"sentry\" mechanism\ninline static void ggml_critical_section_end() {\n    atomic_fetch_sub(&g_state_barrier, 1);\n}\n\n////////////////////////////////////////////////////////////////////////////////\n\nvoid ggml_print_object(const struct ggml_object * obj) {\n    GGML_PRINT(\" - ggml_object: offset = %zu, size = %zu, next = %p\\n\",\n            obj->offset, obj->size, (const void *) obj->next);\n}\n\nvoid ggml_print_objects(const struct ggml_context * ctx) {\n    struct ggml_object * obj = ctx->objects_begin;\n\n    GGML_PRINT(\"%s: objects in context %p:\\n\", __func__, (const void *) ctx);\n\n    while (obj != NULL) {\n        ggml_print_object(obj);\n        obj = obj->next;\n    }\n\n    GGML_PRINT(\"%s: --- end ---\\n\", __func__);\n}\n\nint ggml_nelements(const struct ggml_tensor * tensor) {\n    static_assert(GGML_MAX_DIMS == 4, \"GGML_MAX_DIMS is not 4 - update this function\");\n\n    return tensor->ne[0]*tensor->ne[1]*tensor->ne[2]*tensor->ne[3];\n}\n\nint ggml_nrows(const struct ggml_tensor * tensor) {\n    static_assert(GGML_MAX_DIMS == 4, \"GGML_MAX_DIMS is not 4 - update this function\");\n\n    return tensor->ne[1]*tensor->ne[2]*tensor->ne[3];\n}\n\nsize_t ggml_nbytes(const struct ggml_tensor * tensor) {\n    static_assert(GGML_MAX_DIMS == 4, \"GGML_MAX_DIMS is not 4 - update this function\");\n\n    return ggml_nelements(tensor)*GGML_TYPE_SIZE[tensor->type];\n}\n\nsize_t ggml_type_size(enum ggml_type type) {\n    return GGML_TYPE_SIZE[type];\n}\n\nsize_t ggml_element_size(const struct ggml_tensor * tensor) {\n    return GGML_TYPE_SIZE[tensor->type];\n}\n\nbool ggml_is_scalar(const struct ggml_tensor * tensor) {\n    static_assert(GGML_MAX_DIMS == 4, \"GGML_MAX_DIMS is not 4 - update this function\");\n\n    return tensor->ne[0] == 1 && tensor->ne[1] == 1 && tensor->ne[2] == 1 && tensor->ne[3] == 1;\n}\n\nbool ggml_is_vector(const struct ggml_tensor * tensor) {\n    static_assert(GGML_MAX_DIMS == 4, \"GGML_MAX_DIMS is not 4 - update this function\");\n\n    return tensor->ne[1] == 1 && tensor->ne[2] == 1 && tensor->ne[3] == 1;\n}\n\nbool ggml_is_matrix(const struct ggml_tensor * tensor) {\n    static_assert(GGML_MAX_DIMS == 4, \"GGML_MAX_DIMS is not 4 - update this function\");\n\n    return tensor->ne[2] == 1 && tensor->ne[3] == 1;\n}\n\nbool ggml_can_mul_mat(const struct ggml_tensor * t0, const struct ggml_tensor * t1) {\n    static_assert(GGML_MAX_DIMS == 4, \"GGML_MAX_DIMS is not 4 - update this function\");\n\n    return\n        (t0->ne[0]  == t1->ne[0])  &&\n        (t0->ne[2]  == t1->ne[2])  &&\n        (t0->ne[3]  == t1->ne[3]);\n}\n\nbool ggml_is_contiguous(const struct ggml_tensor * tensor) {\n    static_assert(GGML_MAX_DIMS == 4, \"GGML_MAX_DIMS is not 4 - update this function\");\n\n    return\n        tensor->nb[0] == GGML_TYPE_SIZE[tensor->type] &&\n        tensor->nb[1] == tensor->nb[0]*tensor->ne[0] &&\n        tensor->nb[2] == tensor->nb[1]*tensor->ne[1] &&\n        tensor->nb[3] == tensor->nb[2]*tensor->ne[2];\n}\n\nbool ggml_is_padded_1d(const struct ggml_tensor * tensor) {\n    static_assert(GGML_MAX_DIMS == 4, \"GGML_MAX_DIMS is not 4 - update this function\");\n\n    return\n        tensor->nb[0] == GGML_TYPE_SIZE[tensor->type] &&\n        tensor->nb[2] == tensor->nb[1]*tensor->ne[1] &&\n        tensor->nb[3] == tensor->nb[2]*tensor->ne[2];\n}\n\nbool ggml_are_same_shape(const struct ggml_tensor * t0, const struct ggml_tensor * t1) {\n    static_assert(GGML_MAX_DIMS == 4, \"GGML_MAX_DIMS is not 4 - update this function\");\n\n    return\n        (t0->ne[0] == t1->ne[0] ) &&\n        (t0->ne[1] == t1->ne[1] ) &&\n        (t0->ne[2] == t1->ne[2] ) &&\n        (t0->ne[3] == t1->ne[3] );\n}\n\n// check if t1 can be represented as a repeatition of t0\nbool ggml_can_repeat(const struct ggml_tensor * t0, const struct ggml_tensor * t1) {\n    static_assert(GGML_MAX_DIMS == 4, \"GGML_MAX_DIMS is not 4 - update this function\");\n\n    return\n        (t1->ne[0]%t0->ne[0] == 0) &&\n        (t1->ne[1]%t0->ne[1] == 0) &&\n        (t1->ne[2]%t0->ne[2] == 0) &&\n        (t1->ne[3]%t0->ne[3] == 0);\n}\n\nint ggml_up32(int n) {\n    return (n + 31) & ~31;\n}\n\nint ggml_up64(int n) {\n    return (n + 63) & ~63;\n}\n\n// assert that pointer is aligned to GGML_MEM_ALIGN\n#define ggml_assert_aligned(ptr) \\\n    assert(((uintptr_t) (ptr))%GGML_MEM_ALIGN == 0)\n\n////////////////////////////////////////////////////////////////////////////////\n\nstruct ggml_context * ggml_init(struct ggml_init_params params) {\n    // make this function thread safe\n    ggml_critical_section_start();\n\n    static bool is_first_call = true;\n\n    if (is_first_call) {\n        // initialize GELU and EXP tables\n        {\n            const uint64_t t_start = ggml_time_us(); UNUSED(t_start);\n\n            ggml_fp16_t ii;\n            for (int i = 0; i < (1 << 16); ++i) {\n                uint16_t ui = i;\n                memcpy(&ii, &ui, sizeof(ii));\n                const float f = GGML_FP16_TO_FP32(ii);\n                table_gelu_f16[i] = GGML_FP32_TO_FP16(ggml_gelu_f32(f));\n                table_exp_f16[i]  = GGML_FP32_TO_FP16(exp(f));\n            }\n\n            const uint64_t t_end = ggml_time_us(); UNUSED(t_end);\n\n            GGML_PRINT_DEBUG(\"%s: GELU and EXP tables initialized in %f ms\\n\", __func__, (t_end - t_start)/1000.0f);\n        }\n\n        // initialize g_state\n        {\n            const uint64_t t_start = ggml_time_us(); UNUSED(t_start);\n\n            g_state = (struct ggml_state) {\n                /*.contexts =*/ { 0 },\n            };\n\n            for (int i = 0; i < GGML_MAX_CONTEXTS; ++i) {\n                g_state.contexts[i].used = false;\n            }\n\n            const uint64_t t_end = ggml_time_us(); UNUSED(t_end);\n\n            GGML_PRINT_DEBUG(\"%s: g_state initialized in %f ms\\n\", __func__, (t_end - t_start)/1000.0f);\n        }\n\n        is_first_call = false;\n    }\n\n    // find non-used context in g_state\n    struct ggml_context * ctx = NULL;\n\n    for (int i = 0; i < GGML_MAX_CONTEXTS; i++) {\n        if (!g_state.contexts[i].used) {\n            g_state.contexts[i].used = true;\n            ctx = &g_state.contexts[i].context;\n\n            GGML_PRINT_DEBUG(\"%s: found unused context %d\\n\", __func__, i);\n            break;\n        }\n    }\n\n    if (ctx == NULL) {\n        GGML_PRINT_DEBUG(\"%s: no unused context found\\n\", __func__);\n\n        ggml_critical_section_end();\n\n        return NULL;\n    }\n\n    *ctx = (struct ggml_context) {\n        .mem_size         = params.mem_size,\n        .mem_buffer       = params.mem_buffer ? params.mem_buffer : malloc(params.mem_size),\n        .mem_buffer_owned = params.mem_buffer ? false : true,\n        .n_objects        = 0,\n        .objects_begin    = NULL,\n        .objects_end      = NULL,\n    };\n\n    ggml_assert_aligned(ctx->mem_buffer);\n\n    GGML_PRINT_DEBUG(\"%s: context initialized\\n\", __func__);\n\n    ggml_critical_section_end();\n\n    return ctx;\n}\n\nvoid ggml_free(struct ggml_context * ctx) {\n    // make this function thread safe\n    ggml_critical_section_start();\n\n    bool found = false;\n\n    for (int i = 0; i < GGML_MAX_CONTEXTS; i++) {\n        if (&g_state.contexts[i].context == ctx) {\n            g_state.contexts[i].used = false;\n\n            GGML_PRINT_DEBUG(\"%s: context %d with %d objects has been freed. memory used = %zu\\n\",\n                    __func__, i, ctx->n_objects, ctx->objects_end->offset + ctx->objects_end->size);\n\n            if (ctx->mem_buffer_owned) {\n                free(ctx->mem_buffer);\n            }\n\n            found = true;\n            break;\n        }\n    }\n\n    if (!found) {\n        GGML_PRINT_DEBUG(\"%s: context not found\\n\", __func__);\n    }\n\n    ggml_critical_section_end();\n}\n\nsize_t ggml_used_mem(const struct ggml_context * ctx) {\n    return ctx->objects_end->offset + ctx->objects_end->size;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n\nstruct ggml_tensor * ggml_new_tensor_impl(\n        struct ggml_context * ctx,\n        enum   ggml_type type,\n        int    n_dims,\n        const int* ne,\n        void*  data) {\n    // always insert objects at the end of the context's memory pool\n    struct ggml_object * obj_cur = ctx->objects_end;\n\n    const size_t cur_offset = obj_cur == NULL ? 0 : obj_cur->offset;\n    const size_t cur_size   = obj_cur == NULL ? 0 : obj_cur->size;\n    const size_t cur_end    = cur_offset + cur_size;\n\n    size_t size_needed = 0;\n\n    if (data == NULL) {\n        size_needed += GGML_TYPE_SIZE[type];\n        for (int i = 0; i < n_dims; i++) {\n            size_needed *= ne[i];\n        }\n        // align to GGML_MEM_ALIGN\n        size_needed = ((size_needed + GGML_MEM_ALIGN - 1)/GGML_MEM_ALIGN)*GGML_MEM_ALIGN;\n\n    }\n    size_needed += sizeof(struct ggml_tensor);\n\n    if (cur_end + size_needed + GGML_OBJECT_SIZE > ctx->mem_size) {\n        GGML_PRINT(\"%s: not enough space in the context's memory pool\\n\", __func__);\n        assert(false);\n        return NULL;\n    }\n\n    char * const mem_buffer = ctx->mem_buffer;\n\n    struct ggml_object * const obj_new = (struct ggml_object *)(mem_buffer + cur_end);\n\n    *obj_new = (struct ggml_object) {\n        .offset = cur_end + GGML_OBJECT_SIZE,\n        .size   = size_needed,\n        .next   = NULL,\n    };\n\n    if (obj_cur != NULL) {\n        obj_cur->next = obj_new;\n    } else {\n        // this is the first object in this context\n        ctx->objects_begin = obj_new;\n    }\n\n    ctx->objects_end = obj_new;\n\n    //GGML_PRINT_DEBUG(\"%s: inserted new object at %zu\\n\", __func__, cur_end);\n\n    struct ggml_tensor * const result = (struct ggml_tensor *)(mem_buffer + obj_new->offset);\n\n    ggml_assert_aligned(result);\n\n    *result = (struct ggml_tensor) {\n        /*.type         =*/ type,\n        /*.n_dims       =*/ n_dims,\n        /*.ne           =*/ { 1, 1, 1, 1 },\n        /*.nb           =*/ { 0, 0, 0, 0 },\n        /*.op           =*/ GGML_OP_NONE,\n        /*.is_param     =*/ false,\n        /*.grad         =*/ NULL,\n        /*.src0         =*/ NULL,\n        /*.src1         =*/ NULL,\n        /*.opt          =*/ { NULL },\n        /*.n_tasks      =*/ 0,\n        /*.perf_runs    =*/ 0,\n        /*.perf_cycles  =*/ 0,\n        /*.perf_time_us =*/ 0,\n        /*.data         =*/ data == NULL ? (void *)(result + 1) : data,\n        /*.pad          =*/ { 0 },\n    };\n\n    ggml_assert_aligned(result->data);\n\n    for (int i = 0; i < n_dims; i++) {\n        result->ne[i] = ne[i];\n    }\n\n    result->nb[0] = GGML_TYPE_SIZE[type];\n    for (int i = 1; i < GGML_MAX_DIMS; i++) {\n        result->nb[i] = result->nb[i - 1]*result->ne[i - 1];\n    }\n\n    ctx->n_objects++;\n\n    return result;\n}\n\nstruct ggml_tensor * ggml_new_tensor(\n        struct ggml_context * ctx,\n        enum   ggml_type type,\n        int    n_dims,\n        const int* ne) {\n    return ggml_new_tensor_impl(ctx, type, n_dims, ne, NULL);\n}\n\nstruct ggml_tensor * ggml_new_tensor_1d(\n        struct ggml_context * ctx,\n        enum   ggml_type type,\n        int    ne0) {\n    return ggml_new_tensor(ctx, type, 1, &ne0);\n}\n\nstruct ggml_tensor * ggml_new_tensor_2d(\n        struct ggml_context * ctx,\n        enum   ggml_type type,\n        int    ne0,\n        int    ne1) {\n    const int ne[2] = { ne0, ne1 };\n    return ggml_new_tensor(ctx, type, 2, ne);\n}\n\nstruct ggml_tensor * ggml_new_tensor_3d(\n        struct ggml_context * ctx,\n        enum   ggml_type type,\n        int    ne0,\n        int    ne1,\n        int    ne2) {\n    const int ne[3] = { ne0, ne1, ne2 };\n    return ggml_new_tensor(ctx, type, 3, ne);\n}\n\nstruct ggml_tensor * ggml_new_tensor_4d(\n        struct ggml_context * ctx,\n        enum   ggml_type type,\n        int    ne0,\n        int    ne1,\n        int    ne2,\n        int    ne3) {\n    const int ne[4] = { ne0, ne1, ne2, ne3 };\n    return ggml_new_tensor(ctx, type, 4, ne);\n}\n\nstruct ggml_tensor * ggml_new_i32(struct ggml_context * ctx, int32_t value) {\n    struct ggml_tensor * result = ggml_new_tensor_1d(ctx, GGML_TYPE_I32, 1);\n\n    ggml_set_i32(result, value);\n\n    return result;\n}\n\nstruct ggml_tensor * ggml_new_f32(struct ggml_context * ctx, float value) {\n    struct ggml_tensor * result = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, 1);\n\n    ggml_set_f32(result, value);\n\n    return result;\n}\n\nstruct ggml_tensor * ggml_dup_tensor(struct ggml_context * ctx, const struct ggml_tensor * src) {\n    return ggml_new_tensor_impl(ctx, src->type, src->n_dims, src->ne, NULL);\n}\n\nstruct ggml_tensor * ggml_set_zero(struct ggml_tensor * tensor) {\n    memset(tensor->data, 0, ggml_nbytes(tensor));\n    return tensor;\n}\n\nstruct ggml_tensor * ggml_set_i32 (struct ggml_tensor * tensor, int32_t value) {\n    const int n     = ggml_nrows(tensor);\n    const int nc    = tensor->ne[0];\n    const size_t n1 = tensor->nb[1];\n\n    char * const data = tensor->data;\n\n    switch (tensor->type) {\n        case GGML_TYPE_I8:\n            {\n                assert(tensor->nb[0] == sizeof(int8_t));\n                for (int i = 0; i < n; i++) {\n                    ggml_vec_set_i8(nc, (int8_t *)(data + i*n1), value);\n                }\n            } break;\n        case GGML_TYPE_I16:\n            {\n                assert(tensor->nb[0] == sizeof(int16_t));\n                for (int i = 0; i < n; i++) {\n                    ggml_vec_set_i16(nc, (int16_t *)(data + i*n1), value);\n                }\n            } break;\n        case GGML_TYPE_I32:\n            {\n                assert(tensor->nb[0] == sizeof(int32_t));\n                for (int i = 0; i < n; i++) {\n                    ggml_vec_set_i32(nc, (int32_t *)(data + i*n1), value);\n                }\n            } break;\n        case GGML_TYPE_F16:\n            {\n                assert(tensor->nb[0] == sizeof(ggml_fp16_t));\n                for (int i = 0; i < n; i++) {\n                    ggml_vec_set_f16(nc, (ggml_fp16_t *)(data + i*n1), value);\n                }\n            } break;\n        case GGML_TYPE_F32:\n            {\n                assert(tensor->nb[0] == sizeof(float));\n                for (int i = 0; i < n; i++) {\n                    ggml_vec_set_f32(nc, (float *)(data + i*n1), value);\n                }\n            } break;\n        case GGML_TYPE_COUNT:\n            {\n                assert(false);\n            } break;\n    }\n\n    return tensor;\n}\n\nstruct ggml_tensor * ggml_set_f32(struct ggml_tensor * tensor, float value) {\n    const int n     = ggml_nrows(tensor);\n    const int nc    = tensor->ne[0];\n    const size_t n1 = tensor->nb[1];\n\n    char * const data = tensor->data;\n\n    switch (tensor->type) {\n        case GGML_TYPE_I8:\n            {\n                assert(tensor->nb[0] == sizeof(int8_t));\n                for (int i = 0; i < n; i++) {\n                    ggml_vec_set_i8(nc, (int8_t *)(data + i*n1), value);\n                }\n            } break;\n        case GGML_TYPE_I16:\n            {\n                assert(tensor->nb[0] == sizeof(int16_t));\n                for (int i = 0; i < n; i++) {\n                    ggml_vec_set_i16(nc, (int16_t *)(data + i*n1), value);\n                }\n            } break;\n        case GGML_TYPE_I32:\n            {\n                assert(tensor->nb[0] == sizeof(int32_t));\n                for (int i = 0; i < n; i++) {\n                    ggml_vec_set_i32(nc, (int32_t *)(data + i*n1), value);\n                }\n            } break;\n        case GGML_TYPE_F16:\n            {\n                assert(tensor->nb[0] == sizeof(ggml_fp16_t));\n                for (int i = 0; i < n; i++) {\n                    ggml_vec_set_f16(nc, (ggml_fp16_t *)(data + i*n1), value);\n                }\n            } break;\n        case GGML_TYPE_F32:\n            {\n                assert(tensor->nb[0] == sizeof(float));\n                for (int i = 0; i < n; i++) {\n                    ggml_vec_set_f32(nc, (float *)(data + i*n1), value);\n                }\n            } break;\n        case GGML_TYPE_COUNT:\n            {\n                assert(false);\n            } break;\n    }\n\n    return tensor;\n}\n\nint32_t ggml_get_i32_1d(const struct ggml_tensor * tensor, int i) {\n    switch (tensor->type) {\n        case GGML_TYPE_I8:\n            {\n                GGML_ASSERT(tensor->nb[0] == sizeof(int8_t));\n                return ((int8_t *)(tensor->data))[i];\n            } break;\n        case GGML_TYPE_I16:\n            {\n                GGML_ASSERT(tensor->nb[0] == sizeof(int16_t));\n                return ((int16_t *)(tensor->data))[i];\n            } break;\n        case GGML_TYPE_I32:\n            {\n                GGML_ASSERT(tensor->nb[0] == sizeof(int32_t));\n                return ((int32_t *)(tensor->data))[i];\n            } break;\n        case GGML_TYPE_F16:\n            {\n                GGML_ASSERT(tensor->nb[0] == sizeof(ggml_fp16_t));\n                return GGML_FP16_TO_FP32(((ggml_fp16_t *)(tensor->data))[i]);\n            } break;\n        case GGML_TYPE_F32:\n            {\n                GGML_ASSERT(tensor->nb[0] == sizeof(float));\n                return ((float *)(tensor->data))[i];\n            } break;\n        case GGML_TYPE_COUNT:\n            {\n                GGML_ASSERT(false);\n            } break;\n    }\n\n    return 0.0f;\n}\n\nvoid ggml_set_i32_1d(const struct ggml_tensor * tensor, int i, int32_t value) {\n    switch (tensor->type) {\n        case GGML_TYPE_I8:\n            {\n                GGML_ASSERT(tensor->nb[0] == sizeof(int8_t));\n                ((int8_t *)(tensor->data))[i] = value;\n            } break;\n        case GGML_TYPE_I16:\n            {\n                GGML_ASSERT(tensor->nb[0] == sizeof(int16_t));\n                ((int16_t *)(tensor->data))[i] = value;\n            } break;\n        case GGML_TYPE_I32:\n            {\n                GGML_ASSERT(tensor->nb[0] == sizeof(int32_t));\n                ((int32_t *)(tensor->data))[i] = value;\n            } break;\n        case GGML_TYPE_F16:\n            {\n                GGML_ASSERT(tensor->nb[0] == sizeof(ggml_fp16_t));\n                ((ggml_fp16_t *)(tensor->data))[i] = GGML_FP32_TO_FP16(value);\n            } break;\n        case GGML_TYPE_F32:\n            {\n                GGML_ASSERT(tensor->nb[0] == sizeof(float));\n                ((float *)(tensor->data))[i] = value;\n            } break;\n        case GGML_TYPE_COUNT:\n            {\n                GGML_ASSERT(false);\n            } break;\n    }\n}\n\nfloat ggml_get_f32_1d(const struct ggml_tensor * tensor, int i) {\n    switch (tensor->type) {\n        case GGML_TYPE_I8:\n            {\n                GGML_ASSERT(tensor->nb[0] == sizeof(int8_t));\n                return ((int8_t *)(tensor->data))[i];\n            } break;\n        case GGML_TYPE_I16:\n            {\n                GGML_ASSERT(tensor->nb[0] == sizeof(int16_t));\n                return ((int16_t *)(tensor->data))[i];\n            } break;\n        case GGML_TYPE_I32:\n            {\n                GGML_ASSERT(tensor->nb[0] == sizeof(int32_t));\n                return ((int32_t *)(tensor->data))[i];\n            } break;\n        case GGML_TYPE_F16:\n            {\n                GGML_ASSERT(tensor->nb[0] == sizeof(ggml_fp16_t));\n                return GGML_FP16_TO_FP32(((ggml_fp16_t *)(tensor->data))[i]);\n            } break;\n        case GGML_TYPE_F32:\n            {\n                GGML_ASSERT(tensor->nb[0] == sizeof(float));\n                return ((float *)(tensor->data))[i];\n            } break;\n        case GGML_TYPE_COUNT:\n            {\n                GGML_ASSERT(false);\n            } break;\n    }\n\n    return 0.0f;\n}\n\nvoid ggml_set_f32_1d(const struct ggml_tensor * tensor, int i, float value) {\n    switch (tensor->type) {\n        case GGML_TYPE_I8:\n            {\n                GGML_ASSERT(tensor->nb[0] == sizeof(int8_t));\n                ((int8_t *)(tensor->data))[i] = value;\n            } break;\n        case GGML_TYPE_I16:\n            {\n                GGML_ASSERT(tensor->nb[0] == sizeof(int16_t));\n                ((int16_t *)(tensor->data))[i] = value;\n            } break;\n        case GGML_TYPE_I32:\n            {\n                GGML_ASSERT(tensor->nb[0] == sizeof(int32_t));\n                ((int32_t *)(tensor->data))[i] = value;\n            } break;\n        case GGML_TYPE_F16:\n            {\n                GGML_ASSERT(tensor->nb[0] == sizeof(ggml_fp16_t));\n                ((ggml_fp16_t *)(tensor->data))[i] = GGML_FP32_TO_FP16(value);\n            } break;\n        case GGML_TYPE_F32:\n            {\n                GGML_ASSERT(tensor->nb[0] == sizeof(float));\n                ((float *)(tensor->data))[i] = value;\n            } break;\n        case GGML_TYPE_COUNT:\n            {\n                GGML_ASSERT(false);\n            } break;\n    }\n}\n\nvoid * ggml_get_data(const struct ggml_tensor * tensor) {\n    return tensor->data;\n}\n\nfloat * ggml_get_data_f32(const struct ggml_tensor * tensor) {\n    assert(tensor->type == GGML_TYPE_F32);\n    return (float *)(tensor->data);\n}\n\nstruct ggml_tensor * ggml_view_tensor(\n        struct ggml_context * ctx,\n        const struct ggml_tensor * src) {\n    return ggml_new_tensor_impl(ctx, src->type, src->n_dims, src->ne, src->data);\n}\n\n////////////////////////////////////////////////////////////////////////////////\n\n// ggml_dup\n\nstruct ggml_tensor * ggml_dup_impl(\n        struct ggml_context * ctx,\n        struct ggml_tensor * a,\n        bool inplace) {\n    bool is_node = false;\n\n    if (!inplace && (a->grad)) {\n        is_node = true;\n    }\n\n    struct ggml_tensor * result = inplace ? ggml_view_tensor(ctx, a) : ggml_dup_tensor(ctx, a);\n\n    result->op   = GGML_OP_DUP;\n    result->grad = is_node ? ggml_dup_tensor(ctx, result) : NULL;\n    result->src0 = a;\n    result->src1 = NULL;\n\n    return result;\n}\n\nstruct ggml_tensor * ggml_dup(\n        struct ggml_context * ctx,\n        struct ggml_tensor * a) {\n    return ggml_dup_impl(ctx, a, false);\n}\n\nstruct ggml_tensor * ggml_dup_inplace(\n        struct ggml_context * ctx,\n        struct ggml_tensor * a) {\n    return ggml_dup_impl(ctx, a, true);\n}\n\n// ggml_add\n\nstruct ggml_tensor * ggml_add_impl(\n        struct ggml_context * ctx,\n        struct ggml_tensor * a,\n        struct ggml_tensor * b,\n        bool inplace) {\n    assert(ggml_are_same_shape(a, b));\n\n    bool is_node = false;\n\n    if (!inplace && (a->grad || b->grad)) {\n        is_node = true;\n    }\n\n    struct ggml_tensor * result = inplace ? ggml_view_tensor(ctx, a) : ggml_dup_tensor(ctx, a);\n\n    result->op   = GGML_OP_ADD;\n    result->grad = is_node ? ggml_dup_tensor(ctx, result) : NULL;\n    result->src0 = a;\n    result->src1 = b;\n\n    return result;\n}\n\nstruct ggml_tensor * ggml_add(\n        struct ggml_context * ctx,\n        struct ggml_tensor * a,\n        struct ggml_tensor * b) {\n    return ggml_add_impl(ctx, a, b, false);\n}\n\nstruct ggml_tensor * ggml_add_inplace(\n        struct ggml_context * ctx,\n        struct ggml_tensor * a,\n        struct ggml_tensor * b) {\n    return ggml_add_impl(ctx, a, b, true);\n}\n\n// ggml_sub\n\nstruct ggml_tensor * ggml_sub_impl(\n        struct ggml_context * ctx,\n        struct ggml_tensor * a,\n        struct ggml_tensor * b,\n        bool inplace) {\n    assert(ggml_are_same_shape(a, b));\n\n    bool is_node = false;\n\n    if (!inplace && (a->grad || b->grad)) {\n        is_node = true;\n    }\n\n    struct ggml_tensor * result = inplace ? ggml_view_tensor(ctx, a) : ggml_dup_tensor(ctx, a);\n\n    result->op   = GGML_OP_SUB;\n    result->grad = is_node ? ggml_dup_tensor(ctx, result) : NULL;\n    result->src0 = a;\n    result->src1 = b;\n\n    return result;\n}\n\nstruct ggml_tensor * ggml_sub(\n        struct ggml_context * ctx,\n        struct ggml_tensor * a,\n        struct ggml_tensor * b) {\n    return ggml_sub_impl(ctx, a, b, false);\n}\n\nstruct ggml_tensor * ggml_sub_inplace(\n        struct ggml_context * ctx,\n        struct ggml_tensor * a,\n        struct ggml_tensor * b) {\n    return ggml_sub_impl(ctx, a, b, true);\n}\n\n// ggml_mul\n\nstruct ggml_tensor * ggml_mul_impl(\n        struct ggml_context * ctx,\n        struct ggml_tensor * a,\n        struct ggml_tensor * b,\n        bool inplace) {\n    assert(ggml_are_same_shape(a, b));\n\n    bool is_node = false;\n\n    if (!inplace && (a->grad || b->grad)) {\n        is_node = true;\n    }\n\n    if (inplace) {\n        assert(is_node == false);\n    }\n\n    struct ggml_tensor * result = inplace ? ggml_view_tensor(ctx, a) : ggml_dup_tensor(ctx, a);\n\n    result->op   = GGML_OP_MUL;\n    result->grad = is_node ? ggml_dup_tensor(ctx, result) : NULL;\n    result->src0 = a;\n    result->src1 = b;\n\n    return result;\n}\n\nstruct ggml_tensor * ggml_mul(\n        struct ggml_context * ctx,\n        struct ggml_tensor  * a,\n        struct ggml_tensor  * b) {\n    return ggml_mul_impl(ctx, a, b, false);\n}\n\nstruct ggml_tensor * ggml_mul_inplace(\n        struct ggml_context * ctx,\n        struct ggml_tensor  * a,\n        struct ggml_tensor  * b) {\n    return ggml_mul_impl(ctx, a, b, true);\n}\n\n// ggml_div\n\nstruct ggml_tensor * ggml_div_impl(\n        struct ggml_context * ctx,\n        struct ggml_tensor * a,\n        struct ggml_tensor * b,\n        bool inplace) {\n    assert(ggml_are_same_shape(a, b));\n\n    bool is_node = false;\n\n    if (!inplace && (a->grad || b->grad)) {\n        is_node = true;\n    }\n\n    if (inplace) {\n        assert(is_node == false);\n    }\n\n    struct ggml_tensor * result = inplace ? ggml_view_tensor(ctx, a) : ggml_dup_tensor(ctx, a);\n\n    result->op   = GGML_OP_DIV;\n    result->grad = is_node ? ggml_dup_tensor(ctx, result) : NULL;\n    result->src0 = a;\n    result->src1 = b;\n\n    return result;\n}\n\nstruct ggml_tensor * ggml_div(\n        struct ggml_context * ctx,\n        struct ggml_tensor  * a,\n        struct ggml_tensor  * b) {\n    return ggml_div_impl(ctx, a, b, false);\n}\n\nstruct ggml_tensor * ggml_div_inplace(\n        struct ggml_context * ctx,\n        struct ggml_tensor  * a,\n        struct ggml_tensor  * b) {\n    return ggml_div_impl(ctx, a, b, true);\n}\n\n// ggml_sqr\n\nstruct ggml_tensor * ggml_sqr_impl(\n        struct ggml_context * ctx,\n        struct ggml_tensor * a,\n        bool inplace) {\n    bool is_node = false;\n\n    if (!inplace && (a->grad)) {\n        is_node = true;\n    }\n\n    struct ggml_tensor * result = inplace ? ggml_view_tensor(ctx, a) : ggml_dup_tensor(ctx, a);\n\n    result->op   = GGML_OP_SQR;\n    result->grad = is_node ? ggml_dup_tensor(ctx, result) : NULL;\n    result->src0 = a;\n    result->src1 = NULL;\n\n    return result;\n}\n\nstruct ggml_tensor * ggml_sqr(\n        struct ggml_context * ctx,\n        struct ggml_tensor  * a) {\n    return ggml_sqr_impl(ctx, a, false);\n}\n\nstruct ggml_tensor * ggml_sqr_inplace(\n        struct ggml_context * ctx,\n        struct ggml_tensor  * a) {\n    return ggml_sqr_impl(ctx, a, true);\n}\n\n// ggml_sqrt\n\nstruct ggml_tensor * ggml_sqrt_impl(\n        struct ggml_context * ctx,\n        struct ggml_tensor * a,\n        bool inplace) {\n    bool is_node = false;\n\n    if (!inplace && (a->grad)) {\n        is_node = true;\n    }\n\n    struct ggml_tensor * result = inplace ? ggml_view_tensor(ctx, a) : ggml_dup_tensor(ctx, a);\n\n    result->op   = GGML_OP_SQRT;\n    result->grad = is_node ? ggml_dup_tensor(ctx, result) : NULL;\n    result->src0 = a;\n    result->src1 = NULL;\n\n    return result;\n}\n\nstruct ggml_tensor * ggml_sqrt(\n        struct ggml_context * ctx,\n        struct ggml_tensor  * a) {\n    return ggml_sqrt_impl(ctx, a, false);\n}\n\nstruct ggml_tensor * ggml_sqrt_inplace(\n        struct ggml_context * ctx,\n        struct ggml_tensor  * a) {\n    return ggml_sqrt_impl(ctx, a, true);\n}\n\n// ggml_sum\n\nstruct ggml_tensor * ggml_sum(\n        struct ggml_context * ctx,\n        struct ggml_tensor * a) {\n    bool is_node = false;\n\n    if (a->grad) {\n        is_node = true;\n    }\n\n    struct ggml_tensor * result = ggml_new_tensor_1d(ctx, a->type, 1);\n\n    result->op   = GGML_OP_SUM;\n    result->grad = is_node ? ggml_dup_tensor(ctx, result) : NULL;\n    result->src0 = a;\n    result->src1 = NULL;\n\n    return result;\n}\n\n// ggml_mean\n\nstruct ggml_tensor * ggml_mean(\n        struct ggml_context * ctx,\n        struct ggml_tensor * a) {\n    bool is_node = false;\n\n    if (a->grad) {\n        assert(false); // TODO: implement\n        is_node = true;\n    }\n\n    int ne[GGML_MAX_DIMS] = { 1, a->ne[1], a->ne[2], a->ne[3] };\n    struct ggml_tensor * result = ggml_new_tensor(ctx, GGML_TYPE_F32, a->n_dims, ne);\n\n    result->op   = GGML_OP_MEAN;\n    result->grad = is_node ? ggml_dup_tensor(ctx, result) : NULL;\n    result->src0 = a;\n    result->src1 = NULL;\n\n    return result;\n}\n\n// ggml_repeat\n\nstruct ggml_tensor * ggml_repeat(\n        struct ggml_context * ctx,\n        struct ggml_tensor * a,\n        struct ggml_tensor * b) {\n    assert(ggml_can_repeat(a, b));\n\n    bool is_node = false;\n\n    if (a->grad) {\n        is_node = true;\n    }\n\n    if (ggml_are_same_shape(a, b) && !is_node) {\n        return a;\n    }\n\n    struct ggml_tensor * result = ggml_new_tensor(ctx, a->type, b->n_dims, b->ne);\n\n    result->op   = GGML_OP_REPEAT;\n    result->grad = is_node ? ggml_dup_tensor(ctx, result) : NULL;\n    result->src0 = a;\n    result->src1 = NULL;\n\n    return result;\n}\n\n// ggml_abs\n\nstruct ggml_tensor * ggml_abs_impl(\n        struct ggml_context * ctx,\n        struct ggml_tensor * a,\n        bool inplace) {\n    bool is_node = false;\n\n    if (!inplace && (a->grad)) {\n        is_node = true;\n    }\n\n    struct ggml_tensor * result = inplace ? ggml_view_tensor(ctx, a) : ggml_dup_tensor(ctx, a);\n\n    result->op   = GGML_OP_ABS;\n    result->grad = is_node ? ggml_dup_tensor(ctx, result) : NULL;\n    result->src0 = a;\n    result->src1 = NULL;\n\n    return result;\n}\n\nstruct ggml_tensor * ggml_abs(\n        struct ggml_context * ctx,\n        struct ggml_tensor  * a) {\n    return ggml_abs_impl(ctx, a, false);\n}\n\nstruct ggml_tensor * ggml_abs_inplace(\n        struct ggml_context * ctx,\n        struct ggml_tensor  * a) {\n    return ggml_abs_impl(ctx, a, true);\n}\n\n\n// ggml_sgn\n\nstruct ggml_tensor * ggml_sgn_impl(\n        struct ggml_context * ctx,\n        struct ggml_tensor * a,\n        bool inplace) {\n    bool is_node = false;\n\n    if (!inplace && (a->grad)) {\n        is_node = true;\n    }\n\n    struct ggml_tensor * result = inplace ? ggml_view_tensor(ctx, a) : ggml_dup_tensor(ctx, a);\n\n    result->op   = GGML_OP_SGN;\n    result->grad = is_node ? ggml_dup_tensor(ctx, result) : NULL;\n    result->src0 = a;\n    result->src1 = NULL;\n\n    return result;\n}\n\nstruct ggml_tensor * ggml_sgn(\n        struct ggml_context * ctx,\n        struct ggml_tensor  * a) {\n    return ggml_sgn_impl(ctx, a, false);\n}\n\nstruct ggml_tensor * ggml_sgn_inplace(\n        struct ggml_context * ctx,\n        struct ggml_tensor  * a) {\n    return ggml_sgn_impl(ctx, a, true);\n}\n\n// ggml_neg\n\nstruct ggml_tensor * ggml_neg_impl(\n        struct ggml_context * ctx,\n        struct ggml_tensor * a,\n        bool inplace) {\n    bool is_node = false;\n\n    if (!inplace && (a->grad)) {\n        is_node = true;\n    }\n\n    struct ggml_tensor * result = inplace ? ggml_view_tensor(ctx, a) : ggml_dup_tensor(ctx, a);\n\n    result->op   = GGML_OP_NEG;\n    result->grad = is_node ? ggml_dup_tensor(ctx, result) : NULL;\n    result->src0 = a;\n    result->src1 = NULL;\n\n    return result;\n}\n\nstruct ggml_tensor * ggml_neg(\n        struct ggml_context * ctx,\n        struct ggml_tensor  * a) {\n    return ggml_neg_impl(ctx, a, false);\n}\n\nstruct ggml_tensor * ggml_neg_inplace(\n        struct ggml_context * ctx,\n        struct ggml_tensor  * a) {\n    return ggml_neg_impl(ctx, a, true);\n}\n\n// ggml_step\n\nstruct ggml_tensor * ggml_step_impl(\n        struct ggml_context * ctx,\n        struct ggml_tensor * a,\n        bool inplace) {\n    bool is_node = false;\n\n    if (!inplace && (a->grad)) {\n        is_node = true;\n    }\n\n    struct ggml_tensor * result = inplace ? ggml_view_tensor(ctx, a) : ggml_dup_tensor(ctx, a);\n\n    result->op   = GGML_OP_STEP;\n    result->grad = is_node ? ggml_dup_tensor(ctx, result) : NULL;\n    result->src0 = a;\n    result->src1 = NULL;\n\n    return result;\n}\n\nstruct ggml_tensor * ggml_step(\n        struct ggml_context * ctx,\n        struct ggml_tensor  * a) {\n    return ggml_step_impl(ctx, a, false);\n}\n\nstruct ggml_tensor * ggml_step_inplace(\n        struct ggml_context * ctx,\n        struct ggml_tensor  * a) {\n    return ggml_step_impl(ctx, a, true);\n}\n\n// ggml_relu\n\nstruct ggml_tensor * ggml_relu_impl(\n        struct ggml_context * ctx,\n        struct ggml_tensor * a,\n        bool inplace) {\n    bool is_node = false;\n\n    if (!inplace && (a->grad)) {\n        is_node = true;\n    }\n\n    struct ggml_tensor * result = inplace ? ggml_view_tensor(ctx, a) : ggml_dup_tensor(ctx, a);\n\n    result->op   = GGML_OP_RELU;\n    result->grad = is_node ? ggml_dup_tensor(ctx, result) : NULL;\n    result->src0 = a;\n    result->src1 = NULL;\n\n    return result;\n}\n\nstruct ggml_tensor * ggml_relu(\n        struct ggml_context * ctx,\n        struct ggml_tensor  * a) {\n    return ggml_relu_impl(ctx, a, false);\n}\n\nstruct ggml_tensor * ggml_relu_inplace(\n        struct ggml_context * ctx,\n        struct ggml_tensor  * a) {\n    return ggml_relu_impl(ctx, a, true);\n}\n\n// ggml_gelu\n\nstruct ggml_tensor * ggml_gelu_impl(\n        struct ggml_context * ctx,\n        struct ggml_tensor * a,\n        bool inplace) {\n    bool is_node = false;\n\n    if (!inplace && (a->grad)) {\n        is_node = true;\n    }\n\n    struct ggml_tensor * result = inplace ? ggml_view_tensor(ctx, a) : ggml_dup_tensor(ctx, a);\n\n    result->op   = GGML_OP_GELU;\n    result->grad = is_node ? ggml_dup_tensor(ctx, result) : NULL;\n    result->src0 = a;\n    result->src1 = NULL;\n\n    return result;\n}\n\nstruct ggml_tensor * ggml_gelu(\n        struct ggml_context * ctx,\n        struct ggml_tensor  * a) {\n    return ggml_gelu_impl(ctx, a, false);\n}\n\nstruct ggml_tensor * ggml_gelu_inplace(\n        struct ggml_context * ctx,\n        struct ggml_tensor  * a) {\n    return ggml_gelu_impl(ctx, a, true);\n}\n\n// ggml_norm\n\nstruct ggml_tensor * ggml_norm_impl(\n        struct ggml_context * ctx,\n        struct ggml_tensor  * a,\n        bool inplace) {\n    bool is_node = false;\n\n    if (!inplace && (a->grad)) {\n        assert(false); // TODO: implement backward\n        is_node = true;\n    }\n\n    struct ggml_tensor * result = inplace ? ggml_view_tensor(ctx, a) : ggml_dup_tensor(ctx, a);\n\n    result->op   = GGML_OP_NORM;\n    result->grad = is_node ? ggml_dup_tensor(ctx, result) : NULL;\n    result->src0 = a;\n    result->src1 = NULL; // TODO: maybe store epsilon here?\n\n    return result;\n}\n\nstruct ggml_tensor * ggml_norm(\n        struct ggml_context * ctx,\n        struct ggml_tensor  * a) {\n    return ggml_norm_impl(ctx, a, false);\n}\n\nstruct ggml_tensor * ggml_norm_inplace(\n        struct ggml_context * ctx,\n        struct ggml_tensor  * a) {\n    return ggml_norm_impl(ctx, a, true);\n}\n\n// ggml_mul_mat\n\nstruct ggml_tensor * ggml_mul_mat(\n        struct ggml_context * ctx,\n        struct ggml_tensor  * a,\n        struct ggml_tensor  * b) {\n    assert(ggml_can_mul_mat(a, b));\n\n\t// printUniqueTensorSize( \"ggml_mul_mat\", a->ne, b->ne );\n    bool is_node = false;\n\n    if (a->grad || b->grad) {\n        is_node = true;\n    }\n\n    const int ne[4] = { a->ne[1], b->ne[1], a->ne[2], b->ne[3] };\n    struct ggml_tensor * result = ggml_new_tensor(ctx, GGML_TYPE_F32, MIN(a->n_dims, b->n_dims), ne);\n\n    result->op   = GGML_OP_MUL_MAT;\n    result->grad = is_node ? ggml_dup_tensor(ctx, result) : NULL;\n    result->src0 = a;\n    result->src1 = b;\n\n    return result;\n}\n\n// ggml_scale\n\nstruct ggml_tensor * ggml_scale_impl(\n        struct ggml_context * ctx,\n        struct ggml_tensor  * a,\n        struct ggml_tensor  * b,\n        bool inplace) {\n    assert(ggml_is_scalar(b));\n    assert(ggml_is_padded_1d(a));\n\n    bool is_node = false;\n\n    if (!inplace && (a->grad || b->grad)) {\n        assert(false); // TODO: implement backward\n        is_node = true;\n    }\n\n    // TODO: when implement backward, fix this:\n    //struct ggml_tensor * result = inplace ? ggml_view_tensor(ctx, a) : ggml_dup_tensor(ctx, a);\n    struct ggml_tensor * result = ggml_view_tensor(ctx, a);\n\n    result->op   = GGML_OP_SCALE;\n    result->grad = is_node ? ggml_dup_tensor(ctx, result) : NULL;\n    result->src0 = a;\n    result->src1 = b;\n\n    return result;\n}\n\nstruct ggml_tensor * ggml_scale(\n        struct ggml_context * ctx,\n        struct ggml_tensor * a,\n        struct ggml_tensor * b) {\n    return ggml_scale_impl(ctx, a, b, false);\n}\n\nstruct ggml_tensor * ggml_scale_inplace(\n        struct ggml_context * ctx,\n        struct ggml_tensor * a,\n        struct ggml_tensor * b) {\n    return ggml_scale_impl(ctx, a, b, true);\n}\n\n// ggml_cpy\n\nstruct ggml_tensor * ggml_cpy_impl(\n        struct ggml_context * ctx,\n        struct ggml_tensor  * a,\n        struct ggml_tensor  * b,\n        bool inplace) {\n    assert(ggml_nelements(a) == ggml_nelements(b));\n\n    bool is_node = false;\n\n    if (!inplace && (a->grad || b->grad)) {\n        assert(false); // TODO: implement backward\n        is_node = true;\n    }\n\n    // make a view of the destination\n    struct ggml_tensor * result = ggml_view_tensor(ctx, b);\n\n    result->op   = GGML_OP_CPY;\n    result->grad = is_node ? ggml_dup_tensor(ctx, result) : NULL;\n    result->src0 = a;\n    result->src1 = b;\n\n    return result;\n}\n\nstruct ggml_tensor * ggml_cpy(\n        struct ggml_context * ctx,\n        struct ggml_tensor * a,\n        struct ggml_tensor * b) {\n    return ggml_cpy_impl(ctx, a, b, false);\n}\n\nstruct ggml_tensor * ggml_cpy_inplace(\n        struct ggml_context * ctx,\n        struct ggml_tensor * a,\n        struct ggml_tensor * b) {\n    return ggml_cpy_impl(ctx, a, b, true);\n}\n\n// ggml_reshape\n\nstruct ggml_tensor * ggml_reshape(\n        struct ggml_context * ctx,\n        struct ggml_tensor * a,\n        struct ggml_tensor * b) {\n    assert(ggml_is_contiguous(a));\n    assert(ggml_is_contiguous(b));\n    assert(ggml_nelements(a) == ggml_nelements(b));\n\n    bool is_node = false;\n\n    if (a->grad || b->grad) {\n        assert(false); // TODO: implement backward\n        is_node = true;\n    }\n\n    struct ggml_tensor * result = ggml_new_tensor_impl(ctx, a->type, b->n_dims, b->ne, a->data);\n\n    result->op   = GGML_OP_RESHAPE;\n    result->grad = is_node ? ggml_dup_tensor(ctx, result) : NULL;\n    result->src0 = a;\n    result->src1 = NULL;\n\n    return result;\n}\n\nstruct ggml_tensor * ggml_reshape_2d(\n        struct ggml_context * ctx,\n        struct ggml_tensor  * a,\n        int                   ne0,\n        int                   ne1) {\n    assert(ggml_is_contiguous(a));\n    assert(ggml_nelements(a) == ne0*ne1);\n\n    bool is_node = false;\n\n    if (a->grad) {\n        assert(false); // TODO: implement backward\n        is_node = true;\n    }\n\n    const int ne[2] = { ne0, ne1 };\n    struct ggml_tensor * result = ggml_new_tensor_impl(ctx, a->type, 2, ne, a->data);\n\n    result->op   = GGML_OP_RESHAPE;\n    result->grad = is_node ? ggml_dup_tensor(ctx, result) : NULL;\n    result->src0 = a;\n    result->src1 = NULL;\n\n    return result;\n}\n\nstruct ggml_tensor * ggml_reshape_3d(\n        struct ggml_context * ctx,\n        struct ggml_tensor  * a,\n        int                   ne0,\n        int                   ne1,\n        int                   ne2) {\n    assert(ggml_is_contiguous(a));\n    assert(ggml_nelements(a) == ne0*ne1*ne2);\n\n    bool is_node = false;\n\n    if (a->grad) {\n        assert(false); // TODO: implement backward\n        is_node = true;\n    }\n\n    const int ne[3] = { ne0, ne1, ne2 };\n    struct ggml_tensor * result = ggml_new_tensor_impl(ctx, a->type, 3, ne, a->data);\n\n    result->op   = GGML_OP_RESHAPE;\n    result->grad = is_node ? ggml_dup_tensor(ctx, result) : NULL;\n    result->src0 = a;\n    result->src1 = NULL;\n\n    return result;\n}\n\n// ggml_view_1d\n\nstruct ggml_tensor * ggml_view_1d(\n        struct ggml_context * ctx,\n        struct ggml_tensor  * a,\n        int                   ne0,\n        size_t                offset) {\n    if (a->grad) {\n        assert(false); // gradient propagation is not supported\n    }\n\n    struct ggml_tensor * result = ggml_new_tensor_impl(ctx, a->type, 1, &ne0, (char *) a->data + offset);\n\n    result->op   = GGML_OP_VIEW;\n    result->grad = NULL;\n    result->src0 = a;\n    result->src1 = NULL; // TODO: maybe store the offset here?\n\n    return result;\n}\n\n// ggml_view_2d\n\nstruct ggml_tensor * ggml_view_2d(\n        struct ggml_context * ctx,\n        struct ggml_tensor  * a,\n        int                   ne0,\n        int                   ne1,\n        size_t                nb1,\n        size_t                offset) {\n    if (a->grad) {\n        assert(false); // gradient propagation is not supported\n    }\n\n    const int ne[GGML_MAX_DIMS] = { ne0, ne1, 1, 1 };\n\n    struct ggml_tensor * result = ggml_new_tensor_impl(ctx, a->type, 2, ne, (char *) a->data + offset);\n\n    result->nb[1] = nb1;\n    result->nb[2] = result->nb[1]*ne1;\n    result->nb[3] = result->nb[2];\n\n    result->op   = GGML_OP_VIEW;\n    result->grad = NULL;\n    result->src0 = a;\n    result->src1 = NULL; // TODO: maybe store the offset here?\n\n    return result;\n}\n\n// ggml_permute\n\nstruct ggml_tensor * ggml_permute(\n        struct ggml_context * ctx,\n        struct ggml_tensor  * a,\n        int                   axis0,\n        int                   axis1,\n        int                   axis2,\n        int                   axis3) {\n    assert(axis0 >= 0 && axis0 < GGML_MAX_DIMS);\n    assert(axis1 >= 0 && axis1 < GGML_MAX_DIMS);\n    assert(axis2 >= 0 && axis2 < GGML_MAX_DIMS);\n    assert(axis3 >= 0 && axis3 < GGML_MAX_DIMS);\n\n    assert(axis0 != axis1);\n    assert(axis0 != axis2);\n    assert(axis0 != axis3);\n    assert(axis1 != axis2);\n    assert(axis1 != axis3);\n    assert(axis2 != axis3);\n\n    bool is_node = false;\n\n    if (a->grad) {\n        assert(false); // TODO: implement backward\n        is_node = true;\n    }\n\n    struct ggml_tensor * result = ggml_view_tensor(ctx, a);\n\n    int ne[GGML_MAX_DIMS];\n    int nb[GGML_MAX_DIMS];\n\n    ne[axis0] = a->ne[0];\n    ne[axis1] = a->ne[1];\n    ne[axis2] = a->ne[2];\n    ne[axis3] = a->ne[3];\n\n    nb[axis0] = a->nb[0];\n    nb[axis1] = a->nb[1];\n    nb[axis2] = a->nb[2];\n    nb[axis3] = a->nb[3];\n\n    result->ne[0] = ne[0];\n    result->ne[1] = ne[1];\n    result->ne[2] = ne[2];\n    result->ne[3] = ne[3];\n\n    result->nb[0] = nb[0];\n    result->nb[1] = nb[1];\n    result->nb[2] = nb[2];\n    result->nb[3] = nb[3];\n\n    result->op   = GGML_OP_PERMUTE;\n    result->grad = is_node ? ggml_dup_tensor(ctx, result) : NULL;\n    result->src0 = a;\n    result->src1 = NULL; // TODO: maybe store the permutation here?\n\n    return result;\n}\n\n// ggml_transpose\n\nstruct ggml_tensor * ggml_transpose(\n        struct ggml_context * ctx,\n        struct ggml_tensor  * a) {\n    bool is_node = false;\n\n    if (a->grad) {\n        assert(false); // TODO: implement backward\n        is_node = true;\n    }\n\n    struct ggml_tensor * result = ggml_view_tensor(ctx, a);\n\n    result->ne[0] = a->ne[1];\n    result->ne[1] = a->ne[0];\n\n    result->nb[0] = a->nb[1];\n    result->nb[1] = a->nb[0];\n\n    result->op   = GGML_OP_TRANSPOSE;\n    result->grad = is_node ? ggml_dup_tensor(ctx, result) : NULL;\n    result->src0 = a;\n    result->src1 = NULL;\n\n    return result;\n}\n\n// ggml_get_rows\n\nstruct ggml_tensor * ggml_get_rows(\n        struct ggml_context * ctx,\n        struct ggml_tensor  * a,\n        struct ggml_tensor  * b) {\n    assert(ggml_is_matrix(a) && ggml_is_vector(b) && b->type == GGML_TYPE_I32);\n\n    bool is_node = false;\n\n    if (a->grad || b->grad) {\n        assert(false); // TODO: implement backward\n        is_node = true;\n    }\n\n    // TODO: implement non F32 return\n    //struct ggml_tensor * result = ggml_new_tensor_2d(ctx, a->type, a->ne[0], b->ne[0]);\n    struct ggml_tensor * result = ggml_new_tensor_2d(ctx, GGML_TYPE_F32, a->ne[0], b->ne[0]);\n\n    result->op   = GGML_OP_GET_ROWS;\n    result->grad = is_node ? ggml_dup_tensor(ctx, result) : NULL;\n    result->src0 = a;\n    result->src1 = b;\n\n    return result;\n}\n\n// ggml_diag_mask_inf\n\nstruct ggml_tensor * ggml_diag_mask_inf(\n        struct ggml_context * ctx,\n        struct ggml_tensor  * a,\n        int                   n_past) {\n    bool is_node = false;\n\n    if (a->grad) {\n        assert(false); // TODO: implement backward\n        is_node = true;\n    }\n\n    // TODO: when implement backward, fix this:\n    //struct ggml_tensor * result = inplace ? ggml_view_tensor(ctx, a) : ggml_dup_tensor(ctx, a);\n    struct ggml_tensor * result = ggml_view_tensor(ctx, a);\n\n    struct ggml_tensor * b = ggml_new_tensor_1d(ctx, GGML_TYPE_I32, 1);\n    ((int32_t *) b->data)[0] = n_past;\n\n    result->op   = GGML_OP_DIAG_MASK_INF;\n    result->grad = is_node ? ggml_dup_tensor(ctx, result) : NULL;\n    result->src0 = a;\n    result->src1 = b;\n\n    return result;\n}\n\n// ggml_soft_max\n\nstruct ggml_tensor * ggml_soft_max(\n        struct ggml_context * ctx,\n        struct ggml_tensor  * a) {\n    bool is_node = false;\n\n    if (a->grad) {\n        assert(false); // TODO: implement backward\n        is_node = true;\n    }\n\n    // TODO: when implement backward, fix this:\n    //struct ggml_tensor * result = inplace ? ggml_view_tensor(ctx, a) : ggml_dup_tensor(ctx, a);\n    struct ggml_tensor * result = ggml_view_tensor(ctx, a);\n\n    result->op   = GGML_OP_SOFT_MAX;\n    result->grad = is_node ? ggml_dup_tensor(ctx, result) : NULL;\n    result->src0 = a;\n    result->src1 = NULL;\n\n    return result;\n}\n\n// ggml_rope\n\nstruct ggml_tensor * ggml_rope(\n        struct ggml_context * ctx,\n        struct ggml_tensor  * a,\n        int                   n_past,\n        int                   n_dims,\n        int                   mode) {\n    assert(n_past >= 0);\n    bool is_node = false;\n\n    if (a->grad) {\n        assert(false); // TODO: implement backward\n        is_node = true;\n    }\n\n    // TODO: when implement backward, fix this:\n    //struct ggml_tensor * result = inplace ? ggml_view_tensor(ctx, a) : ggml_dup_tensor(ctx, a);\n    struct ggml_tensor * result = ggml_view_tensor(ctx, a);\n\n    struct ggml_tensor * b = ggml_new_tensor_1d(ctx, GGML_TYPE_I32, 3);\n    ((int32_t *) b->data)[0] = n_past;\n    ((int32_t *) b->data)[1] = n_dims;\n    ((int32_t *) b->data)[2] = mode;\n\n    result->op   = GGML_OP_ROPE;\n    result->grad = is_node ? ggml_dup_tensor(ctx, result) : NULL;\n    result->src0 = a;\n    result->src1 = b;\n\n    return result;\n}\n\n// ggml_conv_1d_1s\n\nstruct ggml_tensor * ggml_conv_1d_1s(\n        struct ggml_context * ctx,\n        struct ggml_tensor  * a,\n        struct ggml_tensor  * b) {\n    assert(ggml_is_matrix(b));\n    assert(a->ne[1] == b->ne[1]);\n    assert(a->ne[3] == 1);\n    bool is_node = false;\n\n    if (a->grad || b->grad) {\n        assert(false); // TODO: implement backward\n        is_node = true;\n    }\n\n    const int ne[4] = { b->ne[0], a->ne[2], 1, 1, };\n    struct ggml_tensor * result = ggml_new_tensor(ctx, GGML_TYPE_F32, 2, ne);\n\n    result->op   = GGML_OP_CONV_1D_1S;\n    result->grad = is_node ? ggml_dup_tensor(ctx, result) : NULL;\n    result->src0 = a;\n    result->src1 = b;\n\n    return result;\n}\n\n// ggml_conv_1d_2s\n\nstruct ggml_tensor * ggml_conv_1d_2s(\n        struct ggml_context * ctx,\n        struct ggml_tensor  * a,\n        struct ggml_tensor  * b) {\n    assert(ggml_is_matrix(b));\n    assert(a->ne[1] == b->ne[1]);\n    assert(a->ne[3] == 1);\n    bool is_node = false;\n\n    if (a->grad || b->grad) {\n        assert(false); // TODO: implement backward\n        is_node = true;\n    }\n\n    const int ne[4] = { b->ne[0]/2, a->ne[2], 1, 1, };\n    struct ggml_tensor * result = ggml_new_tensor(ctx, GGML_TYPE_F32, 2, ne);\n\n    result->op   = GGML_OP_CONV_1D_2S;\n    result->grad = is_node ? ggml_dup_tensor(ctx, result) : NULL;\n    result->src0 = a;\n    result->src1 = b;\n\n    return result;\n}\n\n// ggml_flash_attn\n\nstruct ggml_tensor * ggml_flash_attn(\n        struct ggml_context * ctx,\n        struct ggml_tensor  * q,\n        struct ggml_tensor  * k,\n        struct ggml_tensor  * v,\n        bool                  masked) {\n    assert(ggml_can_mul_mat(k, q));\n    // TODO: check if vT can be multiplied by (k*qT)\n\n    bool is_node = false;\n\n    if (q->grad || k->grad || v->grad) {\n        GGML_ASSERT(false); // TODO: implement backward\n        is_node = true;\n    }\n\n    //struct ggml_tensor * result = ggml_dup_tensor(ctx, q);\n    struct ggml_tensor * result = ggml_new_tensor(ctx, GGML_TYPE_F32, 4, q->ne);\n\n    result->op   = GGML_OP_FLASH_ATTN;\n    result->grad = is_node ? ggml_dup_tensor(ctx, result) : NULL;\n    result->src0 = q;\n    result->src1 = k;\n    result->opt[0] = v;\n    result->opt[1] = ggml_new_i32(ctx, masked ? 1 : 0);\n\n    return result;\n}\n\n// ggml_flash_ff\n\nstruct ggml_tensor * ggml_flash_ff(\n        struct ggml_context * ctx,\n        struct ggml_tensor  * a,\n        struct ggml_tensor  * b0,\n        struct ggml_tensor  * b1,\n        struct ggml_tensor  * c0,\n        struct ggml_tensor  * c1) {\n    assert(ggml_can_mul_mat(b0, a));\n    // TODO: more checks\n\n    bool is_node = false;\n\n    if (a->grad || b0->grad || b1->grad || c0->grad || c1->grad) {\n        GGML_ASSERT(false); // TODO: implement backward\n        is_node = true;\n    }\n\n    //struct ggml_tensor * result = ggml_dup_tensor(ctx, a);\n    struct ggml_tensor * result = ggml_new_tensor(ctx, GGML_TYPE_F32, 4, a->ne);\n\n    result->op   = GGML_OP_FLASH_FF;\n    result->grad = is_node ? ggml_dup_tensor(ctx, result) : NULL;\n    result->src0 = a;\n    result->src1 = b0;\n    result->opt[0] = b1;\n    result->opt[1] = c0;\n    result->opt[2] = c1;\n\n    return result;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n\nvoid ggml_set_param(\n        struct ggml_context * ctx,\n        struct ggml_tensor * tensor) {\n    tensor->is_param = true;\n\n    assert(tensor->grad == NULL);\n    tensor->grad = ggml_dup_tensor(ctx, tensor);\n}\n\n// ggml_compute_forward_dup\n\nstatic void ggml_compute_forward_dup_f16(\n        const struct ggml_compute_params * params,\n        const struct ggml_tensor * src0,\n        struct ggml_tensor * dst) {\n    assert(params->ith == 0);\n    assert(ggml_is_contiguous(dst));\n    assert(ggml_nelements(dst) == ggml_nelements(src0));\n\n    if (params->type == GGML_TASK_INIT || params->type == GGML_TASK_FINALIZE) {\n        return;\n    }\n\n    const int ne00 = src0->ne[0];\n    const int ne01 = src0->ne[1];\n    const int ne02 = src0->ne[2];\n    const int ne03 = src0->ne[3];\n\n    const size_t nb00 = src0->nb[0];\n    const size_t nb01 = src0->nb[1];\n    const size_t nb02 = src0->nb[2];\n    const size_t nb03 = src0->nb[3];\n\n    if (ggml_is_contiguous(src0) && src0->type == dst->type) {\n        memcpy(dst->data, src0->data, ggml_nelements(dst) * GGML_TYPE_SIZE[src0->type]);\n        return;\n    }\n\n    if (src0->nb[0] == sizeof(ggml_fp16_t)) {\n        if (dst->type == GGML_TYPE_F16) {\n            int id = 0;\n            const size_t rs = ne00*nb00;\n\n            for (int i03 = 0; i03 < ne03; i03++) {\n                for (int i02 = 0; i02 < ne02; i02++) {\n                    for (int i01 = 0; i01 < ne01; i01++) {\n                        const char * src0_ptr = (char *) src0->data + i01*nb01 + i02*nb02 + i03*nb03;\n                        char * dst_ptr = (char *) dst->data + id*rs;\n\n                        memcpy(dst_ptr, src0_ptr, rs);\n\n                        id++;\n                    }\n                }\n            }\n        } else if (dst->type == GGML_TYPE_F32) {\n            int id = 0;\n            float * dst_ptr = (float *) dst->data;\n\n            for (int i03 = 0; i03 < ne03; i03++) {\n                for (int i02 = 0; i02 < ne02; i02++) {\n                    for (int i01 = 0; i01 < ne01; i01++) {\n                        for (int i00 = 0; i00 < ne00; i00++) {\n                            const ggml_fp16_t * src0_ptr = (ggml_fp16_t *) ((char *) src0->data + i00*nb00 + i01*nb01 + i02*nb02 + i03*nb03);\n\n                            dst_ptr[id] = GGML_FP16_TO_FP32(*src0_ptr);\n                            id++;\n                        }\n                    }\n                }\n            }\n        } else {\n            GGML_ASSERT(false); // TODO: implement\n        }\n    } else {\n        //printf(\"%s: this is not optimal - fix me\\n\", __func__);\n\n        if (dst->type == GGML_TYPE_F32) {\n            int id = 0;\n            float * dst_ptr = (float *) dst->data;\n\n            for (int i03 = 0; i03 < ne03; i03++) {\n                for (int i02 = 0; i02 < ne02; i02++) {\n                    for (int i01 = 0; i01 < ne01; i01++) {\n                        for (int i00 = 0; i00 < ne00; i00++) {\n                            const ggml_fp16_t * src0_ptr = (ggml_fp16_t *) ((char *) src0->data + i00*nb00 + i01*nb01 + i02*nb02 + i03*nb03);\n\n                            dst_ptr[id] = GGML_FP16_TO_FP32(*src0_ptr);\n                            id++;\n                        }\n                    }\n                }\n            }\n        } else if (dst->type == GGML_TYPE_F16) {\n            int id = 0;\n            ggml_fp16_t * dst_ptr = (ggml_fp16_t *) dst->data;\n\n            for (int i03 = 0; i03 < ne03; i03++) {\n                for (int i02 = 0; i02 < ne02; i02++) {\n                    for (int i01 = 0; i01 < ne01; i01++) {\n                        for (int i00 = 0; i00 < ne00; i00++) {\n                            const ggml_fp16_t * src0_ptr = (ggml_fp16_t *) ((char *) src0->data + i00*nb00 + i01*nb01 + i02*nb02 + i03*nb03);\n\n                            dst_ptr[id] = *src0_ptr;\n                            id++;\n                        }\n                    }\n                }\n            }\n        } else {\n            GGML_ASSERT(false); // TODO: implement\n        }\n    }\n}\n\nstatic void ggml_compute_forward_dup_f32(\n        const struct ggml_compute_params * params,\n        const struct ggml_tensor * src0,\n        struct ggml_tensor * dst) {\n    GGML_ASSERT(params->ith == 0);\n    GGML_ASSERT(ggml_is_contiguous(dst));\n    GGML_ASSERT(ggml_nelements(dst) == ggml_nelements(src0));\n\n    if (params->type == GGML_TASK_INIT || params->type == GGML_TASK_FINALIZE) {\n        return;\n    }\n\n    const int ne00 = src0->ne[0];\n    const int ne01 = src0->ne[1];\n    const int ne02 = src0->ne[2];\n    const int ne03 = src0->ne[3];\n\n    const size_t nb00 = src0->nb[0];\n    const size_t nb01 = src0->nb[1];\n    const size_t nb02 = src0->nb[2];\n    const size_t nb03 = src0->nb[3];\n\n    if (ggml_is_contiguous(src0) && src0->type == dst->type) {\n        memcpy(dst->data, src0->data, ggml_nelements(dst) * GGML_TYPE_SIZE[src0->type]);\n        return;\n    }\n\n    if (src0->nb[0] == sizeof(float)) {\n        if (dst->type == GGML_TYPE_F32) {\n            int id = 0;\n            const size_t rs = ne00*nb00;\n\n            for (int i03 = 0; i03 < ne03; i03++) {\n                for (int i02 = 0; i02 < ne02; i02++) {\n                    for (int i01 = 0; i01 < ne01; i01++) {\n                        const char * src0_ptr = (char *) src0->data + i01*nb01 + i02*nb02 + i03*nb03;\n                        char * dst_ptr = (char *) dst->data + id*rs;\n\n                        memcpy(dst_ptr, src0_ptr, rs);\n\n                        id++;\n                    }\n                }\n            }\n        } else if (dst->type == GGML_TYPE_F16) {\n            int id = 0;\n            ggml_fp16_t * dst_ptr = (ggml_fp16_t *) dst->data;\n\n            for (int i03 = 0; i03 < ne03; i03++) {\n                for (int i02 = 0; i02 < ne02; i02++) {\n                    for (int i01 = 0; i01 < ne01; i01++) {\n                        for (int i00 = 0; i00 < ne00; i00++) {\n                            const float * src0_ptr = (float *) ((char *) src0->data + i00*nb00 + i01*nb01 + i02*nb02 + i03*nb03);\n\n                            dst_ptr[id] = GGML_FP32_TO_FP16(*src0_ptr);\n                            id++;\n                        }\n                    }\n                }\n            }\n        } else {\n            GGML_ASSERT(false); // TODO: implement\n        }\n    } else {\n        //printf(\"%s: this is not optimal - fix me\\n\", __func__);\n\n        if (dst->type == GGML_TYPE_F32) {\n            int id = 0;\n            float * dst_ptr = (float *) dst->data;\n\n            for (int i03 = 0; i03 < ne03; i03++) {\n                for (int i02 = 0; i02 < ne02; i02++) {\n                    for (int i01 = 0; i01 < ne01; i01++) {\n                        for (int i00 = 0; i00 < ne00; i00++) {\n                            const float * src0_ptr = (float *) ((char *) src0->data + i00*nb00 + i01*nb01 + i02*nb02 + i03*nb03);\n\n                            dst_ptr[id] = *src0_ptr;\n                            id++;\n                        }\n                    }\n                }\n            }\n        } else if (dst->type == GGML_TYPE_F16) {\n            int id = 0;\n            ggml_fp16_t * dst_ptr = (ggml_fp16_t *) dst->data;\n\n            for (int i03 = 0; i03 < ne03; i03++) {\n                for (int i02 = 0; i02 < ne02; i02++) {\n                    for (int i01 = 0; i01 < ne01; i01++) {\n                        for (int i00 = 0; i00 < ne00; i00++) {\n                            const float * src0_ptr = (float *) ((char *) src0->data + i00*nb00 + i01*nb01 + i02*nb02 + i03*nb03);\n\n                            dst_ptr[id] = GGML_FP32_TO_FP16(*src0_ptr);\n                            id++;\n                        }\n                    }\n                }\n            }\n        } else {\n            GGML_ASSERT(false); // TODO: implement\n        }\n    }\n}\n\nstatic void ggml_compute_forward_dup(\n        const struct ggml_compute_params * params,\n        const struct ggml_tensor * src0,\n        struct ggml_tensor * dst) {\n    switch (src0->type) {\n        case GGML_TYPE_F16:\n            {\n                ggml_compute_forward_dup_f16(params, src0, dst);\n            } break;\n        case GGML_TYPE_F32:\n            {\n                ggml_compute_forward_dup_f32(params, src0, dst);\n            } break;\n        case GGML_TYPE_I8:\n        case GGML_TYPE_I16:\n        case GGML_TYPE_I32:\n        case GGML_TYPE_COUNT:\n            {\n                GGML_ASSERT(false);\n            } break;\n    }\n}\n\n// ggml_compute_forward_add\n\nstatic void ggml_compute_forward_add_f32(\n        const struct ggml_compute_params * params,\n        const struct ggml_tensor * src0,\n        const struct ggml_tensor * src1,\n        struct ggml_tensor * dst) {\n    GGML_ASSERT(ggml_are_same_shape(src0, src1) && ggml_are_same_shape(src0, dst));\n\n    if (params->type == GGML_TASK_INIT || params->type == GGML_TASK_FINALIZE) {\n        return;\n    }\n\n    const int ith = params->ith;\n    const int nth = params->nth;\n\n    const int n  = ggml_nrows(src0);\n    const int nc = src0->ne[0];\n\n    const size_t nb00 = src0->nb[0];\n    const size_t nb01 = src0->nb[1];\n\n    const size_t nb10 = src1->nb[0];\n    const size_t nb11 = src1->nb[1];\n\n    const size_t nb0 = dst->nb[0];\n    const size_t nb1 = dst->nb[1];\n\n    GGML_ASSERT( nb0 == sizeof(float));\n    GGML_ASSERT(nb00 == sizeof(float));\n\n    if (nb10 == sizeof(float)) {\n        const int j0 = (n/nth)*ith;\n        const int j1 = ith == nth - 1 ? n : (n/nth)*(ith + 1);\n\n        for (int j = j0; j < j1; j++) {\n            ggml_vec_add_f32(nc,\n                    (float *) ((char *) dst->data  + j*nb1),\n                    (float *) ((char *) src0->data + j*nb01),\n                    (float *) ((char *) src1->data + j*nb11));\n        }\n    } else {\n        // src1 is not contiguous\n        for (int j = ith; j < n; j += nth) {\n            float * dst_ptr  = (float *) ((char *) dst->data  + j*nb1);\n            float * src0_ptr = (float *) ((char *) src0->data + j*nb01);\n            for (int i = 0; i < nc; i++) {\n                float * src1_ptr = (float *) ((char *) src1->data + j*nb11 + i*nb10);\n\n                dst_ptr[i] = src0_ptr[i] + *src1_ptr;\n            }\n        }\n    }\n}\n\nstatic void ggml_compute_forward_add(\n        const struct ggml_compute_params * params,\n        const struct ggml_tensor * src0,\n        const struct ggml_tensor * src1,\n        struct ggml_tensor * dst) {\n    switch (src0->type) {\n        case GGML_TYPE_F32:\n            {\n                ggml_compute_forward_add_f32(params, src0, src1, dst);\n            } break;\n        case GGML_TYPE_I8:\n        case GGML_TYPE_I16:\n        case GGML_TYPE_I32:\n        case GGML_TYPE_F16:\n        case GGML_TYPE_COUNT:\n            {\n                assert(false);\n            } break;\n    }\n}\n\n// ggml_compute_forward_sub\n\nstatic void ggml_compute_forward_sub_f32(\n        const struct ggml_compute_params * params,\n        const struct ggml_tensor * src0,\n        const struct ggml_tensor * src1,\n        struct ggml_tensor * dst) {\n    assert(params->ith == 0);\n    assert(ggml_are_same_shape(src0, src1) && ggml_are_same_shape(src0, dst));\n\n    if (params->type == GGML_TASK_INIT || params->type == GGML_TASK_FINALIZE) {\n        return;\n    }\n\n    const int n  = ggml_nrows(src0);\n    const int nc = src0->ne[0];\n\n    assert( dst->nb[0] == sizeof(float));\n    assert(src0->nb[0] == sizeof(float));\n    assert(src1->nb[0] == sizeof(float));\n\n    for (int i = 0; i < n; i++) {\n        ggml_vec_sub_f32(nc,\n                (float *) ((char *) dst->data  + i*( dst->nb[1])),\n                (float *) ((char *) src0->data + i*(src0->nb[1])),\n                (float *) ((char *) src1->data + i*(src1->nb[1])));\n    }\n}\n\nstatic void ggml_compute_forward_sub(\n        const struct ggml_compute_params * params,\n        const struct ggml_tensor * src0,\n        const struct ggml_tensor * src1,\n        struct ggml_tensor * dst) {\n    switch (src0->type) {\n        case GGML_TYPE_F32:\n            {\n                ggml_compute_forward_sub_f32(params, src0, src1, dst);\n            } break;\n        case GGML_TYPE_I8:\n        case GGML_TYPE_I16:\n        case GGML_TYPE_I32:\n        case GGML_TYPE_F16:\n        case GGML_TYPE_COUNT:\n            {\n                assert(false);\n            } break;\n    }\n}\n\n// ggml_compute_forward_mul\n\nstatic void ggml_compute_forward_mul_f32(\n        const struct ggml_compute_params * params,\n        const struct ggml_tensor * src0,\n        const struct ggml_tensor * src1,\n        struct ggml_tensor * dst) {\n    assert(params->ith == 0);\n    assert(ggml_are_same_shape(src0, src1) && ggml_are_same_shape(src0, dst));\n\n    if (params->type == GGML_TASK_INIT || params->type == GGML_TASK_FINALIZE) {\n        return;\n    }\n\n    const int n  = ggml_nrows(src0);\n    const int nc = src0->ne[0];\n\n    assert( dst->nb[0] == sizeof(float));\n    assert(src0->nb[0] == sizeof(float));\n    assert(src1->nb[0] == sizeof(float));\n\n    for (int i = 0; i < n; i++) {\n        ggml_vec_mul_f32(nc,\n                (float *) ((char *) dst->data  + i*( dst->nb[1])),\n                (float *) ((char *) src0->data + i*(src0->nb[1])),\n                (float *) ((char *) src1->data + i*(src1->nb[1])));\n    }\n}\n\nstatic void ggml_compute_forward_mul(\n        const struct ggml_compute_params * params,\n        const struct ggml_tensor * src0,\n        const struct ggml_tensor * src1,\n        struct ggml_tensor * dst) {\n    switch (src0->type) {\n        case GGML_TYPE_F32:\n            {\n                ggml_compute_forward_mul_f32(params, src0, src1, dst);\n            } break;\n        case GGML_TYPE_I8:\n        case GGML_TYPE_I16:\n        case GGML_TYPE_I32:\n        case GGML_TYPE_F16:\n        case GGML_TYPE_COUNT:\n            {\n                assert(false);\n            } break;\n    }\n}\n\n// ggml_compute_forward_div\n\nstatic void ggml_compute_forward_div_f32(\n        const struct ggml_compute_params * params,\n        const struct ggml_tensor * src0,\n        const struct ggml_tensor * src1,\n        struct ggml_tensor * dst) {\n    assert(params->ith == 0);\n    assert(ggml_are_same_shape(src0, src1) && ggml_are_same_shape(src0, dst));\n\n    if (params->type == GGML_TASK_INIT || params->type == GGML_TASK_FINALIZE) {\n        return;\n    }\n\n    const int n  = ggml_nrows(src0);\n    const int nc = src0->ne[0];\n\n    assert( dst->nb[0] == sizeof(float));\n    assert(src0->nb[0] == sizeof(float));\n    assert(src1->nb[0] == sizeof(float));\n\n    for (int i = 0; i < n; i++) {\n        ggml_vec_div_f32(nc,\n                (float *) ((char *) dst->data  + i*( dst->nb[1])),\n                (float *) ((char *) src0->data + i*(src0->nb[1])),\n                (float *) ((char *) src1->data + i*(src1->nb[1])));\n    }\n}\n\nstatic void ggml_compute_forward_div(\n        const struct ggml_compute_params * params,\n        const struct ggml_tensor * src0,\n        const struct ggml_tensor * src1,\n        struct ggml_tensor * dst) {\n    switch (src0->type) {\n        case GGML_TYPE_F32:\n            {\n                ggml_compute_forward_div_f32(params, src0, src1, dst);\n            } break;\n        case GGML_TYPE_I8:\n        case GGML_TYPE_I16:\n        case GGML_TYPE_I32:\n        case GGML_TYPE_F16:\n        case GGML_TYPE_COUNT:\n            {\n                assert(false);\n            } break;\n    }\n}\n\n// ggml_compute_forward_sqr\n\nstatic void ggml_compute_forward_sqr_f32(\n        const struct ggml_compute_params * params,\n        const struct ggml_tensor * src0,\n        struct ggml_tensor * dst) {\n    assert(params->ith == 0);\n    assert(ggml_are_same_shape(src0, dst));\n\n    if (params->type == GGML_TASK_INIT || params->type == GGML_TASK_FINALIZE) {\n        return;\n    }\n\n    const int n     = ggml_nrows(src0);\n    const int nc    = src0->ne[0];\n\n    assert( dst->nb[0] == sizeof(float));\n    assert(src0->nb[0] == sizeof(float));\n\n    for (int i = 0; i < n; i++) {\n        ggml_vec_sqr_f32(nc,\n                (float *) ((char *) dst->data  + i*( dst->nb[1])),\n                (float *) ((char *) src0->data + i*(src0->nb[1])));\n    }\n}\n\nstatic void ggml_compute_forward_sqr(\n        const struct ggml_compute_params * params,\n        const struct ggml_tensor * src0,\n        struct ggml_tensor * dst) {\n    switch (src0->type) {\n        case GGML_TYPE_F32:\n            {\n                ggml_compute_forward_sqr_f32(params, src0, dst);\n            } break;\n        case GGML_TYPE_I8:\n        case GGML_TYPE_I16:\n        case GGML_TYPE_I32:\n        case GGML_TYPE_F16:\n        case GGML_TYPE_COUNT:\n            {\n                assert(false);\n            } break;\n    }\n}\n\n// ggml_compute_forward_sqrt\n\nstatic void ggml_compute_forward_sqrt_f32(\n        const struct ggml_compute_params * params,\n        const struct ggml_tensor * src0,\n        struct ggml_tensor * dst) {\n    assert(params->ith == 0);\n    assert(ggml_are_same_shape(src0, dst));\n\n    if (params->type == GGML_TASK_INIT || params->type == GGML_TASK_FINALIZE) {\n        return;\n    }\n\n    const int n  = ggml_nrows(src0);\n    const int nc = src0->ne[0];\n\n    assert( dst->nb[0] == sizeof(float));\n    assert(src0->nb[0] == sizeof(float));\n\n    for (int i = 0; i < n; i++) {\n        ggml_vec_sqrt_f32(nc,\n                (float *) ((char *) dst->data  + i*( dst->nb[1])),\n                (float *) ((char *) src0->data + i*(src0->nb[1])));\n    }\n}\n\nstatic void ggml_compute_forward_sqrt(\n        const struct ggml_compute_params * params,\n        const struct ggml_tensor * src0,\n        struct ggml_tensor * dst) {\n    switch (src0->type) {\n        case GGML_TYPE_F32:\n            {\n                ggml_compute_forward_sqrt_f32(params, src0, dst);\n            } break;\n        case GGML_TYPE_I8:\n        case GGML_TYPE_I16:\n        case GGML_TYPE_I32:\n        case GGML_TYPE_F16:\n        case GGML_TYPE_COUNT:\n            {\n                assert(false);\n            } break;\n    }\n}\n\n// ggml_compute_forward_sum\n\nstatic void ggml_compute_forward_sum_f32(\n        const struct ggml_compute_params * params,\n        const struct ggml_tensor * src0,\n        struct ggml_tensor * dst) {\n    assert(params->ith == 0);\n    assert(ggml_is_scalar(dst));\n\n    if (params->type == GGML_TASK_INIT || params->type == GGML_TASK_FINALIZE) {\n        return;\n    }\n\n    assert(ggml_is_scalar(dst));\n    assert(src0->nb[0] == sizeof(float));\n\n    *(float *) (dst->data) = 0.0f;\n\n    const int ne00 = src0->ne[0];\n    const int ne01 = src0->ne[1];\n    const int ne02 = src0->ne[2];\n    const int ne03 = src0->ne[3];\n\n    const size_t nb01 = src0->nb[1];\n    const size_t nb02 = src0->nb[2];\n    const size_t nb03 = src0->nb[3];\n\n    for (int i03 = 0; i03 < ne03; i03++) {\n        for (int i02 = 0; i02 < ne02; i02++) {\n            for (int i01 = 0; i01 < ne01; i01++) {\n                ggml_vec_sum_f32(ne00,\n                        (float *) (dst->data),\n                        (float *) ((char *) src0->data + i01*nb01 + i02*nb02 + i03*nb03));\n            }\n        }\n    }\n}\n\nstatic void ggml_compute_forward_sum(\n        const struct ggml_compute_params * params,\n        const struct ggml_tensor * src0,\n        struct ggml_tensor * dst) {\n    switch (src0->type) {\n        case GGML_TYPE_F32:\n            {\n                ggml_compute_forward_sum_f32(params, src0, dst);\n            } break;\n        case GGML_TYPE_I8:\n        case GGML_TYPE_I16:\n        case GGML_TYPE_I32:\n        case GGML_TYPE_F16:\n        case GGML_TYPE_COUNT:\n            {\n                assert(false);\n            } break;\n    }\n}\n\n// ggml_compute_forward_mean\n\nstatic void ggml_compute_forward_mean_f32(\n        const struct ggml_compute_params * params,\n        const struct ggml_tensor * src0,\n        struct ggml_tensor * dst) {\n    assert(params->ith == 0);\n\n    if (params->type == GGML_TASK_INIT || params->type == GGML_TASK_FINALIZE) {\n        return;\n    }\n\n    assert(src0->nb[0] == sizeof(float));\n\n    const int ne00 = src0->ne[0];\n    const int ne01 = src0->ne[1];\n    const int ne02 = src0->ne[2];\n    const int ne03 = src0->ne[3];\n\n    const size_t nb01 = src0->nb[1];\n    const size_t nb02 = src0->nb[2];\n    const size_t nb03 = src0->nb[3];\n\n    const int ne0 = dst->ne[0];\n    const int ne1 = dst->ne[1];\n    const int ne2 = dst->ne[2];\n    const int ne3 = dst->ne[3];\n\n    assert(ne0 == 1);\n    assert(ne1 == ne01);\n    assert(ne2 == ne02);\n    assert(ne3 == ne03);\n\n    UNUSED(ne0);\n    UNUSED(ne1);\n    UNUSED(ne2);\n    UNUSED(ne3);\n\n    const size_t nb1 = dst->nb[1];\n    const size_t nb2 = dst->nb[2];\n    const size_t nb3 = dst->nb[3];\n\n    for (int i03 = 0; i03 < ne03; i03++) {\n        for (int i02 = 0; i02 < ne02; i02++) {\n            for (int i01 = 0; i01 < ne01; i01++) {\n                *(float *) ((char *) dst->data + i01*nb1 + i02*nb2 + i03*nb3) = 0.0f;\n\n                ggml_vec_sum_f32(ne00,\n                        (float *) ((char *)  dst->data + i01*nb1  + i02*nb2  + i03*nb3),\n                        (float *) ((char *) src0->data + i01*nb01 + i02*nb02 + i03*nb03));\n\n                *(float *) ((char *) dst->data + i01*nb1 + i02*nb2 + i03*nb3) /= (float) ne00;\n            }\n        }\n    }\n}\n\nstatic void ggml_compute_forward_mean(\n        const struct ggml_compute_params * params,\n        const struct ggml_tensor * src0,\n        struct ggml_tensor * dst) {\n    switch (src0->type) {\n        case GGML_TYPE_F32:\n            {\n                ggml_compute_forward_mean_f32(params, src0, dst);\n            } break;\n        case GGML_TYPE_I8:\n        case GGML_TYPE_I16:\n        case GGML_TYPE_I32:\n        case GGML_TYPE_F16:\n        case GGML_TYPE_COUNT:\n            {\n                assert(false);\n            } break;\n    }\n}\n\n// ggml_compute_forward_repeat\n\nstatic void ggml_compute_forward_repeat_f32(\n        const struct ggml_compute_params * params,\n        const struct ggml_tensor * src0,\n        struct ggml_tensor * dst) {\n    assert(params->ith == 0);\n    assert(ggml_can_repeat(src0, dst));\n\n    if (params->type == GGML_TASK_INIT || params->type == GGML_TASK_FINALIZE) {\n        return;\n    }\n\n    // TODO: implement support for rank > 2 tensors\n    assert(src0->ne[2] == 1);\n    assert(src0->ne[3] == 1);\n    assert( dst->ne[2] == 1);\n    assert( dst->ne[3] == 1);\n\n    const int nc  = dst->ne[0];\n    const int nr  = dst->ne[1];\n    const int nc0 = src0->ne[0];\n    const int nr0 = src0->ne[1];\n    const int ncr = nc/nc0; // guaranteed to be an integer due to the check in ggml_can_repeat\n    const int nrr = nr/nr0; // guaranteed to be an integer due to the check in ggml_can_repeat\n\n    // TODO: support for transposed / permuted tensors\n    assert( dst->nb[0] == sizeof(float));\n    assert(src0->nb[0] == sizeof(float));\n\n    // TODO: maybe this is not optimal?\n    for (int i = 0; i < nrr; i++) {\n        for (int j = 0; j < ncr; j++) {\n            for (int k = 0; k < nr0; k++) {\n                ggml_vec_cpy_f32(nc0,\n                        (float *) ((char *)  dst->data + (i*nr0 + k)*( dst->nb[1]) + j*nc0*( dst->nb[0])),\n                        (float *) ((char *) src0->data + (        k)*(src0->nb[1])));\n            }\n        }\n    }\n}\n\nstatic void ggml_compute_forward_repeat(\n        const struct ggml_compute_params * params,\n        const struct ggml_tensor * src0,\n        struct ggml_tensor * dst) {\n    switch (src0->type) {\n        case GGML_TYPE_F32:\n            {\n                ggml_compute_forward_repeat_f32(params, src0, dst);\n            } break;\n        case GGML_TYPE_I8:\n        case GGML_TYPE_I16:\n        case GGML_TYPE_I32:\n        case GGML_TYPE_F16:\n        case GGML_TYPE_COUNT:\n            {\n                assert(false);\n            } break;\n    }\n}\n\n// ggml_compute_forward_abs\n\nstatic void ggml_compute_forward_abs_f32(\n        const struct ggml_compute_params * params,\n        const struct ggml_tensor * src0,\n        struct ggml_tensor * dst) {\n    assert(params->ith == 0);\n    assert(ggml_are_same_shape(src0, dst));\n\n    if (params->type == GGML_TASK_INIT || params->type == GGML_TASK_FINALIZE) {\n        return;\n    }\n\n    const int n  = ggml_nrows(src0);\n    const int nc = src0->ne[0];\n\n    assert(dst->nb[0]  == sizeof(float));\n    assert(src0->nb[0] == sizeof(float));\n\n    for (int i = 0; i < n; i++) {\n        ggml_vec_abs_f32(nc,\n                (float *) ((char *) dst->data  + i*( dst->nb[1])),\n                (float *) ((char *) src0->data + i*(src0->nb[1])));\n    }\n}\n\nstatic void ggml_compute_forward_abs(\n        const struct ggml_compute_params * params,\n        const struct ggml_tensor * src0,\n        struct ggml_tensor * dst) {\n    switch (src0->type) {\n        case GGML_TYPE_F32:\n            {\n                ggml_compute_forward_abs_f32(params, src0, dst);\n            } break;\n        case GGML_TYPE_I8:\n        case GGML_TYPE_I16:\n        case GGML_TYPE_I32:\n        case GGML_TYPE_F16:\n        case GGML_TYPE_COUNT:\n            {\n                assert(false);\n            } break;\n    }\n}\n\n// ggml_compute_forward_sgn\n\nstatic void ggml_compute_forward_sgn_f32(\n        const struct ggml_compute_params * params,\n        const struct ggml_tensor * src0,\n        struct ggml_tensor * dst) {\n    assert(params->ith == 0);\n    assert(ggml_are_same_shape(src0, dst));\n\n    if (params->type == GGML_TASK_INIT || params->type == GGML_TASK_FINALIZE) {\n        return;\n    }\n\n    const int n  = ggml_nrows(src0);\n    const int nc = src0->ne[0];\n\n    assert(dst->nb[0]  == sizeof(float));\n    assert(src0->nb[0] == sizeof(float));\n\n    for (int i = 0; i < n; i++) {\n        ggml_vec_sgn_f32(nc,\n                (float *) ((char *) dst->data  + i*( dst->nb[1])),\n                (float *) ((char *) src0->data + i*(src0->nb[1])));\n    }\n}\n\nstatic void ggml_compute_forward_sgn(\n        const struct ggml_compute_params * params,\n        const struct ggml_tensor * src0,\n        struct ggml_tensor * dst) {\n    switch (src0->type) {\n        case GGML_TYPE_F32:\n            {\n                ggml_compute_forward_sgn_f32(params, src0, dst);\n            } break;\n        case GGML_TYPE_I8:\n        case GGML_TYPE_I16:\n        case GGML_TYPE_I32:\n        case GGML_TYPE_F16:\n        case GGML_TYPE_COUNT:\n            {\n                assert(false);\n            } break;\n    }\n}\n\n// ggml_compute_forward_neg\n\nstatic void ggml_compute_forward_neg_f32(\n        const struct ggml_compute_params * params,\n        const struct ggml_tensor * src0,\n        struct ggml_tensor * dst) {\n    assert(params->ith == 0);\n    assert(ggml_are_same_shape(src0, dst));\n\n    if (params->type == GGML_TASK_INIT || params->type == GGML_TASK_FINALIZE) {\n        return;\n    }\n\n    const int n  = ggml_nrows(src0);\n    const int nc = src0->ne[0];\n\n    assert(dst->nb[0]  == sizeof(float));\n    assert(src0->nb[0] == sizeof(float));\n\n    for (int i = 0; i < n; i++) {\n        ggml_vec_neg_f32(nc,\n                (float *) ((char *) dst->data  + i*( dst->nb[1])),\n                (float *) ((char *) src0->data + i*(src0->nb[1])));\n    }\n}\n\nstatic void ggml_compute_forward_neg(\n        const struct ggml_compute_params * params,\n        const struct ggml_tensor * src0,\n        struct ggml_tensor * dst) {\n    switch (src0->type) {\n        case GGML_TYPE_F32:\n            {\n                ggml_compute_forward_neg_f32(params, src0, dst);\n            } break;\n        case GGML_TYPE_I8:\n        case GGML_TYPE_I16:\n        case GGML_TYPE_I32:\n        case GGML_TYPE_F16:\n        case GGML_TYPE_COUNT:\n            {\n                assert(false);\n            } break;\n    }\n}\n\n// ggml_compute_forward_step\n\nstatic void ggml_compute_forward_step_f32(\n        const struct ggml_compute_params * params,\n        const struct ggml_tensor * src0,\n        struct ggml_tensor * dst) {\n    assert(params->ith == 0);\n    assert(ggml_are_same_shape(src0, dst));\n\n    if (params->type == GGML_TASK_INIT || params->type == GGML_TASK_FINALIZE) {\n        return;\n    }\n\n    const int n  = ggml_nrows(src0);\n    const int nc = src0->ne[0];\n\n    assert(dst->nb[0]  == sizeof(float));\n    assert(src0->nb[0] == sizeof(float));\n\n    for (int i = 0; i < n; i++) {\n        ggml_vec_step_f32(nc,\n                (float *) ((char *) dst->data  + i*( dst->nb[1])),\n                (float *) ((char *) src0->data + i*(src0->nb[1])));\n    }\n}\n\nstatic void ggml_compute_forward_step(\n        const struct ggml_compute_params * params,\n        const struct ggml_tensor * src0,\n        struct ggml_tensor * dst) {\n    switch (src0->type) {\n        case GGML_TYPE_F32:\n            {\n                ggml_compute_forward_step_f32(params, src0, dst);\n            } break;\n        case GGML_TYPE_I8:\n        case GGML_TYPE_I16:\n        case GGML_TYPE_I32:\n        case GGML_TYPE_F16:\n        case GGML_TYPE_COUNT:\n            {\n                assert(false);\n            } break;\n    }\n}\n\n// ggml_compute_forward_relu\n\nstatic void ggml_compute_forward_relu_f32(\n        const struct ggml_compute_params * params,\n        const struct ggml_tensor * src0,\n        struct ggml_tensor * dst) {\n    assert(params->ith == 0);\n    assert(ggml_are_same_shape(src0, dst));\n\n    if (params->type == GGML_TASK_INIT || params->type == GGML_TASK_FINALIZE) {\n        return;\n    }\n\n    const int n  = ggml_nrows(src0);\n    const int nc = src0->ne[0];\n\n    assert(dst->nb[0]  == sizeof(float));\n    assert(src0->nb[0] == sizeof(float));\n\n    for (int i = 0; i < n; i++) {\n        ggml_vec_relu_f32(nc,\n                (float *) ((char *) dst->data  + i*( dst->nb[1])),\n                (float *) ((char *) src0->data + i*(src0->nb[1])));\n    }\n}\n\nstatic void ggml_compute_forward_relu(\n        const struct ggml_compute_params * params,\n        const struct ggml_tensor * src0,\n        struct ggml_tensor * dst) {\n    switch (src0->type) {\n        case GGML_TYPE_F32:\n            {\n                ggml_compute_forward_relu_f32(params, src0, dst);\n            } break;\n        case GGML_TYPE_I8:\n        case GGML_TYPE_I16:\n        case GGML_TYPE_I32:\n        case GGML_TYPE_F16:\n        case GGML_TYPE_COUNT:\n            {\n                assert(false);\n            } break;\n    }\n}\n\n// ggml_compute_forward_gelu\n\nstatic void ggml_compute_forward_gelu_f32(\n        const struct ggml_compute_params * params,\n        const struct ggml_tensor * src0,\n        struct ggml_tensor * dst) {\n    GGML_ASSERT(ggml_is_contiguous(src0));\n    GGML_ASSERT(ggml_is_contiguous(dst));\n    GGML_ASSERT(ggml_are_same_shape(src0, dst));\n\n    if (params->type == GGML_TASK_INIT || params->type == GGML_TASK_FINALIZE) {\n        return;\n    }\n\n    const int ith = params->ith;\n    const int nth = params->nth;\n\n    const int nc = src0->ne[0];\n    const int nr = ggml_nrows(src0);\n\n    // rows per thread\n    const int dr = (nr + nth - 1)/nth;\n\n    // row range for this thread\n    const int ir0 = dr*ith;\n    const int ir1 = MIN(ir0 + dr, nr);\n\n    for (int i1 = ir0; i1 < ir1; i1++) {\n        ggml_vec_gelu_f32(nc,\n                (float *) ((char *) dst->data  + i1*( dst->nb[1])),\n                (float *) ((char *) src0->data + i1*(src0->nb[1])));\n\n#ifndef NDEBUG\n        for (int k = 0; k < nc; k++) {\n            const float x = ((float *) ((char *) dst->data + i1*( dst->nb[1])))[k];\n            UNUSED(x);\n            assert(!isnan(x));\n            assert(!isinf(x));\n        }\n#endif\n    }\n}\n\nstatic void ggml_compute_forward_gelu(\n        const struct ggml_compute_params * params,\n        const struct ggml_tensor * src0,\n        struct ggml_tensor * dst) {\n    switch (src0->type) {\n        case GGML_TYPE_F32:\n            {\n                ggml_compute_forward_gelu_f32(params, src0, dst);\n            } break;\n        case GGML_TYPE_I8:\n        case GGML_TYPE_I16:\n        case GGML_TYPE_I32:\n        case GGML_TYPE_F16:\n        case GGML_TYPE_COUNT:\n            {\n                assert(false);\n            } break;\n    }\n}\n\n// ggml_compute_forward_norm\n\nstatic void ggml_compute_forward_norm_f32(\n        const struct ggml_compute_params * params,\n        const struct ggml_tensor * src0,\n        struct ggml_tensor * dst) {\n    GGML_ASSERT(ggml_are_same_shape(src0, dst));\n\n    if (params->type == GGML_TASK_INIT || params->type == GGML_TASK_FINALIZE) {\n        return;\n    }\n\n    GGML_ASSERT(src0->nb[0] == sizeof(float));\n\n    const int ith = params->ith;\n    const int nth = params->nth;\n\n    const int ne00 = src0->ne[0];\n    const int ne01 = src0->ne[1];\n    const int ne02 = src0->ne[2];\n    const int ne03 = src0->ne[3];\n\n    const size_t nb01 = src0->nb[1];\n    const size_t nb02 = src0->nb[2];\n    const size_t nb03 = src0->nb[3];\n\n    const size_t nb1 = dst->nb[1];\n    const size_t nb2 = dst->nb[2];\n    const size_t nb3 = dst->nb[3];\n\n    const ggml_float eps = 1e-5f; // TODO: make this a parameter\n\n    // TODO: optimize\n    for (int i03 = 0; i03 < ne03; i03++) {\n        for (int i02 = 0; i02 < ne02; i02++) {\n            for (int i01 = ith; i01 < ne01; i01 += nth) {\n                const float * x = (float *) ((char *) src0->data + i01*nb01 + i02*nb02 + i03*nb03);\n\n                ggml_float mean = 0.0;\n                for (int i00 = 0; i00 < ne00; i00++) {\n                    mean += x[i00];\n                }\n\n                mean /= ne00;\n\n                float * y = (float *) ((char *) dst->data + i01*nb1 + i02*nb2 + i03*nb3);\n\n                ggml_float sum2 = 0.0;\n                for (int i00 = 0; i00 < ne00; i00++) {\n                    ggml_float v = x[i00] - mean;\n                    y[i00] = v;\n                    sum2 += v*v;\n                }\n\n                const float scale = 1.0/sqrt(sum2/ne00 + eps);\n\n                ggml_vec_scale_f32(ne00, y, scale);\n            }\n        }\n    }\n}\n\nstatic void ggml_compute_forward_norm(\n        const struct ggml_compute_params * params,\n        const struct ggml_tensor * src0,\n        struct ggml_tensor * dst) {\n    switch (src0->type) {\n        case GGML_TYPE_F32:\n            {\n                ggml_compute_forward_norm_f32(params, src0, dst);\n            } break;\n        case GGML_TYPE_I8:\n        case GGML_TYPE_I16:\n        case GGML_TYPE_I32:\n        case GGML_TYPE_F16:\n        case GGML_TYPE_COUNT:\n            {\n                assert(false);\n            } break;\n    }\n}\n\n// ggml_compute_forward_mul_mat\n\n#if defined(GGML_USE_ACCELERATE) || defined(GGML_USE_OPENBLAS)\n// helper function to determine if it is better to use BLAS or not\n// for large matrices, BLAS is faster\nstatic bool ggml_compute_forward_mul_mat_use_blas(\n        const struct ggml_tensor * src0,\n        const struct ggml_tensor * src1,\n              struct ggml_tensor * dst) {\n    UNUSED(src0);\n\n    const int ne10 = src1->ne[0];\n\n    const int ne0 = dst->ne[0];\n    const int ne1 = dst->ne[1];\n\n    // TODO: find the optimal values for these\n    if (ggml_is_contiguous(src0) && ggml_is_contiguous(src1) && ne0 >= 32 && ne1 >= 32 && ne10 >= 32) {\n        //printf(\"BLAS: %d %d %d\\n\", ne0, ne1, ne10);\n        return true;\n    }\n\n    return false;\n}\n#endif\n\nstatic void ggml_compute_forward_mul_mat_f32(\n        const struct ggml_compute_params * params,\n        const struct ggml_tensor * src0,\n        const struct ggml_tensor * src1,\n              struct ggml_tensor * dst) {\n    int64_t t0 = ggml_perf_time_us();\n    UNUSED(t0);\n\n    const int ne00 = src0->ne[0];\n    const int ne01 = src0->ne[1];\n    const int ne02 = src0->ne[2];\n    const int ne03 = src0->ne[3];\n\n    const int ne10 = src1->ne[0];\n    const int ne11 = src1->ne[1];\n    const int ne12 = src1->ne[2];\n    const int ne13 = src1->ne[3];\n\n    const int ne0  = dst->ne[0];\n    const int ne1  = dst->ne[1];\n    const int ne2  = dst->ne[2];\n    const int ne3  = dst->ne[3];\n    const int ne   = ne0*ne1*ne2*ne3;\n\n    const int nb00 = src0->nb[0];\n    const int nb01 = src0->nb[1];\n    const int nb02 = src0->nb[2];\n    const int nb03 = src0->nb[3];\n\n    const int nb10 = src1->nb[0];\n    const int nb11 = src1->nb[1];\n    const int nb12 = src1->nb[2];\n    const int nb13 = src1->nb[3];\n\n    const int nb0  = dst->nb[0];\n    const int nb1  = dst->nb[1];\n    const int nb2  = dst->nb[2];\n    const int nb3  = dst->nb[3];\n\n    const int ith = params->ith;\n    const int nth = params->nth;\n\n    assert(ne02 == ne12);\n    assert(ne03 == ne13);\n    assert(ne2  == ne12);\n    assert(ne3  == ne13);\n\n    // TODO: we don't support permuted src0\n    assert(nb00 == sizeof(float) || nb01 == sizeof(float));\n\n    // dst cannot be transposed or permuted\n    assert(nb0 == sizeof(float));\n    assert(nb0 <= nb1);\n    assert(nb1 <= nb2);\n    assert(nb2 <= nb3);\n\n    assert(ne0 == ne01);\n    assert(ne1 == ne11);\n    assert(ne2 == ne02);\n    assert(ne3 == ne03);\n\n    // nb01 >= nb00 - src0 is not transposed\n    //   compute by src0 rows\n    //\n    // nb00 <  nb01 - src0 is transposed\n    //   compute by src0 columns\n\n#if defined(GGML_USE_ACCELERATE) || defined(GGML_USE_OPENBLAS)\n    if (ggml_compute_forward_mul_mat_use_blas(src0, src1, dst)) {\n        GGML_ASSERT(nb10 == sizeof(float));\n\n        if (params->ith != 0) return;\n\n        if (params->type == GGML_TASK_INIT) {\n            return;\n        }\n\n        if (params->type == GGML_TASK_FINALIZE) {\n            return;\n        }\n\n        for (int i03 = 0; i03 < ne03; i03++) {\n            for (int i02 = 0; i02 < ne02; i02++) {\n                const float * x = (float *) (src0->data);\n                const float * y = (float *) ((char *) src1->data + i02*nb12 + i03*nb13);\n\n                float * d = (float *) ((char *) dst->data + i02*nb2 + i03*nb3);\n\n                // zT = y * xT\n                {\n                    cblas_sgemm(CblasRowMajor, CblasNoTrans, CblasTrans,\n                            ne11, ne01, ne10,\n                            1.0f,    y, ne10,\n                                     x, ne10,\n                            0.0f,    d, ne01);\n                }\n            }\n        }\n\n        //printf(\"CBLAS F32 = %f ms, %d x %d x %d x %d\\n\", (ggml_perf_time_us() - t0)/1000.0, ne0, ne1, ne2, ne3);\n\n        return;\n    }\n#endif\n\n    if (params->type == GGML_TASK_INIT) {\n        if (nb01 >= nb00) {\n            return;\n        }\n\n        // TODO: fix this memset (wsize is overestimated)\n        memset(params->wdata, 0, params->wsize);\n        return;\n    }\n\n    if (params->type == GGML_TASK_FINALIZE) {\n        if (nb01 >= nb00) {\n            return;\n        }\n\n        // TODO: fix this memset (wsize is overestimated)\n        //assert(params->wsize == (ggml_nbytes(dst) + CACHE_LINE_SIZE)*nth);\n\n        float * const wdata = params->wdata;\n\n        // cols per thread\n        const int dc = (ne + nth - 1)/nth;\n\n        // col range for this thread\n        const int ic0 = dc*ith;\n        const int ic1 = MIN(ic0 + dc, ne);\n\n        ggml_vec_cpy_f32(ic1 - ic0, (float *) dst->data + ic0, wdata + ic0);\n\n        for (int k = 1; k < nth; k++) {\n            ggml_vec_acc_f32(ic1 - ic0, (float *) dst->data + ic0, wdata + (ne + CACHE_LINE_SIZE_F32)*k + ic0);\n        }\n\n        return;\n    }\n\n    if (nb01 >= nb00) {\n        // TODO: do not support transposed src1\n        assert(nb10 == sizeof(float));\n\n        // parallelize by src0 rows using ggml_vec_dot_f32\n\n        // total rows in src0\n        const int nr = ne01*ne02*ne03;\n\n        // rows per thread\n        const int dr = (nr + nth - 1)/nth;\n\n        // row range for this thread\n        const int ir0 = dr*ith;\n        const int ir1 = MIN(ir0 + dr, nr);\n\n        for (int ir = ir0; ir < ir1; ++ir) {\n            // src0 indices\n            const int i03 = ir/(ne02*ne01);\n            const int i02 = (ir - i03*ne02*ne01)/ne01;\n            const int i01 = (ir - i03*ne02*ne01 - i02*ne01);\n\n            for (int ic = 0; ic < ne11; ++ic) {\n                // src1 indices\n                const int i13 = i03;\n                const int i12 = i02;\n                const int i11 = ic;\n\n                // dst indices\n                const int i0 = i01;\n                const int i1 = i11;\n                const int i2 = i02;\n                const int i3 = i03;\n\n                ggml_vec_dot_f32(ne00,\n                        (float *) ((char *)  dst->data + (i0*nb0 + i1*nb1 + i2*nb2 + i3*nb3)),\n                        (float *) ((char *) src0->data + (i01*nb01 + i02*nb02 + i03*nb03)),\n                        (float *) ((char *) src1->data + (i11*nb11 + i12*nb12 + i13*nb13)));\n            }\n        }\n    } else {\n        // parallelize by src1 columns using ggml_vec_mad_f32\n        // each thread has its own work data\n        // during FINALIZE we accumulate all work data into dst\n\n        // total columns in src1\n        const int nc = ne10;\n\n        // columns per thread\n        const int dc = (nc + nth - 1)/nth;\n\n        // column range for this thread\n        const int ic0 = dc*ith;\n        const int ic1 = MIN(ic0 + dc, nc);\n\n        // work data for thread\n        const int wo = (ne + CACHE_LINE_SIZE_F32)*ith;\n        float * const wdata = params->wdata;\n\n        for (int i13 = 0; i13 < ne13; ++i13) {\n            for (int i12 = 0; i12 < ne12; ++i12) {\n                for (int i11 = 0; i11 < ne11; ++i11) {\n                    for (int ic = ic0; ic < ic1; ++ic) {\n                        // src1 indices\n                        const int i10 = ic;\n\n                        // src0 indices\n                        const int i03 = i13;\n                        const int i02 = i12;\n                        const int i00 = ic;\n\n                        // dst indices\n                        const int i1 = i11;\n                        const int i2 = i12;\n                        const int i3 = i13;\n\n                        assert(sizeof(float)*(wo + i3*ne2*ne1*ne0 + i2*ne1*ne0 + i1*ne0 + ne01) <= params->wsize);\n\n                        ggml_vec_mad_f32(ne01,\n                                (float *) (wdata + wo + i3*ne2*ne1*ne0 + i2*ne1*ne0 + i1*ne0),\n                                (float *) ((char *) src0->data + (i00*nb00 + i02*nb02 + i03*nb03)),\n                               *(float *) ((char *) src1->data + (i10*nb10 + i11*nb11 + i12*nb12 + i13*nb13)));\n                    }\n                }\n            }\n        }\n    }\n\n    //int64_t t1 = ggml_perf_time_us();\n    //static int64_t acc = 0;\n    //acc += t1 - t0;\n    //if (t1 - t0 > 10) {\n    //    printf(\"\\n\");\n    //    printf(\"ne00 = %5d, ne01 = %5d, ne02 = %5d, ne03 = %5d\\n\", ne00, ne01, ne02, ne03);\n    //    printf(\"nb00 = %5d, nb01 = %5d, nb02 = %5d, nb03 = %5d\\n\", nb00, nb01, nb02, nb03);\n    //    printf(\"ne10 = %5d, ne11 = %5d, ne12 = %5d, ne13 = %5d\\n\", ne10, ne11, ne12, ne13);\n    //    printf(\"nb10 = %5d, nb11 = %5d, nb12 = %5d, nb13 = %5d\\n\", nb10, nb11, nb12, nb13);\n\n    //    printf(\"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX task %d/%d: %d us, acc = %d\\n\", ith, nth, (int) (t1 - t0), (int) acc);\n    //}\n}\n\nstatic void ggml_compute_forward_mul_mat_f16_f32(\n        const struct ggml_compute_params * params,\n        const struct ggml_tensor * src0,\n        const struct ggml_tensor * src1,\n              struct ggml_tensor * dst) {\n    int64_t t0 = ggml_perf_time_us();\n    UNUSED(t0);\n\n    const int ne00 = src0->ne[0];\n    const int ne01 = src0->ne[1];\n    const int ne02 = src0->ne[2];\n    const int ne03 = src0->ne[3];\n\n    const int ne10 = src1->ne[0];\n    const int ne11 = src1->ne[1];\n    const int ne12 = src1->ne[2];\n    const int ne13 = src1->ne[3];\n\n    const int ne0  = dst->ne[0];\n    const int ne1  = dst->ne[1];\n    const int ne2  = dst->ne[2];\n    const int ne3  = dst->ne[3];\n    const int ne   = ne0*ne1*ne2*ne3;\n\n    const int nb00 = src0->nb[0];\n    const int nb01 = src0->nb[1];\n    const int nb02 = src0->nb[2];\n    const int nb03 = src0->nb[3];\n\n    const int nb10 = src1->nb[0];\n    const int nb11 = src1->nb[1];\n    const int nb12 = src1->nb[2];\n    const int nb13 = src1->nb[3];\n\n    const int nb0  = dst->nb[0];\n    const int nb1  = dst->nb[1];\n    const int nb2  = dst->nb[2];\n    const int nb3  = dst->nb[3];\n\n    const int ith = params->ith;\n    const int nth = params->nth;\n\n    GGML_ASSERT(ne02 == ne12);\n    GGML_ASSERT(ne03 == ne13);\n    GGML_ASSERT(ne2  == ne12);\n    GGML_ASSERT(ne3  == ne13);\n\n    // TODO: we don't support permuted src0\n    GGML_ASSERT(nb00 == sizeof(ggml_fp16_t) || nb01 == sizeof(ggml_fp16_t));\n\n    // dst cannot be transposed or permuted\n    GGML_ASSERT(nb0 == sizeof(float));\n    GGML_ASSERT(nb0 <= nb1);\n    GGML_ASSERT(nb1 <= nb2);\n    GGML_ASSERT(nb2 <= nb3);\n\n    GGML_ASSERT(ne0 == ne01);\n    GGML_ASSERT(ne1 == ne11);\n    GGML_ASSERT(ne2 == ne02);\n    GGML_ASSERT(ne3 == ne03);\n\n    // nb01 >= nb00 - src0 is not transposed\n    //   compute by src0 rows\n    //\n    // nb00 <  nb01 - src0 is transposed\n    //   compute by src0 columns\n\n#if defined(GGML_USE_ACCELERATE) || defined(GGML_USE_OPENBLAS)\n    if (ggml_compute_forward_mul_mat_use_blas(src0, src1, dst)) {\n        GGML_ASSERT(nb10 == sizeof(float));\n\n        if (params->ith != 0) return;\n\n        if (params->type == GGML_TASK_INIT) {\n            return;\n        }\n\n        if (params->type == GGML_TASK_FINALIZE) {\n            return;\n        }\n\n        float * const wdata = params->wdata;\n\n        for (int i03 = 0; i03 < ne03; i03++) {\n            for (int i02 = 0; i02 < ne02; i02++) {\n                {\n                    int id = 0;\n                    for (int i01 = 0; i01 < ne01; ++i01) {\n                        for (int i00 = 0; i00 < ne00; ++i00) {\n                            wdata[id++] = GGML_FP16_TO_FP32(*(ggml_fp16_t *) ((char *) src0->data + i03*nb03 + i02*nb02 + i01*nb01 + i00*nb00));\n                        }\n                    }\n                }\n\n                const float * x = wdata;\n                const float * y = (float *) ((char *) src1->data + i02*nb12 + i03*nb13);\n\n                //      float * z =                          wdata + ne00*ne01;\n\n                // z = x * yT\n                //{\n                //    cblas_sgemm(CblasRowMajor, CblasNoTrans, CblasTrans,\n                //            ne01, ne11, ne00,\n                //            1.0f, x, ne00,\n                //                  y, ne00,\n                //            0.0f, z, ne11);\n                //}\n\n                float * d = (float *) ((char *) dst->data + i02*nb2 + i03*nb3);\n\n                // transpose z\n                //for (int j = 0; j < ne11; ++j) {\n                //    for (int i = 0; i < ne01; ++i) {\n                //        d[j*ne01 + i] = z[i*ne11 + j];\n                //    }\n                //}\n\n                {\n#if 1\n                    // zT = y * xT\n                    cblas_sgemm(CblasRowMajor, CblasNoTrans, CblasTrans,\n                            ne11, ne01, ne10,\n                            1.0f,    y, ne00,\n                                     x, ne00,\n                            0.0f,    d, ne01);\n#else\n                    // zT = (xT * y)T\n                    cblas_sgemm(CblasColMajor, CblasTrans, CblasNoTrans,\n                            ne01, ne11, ne10,\n                            1.0f,    x, ne00,\n                                     y, ne00,\n                            0.0f,    d, ne01);\n#endif\n                }\n            }\n        }\n\n        //printf(\"CBLAS = %f ms, %d x %d x %d x %d\\n\", (ggml_perf_time_us() - t0)/1000.0, ne0, ne1, ne2, ne3);\n\n        return;\n    }\n#endif\n\n    if (params->type == GGML_TASK_INIT) {\n        if (nb01 >= nb00) {\n            ggml_fp16_t * const wdata = params->wdata;\n\n            int id = 0;\n            for (int i13 = 0; i13 < ne13; ++i13) {\n                for (int i12 = 0; i12 < ne12; ++i12) {\n                    for (int i11 = 0; i11 < ne11; ++i11) {\n                        for (int i10 = 0; i10 < ne10; ++i10) {\n                            wdata[id++] = GGML_FP32_TO_FP16(*(float *)((char *) src1->data + i13*nb13 + i12*nb12 + i11*nb11 + i10*nb10));\n                        }\n                    }\n                }\n            }\n\n            GGML_ASSERT(id*sizeof(ggml_fp16_t) <= params->wsize);\n\n            return;\n        }\n\n        // TODO: fix this memset (wsize is overestimated)\n        memset(params->wdata, 0, params->wsize);\n        return;\n    }\n\n    if (params->type == GGML_TASK_FINALIZE) {\n        if (nb01 >= nb00) {\n            return;\n        }\n\n        // TODO: fix this memset (wsize is overestimated)\n        //assert(params->wsize == (ggml_nbytes(dst) + CACHE_LINE_SIZE)*nth);\n\n        ggml_fp16_t * const wdata = params->wdata;\n\n        // cols per thread\n        const int dc = (ne + nth - 1)/nth;\n\n        // col range for this thread\n        const int ic0 = dc*ith;\n        const int ic1 = MIN(ic0 + dc, ne);\n\n        for (int i = ic0; i < ic1; ++i) {\n            ((float *) dst->data)[i] = GGML_FP16_TO_FP32(wdata[i]);\n        }\n\n        for (int k = 1; k < nth; k++) {\n            for (int i = ic0; i < ic1; ++i) {\n                ((float *) dst->data)[i] += GGML_FP16_TO_FP32(wdata[(ne + CACHE_LINE_SIZE_F32)*k + i]);\n            }\n        }\n\n        return;\n    }\n\n    if (nb01 >= nb00) {\n        // fp16 -> half the size, so divide by 2\n        // TODO: do not support transposed src1\n        assert(nb10/2 == sizeof(ggml_fp16_t));\n\n        // parallelize by src0 rows using ggml_vec_dot_f32\n\n        // total rows in src0\n        const int nr = ne01*ne02*ne03;\n\n        // rows per thread\n        const int dr = (nr + nth - 1)/nth;\n\n        // row range for this thread\n        const int ir0 = dr*ith;\n        const int ir1 = MIN(ir0 + dr, nr);\n\n        ggml_fp16_t * wdata = params->wdata;\n\n        for (int ir = ir0; ir < ir1; ++ir) {\n            // src0 indices\n            const int i03 = ir/(ne02*ne01);\n            const int i02 = (ir - i03*ne02*ne01)/ne01;\n            const int i01 = (ir - i03*ne02*ne01 - i02*ne01);\n\n            const int i13 = i03;\n            const int i12 = i02;\n\n            const int i0 = i01;\n            const int i2 = i02;\n            const int i3 = i03;\n\n            ggml_fp16_t * src0_row = (ggml_fp16_t *) ((char *) src0->data + (i01*nb01 + i02*nb02 + i03*nb03));\n            ggml_fp16_t * src1_col = wdata + (i13*ne12*ne11 + i12*ne11 + 0)*ne00;\n\n            float * dst_col = (float *) ((char *) dst->data + (i0*nb0 + 0*nb1 + i2*nb2 + i3*nb3));\n\n            for (int ic = 0; ic < ne11; ++ic) {\n                assert(ne00 % 32 == 0);\n\n                ggml_vec_dot_f16(ne00, &dst_col[ic*ne0], src0_row, src1_col + ic*ne00);\n            }\n        }\n    } else {\n        // parallelize by src1 columns using ggml_vec_mad_f32\n        // each thread has its own work data\n        // during FINALIZE we accumulate all work data into dst\n\n        // total columns in src1\n        const int nc = ne10;\n\n        // columns per thread\n        const int dc = (nc + nth - 1)/nth;\n\n        // column range for this thread\n        const int ic0 = dc*ith;\n        const int ic1 = MIN(ic0 + dc, nc);\n\n        // work data for thread\n        const int wo = (ne + CACHE_LINE_SIZE_F32)*ith;\n        ggml_fp16_t * const wdata = params->wdata;\n\n        for (int i13 = 0; i13 < ne13; ++i13) {\n            for (int i12 = 0; i12 < ne12; ++i12) {\n                for (int i11 = 0; i11 < ne11; ++i11) {\n                    // dst indices\n                    const int i1 = i11;\n                    const int i2 = i12;\n                    const int i3 = i13;\n\n                    ggml_fp16_t * dst_row = wdata + wo + i3*ne2*ne1*ne0 + i2*ne1*ne0 + i1*ne0;\n\n                    for (int ic = ic0; ic < ic1; ++ic) {\n                        // src1 indices\n                        const int i10 = ic;\n\n                        // src0 indices\n                        const int i03 = i13;\n                        const int i02 = i12;\n                        const int i00 = ic;\n\n                        assert(sizeof(ggml_fp16_t)*(wo + i3*ne2*ne1*ne0 + i2*ne1*ne0 + i1*ne0 + ne01) <= params->wsize);\n\n                        ggml_fp16_t * src0_col =  (ggml_fp16_t *) ((char *) src0->data + (i00*nb00 + i02*nb02 + i03*nb03));\n                        float         src1_val = *      (float *) ((char *) src1->data + (i10*nb10 + i11*nb11 + i12*nb12 + i13*nb13));\n\n                        ggml_vec_mad_f16(ne01, dst_row, src0_col, src1_val);\n                    }\n                }\n            }\n        }\n    }\n\n    //int64_t t1 = ggml_time_us();\n    //static int64_t acc = 0;\n    //acc += t1 - t0;\n    //if (t1 - t0 > 10) {\n    //    printf(\"\\n\");\n    //    printf(\"ne00 = %5d, ne01 = %5d, ne02 = %5d, ne03 = %5d\\n\", ne00, ne01, ne02, ne03);\n    //    printf(\"nb00 = %5d, nb01 = %5d, nb02 = %5d, nb03 = %5d\\n\", nb00, nb01, nb02, nb03);\n    //    printf(\"ne10 = %5d, ne11 = %5d, ne12 = %5d, ne13 = %5d\\n\", ne10, ne11, ne12, ne13);\n\n    //    printf(\"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX task %d/%d: %d us, acc = %d\\n\", ith, nth, (int) (t1 - t0), (int) acc);\n    //}\n}\n\nstatic void ggml_compute_forward_mul_mat(\n        const struct ggml_compute_params * params,\n        const struct ggml_tensor * src0,\n        const struct ggml_tensor * src1,\n        struct ggml_tensor * dst) {\n    switch (src0->type) {\n        case GGML_TYPE_F16:\n            {\n                ggml_compute_forward_mul_mat_f16_f32(params, src0, src1, dst);\n            } break;\n        case GGML_TYPE_F32:\n            {\n                ggml_compute_forward_mul_mat_f32(params, src0, src1, dst);\n            } break;\n        case GGML_TYPE_I8:\n        case GGML_TYPE_I16:\n        case GGML_TYPE_I32:\n        case GGML_TYPE_COUNT:\n            {\n                assert(false);\n            } break;\n    }\n}\n\n// ggml_compute_forward_scale\n\nstatic void ggml_compute_forward_scale_f32(\n        const struct ggml_compute_params * params,\n        const struct ggml_tensor * src0,\n        const struct ggml_tensor * src1,\n        struct ggml_tensor * dst) {\n    GGML_ASSERT(ggml_is_contiguous(src0));\n    GGML_ASSERT(ggml_is_contiguous(dst));\n    GGML_ASSERT(ggml_are_same_shape(src0, dst));\n    GGML_ASSERT(ggml_is_scalar(src1));\n\n    if (params->type == GGML_TASK_INIT || params->type == GGML_TASK_FINALIZE) {\n        return;\n    }\n\n    // scale factor\n    const float v = *(float *) src1->data;\n\n    const int ith = params->ith;\n    const int nth = params->nth;\n\n    const int nc = src0->ne[0];\n    const int nr = ggml_nrows(src0);\n\n    // rows per thread\n    const int dr = (nr + nth - 1)/nth;\n\n    // row range for this thread\n    const int ir0 = dr*ith;\n    const int ir1 = MIN(ir0 + dr, nr);\n\n    for (int i1 = ir0; i1 < ir1; i1++) {\n        ggml_vec_scale_f32(nc, (float *) ((char *) dst->data + i1*(dst->nb[1])), v);\n    }\n}\n\nstatic void ggml_compute_forward_scale(\n        const struct ggml_compute_params * params,\n        const struct ggml_tensor * src0,\n        const struct ggml_tensor * src1,\n        struct ggml_tensor * dst) {\n    switch (src0->type) {\n        case GGML_TYPE_F32:\n            {\n                ggml_compute_forward_scale_f32(params, src0, src1, dst);\n            } break;\n        case GGML_TYPE_I8:\n        case GGML_TYPE_I16:\n        case GGML_TYPE_I32:\n        case GGML_TYPE_F16:\n        case GGML_TYPE_COUNT:\n            {\n                assert(false);\n            } break;\n    }\n}\n\n// ggml_compute_forward_cpy\n\nstatic void ggml_compute_forward_cpy(\n        const struct ggml_compute_params * params,\n        const struct ggml_tensor * src0,\n        struct ggml_tensor * dst) {\n    ggml_compute_forward_dup(params, src0, dst);\n}\n\n// ggml_compute_forward_reshape\n\nstatic void ggml_compute_forward_reshape(\n        const struct ggml_compute_params * params,\n        const struct ggml_tensor * src0,\n        struct ggml_tensor * dst) {\n    // NOP\n    UNUSED(params);\n    UNUSED(src0);\n    UNUSED(dst);\n}\n\n// ggml_compute_forward_view\n\nstatic void ggml_compute_forward_view(\n        const struct ggml_compute_params * params,\n        const struct ggml_tensor * src0) {\n    // NOP\n    UNUSED(params);\n    UNUSED(src0);\n}\n\n// ggml_compute_forward_permute\n\nstatic void ggml_compute_forward_permute(\n        const struct ggml_compute_params * params,\n        const struct ggml_tensor * src0) {\n    // NOP\n    UNUSED(params);\n    UNUSED(src0);\n}\n\n// ggml_compute_forward_transpose\n\nstatic void ggml_compute_forward_transpose(\n        const struct ggml_compute_params * params,\n        const struct ggml_tensor * src0) {\n    // NOP\n    UNUSED(params);\n    UNUSED(src0);\n}\n\n// ggml_compute_forward_get_rows\n\nstatic void ggml_compute_forward_get_rows_f16(\n        const struct ggml_compute_params * params,\n        const struct ggml_tensor * src0,\n        const struct ggml_tensor * src1,\n              struct ggml_tensor * dst) {\n    assert(params->ith == 0);\n\n    if (params->type == GGML_TASK_INIT || params->type == GGML_TASK_FINALIZE) {\n        return;\n    }\n\n    const int nc = src0->ne[0];\n    const int nr = ggml_nelements(src1);\n\n    assert( dst->ne[0] == nc);\n    assert( dst->ne[1] == nr);\n    assert(src0->nb[0] == sizeof(ggml_fp16_t));\n\n    for (int i = 0; i < nr; ++i) {\n        const int r = ((int32_t *) src1->data)[i];\n\n        for (int j = 0; j < nc; ++j) {\n            ggml_fp16_t v = ((ggml_fp16_t *) ((char *) src0->data + r*src0->nb[1]))[j];\n            ((float *) ((char *)  dst->data + i*dst->nb[1]))[j] = GGML_FP16_TO_FP32(v);\n        }\n    }\n}\n\nstatic void ggml_compute_forward_get_rows_f32(\n        const struct ggml_compute_params * params,\n        const struct ggml_tensor * src0,\n        const struct ggml_tensor * src1,\n              struct ggml_tensor * dst) {\n    assert(params->ith == 0);\n\n    if (params->type == GGML_TASK_INIT || params->type == GGML_TASK_FINALIZE) {\n        return;\n    }\n\n    const int nc = src0->ne[0];\n    const int nr = ggml_nelements(src1);\n\n    assert( dst->ne[0] == nc);\n    assert( dst->ne[1] == nr);\n    assert(src0->nb[0] == sizeof(float));\n\n    for (int i = 0; i < nr; ++i) {\n        const int r = ((int32_t *) src1->data)[i];\n\n        ggml_vec_cpy_f32(nc,\n                (float *) ((char *)  dst->data + i*dst->nb[1]),\n                (float *) ((char *) src0->data + r*src0->nb[1]));\n    }\n}\n\nstatic void ggml_compute_forward_get_rows(\n        const struct ggml_compute_params * params,\n        const struct ggml_tensor * src0,\n        const struct ggml_tensor * src1,\n        struct ggml_tensor * dst) {\n    switch (src0->type) {\n        case GGML_TYPE_F16:\n            {\n                ggml_compute_forward_get_rows_f16(params, src0, src1, dst);\n            } break;\n        case GGML_TYPE_F32:\n            {\n                ggml_compute_forward_get_rows_f32(params, src0, src1, dst);\n            } break;\n        case GGML_TYPE_I8:\n        case GGML_TYPE_I16:\n        case GGML_TYPE_I32:\n        case GGML_TYPE_COUNT:\n            {\n                assert(false);\n            } break;\n    }\n}\n\n// ggml_compute_forward_diag_mask_inf\n\nstatic void ggml_compute_forward_diag_mask_inf_f32(\n        const struct ggml_compute_params * params,\n        const struct ggml_tensor * src0,\n        const struct ggml_tensor * src1,\n        struct ggml_tensor * dst) {\n    assert(params->ith == 0);\n    assert(src1->type == GGML_TYPE_I32);\n    assert(ggml_nelements(src1) == 1);\n\n    if (params->type == GGML_TASK_INIT || params->type == GGML_TASK_FINALIZE) {\n        return;\n    }\n\n    const int n_past = ((int32_t *) src1->data)[0];\n\n    // TODO: handle transposed/permuted matrices\n\n    const int n  = ggml_nrows(src0);\n    const int nc = src0->ne[0];\n    const int nr = src0->ne[1];\n    const int nz = n/nr;\n\n    assert( dst->nb[0] == sizeof(float));\n    assert(src0->nb[0] == sizeof(float));\n\n    for (int k = 0; k < nz; k++) {\n        for (int j = 0; j < nr; j++) {\n            for (int i = n_past; i < nc; i++) {\n                if (i > n_past + j) {\n                    *(float *)((char *) dst->data + k*dst->nb[2] + j*dst->nb[1] + i*dst->nb[0]) = -INFINITY;\n                }\n            }\n        }\n    }\n}\n\nstatic void ggml_compute_forward_diag_mask_inf(\n        const struct ggml_compute_params * params,\n        const struct ggml_tensor * src0,\n        const struct ggml_tensor * src1,\n        struct ggml_tensor * dst) {\n    switch (src0->type) {\n        case GGML_TYPE_F32:\n            {\n                ggml_compute_forward_diag_mask_inf_f32(params, src0, src1, dst);\n            } break;\n        case GGML_TYPE_I8:\n        case GGML_TYPE_I16:\n        case GGML_TYPE_I32:\n        case GGML_TYPE_F16:\n        case GGML_TYPE_COUNT:\n            {\n                assert(false);\n            } break;\n    }\n}\n\n// ggml_compute_forward_soft_max\n\nstatic void ggml_compute_forward_soft_max_f32(\n        const struct ggml_compute_params * params,\n        const struct ggml_tensor * src0,\n        struct ggml_tensor * dst) {\n    GGML_ASSERT(ggml_is_contiguous(src0));\n    GGML_ASSERT(ggml_is_contiguous(dst));\n    GGML_ASSERT(ggml_are_same_shape(src0, dst));\n\n    if (params->type == GGML_TASK_INIT || params->type == GGML_TASK_FINALIZE) {\n        return;\n    }\n\n    // TODO: handle transposed/permuted matrices\n\n    const int ith = params->ith;\n    const int nth = params->nth;\n\n    const int nc = src0->ne[0];\n    const int nr = ggml_nrows(src0);\n\n    // rows per thread\n    const int dr = (nr + nth - 1)/nth;\n\n    // row range for this thread\n    const int ir0 = dr*ith;\n    const int ir1 = MIN(ir0 + dr, nr);\n\n    for (int i1 = ir0; i1 < ir1; i1++) {\n        float *p = (float *)((char *) dst->data + i1*dst->nb[1]);\n\n#ifndef NDEBUG\n        for (int i = 0; i < nc; ++i) {\n            assert(!isnan(p[i]));\n        }\n#endif\n\n        float max = -INFINITY;\n        for (int i = 0; i < nc; i++) {\n            max = MAX(max, p[i]);\n        }\n\n        ggml_float sum = 0.0;\n\n        uint16_t ss;\n        for (int i = 0; i < nc; i++) {\n            if (p[i] == -INFINITY) {\n                p[i] = 0.0;\n            } else {\n                //const float val = (p[i] == -INFINITY) ? 0.0 : exp(p[i] - max);\n                ggml_fp16_t s = GGML_FP32_TO_FP16(p[i] - max);\n                memcpy(&ss, &s, sizeof(ss));\n                const float val = GGML_FP16_TO_FP32(table_exp_f16[ss]);\n                sum += val;\n                p[i] = val;\n            }\n        }\n\n        assert(sum > 0.0f);\n\n        sum = 1.0/sum;\n        ggml_vec_scale_f32(nc, p, sum);\n\n#ifndef NDEBUG\n        for (int i = 0; i < nc; ++i) {\n            assert(!isnan(p[i]));\n            assert(!isinf(p[i]));\n        }\n#endif\n    }\n}\n\nstatic void ggml_compute_forward_soft_max(\n        const struct ggml_compute_params * params,\n        const struct ggml_tensor * src0,\n        struct ggml_tensor * dst) {\n    switch (src0->type) {\n        case GGML_TYPE_F32:\n            {\n                ggml_compute_forward_soft_max_f32(params, src0, dst);\n            } break;\n        case GGML_TYPE_I8:\n        case GGML_TYPE_I16:\n        case GGML_TYPE_I32:\n        case GGML_TYPE_F16:\n        case GGML_TYPE_COUNT:\n            {\n                assert(false);\n            } break;\n    }\n}\n\n// ggml_compute_forward_rope\n\nstatic void ggml_compute_forward_rope_f32(\n        const struct ggml_compute_params * params,\n        const struct ggml_tensor * src0,\n        const struct ggml_tensor * src1,\n        struct ggml_tensor * dst) {\n    assert(params->ith == 0);\n    assert(src1->type == GGML_TYPE_I32);\n    assert(ggml_nelements(src1) == 3);\n\n    if (params->type == GGML_TASK_INIT || params->type == GGML_TASK_FINALIZE) {\n        return;\n    }\n\n    const int n_past = ((int32_t *) src1->data)[0];\n    const int n_dims = ((int32_t *) src1->data)[1];\n    const int mode   = ((int32_t *) src1->data)[2];\n\n    //const int ne0 = src0->ne[0];\n    const int ne1 = src0->ne[1];\n    const int ne2 = src0->ne[2];\n    const int ne3 = src0->ne[3];\n\n    const int nb0 = src0->nb[0];\n    const int nb1 = src0->nb[1];\n    const int nb2 = src0->nb[2];\n    const int nb3 = src0->nb[3];\n\n    //printf(\"ne0: %d, ne1: %d, ne2: %d, ne3: %d\\n\", ne0, ne1, ne2, ne3);\n    //printf(\"n_past = %d, ne2 = %d\\n\", n_past, ne2);\n\n    assert(nb0 == sizeof(float));\n\n    // TODO: optimize\n    for (int i3 = 0; i3 < ne3; i3++) {\n        for (int i2 = (mode == 0 ? 0 : n_past); i2 < ne2; i2++) {\n            const int p = (mode == 0 ? n_past + i2 : i2);\n            for (int i1 = 0; i1 < ne1; i1++) {\n                for (int i0 = 0; i0 < n_dims; i0 += 2) {\n                    const double theta = pow(10000.0, ((double)-i0)/n_dims);\n\n                    const double cos_theta = cos(p*theta);\n                    const double sin_theta = sin(p*theta);\n\n                    const float * const src = (float *)((char *) src0->data + i3*nb3 + i2*nb2 + i1*nb1 + i0*nb0);\n                          float * dst_data  = (float *)((char *)  dst->data + i3*nb3 + i2*nb2 + i1*nb1 + i0*nb0);\n\n                    double x0 = src[0];\n                    double x1 = src[1];\n\n                    dst_data[0] = x0*cos_theta - x1*sin_theta;\n                    dst_data[1] = x0*sin_theta + x1*cos_theta;\n                }\n            }\n        }\n    }\n}\n\nstatic void ggml_compute_forward_rope(\n        const struct ggml_compute_params * params,\n        const struct ggml_tensor * src0,\n        const struct ggml_tensor * src1,\n        struct ggml_tensor * dst) {\n    switch (src0->type) {\n        case GGML_TYPE_F32:\n            {\n                ggml_compute_forward_rope_f32(params, src0, src1, dst);\n            } break;\n        case GGML_TYPE_I8:\n        case GGML_TYPE_I16:\n        case GGML_TYPE_I32:\n        case GGML_TYPE_F16:\n        case GGML_TYPE_COUNT:\n            {\n                assert(false);\n            } break;\n    }\n}\n\n// ggml_compute_forward_conv_1d_1s\n\nstatic void ggml_compute_forward_conv_1d_1s_f16_f32(\n        const struct ggml_compute_params * params,\n        const struct ggml_tensor * src0,\n        const struct ggml_tensor * src1,\n              struct ggml_tensor * dst) {\n    GGML_ASSERT(src0->type == GGML_TYPE_F16);\n    GGML_ASSERT(src1->type == GGML_TYPE_F32);\n    GGML_ASSERT( dst->type == GGML_TYPE_F32);\n\n    int64_t t0 = ggml_perf_time_us();\n    UNUSED(t0);\n\n    const int ne00 = src0->ne[0];\n    const int ne01 = src0->ne[1];\n    const int ne02 = src0->ne[2];\n    //const int ne03 = src0->ne[3];\n\n    const int ne10 = src1->ne[0];\n    const int ne11 = src1->ne[1];\n    //const int ne12 = src1->ne[2];\n    //const int ne13 = src1->ne[3];\n\n    //const int ne0  = dst->ne[0];\n    //const int ne1  = dst->ne[1];\n    //const int ne2  = dst->ne[2];\n    //const int ne3  = dst->ne[3];\n    //const int ne   = ne0*ne1*ne2*ne3;\n\n    const int nb00 = src0->nb[0];\n    const int nb01 = src0->nb[1];\n    const int nb02 = src0->nb[2];\n    //const int nb03 = src0->nb[3];\n\n    const int nb10 = src1->nb[0];\n    const int nb11 = src1->nb[1];\n    //const int nb12 = src1->nb[2];\n    //const int nb13 = src1->nb[3];\n\n    //const int nb0  = dst->nb[0];\n    const int nb1  = dst->nb[1];\n    //const int nb2  = dst->nb[2];\n    //const int nb3  = dst->nb[3];\n\n    const int ith = params->ith;\n    const int nth = params->nth;\n\n    const int nk = ne00;\n    const int nh = nk/2;\n\n    const int ew0 = ggml_up32(ne01);\n\n    GGML_ASSERT(ne00 % 2 == 1); // TODO: support even kernel sizes\n    GGML_ASSERT(nb00 == sizeof(ggml_fp16_t));\n    GGML_ASSERT(nb10 == sizeof(float));\n\n    if (params->type == GGML_TASK_INIT) {\n        // TODO: fix this memset (wsize is overestimated)\n        memset(params->wdata, 0, params->wsize);\n\n        // prepare kernel data (src0)\n        {\n            ggml_fp16_t * const wdata = (ggml_fp16_t *) params->wdata + 0;\n\n            for (int i02 = 0; i02 < ne02; i02++) {\n                for (int i01 = 0; i01 < ne01; i01++) {\n                    const ggml_fp16_t * const src = (ggml_fp16_t *)((char *) src0->data + i02*nb02 + i01*nb01);\n                    ggml_fp16_t * dst_data = wdata + i02*ew0*ne00;\n                    for (int i00 = 0; i00 < ne00; i00++) {\n                        dst_data[i00*ew0 + i01] = src[i00];\n                    }\n                }\n            }\n        }\n\n        // prepare source data (src1)\n        {\n            ggml_fp16_t * const wdata = (ggml_fp16_t *) params->wdata + ne02*ew0*ne00;\n\n            for (int i11 = 0; i11 < ne11; i11++) {\n                const float * const src = (float *)((char *) src1->data + i11*nb11);\n                ggml_fp16_t * dst_data = wdata;\n                for (int i10 = 0; i10 < ne10; i10++) {\n                    dst_data[(i10 + nh)*ew0 + i11] = GGML_FP32_TO_FP16(src[i10]);\n                }\n            }\n        }\n\n        return;\n    }\n\n    if (params->type == GGML_TASK_FINALIZE) {\n        return;\n    }\n\n    // total rows in dst\n    const int nr = ne02;\n\n    // rows per thread\n    const int dr = (nr + nth - 1)/nth;\n\n    // row range for this thread\n    const int ir0 = dr*ith;\n    const int ir1 = MIN(ir0 + dr, nr);\n\n    for (int i1 = ir0; i1 < ir1; i1++) {\n        float * dst_data = (float *)((char *) dst->data + i1*nb1);\n        for (int i0 = 0; i0 < ne10; ++i0) {\n            dst_data[i0] = 0;\n            for (int k = -nh; k <= nh; k++) {\n                float v = 0.0f;\n                ggml_vec_dot_f16(ew0, &v,\n                        (ggml_fp16_t *) params->wdata +   i1*ew0*ne00 +      (nh + k)*ew0,\n                        (ggml_fp16_t *) params->wdata + ne02*ew0*ne00 + (i0 + nh + k)*ew0);\n\n                dst_data[i0] += v;\n            }\n        }\n    }\n}\n\nstatic void ggml_compute_forward_conv_1d_1s_f32(\n        const struct ggml_compute_params * params,\n        const struct ggml_tensor * src0,\n        const struct ggml_tensor * src1,\n              struct ggml_tensor * dst) {\n    GGML_ASSERT(src0->type == GGML_TYPE_F32);\n    GGML_ASSERT(src1->type == GGML_TYPE_F32);\n    GGML_ASSERT( dst->type == GGML_TYPE_F32);\n\n    int64_t t0 = ggml_perf_time_us();\n    UNUSED(t0);\n\n    const int ne00 = src0->ne[0];\n    const int ne01 = src0->ne[1];\n    const int ne02 = src0->ne[2];\n    //const int ne03 = src0->ne[3];\n\n    const int ne10 = src1->ne[0];\n    const int ne11 = src1->ne[1];\n    //const int ne12 = src1->ne[2];\n    //const int ne13 = src1->ne[3];\n\n    //const int ne0  = dst->ne[0];\n    //const int ne1  = dst->ne[1];\n    //const int ne2  = dst->ne[2];\n    //const int ne3  = dst->ne[3];\n    //const int ne   = ne0*ne1*ne2*ne3;\n\n    const int nb00 = src0->nb[0];\n    const int nb01 = src0->nb[1];\n    const int nb02 = src0->nb[2];\n    //const int nb03 = src0->nb[3];\n\n    const int nb10 = src1->nb[0];\n    const int nb11 = src1->nb[1];\n    //const int nb12 = src1->nb[2];\n    //const int nb13 = src1->nb[3];\n\n    //const int nb0  = dst->nb[0];\n    const int nb1  = dst->nb[1];\n    //const int nb2  = dst->nb[2];\n    //const int nb3  = dst->nb[3];\n\n    const int ith = params->ith;\n    const int nth = params->nth;\n\n    const int nk = ne00;\n    const int nh = nk/2;\n\n    const int ew0 = ggml_up32(ne01);\n\n    GGML_ASSERT(ne00 % 2 == 1); // TODO: support even kernel sizes\n    GGML_ASSERT(nb00 == sizeof(float));\n    GGML_ASSERT(nb10 == sizeof(float));\n\n    if (params->type == GGML_TASK_INIT) {\n        // TODO: fix this memset (wsize is overestimated)\n        memset(params->wdata, 0, params->wsize);\n\n        // prepare kernel data (src0)\n        {\n            float * const wdata = (float *) params->wdata + 0;\n\n            for (int i02 = 0; i02 < ne02; i02++) {\n                for (int i01 = 0; i01 < ne01; i01++) {\n                    const float * const src = (float *)((char *) src0->data + i02*nb02 + i01*nb01);\n                    float * dst_data = wdata + i02*ew0*ne00;\n                    for (int i00 = 0; i00 < ne00; i00++) {\n                        dst_data[i00*ew0 + i01] = src[i00];\n                    }\n                }\n            }\n        }\n\n        // prepare source data (src1)\n        {\n            float * const wdata = (float *) params->wdata + ne02*ew0*ne00;\n\n            for (int i11 = 0; i11 < ne11; i11++) {\n                const float * const src = (float *)((char *) src1->data + i11*nb11);\n                float * dst_data = wdata;\n                for (int i10 = 0; i10 < ne10; i10++) {\n                    dst_data[(i10 + nh)*ew0 + i11] = src[i10];\n                }\n            }\n        }\n\n        return;\n    }\n\n    if (params->type == GGML_TASK_FINALIZE) {\n        return;\n    }\n\n    // total rows in dst\n    const int nr = ne02;\n\n    // rows per thread\n    const int dr = (nr + nth - 1)/nth;\n\n    // row range for this thread\n    const int ir0 = dr*ith;\n    const int ir1 = MIN(ir0 + dr, nr);\n\n    for (int i1 = ir0; i1 < ir1; i1++) {\n        float * dst_data = (float *)((char *) dst->data + i1*nb1);\n        for (int i0 = 0; i0 < ne10; ++i0) {\n            dst_data[i0] = 0;\n            for (int k = -nh; k <= nh; k++) {\n                float v = 0.0f;\n                ggml_vec_dot_f32(ew0, &v,\n                        (float *) params->wdata +   i1*ew0*ne00 +      (nh + k)*ew0,\n                        (float *) params->wdata + ne02*ew0*ne00 + (i0 + nh + k)*ew0);\n\n                dst_data[i0] += v;\n            }\n        }\n    }\n}\n\nstatic void ggml_compute_forward_conv_1d_1s(\n        const struct ggml_compute_params * params,\n        const struct ggml_tensor * src0,\n        const struct ggml_tensor * src1,\n        struct ggml_tensor * dst) {\n    switch (src0->type) {\n        case GGML_TYPE_F16:\n            {\n                ggml_compute_forward_conv_1d_1s_f16_f32(params, src0, src1, dst);\n            } break;\n        case GGML_TYPE_F32:\n            {\n                ggml_compute_forward_conv_1d_1s_f32(params, src0, src1, dst);\n            } break;\n        case GGML_TYPE_I8:\n        case GGML_TYPE_I16:\n        case GGML_TYPE_I32:\n        case GGML_TYPE_COUNT:\n            {\n                GGML_ASSERT(false);\n            } break;\n    }\n}\n\n// ggml_compute_forward_conv_1d_2s\n\nstatic void ggml_compute_forward_conv_1d_2s_f16_f32(\n        const struct ggml_compute_params * params,\n        const struct ggml_tensor * src0,\n        const struct ggml_tensor * src1,\n              struct ggml_tensor * dst) {\n    GGML_ASSERT(src0->type == GGML_TYPE_F16);\n    GGML_ASSERT(src1->type == GGML_TYPE_F32);\n    GGML_ASSERT( dst->type == GGML_TYPE_F32);\n\n    int64_t t0 = ggml_perf_time_us();\n    UNUSED(t0);\n\n    const int ne00 = src0->ne[0];\n    const int ne01 = src0->ne[1];\n    const int ne02 = src0->ne[2];\n    //const int ne03 = src0->ne[3];\n\n    const int ne10 = src1->ne[0];\n    const int ne11 = src1->ne[1];\n    //const int ne12 = src1->ne[2];\n    //const int ne13 = src1->ne[3];\n\n    //const int ne0  = dst->ne[0];\n    //const int ne1  = dst->ne[1];\n    //const int ne2  = dst->ne[2];\n    //const int ne3  = dst->ne[3];\n    //const int ne   = ne0*ne1*ne2*ne3;\n\n    const int nb00 = src0->nb[0];\n    const int nb01 = src0->nb[1];\n    const int nb02 = src0->nb[2];\n    //const int nb03 = src0->nb[3];\n\n    const int nb10 = src1->nb[0];\n    const int nb11 = src1->nb[1];\n    //const int nb12 = src1->nb[2];\n    //const int nb13 = src1->nb[3];\n\n    //const int nb0  = dst->nb[0];\n    const int nb1  = dst->nb[1];\n    //const int nb2  = dst->nb[2];\n    //const int nb3  = dst->nb[3];\n\n    const int ith = params->ith;\n    const int nth = params->nth;\n\n    const int nk = ne00;\n    const int nh = nk/2;\n\n    const int ew0 = ggml_up32(ne01);\n\n    GGML_ASSERT(ne00 % 2 == 1); // TODO: support even kernel sizes\n    GGML_ASSERT(nb00 == sizeof(ggml_fp16_t));\n    GGML_ASSERT(nb10 == sizeof(float));\n\n    if (params->type == GGML_TASK_INIT) {\n        // TODO: fix this memset (wsize is overestimated)\n        memset(params->wdata, 0, params->wsize);\n\n        // prepare kernel data (src0)\n        {\n            ggml_fp16_t * const wdata = (ggml_fp16_t *) params->wdata + 0;\n\n            for (int i02 = 0; i02 < ne02; i02++) {\n                for (int i01 = 0; i01 < ne01; i01++) {\n                    const ggml_fp16_t * const src = (ggml_fp16_t *)((char *) src0->data + i02*nb02 + i01*nb01);\n                    ggml_fp16_t * dst_data = wdata + i02*ew0*ne00;\n                    for (int i00 = 0; i00 < ne00; i00++) {\n                        dst_data[i00*ew0 + i01] = src[i00];\n                    }\n                }\n            }\n        }\n\n        // prepare source data (src1)\n        {\n            ggml_fp16_t * const wdata = (ggml_fp16_t *) params->wdata + ne02*ew0*ne00;\n\n            for (int i11 = 0; i11 < ne11; i11++) {\n                const float * const src = (float *)((char *) src1->data + i11*nb11);\n                ggml_fp16_t * dst_data = wdata;\n                for (int i10 = 0; i10 < ne10; i10++) {\n                    dst_data[(i10 + nh)*ew0 + i11] = GGML_FP32_TO_FP16(src[i10]);\n                }\n            }\n        }\n\n        return;\n    }\n\n    if (params->type == GGML_TASK_FINALIZE) {\n        return;\n    }\n\n    // total rows in dst\n    const int nr = ne02;\n\n    // rows per thread\n    const int dr = (nr + nth - 1)/nth;\n\n    // row range for this thread\n    const int ir0 = dr*ith;\n    const int ir1 = MIN(ir0 + dr, nr);\n\n    for (int i1 = ir0; i1 < ir1; i1++) {\n        float * dst_data = (float *)((char *) dst->data + i1*nb1);\n        for (int i0 = 0; i0 < ne10; i0 += 2) {\n            dst_data[i0/2] = 0;\n            for (int k = -nh; k <= nh; k++) {\n                float v = 0.0f;\n                ggml_vec_dot_f16(ew0, &v,\n                        (ggml_fp16_t *) params->wdata +   i1*ew0*ne00 +      (nh + k)*ew0,\n                        (ggml_fp16_t *) params->wdata + ne02*ew0*ne00 + (i0 + nh + k)*ew0);\n\n                dst_data[i0/2] += v;\n            }\n        }\n    }\n}\n\nstatic void ggml_compute_forward_conv_1d_2s_f32(\n        const struct ggml_compute_params * params,\n        const struct ggml_tensor * src0,\n        const struct ggml_tensor * src1,\n              struct ggml_tensor * dst) {\n    GGML_ASSERT(src0->type == GGML_TYPE_F32);\n    GGML_ASSERT(src1->type == GGML_TYPE_F32);\n    GGML_ASSERT( dst->type == GGML_TYPE_F32);\n\n    int64_t t0 = ggml_perf_time_us();\n    UNUSED(t0);\n\n    const int ne00 = src0->ne[0];\n    const int ne01 = src0->ne[1];\n    const int ne02 = src0->ne[2];\n    //const int ne03 = src0->ne[3];\n\n    const int ne10 = src1->ne[0];\n    const int ne11 = src1->ne[1];\n    //const int ne12 = src1->ne[2];\n    //const int ne13 = src1->ne[3];\n\n    //const int ne0  = dst->ne[0];\n    //const int ne1  = dst->ne[1];\n    //const int ne2  = dst->ne[2];\n    //const int ne3  = dst->ne[3];\n    //const int ne   = ne0*ne1*ne2*ne3;\n\n    const int nb00 = src0->nb[0];\n    const int nb01 = src0->nb[1];\n    const int nb02 = src0->nb[2];\n    //const int nb03 = src0->nb[3];\n\n    const int nb10 = src1->nb[0];\n    const int nb11 = src1->nb[1];\n    //const int nb12 = src1->nb[2];\n    //const int nb13 = src1->nb[3];\n\n    //const int nb0  = dst->nb[0];\n    const int nb1  = dst->nb[1];\n    //const int nb2  = dst->nb[2];\n    //const int nb3  = dst->nb[3];\n\n    const int ith = params->ith;\n    const int nth = params->nth;\n\n    const int nk = ne00;\n    const int nh = nk/2;\n\n    const int ew0 = ggml_up32(ne01);\n\n    GGML_ASSERT(ne00 % 2 == 1); // TODO: support even kernel sizes\n    GGML_ASSERT(nb00 == sizeof(float));\n    GGML_ASSERT(nb10 == sizeof(float));\n\n    if (params->type == GGML_TASK_INIT) {\n        // TODO: fix this memset (wsize is overestimated)\n        memset(params->wdata, 0, params->wsize);\n\n        // prepare kernel data (src0)\n        {\n            float * const wdata = (float *) params->wdata + 0;\n\n            for (int i02 = 0; i02 < ne02; i02++) {\n                for (int i01 = 0; i01 < ne01; i01++) {\n                    const float * const src = (float *)((char *) src0->data + i02*nb02 + i01*nb01);\n                    float * dst_data = wdata + i02*ew0*ne00;\n                    for (int i00 = 0; i00 < ne00; i00++) {\n                        dst_data[i00*ew0 + i01] = src[i00];\n                    }\n                }\n            }\n        }\n\n        // prepare source data (src1)\n        {\n            float * const wdata = (float *) params->wdata + ne02*ew0*ne00;\n\n            for (int i11 = 0; i11 < ne11; i11++) {\n                const float * const src = (float *)((char *) src1->data + i11*nb11);\n                float * dst_data = wdata;\n                for (int i10 = 0; i10 < ne10; i10++) {\n                    dst_data[(i10 + nh)*ew0 + i11] = src[i10];\n                }\n            }\n        }\n\n        return;\n    }\n\n    if (params->type == GGML_TASK_FINALIZE) {\n        return;\n    }\n\n    // total rows in dst\n    const int nr = ne02;\n\n    // rows per thread\n    const int dr = (nr + nth - 1)/nth;\n\n    // row range for this thread\n    const int ir0 = dr*ith;\n    const int ir1 = MIN(ir0 + dr, nr);\n\n    for (int i1 = ir0; i1 < ir1; i1++) {\n        float * dst_data = (float *)((char *) dst->data + i1*nb1);\n        for (int i0 = 0; i0 < ne10; i0 += 2) {\n            dst_data[i0/2] = 0;\n            for (int k = -nh; k <= nh; k++) {\n                float v = 0.0f;\n                ggml_vec_dot_f32(ew0, &v,\n                        (float *) params->wdata +   i1*ew0*ne00 +      (nh + k)*ew0,\n                        (float *) params->wdata + ne02*ew0*ne00 + (i0 + nh + k)*ew0);\n\n                dst_data[i0/2] += v;\n            }\n        }\n    }\n}\n\nstatic void ggml_compute_forward_conv_1d_2s(\n        const struct ggml_compute_params * params,\n        const struct ggml_tensor * src0,\n        const struct ggml_tensor * src1,\n        struct ggml_tensor * dst) {\n    switch (src0->type) {\n        case GGML_TYPE_F16:\n            {\n                ggml_compute_forward_conv_1d_2s_f16_f32(params, src0, src1, dst);\n            } break;\n        case GGML_TYPE_F32:\n            {\n                ggml_compute_forward_conv_1d_2s_f32(params, src0, src1, dst);\n            } break;\n        case GGML_TYPE_I8:\n        case GGML_TYPE_I16:\n        case GGML_TYPE_I32:\n        case GGML_TYPE_COUNT:\n            {\n                GGML_ASSERT(false);\n            } break;\n    }\n}\n\n// ggml_compute_forward_flash_attn\n\nstatic void ggml_compute_forward_flash_attn_f32(\n        const struct ggml_compute_params * params,\n        const struct ggml_tensor * q,\n        const struct ggml_tensor * k,\n        const struct ggml_tensor * v,\n        const bool masked,\n             struct ggml_tensor * dst) {\n    int64_t t0 = ggml_perf_time_us();\n    UNUSED(t0);\n\n    const int neq0 = q->ne[0];\n    const int neq1 = q->ne[1];\n    const int neq2 = q->ne[2];\n    const int neq3 = q->ne[3];\n\n    const int nek0 = k->ne[0];\n    const int nek1 = k->ne[1];\n    //const int nek2 = k->ne[2];\n    //const int nek3 = k->ne[3];\n\n    //const int nev0 = v->ne[0];\n    const int nev1 = v->ne[1];\n    //const int nev2 = v->ne[2];\n    //const int nev3 = v->ne[3];\n\n    const int ne0  = dst->ne[0];\n    const int ne1  = dst->ne[1];\n    //const int ne2  = dst->ne[2];\n    //const int ne3  = dst->ne[3];\n\n    const int nbk0 = k->nb[0];\n    const int nbk1 = k->nb[1];\n    const int nbk2 = k->nb[2];\n    const int nbk3 = k->nb[3];\n\n    const int nbq0 = q->nb[0];\n    const int nbq1 = q->nb[1];\n    const int nbq2 = q->nb[2];\n    const int nbq3 = q->nb[3];\n\n    const int nbv0 = v->nb[0];\n    const int nbv1 = v->nb[1];\n    const int nbv2 = v->nb[2];\n    const int nbv3 = v->nb[3];\n\n    const int nb0  = dst->nb[0];\n    const int nb1  = dst->nb[1];\n    const int nb2  = dst->nb[2];\n    const int nb3  = dst->nb[3];\n\n    const int ith = params->ith;\n    const int nth = params->nth;\n\n    const int D = neq0;\n    const int N = neq1;\n    const int P = nek1 - N;\n    const int M = P + N;\n\n    GGML_ASSERT(ne0 == D);\n    GGML_ASSERT(ne1 == N);\n    GGML_ASSERT(P >= 0);\n\n    GGML_ASSERT(nbq0 == sizeof(float));\n    GGML_ASSERT(nbk0 == sizeof(float));\n    GGML_ASSERT(nbv0 == sizeof(float));\n\n    GGML_ASSERT(neq0 == D);\n    GGML_ASSERT(nek0 == D);\n    GGML_ASSERT(nev1 == D);\n\n    GGML_ASSERT(neq1 == N);\n    GGML_ASSERT(nek1 == N + P);\n    GGML_ASSERT(nev1 == D);\n\n    // dst cannot be transposed or permuted\n    GGML_ASSERT(nb0 == sizeof(float));\n    GGML_ASSERT(nb0 <= nb1);\n    GGML_ASSERT(nb1 <= nb2);\n    GGML_ASSERT(nb2 <= nb3);\n\n    if (params->type == GGML_TASK_INIT) {\n        return;\n    }\n\n    if (params->type == GGML_TASK_FINALIZE) {\n        return;\n    }\n\n    // parallelize by q rows using ggml_vec_dot_f32\n\n    // total rows in q\n    const int nr = neq1*neq2*neq3;\n\n    // rows per thread\n    const int dr = (nr + nth - 1)/nth;\n\n    // row range for this thread\n    const int ir0 = dr*ith;\n    const int ir1 = MIN(ir0 + dr, nr);\n\n    const float scale = 1.0/sqrt((double) D);\n\n    //printf(\"P=%d N=%d D=%d ir0=%d ir1=%d scale = %f\\n\", P, N, D, ir0, ir1, scale);\n\n    for (int ir = ir0; ir < ir1; ++ir) {\n        // q indices\n        const int iq3 = ir/(neq2*neq1);\n        const int iq2 = (ir - iq3*neq2*neq1)/neq1;\n        const int iq1 = (ir - iq3*neq2*neq1 - iq2*neq1);\n\n        float * S = (float *) params->wdata + ith*(M + CACHE_LINE_SIZE_F32);\n\n        for (int ic = 0; ic < nek1; ++ic) {\n            // k indices\n            const int ik3 = iq3;\n            const int ik2 = iq2;\n            const int ik1 = ic;\n\n            // S indices\n            const int i1 = ik1;\n\n            ggml_vec_dot_f32(neq0,\n                    S + i1,\n                    (float *) ((char *) k->data + (ik1*nbk1 + ik2*nbk2 + ik3*nbk3)),\n                    (float *) ((char *) q->data + (iq1*nbq1 + iq2*nbq2 + iq3*nbq3)));\n        }\n\n        // scale\n        ggml_vec_scale_f32(nek1, S, scale);\n\n        if (masked) {\n            for (int i = P; i < M; i++) {\n                if (i > P + iq1) {\n                    S[i] = -INFINITY;\n                }\n            }\n        }\n\n        // softmax\n        {\n            float max = -INFINITY;\n            for (int i = 0; i < M; i++) {\n                max = MAX(max, S[i]);\n            }\n\n            ggml_float sum = 0.0;\n\n            uint16_t ss;\n            for (int i = 0; i < M; i++) {\n                if (S[i] == -INFINITY) {\n                    S[i] = 0.0;\n                } else {\n                    //const float val = (S[i] == -INFINITY) ? 0.0 : exp(S[i] - max);\n                    ggml_fp16_t s = GGML_FP32_TO_FP16(S[i] - max);\n                    memcpy(&ss, &s, sizeof(ss));\n                    const float val = GGML_FP16_TO_FP32(table_exp_f16[ss]);\n                    sum += val;\n                    S[i] = val;\n                }\n            }\n\n            assert(sum > 0.0f);\n\n            sum = 1.0/sum;\n            ggml_vec_scale_f32(M, S, sum);\n        }\n\n        for (int ic = 0; ic < nev1; ++ic) {\n            // dst indices\n            const int i1 = iq1;\n            const int i2 = iq2;\n            const int i3 = iq3;\n\n            ggml_vec_dot_f32(nek1,\n                    (float *) ((char *) dst->data + (ic*nb0 + i1*nb1  + i2*nb2  + i3*nb3)),\n                    (float *) ((char *) v->data   + (         ic*nbv1 + i2*nbv2 + i3*nbv3)),\n                    S);\n        }\n    }\n}\n\nstatic void ggml_compute_forward_flash_attn_f16(\n        const struct ggml_compute_params * params,\n        const struct ggml_tensor * q,\n        const struct ggml_tensor * k,\n        const struct ggml_tensor * v,\n        const bool masked,\n             struct ggml_tensor * dst) {\n    int64_t t0 = ggml_perf_time_us();\n    UNUSED(t0);\n\n    const int neq0 = q->ne[0];\n    const int neq1 = q->ne[1];\n    const int neq2 = q->ne[2];\n    const int neq3 = q->ne[3];\n\n    const int nek0 = k->ne[0];\n    const int nek1 = k->ne[1];\n    //const int nek2 = k->ne[2];\n    //const int nek3 = k->ne[3];\n\n    //const int nev0 = v->ne[0];\n    const int nev1 = v->ne[1];\n    //const int nev2 = v->ne[2];\n    //const int nev3 = v->ne[3];\n\n    const int ne0  = dst->ne[0];\n    const int ne1  = dst->ne[1];\n    //const int ne2  = dst->ne[2];\n    //const int ne3  = dst->ne[3];\n\n    const int nbk0 = k->nb[0];\n    const int nbk1 = k->nb[1];\n    const int nbk2 = k->nb[2];\n    const int nbk3 = k->nb[3];\n\n    const int nbq0 = q->nb[0];\n    const int nbq1 = q->nb[1];\n    const int nbq2 = q->nb[2];\n    const int nbq3 = q->nb[3];\n\n    const int nbv0 = v->nb[0];\n    const int nbv1 = v->nb[1];\n    const int nbv2 = v->nb[2];\n    const int nbv3 = v->nb[3];\n\n    const int nb0  = dst->nb[0];\n    const int nb1  = dst->nb[1];\n    const int nb2  = dst->nb[2];\n    const int nb3  = dst->nb[3];\n\n    const int ith = params->ith;\n    const int nth = params->nth;\n\n    const int D = neq0;\n    const int N = neq1;\n    const int P = nek1 - N;\n    const int M = P + N;\n\n    GGML_ASSERT(ne0 == D);\n    GGML_ASSERT(ne1 == N);\n    GGML_ASSERT(P >= 0);\n\n    GGML_ASSERT(nbq0 == sizeof(ggml_fp16_t));\n    GGML_ASSERT(nbk0 == sizeof(ggml_fp16_t));\n    GGML_ASSERT(nbv0 == sizeof(ggml_fp16_t));\n\n    GGML_ASSERT(neq0 == D);\n    GGML_ASSERT(nek0 == D);\n    GGML_ASSERT(nev1 == D);\n\n    GGML_ASSERT(neq1 == N);\n    GGML_ASSERT(nek1 == N + P);\n    GGML_ASSERT(nev1 == D);\n\n    // dst cannot be transposed or permuted\n    GGML_ASSERT(nb0 == sizeof(float));\n    GGML_ASSERT(nb0 <= nb1);\n    GGML_ASSERT(nb1 <= nb2);\n    GGML_ASSERT(nb2 <= nb3);\n\n    if (params->type == GGML_TASK_INIT) {\n        return;\n    }\n\n    if (params->type == GGML_TASK_FINALIZE) {\n        return;\n    }\n\n    // parallelize by q rows using ggml_vec_dot_f32\n\n    // total rows in q\n    const int nr = neq1*neq2*neq3;\n\n    // rows per thread\n    const int dr = (nr + nth - 1)/nth;\n\n    // row range for this thread\n    const int ir0 = dr*ith;\n    const int ir1 = MIN(ir0 + dr, nr);\n\n    const float scale = 1.0/sqrt((double) D);\n\n    //printf(\"P=%d N=%d D=%d ir0=%d ir1=%d scale = %f\\n\", P, N, D, ir0, ir1, scale);\n\n    for (int ir = ir0; ir < ir1; ++ir) {\n        // q indices\n        const int iq3 = ir/(neq2*neq1);\n        const int iq2 = (ir - iq3*neq2*neq1)/neq1;\n        const int iq1 = (ir - iq3*neq2*neq1 - iq2*neq1);\n\n        float * S = (float *) params->wdata + ith*(2*M + CACHE_LINE_SIZE_F32);\n\n        for (int ic = 0; ic < nek1; ++ic) {\n            // k indices\n            const int ik3 = iq3;\n            const int ik2 = iq2;\n            const int ik1 = ic;\n\n            // S indices\n            const int i1 = ik1;\n\n            ggml_vec_dot_f16(neq0,\n                    S + i1,\n                    (ggml_fp16_t *) ((char *) k->data + (ik1*nbk1 + ik2*nbk2 + ik3*nbk3)),\n                    (ggml_fp16_t *) ((char *) q->data + (iq1*nbq1 + iq2*nbq2 + iq3*nbq3)));\n        }\n\n        // scale\n        ggml_vec_scale_f32(nek1, S, scale);\n\n        if (masked) {\n            for (int i = P; i < M; i++) {\n                if (i > P + iq1) {\n                    S[i] = -INFINITY;\n                }\n            }\n        }\n\n        // softmax\n        {\n            float max = -INFINITY;\n            for (int i = 0; i < M; i++) {\n                max = MAX(max, S[i]);\n            }\n\n            ggml_float sum = 0.0;\n\n            uint16_t ss;\n            for (int i = 0; i < M; i++) {\n                if (S[i] == -INFINITY) {\n                    S[i] = 0.0;\n                } else {\n                    //const float val = (S[i] == -INFINITY) ? 0.0 : exp(S[i] - max);\n                    ggml_fp16_t s = GGML_FP32_TO_FP16(S[i] - max);\n                    memcpy(&ss, &s, sizeof(ss));\n                    const float val = GGML_FP16_TO_FP32(table_exp_f16[ss]);\n                    sum += val;\n                    S[i] = val;\n                }\n            }\n\n            assert(sum > 0.0f);\n\n            sum = 1.0/sum;\n            ggml_vec_scale_f32(M, S, sum);\n        }\n\n        ggml_fp16_t * S16 = (ggml_fp16_t *) ((float *) params->wdata + ith*(2*M + CACHE_LINE_SIZE_F32) + M);\n\n        for (int i = 0; i < M; i++) {\n            S16[i] = GGML_FP32_TO_FP16(S[i]);\n        }\n\n        for (int ic = 0; ic < nev1; ++ic) {\n            // dst indices\n            const int i1 = iq1;\n            const int i2 = iq2;\n            const int i3 = iq3;\n\n            ggml_vec_dot_f16(nek1,\n                    (float *)       ((char *) dst->data + (ic*nb0 + i1*nb1  + i2*nb2  + i3*nb3)),\n                    (ggml_fp16_t *) ((char *) v->data   + (         ic*nbv1 + i2*nbv2 + i3*nbv3)),\n                    S16);\n        }\n    }\n}\n\nstatic void ggml_compute_forward_flash_attn(\n        const struct ggml_compute_params * params,\n        const struct ggml_tensor * q,\n        const struct ggml_tensor * k,\n        const struct ggml_tensor * v,\n        const bool masked,\n        struct ggml_tensor * dst) {\n    switch (q->type) {\n        case GGML_TYPE_F16:\n            {\n                ggml_compute_forward_flash_attn_f16(params, q, k, v, masked, dst);\n            } break;\n        case GGML_TYPE_F32:\n            {\n                ggml_compute_forward_flash_attn_f32(params, q, k, v, masked, dst);\n            } break;\n        case GGML_TYPE_I8:\n        case GGML_TYPE_I16:\n        case GGML_TYPE_I32:\n        case GGML_TYPE_COUNT:\n            {\n                assert(false);\n            } break;\n    }\n}\n\n// ggml_compute_forward_flash_ff\n\nstatic void ggml_compute_forward_flash_ff_f16(\n        const struct ggml_compute_params * params,\n        const struct ggml_tensor * a,  // F16\n        const struct ggml_tensor * b0, // F16 fc_w\n        const struct ggml_tensor * b1, // F32 fc_b\n        const struct ggml_tensor * c0, // F16 proj_w\n        const struct ggml_tensor * c1, // F32 proj_b\n        struct ggml_tensor * dst) {\n    int64_t t0 = ggml_perf_time_us();\n    UNUSED(t0);\n\n    const int nea0 = a->ne[0];\n    const int nea1 = a->ne[1];\n    const int nea2 = a->ne[2];\n    const int nea3 = a->ne[3];\n\n    const int neb00 = b0->ne[0];\n    const int neb01 = b0->ne[1];\n    //const int neb02 = b0->ne[2];\n    //const int neb03 = b0->ne[3];\n\n    const int neb10 = b1->ne[0];\n    const int neb11 = b1->ne[1];\n    //const int neb12 = b1->ne[2];\n    //const int neb13 = b1->ne[3];\n\n    const int nec00 = c0->ne[0];\n    const int nec01 = c0->ne[1];\n    //const int nec02 = c0->ne[2];\n    //const int nec03 = c0->ne[3];\n\n    const int nec10 = c1->ne[0];\n    const int nec11 = c1->ne[1];\n    //const int nec12 = c1->ne[2];\n    //const int nec13 = c1->ne[3];\n\n    const int ne0 = dst->ne[0];\n    const int ne1 = dst->ne[1];\n    const int ne2 = dst->ne[2];\n    //const int ne3 = dst->ne[3];\n\n    const int nba0 = a->nb[0];\n    const int nba1 = a->nb[1];\n    const int nba2 = a->nb[2];\n    const int nba3 = a->nb[3];\n\n    const int nbb00 = b0->nb[0];\n    const int nbb01 = b0->nb[1];\n    const int nbb02 = b0->nb[2];\n    const int nbb03 = b0->nb[3];\n\n    const int nbb10 = b1->nb[0];\n    //const int nbb11 = b1->nb[1];\n    //const int nbb12 = b1->nb[2];\n    //const int nbb13 = b1->nb[3];\n\n    const int nbc00 = c0->nb[0];\n    const int nbc01 = c0->nb[1];\n    const int nbc02 = c0->nb[2];\n    const int nbc03 = c0->nb[3];\n\n    const int nbc10 = c1->nb[0];\n    //const int nbc11 = c1->nb[1];\n    //const int nbc12 = c1->nb[2];\n    //const int nbc13 = c1->nb[3];\n\n    const int nb0 = dst->nb[0];\n    const int nb1 = dst->nb[1];\n    const int nb2 = dst->nb[2];\n    const int nb3 = dst->nb[3];\n\n    const int ith = params->ith;\n    const int nth = params->nth;\n\n    const int D = nea0;\n    //const int N = nea1;\n    const int M = neb01;\n\n    GGML_ASSERT(ne0 == nea0);\n    GGML_ASSERT(ne1 == nea1);\n    GGML_ASSERT(ne2 == nea2);\n\n    GGML_ASSERT(nba0  == sizeof(ggml_fp16_t));\n    GGML_ASSERT(nbb00 == sizeof(ggml_fp16_t));\n    GGML_ASSERT(nbb10 == sizeof(float));\n    GGML_ASSERT(nbc00 == sizeof(ggml_fp16_t));\n    GGML_ASSERT(nbc10 == sizeof(float));\n\n    GGML_ASSERT(neb00 == D);\n    GGML_ASSERT(neb01 == M);\n    GGML_ASSERT(neb10 == M);\n    GGML_ASSERT(neb11 == 1);\n\n    GGML_ASSERT(nec00 == M);\n    GGML_ASSERT(nec01 == D);\n    GGML_ASSERT(nec10 == D);\n    GGML_ASSERT(nec11 == 1);\n\n    // dst cannot be transposed or permuted\n    GGML_ASSERT(nb0 == sizeof(float));\n    GGML_ASSERT(nb0 <= nb1);\n    GGML_ASSERT(nb1 <= nb2);\n    GGML_ASSERT(nb2 <= nb3);\n\n    if (params->type == GGML_TASK_INIT) {\n        return;\n    }\n\n    if (params->type == GGML_TASK_FINALIZE) {\n        return;\n    }\n\n    // parallelize by a rows using ggml_vec_dot_f32\n\n    // total rows in a\n    const int nr = nea1*nea2*nea3;\n\n    // rows per thread\n    const int dr = (nr + nth - 1)/nth;\n\n    // row range for this thread\n    const int ir0 = dr*ith;\n    const int ir1 = MIN(ir0 + dr, nr);\n\n    for (int ir = ir0; ir < ir1; ++ir) {\n        // a indices\n        const int ia3 = ir/(nea2*nea1);\n        const int ia2 = (ir - ia3*nea2*nea1)/nea1;\n        const int ia1 = (ir - ia3*nea2*nea1 - ia2*nea1);\n\n        float * S = (float *) params->wdata + ith*(2*M + CACHE_LINE_SIZE_F32);\n\n        for (int ic = 0; ic < neb01; ++ic) {\n            // b0 indices\n            const int ib03 = ia3;\n            const int ib02 = ia2;\n            const int ib01 = ic;\n\n            // S indices\n            const int i1 = ib01;\n\n            ggml_vec_dot_f16(nea0,\n                    S + i1,\n                    (ggml_fp16_t *) ((char *) b0->data + (ib01*nbb01 + ib02*nbb02 + ib03*nbb03)),\n                    (ggml_fp16_t *) ((char *)  a->data + ( ia1*nba1  +  ia2*nba2  +  ia3*nba3)));\n        }\n\n        ggml_vec_add_f32(neb01, S, S, (float *) b1->data);\n        //ggml_vec_gelu_f32(neb01, S, S);\n\n        ggml_fp16_t * S16 = (ggml_fp16_t *) ((float *) params->wdata + ith*(2*M + CACHE_LINE_SIZE_F32) + M);\n\n        for (int i = 0; i < M; i++) {\n            S16[i] = GGML_FP32_TO_FP16(S[i]);\n        }\n\n        ggml_vec_gelu_f16(neb01, S16, S16);\n\n        {\n            // dst indices\n            const int i1 = ia1;\n            const int i2 = ia2;\n            const int i3 = ia3;\n\n            for (int ic = 0; ic < nec01; ++ic) {\n\n                ggml_vec_dot_f16(neb01,\n                        (float *)       ((char *) dst->data + (ic*nb0 + i1*nb1   + i2*nb2   + i3*nb3)),\n                        (ggml_fp16_t *) ((char *) c0->data  + (         ic*nbc01 + i2*nbc02 + i3*nbc03)),\n                        S16);\n            }\n\n            ggml_vec_add_f32(nec01,\n                    (float *) ((char *) dst->data + (i1*nb1 + i2*nb2 + i3*nb3)),\n                    (float *) ((char *) dst->data + (i1*nb1 + i2*nb2 + i3*nb3)),\n                    (float *) c1->data);\n        }\n    }\n}\n\nstatic void ggml_compute_forward_flash_ff(\n        const struct ggml_compute_params * params,\n        const struct ggml_tensor * a,\n        const struct ggml_tensor * b0,\n        const struct ggml_tensor * b1,\n        const struct ggml_tensor * c0,\n        const struct ggml_tensor * c1,\n        struct ggml_tensor * dst) {\n    switch (b0->type) {\n        case GGML_TYPE_F16:\n            {\n                ggml_compute_forward_flash_ff_f16(params, a, b0, b1, c0, c1, dst);\n            } break;\n        case GGML_TYPE_F32:\n            {\n                GGML_ASSERT(false); // TODO\n            } break;\n        case GGML_TYPE_I8:\n        case GGML_TYPE_I16:\n        case GGML_TYPE_I32:\n        case GGML_TYPE_COUNT:\n            {\n                assert(false);\n            } break;\n    }\n}\n\n/////////////////////////////////\n\nstatic void ggml_compute_forward(struct ggml_compute_params * params, struct ggml_tensor * tensor) {\n    assert(params);\n\n    switch (tensor->op) {\n        case GGML_OP_DUP:\n            {\n                ggml_compute_forward_dup(params, tensor->src0, tensor);\n            } break;\n        case GGML_OP_ADD:\n            {\n                ggml_compute_forward_add(params, tensor->src0, tensor->src1, tensor);\n            } break;\n        case GGML_OP_SUB:\n            {\n                ggml_compute_forward_sub(params, tensor->src0, tensor->src1, tensor);\n            } break;\n        case GGML_OP_MUL:\n            {\n                ggml_compute_forward_mul(params, tensor->src0, tensor->src1, tensor);\n            } break;\n        case GGML_OP_DIV:\n            {\n                ggml_compute_forward_div(params, tensor->src0, tensor->src1, tensor);\n            } break;\n        case GGML_OP_SQR:\n            {\n                ggml_compute_forward_sqr(params, tensor->src0, tensor);\n            } break;\n        case GGML_OP_SQRT:\n            {\n                ggml_compute_forward_sqrt(params, tensor->src0, tensor);\n            } break;\n        case GGML_OP_SUM:\n            {\n                ggml_compute_forward_sum(params, tensor->src0, tensor);\n            } break;\n        case GGML_OP_MEAN:\n            {\n                ggml_compute_forward_mean(params, tensor->src0, tensor);\n            } break;\n        case GGML_OP_REPEAT:\n            {\n                ggml_compute_forward_repeat(params, tensor->src0, tensor);\n            } break;\n        case GGML_OP_ABS:\n            {\n                ggml_compute_forward_abs(params, tensor->src0, tensor);\n            } break;\n        case GGML_OP_SGN:\n            {\n                ggml_compute_forward_sgn(params, tensor->src0, tensor);\n            } break;\n        case GGML_OP_NEG:\n            {\n                ggml_compute_forward_neg(params, tensor->src0, tensor);\n            } break;\n        case GGML_OP_STEP:\n            {\n                ggml_compute_forward_step(params, tensor->src0, tensor);\n            } break;\n        case GGML_OP_RELU:\n            {\n                ggml_compute_forward_relu(params, tensor->src0, tensor);\n            } break;\n        case GGML_OP_GELU:\n            {\n                ggml_compute_forward_gelu(params, tensor->src0, tensor);\n            } break;\n        case GGML_OP_NORM:\n            {\n                ggml_compute_forward_norm(params, tensor->src0, tensor);\n            } break;\n        case GGML_OP_MUL_MAT:\n            {\n                ggml_compute_forward_mul_mat(params, tensor->src0, tensor->src1, tensor);\n            } break;\n        case GGML_OP_SCALE:\n            {\n                ggml_compute_forward_scale(params, tensor->src0, tensor->src1, tensor);\n            } break;\n        case GGML_OP_CPY:\n            {\n                ggml_compute_forward_cpy(params, tensor->src0, tensor);\n            } break;\n        case GGML_OP_RESHAPE:\n            {\n                ggml_compute_forward_reshape(params, tensor->src0, tensor);\n            } break;\n        case GGML_OP_VIEW:\n            {\n                ggml_compute_forward_view(params, tensor->src0);\n            } break;\n        case GGML_OP_PERMUTE:\n            {\n                ggml_compute_forward_permute(params, tensor->src0);\n            } break;\n        case GGML_OP_TRANSPOSE:\n            {\n                ggml_compute_forward_transpose(params, tensor->src0);\n            } break;\n        case GGML_OP_GET_ROWS:\n            {\n                ggml_compute_forward_get_rows(params, tensor->src0, tensor->src1, tensor);\n            } break;\n        case GGML_OP_DIAG_MASK_INF:\n            {\n                ggml_compute_forward_diag_mask_inf(params, tensor->src0, tensor->src1, tensor);\n            } break;\n        case GGML_OP_SOFT_MAX:\n            {\n                ggml_compute_forward_soft_max(params, tensor->src0, tensor);\n            } break;\n        case GGML_OP_ROPE:\n            {\n                ggml_compute_forward_rope(params, tensor->src0, tensor->src1, tensor);\n            } break;\n        case GGML_OP_CONV_1D_1S:\n            {\n                ggml_compute_forward_conv_1d_1s(params, tensor->src0, tensor->src1, tensor);\n            } break;\n        case GGML_OP_CONV_1D_2S:\n            {\n                ggml_compute_forward_conv_1d_2s(params, tensor->src0, tensor->src1, tensor);\n            } break;\n        case GGML_OP_FLASH_ATTN:\n            {\n                int32_t t = ggml_get_i32_1d(tensor->opt[1], 0);\n                GGML_ASSERT(t == 0 || t == 1);\n                bool masked = t != 0;\n                ggml_compute_forward_flash_attn(params, tensor->src0, tensor->src1, tensor->opt[0], masked, tensor);\n            } break;\n        case GGML_OP_FLASH_FF:\n            {\n                ggml_compute_forward_flash_ff(params, tensor->src0, tensor->src1, tensor->opt[0], tensor->opt[1], tensor->opt[2], tensor);\n            } break;\n        case GGML_OP_NONE:\n            {\n                // nop\n            } break;\n        case GGML_OP_COUNT:\n            {\n                GGML_ASSERT(false);\n            } break;\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n\nstatic void ggml_compute_backward(struct ggml_context * ctx, struct ggml_tensor * tensor, bool inplace) {\n    struct ggml_tensor * src0 = tensor->src0;\n    struct ggml_tensor * src1 = tensor->src1;\n\n    switch (tensor->op) {\n        case GGML_OP_DUP:\n            {\n                if (src0->grad) {\n                    src0->grad = ggml_add_impl(ctx, src0->grad, tensor->grad, inplace);\n                }\n            } break;\n        case GGML_OP_ADD:\n            {\n                if (src0->grad) {\n                    src0->grad = ggml_add_impl(ctx, src0->grad, tensor->grad, inplace);\n                }\n                if (src1->grad) {\n                    src1->grad = ggml_add_impl(ctx, src1->grad, tensor->grad, inplace);\n                }\n            } break;\n        case GGML_OP_SUB:\n            {\n                if (src0->grad) {\n                    src0->grad = ggml_add_impl(ctx, src0->grad, tensor->grad, inplace);\n                }\n                if (src1->grad) {\n                    src1->grad = ggml_sub_impl(ctx, src1->grad, tensor->grad, inplace);\n                }\n            } break;\n        case GGML_OP_MUL:\n            {\n                if (src0->grad) {\n                    src0->grad =\n                        ggml_add_impl(ctx,\n                                src0->grad,\n                                ggml_mul(ctx, src1, tensor->grad),\n                                inplace);\n                }\n                if (src1->grad) {\n                    src1->grad =\n                        ggml_add_impl(ctx,\n                                src1->grad,\n                                ggml_mul(ctx, src0, tensor->grad),\n                                inplace);\n                }\n            } break;\n        case GGML_OP_DIV:\n            {\n                if (src0->grad) {\n                    src0->grad =\n                        ggml_add_impl(ctx,\n                                src0->grad,\n                                ggml_div(ctx, tensor->grad, src1),\n                                inplace);\n                }\n                if (src1->grad) {\n                    src1->grad =\n                        ggml_sub_impl(ctx,\n                                src1->grad,\n                                ggml_mul(ctx,\n                                    tensor->grad,\n                                    ggml_div(ctx, tensor, src1)),\n                                inplace);\n                }\n            } break;\n        case GGML_OP_SQR:\n            {\n                if (src0->grad) {\n                    src0->grad =\n                        ggml_add_impl(ctx,\n                                src0->grad,\n                                ggml_mul(ctx,\n                                    ggml_mul(ctx, src0, tensor->grad),\n                                    ggml_repeat(ctx, ggml_new_f32(ctx, 2.0f), src0)),\n                                inplace);\n                }\n            } break;\n        case GGML_OP_SQRT:\n            {\n                if (src0->grad) {\n                    src0->grad =\n                        ggml_add_impl(ctx,\n                                src0->grad,\n                                ggml_div(ctx,\n                                    ggml_repeat(ctx, ggml_new_f32(ctx, 0.5f), tensor),\n                                    tensor),\n                                inplace);\n                }\n            } break;\n        case GGML_OP_SUM:\n            {\n                if (src0->grad) {\n                    src0->grad =\n                        ggml_add_impl(ctx,\n                                src0->grad,\n                                ggml_repeat(ctx, tensor->grad, src0->grad),\n                                inplace);\n                }\n            } break;\n        case GGML_OP_MEAN:\n            {\n                assert(false); // TODO: implement\n            } break;\n        case GGML_OP_REPEAT:\n            {\n                if (src0->grad) {\n                    src0->grad =\n                        ggml_add_impl(ctx,\n                                src0->grad,\n                                ggml_sum(ctx, tensor->grad),\n                                inplace);\n                }\n            } break;\n        case GGML_OP_ABS:\n            {\n                if (src0->grad) {\n                    src0->grad =\n                        ggml_add_impl(ctx,\n                                src0->grad,\n                                ggml_mul(ctx,\n                                    ggml_sgn(ctx, src0),\n                                    tensor->grad),\n                                inplace);\n                }\n            } break;\n        case GGML_OP_SGN:\n            {\n                if (src0->grad) {\n                    // noop\n                }\n            } break;\n        case GGML_OP_NEG:\n            {\n                if (src0->grad) {\n                    src0->grad = ggml_sub_impl(ctx, src0->grad, tensor->grad, inplace);\n                }\n            } break;\n        case GGML_OP_STEP:\n            {\n                if (src0->grad) {\n                    // noop\n                }\n            } break;\n        case GGML_OP_RELU:\n            {\n                if (src0->grad) {\n                    src0->grad = ggml_sub_impl(ctx,\n                            src0->grad,\n                            ggml_mul(ctx,\n                                ggml_step(ctx, src0),\n                                tensor->grad),\n                            inplace);\n                }\n            } break;\n        case GGML_OP_GELU:\n            {\n                assert(false); // TODO: not implemented\n            } break;\n        case GGML_OP_NORM:\n            {\n                assert(false); // TODO: not implemented\n            } break;\n        case GGML_OP_MUL_MAT:\n            {\n                if (src0->grad) {\n                    // TODO: this requires outer product - ggml_out_prod(ctx, src1, tensor->grad);\n                    assert(false);\n                }\n                if (src1->grad) {\n                    src1->grad =\n                        ggml_add_impl(ctx,\n                                src1->grad,\n                                // TODO: fix transpose, the node will break the graph connections\n                                ggml_mul_mat(ctx, ggml_transpose(ctx, src0), tensor->grad),\n                                inplace);\n                }\n            } break;\n        case GGML_OP_SCALE:\n            {\n                GGML_ASSERT(false); // TODO: not implemented\n            } break;\n        case GGML_OP_CPY:\n            {\n                GGML_ASSERT(false); // TODO: not implemented\n            } break;\n        case GGML_OP_RESHAPE:\n            {\n                GGML_ASSERT(false); // TODO: not implemented\n            } break;\n        case GGML_OP_VIEW:\n            {\n                GGML_ASSERT(false); // not supported\n            } break;\n        case GGML_OP_PERMUTE:\n            {\n                GGML_ASSERT(false); // TODO: not implemented\n            } break;\n        case GGML_OP_TRANSPOSE:\n            {\n                GGML_ASSERT(false); // TODO: not implemented\n            } break;\n        case GGML_OP_GET_ROWS:\n            {\n                GGML_ASSERT(false); // TODO: not implemented\n            } break;\n        case GGML_OP_DIAG_MASK_INF:\n            {\n                GGML_ASSERT(false); // TODO: not implemented\n            } break;\n        case GGML_OP_SOFT_MAX:\n            {\n                GGML_ASSERT(false); // TODO: not implemented\n            } break;\n        case GGML_OP_ROPE:\n            {\n                GGML_ASSERT(false); // TODO: not implemented\n            } break;\n        case GGML_OP_CONV_1D_1S:\n            {\n                GGML_ASSERT(false); // TODO: not implemented\n            } break;\n        case GGML_OP_CONV_1D_2S:\n            {\n                GGML_ASSERT(false); // TODO: not implemented\n            } break;\n        case GGML_OP_FLASH_ATTN:\n            {\n                GGML_ASSERT(false); // not supported\n            } break;\n        case GGML_OP_FLASH_FF:\n            {\n                GGML_ASSERT(false); // not supported\n            } break;\n        case GGML_OP_NONE:\n            {\n                // nop\n            } break;\n        case GGML_OP_COUNT:\n            {\n                GGML_ASSERT(false);\n            } break;\n    }\n}\n\nstatic void ggml_visit_parents(struct ggml_cgraph * cgraph, struct ggml_tensor * node) {\n    if (node->grad == NULL) {\n        // this usually happens when we generate intermediate nodes from constants in the backward pass\n        // it can also happen during forward pass, if the user performs computations with constants\n        if (node->op != GGML_OP_NONE) {\n            //GGML_PRINT_DEBUG(\"%s: warning: node %p has no grad, but op %d\\n\", __func__, (void *) node, node->op);\n        }\n    }\n\n    // check if already visited\n    for (int i = 0; i < cgraph->n_nodes; i++) {\n        if (cgraph->nodes[i] == node) {\n            return;\n        }\n    }\n\n    for (int i = 0; i < cgraph->n_leafs; i++) {\n        if (cgraph->leafs[i] == node) {\n            return;\n        }\n    }\n\n    if (node->src0) {\n        ggml_visit_parents(cgraph, node->src0);\n    }\n\n    if (node->src1) {\n        ggml_visit_parents(cgraph, node->src1);\n    }\n\n    for (int i = 0; i < GGML_MAX_OPT; ++i) {\n        if (node->opt[i]) {\n            ggml_visit_parents(cgraph, node->opt[i]);\n        }\n    }\n\n    if (node->op == GGML_OP_NONE && node->grad == NULL) {\n        // reached a leaf node, not part of the gradient graph (e.g. a constant)\n        assert(cgraph->n_leafs < GGML_MAX_NODES);\n\n        cgraph->leafs[cgraph->n_leafs] = node;\n        cgraph->n_leafs++;\n    } else {\n        assert(cgraph->n_nodes < GGML_MAX_NODES);\n\n        cgraph->nodes[cgraph->n_nodes] = node;\n        cgraph->grads[cgraph->n_nodes] = node->grad;\n        cgraph->n_nodes++;\n    }\n}\n\nstatic void ggml_build_forward_impl(struct ggml_cgraph * cgraph, struct ggml_tensor * tensor, bool expand) {\n    if (!expand) {\n        cgraph->n_nodes = 0;\n        cgraph->n_leafs = 0;\n    }\n\n    const int n0 = cgraph->n_nodes;\n    UNUSED(n0);\n\n    ggml_visit_parents(cgraph, tensor);\n\n    const int n_new = cgraph->n_nodes - n0;\n    GGML_PRINT_DEBUG(\"%s: visited %d new nodes\\n\", __func__, n_new);\n\n    if (n_new > 0) {\n        // the last added node should always be starting point\n        assert(cgraph->nodes[cgraph->n_nodes - 1] == tensor);\n    }\n}\n\nvoid ggml_build_forward_expand(struct ggml_cgraph * cgraph, struct ggml_tensor * tensor) {\n    ggml_build_forward_impl(cgraph, tensor, true);\n}\n\nstruct ggml_cgraph ggml_build_forward(struct ggml_tensor * tensor) {\n    struct ggml_cgraph result = {\n        /*.n_nodes      =*/ 0,\n        /*.n_leafs      =*/ 0,\n        /*.n_threads    =*/ 0,\n        /*.work_size    =*/ 0,\n        /*.work         =*/ NULL,\n        /*.nodes        =*/ { NULL },\n        /*.grads        =*/ { NULL },\n        /*.leafs        =*/ { NULL },\n        /*.perf_runs    =*/ 0,\n        /*.perf_cycles  =*/ 0,\n        /*.perf_time_us =*/ 0,\n    };\n\n    ggml_build_forward_impl(&result, tensor, false);\n\n    return result;\n}\n\nstruct ggml_cgraph ggml_build_backward(struct ggml_context * ctx, struct ggml_cgraph * gf, bool keep) {\n    struct ggml_cgraph result = *gf;\n\n    assert(gf->n_nodes > 0);\n\n    // if we are keeping the gradient graph, we have to detach the gradient nodes from the original graph\n    if (keep) {\n        for (int i = 0; i < gf->n_nodes; i++) {\n            struct ggml_tensor * node = gf->nodes[i];\n\n            if (node->grad) {\n                node->grad = ggml_dup_tensor(ctx, node);\n                gf->grads[i] = node->grad;\n            }\n        }\n    }\n\n    for (int i = gf->n_nodes - 1; i >= 0; i--) {\n        struct ggml_tensor * node = gf->nodes[i];\n\n        // because we detached the grad nodes from the original graph, we can afford inplace operations\n        if (node->grad) {\n            ggml_compute_backward(ctx, node, keep);\n        }\n    }\n\n    for (int i = gf->n_nodes - 1; i >= 0; i--) {\n        struct ggml_tensor * node = gf->nodes[i];\n\n        if (node->is_param) {\n            GGML_PRINT_DEBUG(\"%s: found root node %p\\n\", __func__, (void *) node);\n            ggml_build_forward_impl(&result, node->grad, true);\n        }\n    }\n\n    return result;\n}\n\n//\n// thread data\n//\n// synchronization is done via busy loops\n// I tried using spin locks, but not sure how to use them correctly - the things I tried were slower than busy loops\n//\n\n#ifdef __APPLE__\n\n//#include <os/lock.h>\n\n//typedef os_unfair_lock ggml_lock_t;\n//\n//#define ggml_lock_init(x)    UNUSED(x)\n//#define ggml_lock_destroy(x) UNUSED(x)\n//#define ggml_lock_lock       os_unfair_lock_lock\n//#define ggml_lock_unlock     os_unfair_lock_unlock\n//\n//#define GGML_LOCK_INITIALIZER OS_UNFAIR_LOCK_INIT\n\ntypedef int ggml_lock_t;\n\n#define ggml_lock_init(x)    UNUSED(x)\n#define ggml_lock_destroy(x) UNUSED(x)\n#define ggml_lock_lock(x)    UNUSED(x)\n#define ggml_lock_unlock(x)  UNUSED(x)\n\n#define GGML_LOCK_INITIALIZER 0\n\ntypedef pthread_t ggml_thread_t;\n\n#define ggml_thread_create pthread_create\n#define ggml_thread_join   pthread_join\n\n#else\n\n//typedef pthread_spinlock_t ggml_lock_t;\n\n//#define ggml_lock_init(x) pthread_spin_init(x, PTHREAD_PROCESS_PRIVATE)\n//#define ggml_lock_destroy pthread_spin_destroy\n//#define ggml_lock_lock    pthread_spin_lock\n//#define ggml_lock_unlock  pthread_spin_unlock\n\ntypedef int ggml_lock_t;\n\n#define ggml_lock_init(x)    UNUSED(x)\n#define ggml_lock_destroy(x) UNUSED(x)\n#define ggml_lock_lock(x)    UNUSED(x)\n#define ggml_lock_unlock(x)  UNUSED(x)\n\n#define GGML_LOCK_INITIALIZER 0\n\ntypedef pthread_t ggml_thread_t;\n\n#define ggml_thread_create pthread_create\n#define ggml_thread_join   pthread_join\n\n#endif\n\nstruct ggml_compute_state_shared {\n    ggml_lock_t spin;\n\n    int n_threads;\n\n    // synchronization primitives\n    atomic_int  n_ready;\n    atomic_bool has_work;\n    atomic_bool stop; // stop all threads\n};\n\nstruct ggml_compute_state {\n    ggml_thread_t thrd;\n\n    struct ggml_compute_params params;\n    struct ggml_tensor * node;\n\n    struct ggml_compute_state_shared * shared;\n};\n\nstatic thread_ret_t ggml_graph_compute_thread(void * data) {\n    struct ggml_compute_state * state = (struct ggml_compute_state *) data;\n\n    const int n_threads = state->shared->n_threads;\n\n    while (true) {\n        if (atomic_fetch_add(&state->shared->n_ready, 1) == n_threads - 1) {\n            atomic_store(&state->shared->has_work, false);\n        } else {\n            while (atomic_load(&state->shared->has_work)) {\n                if (atomic_load(&state->shared->stop)) {\n                    return 0;\n                }\n                ggml_lock_lock  (&state->shared->spin);\n                ggml_lock_unlock(&state->shared->spin);\n            }\n        }\n\n        atomic_fetch_sub(&state->shared->n_ready, 1);\n\n        // wait for work\n        while (!atomic_load(&state->shared->has_work)) {\n            if (atomic_load(&state->shared->stop)) {\n                return 0;\n            }\n            ggml_lock_lock  (&state->shared->spin);\n            ggml_lock_unlock(&state->shared->spin);\n        }\n\n        // check if we should stop\n        if (atomic_load(&state->shared->stop)) {\n            break;\n        }\n\n        if (state->node) {\n            ggml_compute_forward(&state->params, state->node);\n            state->node = NULL;\n        } else {\n            break;\n        }\n    }\n\n    return 0;\n}\n\nvoid ggml_graph_compute(struct ggml_context * ctx, struct ggml_cgraph * cgraph) {\n    if (cgraph->n_threads <= 0) {\n        cgraph->n_threads = 8;\n    }\n\n    const int n_threads = cgraph->n_threads;\n\n    struct ggml_compute_state_shared state_shared = {\n        /*.spin      =*/ GGML_LOCK_INITIALIZER,\n        /*.n_threads =*/ n_threads,\n        /*.n_ready   =*/ 0,\n        /*.has_work  =*/ false,\n        /*.stop      =*/ false,\n    };\n    struct ggml_compute_state * workers = n_threads > 1 ? alloca(sizeof(struct ggml_compute_state)*(n_threads - 1)) : NULL;\n\n    // create thread pool\n    if (n_threads > 1) {\n        ggml_lock_init(&state_shared.spin);\n\n        atomic_store(&state_shared.has_work, true);\n\n        for (int j = 0; j < n_threads - 1; j++) {\n            workers[j] = (struct ggml_compute_state) {\n                .thrd   = 0,\n                .params = {\n                    .type  = GGML_TASK_COMPUTE,\n                    .ith   = j + 1,\n                    .nth   = n_threads,\n                    .wsize = cgraph->work ? ggml_nbytes(cgraph->work) : 0,\n                    .wdata = cgraph->work ? cgraph->work->data : NULL,\n                },\n                .node   = NULL,\n                .shared = &state_shared,\n            };\n            int rc = ggml_thread_create(&workers[j].thrd, NULL, ggml_graph_compute_thread, &workers[j]);\n            assert(rc == 0);\n            UNUSED(rc);\n        }\n    }\n\n    // initialize tasks + work buffer\n    {\n        size_t work_size = 0;\n\n        // thread scheduling for the different operations\n        for (int i = 0; i < cgraph->n_nodes; i++) {\n            struct ggml_tensor * node = cgraph->nodes[i];\n\n            switch (node->op) {\n                case GGML_OP_DUP:\n                    {\n                        node->n_tasks = 1;\n                    } break;\n                case GGML_OP_ADD:\n                    {\n                        node->n_tasks = n_threads;\n                    } break;\n                case GGML_OP_SUB:\n                case GGML_OP_MUL:\n                case GGML_OP_DIV:\n                case GGML_OP_SQR:\n                case GGML_OP_SQRT:\n                case GGML_OP_SUM:\n                case GGML_OP_MEAN:\n                case GGML_OP_REPEAT:\n                case GGML_OP_ABS:\n                case GGML_OP_SGN:\n                case GGML_OP_NEG:\n                case GGML_OP_STEP:\n                case GGML_OP_RELU:\n                    {\n                        node->n_tasks = 1;\n                    } break;\n                case GGML_OP_GELU:\n                    {\n                        node->n_tasks = n_threads;\n                    } break;\n                case GGML_OP_NORM:\n                    {\n                        node->n_tasks = n_threads;\n                    } break;\n                case GGML_OP_MUL_MAT:\n                    {\n                        // TODO: use different scheduling for different matrix sizes\n                        node->n_tasks = n_threads;\n\n                        size_t cur = 0;\n\n                        // TODO: better way to determine if the matrix is transposed\n                        if (node->src0->nb[1] < node->src0->nb[0]) {\n                            cur = ggml_nbytes(node)*node->n_tasks; // TODO: this can become (n_tasks-1)\n                        } else {\n                            if (node->src0->type == GGML_TYPE_F16 &&\n                                node->src1->type == GGML_TYPE_F32) {\n#if defined(GGML_USE_ACCELERATE) || defined(GGML_USE_OPENBLAS)\n                                if (ggml_compute_forward_mul_mat_use_blas(node->src0, node->src1, node)) {\n                                    cur = sizeof(float)*(node->src0->ne[0]*node->src0->ne[1]);\n                                } else {\n                                    cur = sizeof(ggml_fp16_t)*ggml_nelements(node->src1);\n                                }\n#else\n                                cur = sizeof(ggml_fp16_t)*ggml_nelements(node->src1);\n#endif\n                            } else if (node->src0->type == GGML_TYPE_F32 &&\n                                       node->src1->type == GGML_TYPE_F32) {\n                                cur = 0;\n                            } else {\n                                GGML_ASSERT(false);\n                            }\n                        }\n\n                        work_size = MAX(work_size, cur);\n                    } break;\n                case GGML_OP_SCALE:\n                    {\n                        node->n_tasks = n_threads;\n                    } break;\n                case GGML_OP_CPY:\n                case GGML_OP_RESHAPE:\n                case GGML_OP_VIEW:\n                case GGML_OP_PERMUTE:\n                case GGML_OP_TRANSPOSE:\n                case GGML_OP_GET_ROWS:\n                case GGML_OP_DIAG_MASK_INF:\n                    {\n                        node->n_tasks = 1;\n                    } break;\n                case GGML_OP_SOFT_MAX:\n                    {\n                        node->n_tasks = n_threads;\n                    } break;\n                case GGML_OP_ROPE:\n                    {\n                        node->n_tasks = 1;\n                    } break;\n                case GGML_OP_CONV_1D_1S:\n                case GGML_OP_CONV_1D_2S:\n                    {\n                        node->n_tasks = n_threads;\n\n                        GGML_ASSERT(node->src0->ne[3] == 1);\n                        GGML_ASSERT(node->src1->ne[2] == 1);\n                        GGML_ASSERT(node->src1->ne[3] == 1);\n\n                        size_t cur = 0;\n                        const int nk = node->src0->ne[0];\n\n                        if (node->src0->type == GGML_TYPE_F16 &&\n                            node->src1->type == GGML_TYPE_F32) {\n                            cur = sizeof(ggml_fp16_t)*(\n                                    nk*ggml_up32(node->src0->ne[1])*node->src0->ne[2] +\n                                    ( 2*(nk/2) + node->src1->ne[0])*node->src1->ne[1]\n                                    );\n                        } else if (node->src0->type == GGML_TYPE_F32 &&\n                                   node->src1->type == GGML_TYPE_F32) {\n                            cur = sizeof(float)*(\n                                    nk*ggml_up32(node->src0->ne[1])*node->src0->ne[2] +\n                                    ( 2*(nk/2) + node->src1->ne[0])*node->src1->ne[1]\n                                    );\n                        } else {\n                            GGML_ASSERT(false);\n                        }\n\n                        work_size = MAX(work_size, cur);\n                    } break;\n                case GGML_OP_FLASH_ATTN:\n                    {\n                        node->n_tasks = n_threads;\n\n                        size_t cur = 0;\n\n                        if (node->src1->type == GGML_TYPE_F32) {\n                            cur  = sizeof(float)*node->src1->ne[1]*node->n_tasks; // TODO: this can become (n_tasks-1)\n                            cur += sizeof(float)*node->src1->ne[1]*node->n_tasks; // this is overestimated by x2\n                        }\n\n                        if (node->src1->type == GGML_TYPE_F16) {\n                            cur  = sizeof(float)*node->src1->ne[1]*node->n_tasks; // TODO: this can become (n_tasks-1)\n                            cur += sizeof(float)*node->src1->ne[1]*node->n_tasks; // this is overestimated by x2\n                        }\n\n                        work_size = MAX(work_size, cur);\n                    } break;\n                case GGML_OP_FLASH_FF:\n                    {\n                        node->n_tasks = n_threads;\n\n                        size_t cur = 0;\n\n                        if (node->src1->type == GGML_TYPE_F32) {\n                            cur  = sizeof(float)*node->src1->ne[1]*node->n_tasks; // TODO: this can become (n_tasks-1)\n                            cur += sizeof(float)*node->src1->ne[1]*node->n_tasks; // this is overestimated by x2\n                        }\n\n                        if (node->src1->type == GGML_TYPE_F16) {\n                            cur  = sizeof(float)*node->src1->ne[1]*node->n_tasks; // TODO: this can become (n_tasks-1)\n                            cur += sizeof(float)*node->src1->ne[1]*node->n_tasks; // this is overestimated by x2\n                        }\n\n                        work_size = MAX(work_size, cur);\n                    } break;\n                case GGML_OP_NONE:\n                    {\n                        node->n_tasks = 1;\n                    } break;\n                case GGML_OP_COUNT:\n                    {\n                        assert(false);\n                    } break;\n            }\n        }\n\n        if (cgraph->work != NULL && work_size > cgraph->work_size) {\n            assert(false); // TODO: better handling\n        }\n\n        if (work_size > 0 && cgraph->work == NULL) {\n            cgraph->work_size = work_size + CACHE_LINE_SIZE*(n_threads - 1);\n\n            GGML_PRINT_DEBUG(\"%s: allocating work buffer for graph (%zu bytes)\\n\", __func__, cgraph->work_size);\n            cgraph->work = ggml_new_tensor_1d(ctx, GGML_TYPE_I8, cgraph->work_size);\n        }\n    }\n\n    const int64_t perf_start_cycles  = ggml_perf_cycles();\n    const int64_t perf_start_time_us = ggml_perf_time_us();\n\n    for (int i = 0; i < cgraph->n_nodes; i++) {\n        GGML_PRINT_DEBUG_5(\"%s: %d/%d\\n\", __func__, i, cgraph->n_nodes);\n\n        struct ggml_tensor * node = cgraph->nodes[i];\n\n        // TODO: this could be used to avoid unnecessary computations, but it needs to be improved\n        //if (node->grad == NULL && node->perf_runs > 0) {\n        //    continue;\n        //}\n\n        const int64_t perf_node_start_cycles  = ggml_perf_cycles();\n        const int64_t perf_node_start_time_us = ggml_perf_time_us();\n\n        // INIT\n        struct ggml_compute_params params = {\n            /*.type  =*/ GGML_TASK_INIT,\n            /*.ith   =*/ 0,\n            /*.nth   =*/ node->n_tasks,\n            /*.wsize =*/ cgraph->work ? ggml_nbytes(cgraph->work) : 0,\n            /*.wdata =*/ cgraph->work ? cgraph->work->data : NULL,\n        };\n\n        ggml_compute_forward(&params, node);\n\n        // COMPUTE\n        if (node->n_tasks > 1) {\n            if (atomic_fetch_add(&state_shared.n_ready, 1) == n_threads - 1) {\n                atomic_store(&state_shared.has_work, false);\n            }\n\n            while (atomic_load(&state_shared.has_work)) {\n                ggml_lock_lock  (&state_shared.spin);\n                ggml_lock_unlock(&state_shared.spin);\n            }\n\n            // launch thread pool\n            for (int j = 0; j < n_threads - 1; j++) {\n                workers[j].params = (struct ggml_compute_params) {\n                    .type  = GGML_TASK_COMPUTE,\n                    .ith   = j + 1,\n                    .nth   = n_threads,\n                    .wsize = cgraph->work ? ggml_nbytes(cgraph->work) : 0,\n                    .wdata = cgraph->work ? cgraph->work->data : NULL,\n                };\n                workers[j].node = node;\n            }\n\n            atomic_fetch_sub(&state_shared.n_ready, 1);\n\n            while (atomic_load(&state_shared.n_ready) > 0) {\n                ggml_lock_lock  (&state_shared.spin);\n                ggml_lock_unlock(&state_shared.spin);\n            }\n\n            atomic_store(&state_shared.has_work, true);\n        }\n\n        params.type = GGML_TASK_COMPUTE;\n        ggml_compute_forward(&params, node);\n\n        // wait for thread pool\n        if (node->n_tasks > 1) {\n            if (atomic_fetch_add(&state_shared.n_ready, 1) == n_threads - 1) {\n                atomic_store(&state_shared.has_work, false);\n            }\n\n            while (atomic_load(&state_shared.has_work)) {\n                ggml_lock_lock  (&state_shared.spin);\n                ggml_lock_unlock(&state_shared.spin);\n            }\n\n            atomic_fetch_sub(&state_shared.n_ready, 1);\n\n            while (atomic_load(&state_shared.n_ready) != 0) {\n                ggml_lock_lock  (&state_shared.spin);\n                ggml_lock_unlock(&state_shared.spin);\n            }\n        }\n\n        // FINALIZE\n        if (node->n_tasks > 1) {\n            if (atomic_fetch_add(&state_shared.n_ready, 1) == n_threads - 1) {\n                atomic_store(&state_shared.has_work, false);\n            }\n\n            while (atomic_load(&state_shared.has_work)) {\n                ggml_lock_lock  (&state_shared.spin);\n                ggml_lock_unlock(&state_shared.spin);\n            }\n\n            // launch thread pool\n            for (int j = 0; j < n_threads - 1; j++) {\n                workers[j].params = (struct ggml_compute_params) {\n                    .type  = GGML_TASK_FINALIZE,\n                    .ith   = j + 1,\n                    .nth   = n_threads,\n                    .wsize = cgraph->work ? ggml_nbytes(cgraph->work) : 0,\n                    .wdata = cgraph->work ? cgraph->work->data : NULL,\n                };\n                workers[j].node = node;\n            }\n\n            atomic_fetch_sub(&state_shared.n_ready, 1);\n\n            while (atomic_load(&state_shared.n_ready) > 0) {\n                ggml_lock_lock  (&state_shared.spin);\n                ggml_lock_unlock(&state_shared.spin);\n            }\n\n            atomic_store(&state_shared.has_work, true);\n        }\n\n        params.type = GGML_TASK_FINALIZE;\n        ggml_compute_forward(&params, node);\n\n        // wait for thread pool\n        if (node->n_tasks > 1) {\n            if (atomic_fetch_add(&state_shared.n_ready, 1) == n_threads - 1) {\n                atomic_store(&state_shared.has_work, false);\n            }\n\n            while (atomic_load(&state_shared.has_work)) {\n                ggml_lock_lock  (&state_shared.spin);\n                ggml_lock_unlock(&state_shared.spin);\n            }\n\n            atomic_fetch_sub(&state_shared.n_ready, 1);\n\n            while (atomic_load(&state_shared.n_ready) != 0) {\n                ggml_lock_lock  (&state_shared.spin);\n                ggml_lock_unlock(&state_shared.spin);\n            }\n        }\n\n        // performance stats (node)\n        {\n            int64_t perf_cycles_cur  = ggml_perf_cycles()  - perf_node_start_cycles;\n            int64_t perf_time_us_cur = ggml_perf_time_us() - perf_node_start_time_us;\n\n            node->perf_runs++;\n            node->perf_cycles  += perf_cycles_cur;\n            node->perf_time_us += perf_time_us_cur;\n        }\n    }\n\n    // join thread pool\n    if (n_threads > 1) {\n        atomic_store(&state_shared.stop, true);\n        atomic_store(&state_shared.has_work, true);\n\n        for (int j = 0; j < n_threads - 1; j++) {\n            int rc = ggml_thread_join(workers[j].thrd, NULL);\n            assert(rc == 0);\n            UNUSED(rc);\n        }\n\n        ggml_lock_destroy(&state_shared.spin);\n    }\n\n    // performance stats (graph)\n    {\n        int64_t perf_cycles_cur  = ggml_perf_cycles()  - perf_start_cycles;\n        int64_t perf_time_us_cur = ggml_perf_time_us() - perf_start_time_us;\n\n        cgraph->perf_runs++;\n        cgraph->perf_cycles  += perf_cycles_cur;\n        cgraph->perf_time_us += perf_time_us_cur;\n\n        GGML_PRINT_DEBUG(\"%s: perf (%d) - cpu = %.3f / %.3f ms, wall = %.3f / %.3f ms\\n\",\n                __func__, cgraph->perf_runs,\n                (double) perf_cycles_cur      / (double) ggml_cycles_per_ms(),\n                (double) cgraph->perf_cycles  / (double) ggml_cycles_per_ms() / (double) cgraph->perf_runs,\n                (double) perf_time_us_cur     / 1000.0,\n                (double) cgraph->perf_time_us / 1000.0 / cgraph->perf_runs);\n    }\n}\n\nvoid ggml_graph_reset(struct ggml_cgraph * cgraph) {\n    for (int i = 0; i < cgraph->n_nodes; i++) {\n        struct ggml_tensor * grad = cgraph->grads[i];\n\n        if (grad) {\n            ggml_set_zero(grad);\n        }\n    }\n}\n\nvoid ggml_graph_print(const struct ggml_cgraph * cgraph) {\n    int64_t perf_total_per_op_us[GGML_OP_COUNT] = {0};\n\n    GGML_PRINT(\"=== GRAPH ===\\n\");\n\n    GGML_PRINT_DEBUG(\"n_threads       = %d\\n\",       cgraph->n_threads);\n    GGML_PRINT_DEBUG(\"total work size = %zu bytes\\n\",cgraph->work_size);\n\n    GGML_PRINT(\"n_nodes = %d\\n\", cgraph->n_nodes);\n    for (int i = 0; i < cgraph->n_nodes; i++) {\n        struct ggml_tensor * node = cgraph->nodes[i];\n\n        perf_total_per_op_us[node->op] += node->perf_time_us;\n\n        GGML_PRINT(\" - %3d: [ %6d, %6d, %6d] %16s %s (%3d) cpu = %7.3f / %7.3f ms, wall = %7.3f / %7.3f ms\\n\",\n                i,\n                node->ne[0], node->ne[1], node->ne[2],\n                GGML_OP_LABEL[node->op], node->is_param ? \"x\" : node->grad ? \"g\" : \" \", node->perf_runs,\n                (double) node->perf_cycles  / (double) ggml_cycles_per_ms(),\n                (double) node->perf_cycles  / (double) ggml_cycles_per_ms() / (double) node->perf_runs,\n                (double) node->perf_time_us / 1000.0,\n                (double) node->perf_time_us / 1000.0 / node->perf_runs);\n    }\n\n    GGML_PRINT(\"n_leafs = %d\\n\", cgraph->n_leafs);\n    for (int i = 0; i < cgraph->n_leafs; i++) {\n        struct ggml_tensor * node = cgraph->leafs[i];\n\n        GGML_PRINT(\" - %3d: [ %6d, %6d] %8s\\n\",\n                i,\n                node->ne[0], node->ne[1],\n                GGML_OP_LABEL[node->op]);\n    }\n\n    for (int i = 0; i < GGML_OP_COUNT; i++) {\n        GGML_PRINT(\"perf_total_per_op_us[%16s] = %7.3f ms\\n\", GGML_OP_LABEL[i], (double) perf_total_per_op_us[i] / 1000.0);\n    }\n\n    GGML_PRINT(\"========================================\\n\");\n}\n\n// check if node is part of the graph\nstatic bool ggml_graph_find(const struct ggml_cgraph * cgraph, const struct ggml_tensor * node) {\n    if (cgraph == NULL) {\n        return true;\n    }\n\n    for (int i = 0; i < cgraph->n_nodes; i++) {\n        if (cgraph->nodes[i] == node) {\n            return true;\n        }\n    }\n\n    return false;\n}\n\nstatic struct ggml_tensor * ggml_graph_get_parent(const struct ggml_cgraph * cgraph, const struct ggml_tensor * node) {\n    for (int i = 0; i < cgraph->n_nodes; i++) {\n        struct ggml_tensor * parent = cgraph->nodes[i];\n\n        if (parent->grad == node) {\n            return parent;\n        }\n    }\n\n    return NULL;\n}\n\nvoid ggml_graph_dump_dot(const struct ggml_cgraph * gb, const struct ggml_cgraph * gf, const char * filename) {\n    char color[16];\n\n    FILE * fp = fopen(filename, \"w\");\n    assert(fp);\n\n    fprintf(fp, \"digraph G {\\n\");\n    fprintf(fp, \"  newrank = true;\\n\");\n    fprintf(fp, \"  rankdir = LR;\\n\");\n\n    for (int i = 0; i < gb->n_nodes; i++) {\n        struct ggml_tensor * node = gb->nodes[i];\n\n        if (ggml_graph_get_parent(gb, node) != NULL) {\n            continue;\n        }\n\n        if (node->is_param) {\n            snprintf(color, sizeof(color), \"yellow\");\n        } else if (node->grad) {\n            if (ggml_graph_find(gf, node)) {\n                snprintf(color, sizeof(color), \"green\");\n            } else {\n                snprintf(color, sizeof(color), \"lightblue\");\n            }\n        } else {\n            snprintf(color, sizeof(color), \"white\");\n        }\n\n        fprintf(fp, \"  \\\"%p\\\" [ \\\nstyle = filled; fillcolor = %s; shape = record; \\\nlabel=\\\"%d [%d, %d] | <x>%s\",\n                (void *) node, color,\n                i, node->ne[0], node->ne[1],\n                GGML_OP_SYMBOL[node->op]);\n\n        if (node->grad) {\n            fprintf(fp, \" | <g>%s\\\"; ]\\n\", GGML_OP_SYMBOL[node->grad->op]);\n        } else {\n            fprintf(fp, \"\\\"; ]\\n\");\n        }\n    }\n\n    for (int i = 0; i < gb->n_leafs; i++) {\n        struct ggml_tensor * node = gb->leafs[i];\n\n        snprintf(color, sizeof(color), \"pink\");\n\n        if (ggml_nelements(node) == 1) {\n            fprintf(fp, \"  \\\"%p\\\" [ \\\nstyle = filled; fillcolor = %s; shape = record; \\\nlabel=\\\"<x>%.1e\\\"; ]\\n\",\n                    (void *) node, color, ggml_get_f32_1d(node, 0));\n        } else {\n            fprintf(fp, \"  \\\"%p\\\" [ \\\nstyle = filled; fillcolor = %s; shape = record; \\\nlabel=\\\"<x>CONST %d [%d, %d]\\\"; ]\\n\",\n                    (void *) node, color,\n                    i, node->ne[0], node->ne[1]);\n        }\n    }\n\n    for (int i = 0; i < gb->n_nodes; i++) {\n        struct ggml_tensor * node = gb->nodes[i];\n\n        struct ggml_tensor * parent = ggml_graph_get_parent(gb, node);\n\n        if (node->src0) {\n            struct ggml_tensor * parent0 = ggml_graph_get_parent(gb, node->src0);\n\n            fprintf(fp, \"  \\\"%p\\\":%s -> \\\"%p\\\":%s [ arrowhead = %s; style = %s; label = \\\"x\\\"; ]\\n\",\n                    parent0 ? (void *) parent0 : (void *) node->src0,\n                    parent0 ? \"g\" : \"x\",\n                    parent ? (void *) parent : (void *) node,\n                    parent ? \"g\" : \"x\",\n                    parent ? \"empty\" : \"vee\",\n                    parent ? \"dashed\" : \"solid\");\n        }\n\n        if (node->src1) {\n            struct ggml_tensor * parent1 = ggml_graph_get_parent(gb, node->src1);\n\n            fprintf(fp, \"  \\\"%p\\\":%s -> \\\"%p\\\":%s [ arrowhead = %s; style = %s; label = \\\"y\\\"; ]\\n\",\n                    parent1 ? (void *) parent1 : (void *) node->src1,\n                    parent1 ? \"g\" : \"x\",\n                    parent ? (void *) parent : (void *) node,\n                    parent ? \"g\" : \"x\",\n                    parent ? \"empty\" : \"vee\",\n                    parent ? \"dashed\" : \"solid\");\n        }\n    }\n\n    for (int i = 0; i < gb->n_leafs; i++) {\n        struct ggml_tensor * node = gb->leafs[i];\n\n        if (node->src0) {\n            fprintf(fp, \"  \\\"%p\\\":%s -> \\\"%p\\\":%s [ label = \\\"x\\\"; ]\\n\",\n                    (void *) node->src0, \"x\",\n                    (void *) node, \"x\");\n        }\n\n        if (node->src1) {\n            fprintf(fp, \"  \\\"%p\\\":%s -> \\\"%p\\\":%s [ label = \\\"y\\\"; ]\\n\",\n                    (void *) node->src1, \"x\",\n                    (void *) node, \"x\");\n        }\n    }\n\n    fprintf(fp, \"}\\n\");\n\n    fclose(fp);\n\n    GGML_PRINT(\"%s: dot -Tpng %s -o %s.png && open %s.png\\n\", __func__, filename, filename, filename);\n}\n\n////////////////////////////////////////////////////////////////////////////////\n\nstatic void ggml_opt_set_params(int np, struct ggml_tensor * const ps[], const float * x) {\n    int i = 0;\n    for (int p = 0; p < np; ++p) {\n        const int ne = ggml_nelements(ps[p]) ;\n        // TODO: add function to set tensor from array\n        for (int j = 0; j < ne; ++j) {\n            ggml_set_f32_1d(ps[p], j, x[i++]);\n        }\n    }\n}\n\nstatic void ggml_opt_get_params(int np, struct ggml_tensor * const ps[], float * x) {\n    int i = 0;\n    for (int p = 0; p < np; ++p) {\n        const int ne = ggml_nelements(ps[p]) ;\n        // TODO: add function to get all elements at once\n        for (int j = 0; j < ne; ++j) {\n            x[i++] = ggml_get_f32_1d(ps[p], j);\n        }\n    }\n}\n\nstatic void ggml_opt_get_grad(int np, struct ggml_tensor * const ps[], float * g) {\n    int i = 0;\n    for (int p = 0; p < np; ++p) {\n        const int ne = ggml_nelements(ps[p]) ;\n        // TODO: add function to get all elements at once\n        for (int j = 0; j < ne; ++j) {\n            g[i++] = ggml_get_f32_1d(ps[p]->grad, j);\n        }\n    }\n}\n\n//\n// ADAM\n//\n//   ref: https://arxiv.org/pdf/1412.6980.pdf\n//\n\nstatic enum ggml_opt_result ggml_opt_adam(\n        struct ggml_context * ctx,\n        struct ggml_opt_params params,\n        struct ggml_tensor * f,\n        struct ggml_cgraph * gf,\n        struct ggml_cgraph * gb) {\n    assert(ggml_is_scalar(f));\n\n    gf->n_threads = params.n_threads;\n    gb->n_threads = params.n_threads;\n\n    // these will store the parameters we want to optimize\n    struct ggml_tensor * ps[GGML_MAX_PARAMS];\n\n    int np = 0;\n    int nx = 0;\n    for (int i = 0; i < gf->n_nodes; ++i) {\n        if (gf->nodes[i]->is_param) {\n            GGML_PRINT_DEBUG(\"found param %d: grad->op = %d\\n\", np, gf->nodes[i]->grad->op);\n\n            assert(np < GGML_MAX_PARAMS);\n\n            ps[np++] = gf->nodes[i];\n            nx += ggml_nelements(gf->nodes[i]);\n        }\n    }\n\n    // constants\n    const float alpha = params.adam.alpha;\n    const float beta1 = params.adam.beta1;\n    const float beta2 = params.adam.beta2;\n    const float eps   = params.adam.eps;\n\n    float * x  = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, nx)->data; // view of the parameters\n    float * g1 = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, nx)->data; // gradient\n    float * g2 = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, nx)->data; // gradient squared\n    float * m  = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, nx)->data; // first moment\n    float * v  = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, nx)->data; // second moment\n    float * mh = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, nx)->data; // first moment hat\n    float * vh = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, nx)->data; // second moment hat\n\n    float * pf = params.past > 0 ? ggml_new_tensor_1d(ctx, GGML_TYPE_F32, params.past)->data : NULL; // past function values\n\n    // initialize\n    ggml_vec_set_f32(nx, m, 0.0f);\n    ggml_vec_set_f32(nx, v, 0.0f);\n\n    // update view\n    ggml_opt_get_params(np, ps, x);\n\n    // compute the function value\n    ggml_graph_reset  (gf);\n    ggml_set_f32      (f->grad, 1.0f);\n    ggml_graph_compute(ctx, gb);\n\n    float fx_prev = ggml_get_f32_1d(f, 0);\n    if (pf) {\n        pf[0] = fx_prev;\n    }\n\n    int n_no_improvement = 0;\n    float fx_best = fx_prev;\n\n    // run the optimizer\n    for (int t = 0; t < params.adam.n_iter; ++t) {\n        GGML_PRINT_DEBUG  (\"=== iter %d ===\\n\", t);\n\n        GGML_PRINT_DEBUG  (\"f      = %10.6f\\n\", ggml_get_f32_1d(f, 0));\n        GGML_PRINT_DEBUG_5(\"df/dx0 = %10.6f\\n\", ggml_get_f32_1d(ps[0]->grad, 0));\n        GGML_PRINT_DEBUG_5(\"df/dx1 = %10.6f\\n\", ggml_get_f32_1d(ps[1]->grad, 0));\n\n        for (int i = 0; i < np; ++i) {\n            GGML_PRINT_DEBUG(\"param %d: %10.6f, g = %10.6f\\n\", i,\n                    ggml_get_f32_1d(ps[i], 0), ggml_get_f32_1d(ps[i]->grad, 0));\n        }\n\n        const int64_t t_start_wall = ggml_time_us();\n        const int64_t t_start_cpu = ggml_cycles();\n        UNUSED(t_start_wall);\n        UNUSED(t_start_cpu);\n\n        {\n            // update the gradient\n            ggml_opt_get_grad(np, ps, g1);\n\n            // m_t = beta1*m_t-1 + (1 - beta1)*g_t\n            ggml_vec_scale_f32(nx, m, beta1);\n            ggml_vec_mad_f32  (nx, m, g1, 1.0f - beta1);\n\n            // g2 = g1^2\n            ggml_vec_sqr_f32  (nx, g2, g1);\n\n            // v_t = beta2*v_t-1 + (1 - beta2)*g_t^2\n            ggml_vec_scale_f32(nx, v, beta2);\n            ggml_vec_mad_f32  (nx, v, g2, 1.0f - beta2);\n\n            // m^hat = m_t / (1 - beta1^t)\n            // v^hat = v_t / (1 - beta2^t)\n            // x_t = x_t-1 - alpha*m^hat/(sqrt(v^hat) + eps)\n            ggml_vec_cpy_f32  (nx, mh, m);\n            ggml_vec_cpy_f32  (nx, vh, v);\n\n            ggml_vec_scale_f32(nx, mh, alpha/(1.0f - powf(beta1, t + 1)));\n            ggml_vec_scale_f32(nx, vh,  1.0f/(1.0f - powf(beta2, t + 1)));\n\n            ggml_vec_sqrt_f32 (nx, vh, vh);\n            ggml_vec_acc1_f32 (nx, vh, eps);\n\n            ggml_vec_div_f32  (nx, mh, mh, vh);\n            ggml_vec_sub_f32  (nx, x,  x,  mh);\n\n            // update the parameters\n            ggml_opt_set_params(np, ps, x);\n        }\n\n        ggml_graph_reset  (gf);\n        ggml_set_f32      (f->grad, 1.0f);\n        ggml_graph_compute(ctx, gb);\n\n        const float fx = ggml_get_f32_1d(f, 0);\n\n        // check convergence\n        if (fabsf(fx - fx_prev)/fx < params.adam.eps_f) {\n            GGML_PRINT_DEBUG(\"converged\\n\");\n\n            return GGML_OPT_OK;\n        }\n\n        // delta-based convergence test\n        if (pf != NULL) {\n            // need at least params.past iterations to start checking for convergence\n            if (params.past <= t) {\n                const float rate = (pf[t%params.past] - fx)/fx;\n\n                if (fabs(rate) < params.delta) {\n                    return GGML_OPT_OK;\n                }\n            }\n\n            pf[t%params.past] = fx;\n        }\n\n        // check for improvement\n        if (params.max_no_improvement > 0) {\n            if (fx_best > fx) {\n                fx_best = fx;\n                n_no_improvement = 0;\n            } else {\n                ++n_no_improvement;\n\n                if (n_no_improvement >= params.max_no_improvement) {\n                    return GGML_OPT_OK;\n                }\n            }\n        }\n\n        fx_prev = fx;\n\n        {\n            const int64_t t_end_cpu = ggml_cycles();\n            GGML_PRINT_DEBUG(\"time iter:      %5.3f s\\n\", ((float)(t_end_cpu - t_start_cpu))/CLOCKS_PER_SEC);\n            UNUSED(t_end_cpu);\n\n            const int64_t t_end_wall = ggml_time_us();\n            GGML_PRINT_DEBUG(\"wall time iter: %5.3f s\\n\", (t_end_wall - t_start_wall)/1e6);\n            UNUSED(t_end_wall);\n        }\n    }\n\n    return GGML_OPT_DID_NOT_CONVERGE;\n}\n\n//\n// L-BFGS\n//\n// the L-BFGS implementation below is based on the following implementation:\n//\n//   https://github.com/chokkan/liblbfgs\n//\n\nstruct ggml_lbfgs_iteration_data {\n    float alpha;\n    float ys;\n    float * s;\n    float * y;\n};\n\nstatic enum ggml_opt_result linesearch_backtracking(\n        struct ggml_context * ctx,\n        const struct ggml_opt_params * params,\n        int nx,\n        float * x,\n        float * fx,\n        float * g,\n        float * d,\n        float * step,\n        const float * xp,\n        struct ggml_tensor * f,\n        struct ggml_cgraph * gf,\n        struct ggml_cgraph * gb,\n        const int np,\n        struct ggml_tensor * ps[]) {\n    int count = 0;\n\n    float width  = 0.0f;\n    float dg     = 0.0f;\n    float finit  = 0.0f;\n    float dginit = 0.0f;\n    float dgtest = 0.0f;\n\n    const float dec = 0.5f;\n    const float inc = 2.1f;\n\n    if (*step <= 0.) {\n        return GGML_LINESEARCH_INVALID_PARAMETERS;\n    }\n\n    // compute the initial gradient in the search direction\n    ggml_vec_dot_f32(nx, &dginit, g, d);\n\n    // make sure that d points to a descent direction\n    if (0 < dginit) {\n        return GGML_LINESEARCH_FAIL;\n    }\n\n    // initialize local variables\n    finit = *fx;\n    dgtest = params->lbfgs.ftol*dginit;\n\n    while (true) {\n        ggml_vec_cpy_f32(nx, x, xp);\n        ggml_vec_mad_f32(nx, x, d, *step);\n\n        // evaluate the function and gradient values\n        {\n            ggml_opt_set_params(np, ps, x);\n\n            ggml_graph_reset  (gf);\n            ggml_set_f32      (f->grad, 1.0f);\n            ggml_graph_compute(ctx, gb);\n\n            ggml_opt_get_grad(np, ps, g);\n\n            *fx = ggml_get_f32_1d(f, 0);\n        }\n\n        ++count;\n\n        if (*fx > finit + (*step)*dgtest) {\n            width = dec;\n        } else {\n            // Armijo condition is satisfied\n            if (params->lbfgs.linesearch == GGML_LINESEARCH_BACKTRACKING_ARMIJO) {\n                return count;\n            }\n\n            ggml_vec_dot_f32(nx, &dg, g, d);\n\n            // check the Wolfe condition\n            if (dg < params->lbfgs.wolfe * dginit) {\n                width = inc;\n            } else {\n                if(params->lbfgs.linesearch == GGML_LINESEARCH_BACKTRACKING_WOLFE) {\n                    // regular Wolfe conditions\n                    return count;\n                }\n\n                if(dg > -params->lbfgs.wolfe*dginit) {\n                    width = dec;\n                } else {\n                    // strong Wolfe condition (GGML_LINESEARCH_BACKTRACKING_STRONG_WOLFE)\n                    return count;\n                }\n                return count;\n            }\n        }\n\n        if (*step < params->lbfgs.min_step) {\n            return GGML_LINESEARCH_MINIMUM_STEP;\n        }\n        if (*step > params->lbfgs.max_step) {\n            return GGML_LINESEARCH_MAXIMUM_STEP;\n        }\n        if (params->lbfgs.max_linesearch <= count) {\n            return GGML_LINESEARCH_MAXIMUM_ITERATIONS;\n        }\n\n        (*step) *= width;\n    }\n\n    return GGML_LINESEARCH_FAIL;\n}\n\nstatic enum ggml_opt_result ggml_opt_lbfgs(\n        struct ggml_context * ctx,\n        struct ggml_opt_params params,\n        struct ggml_tensor * f,\n        struct ggml_cgraph * gf,\n        struct ggml_cgraph * gb) {\n    if (params.lbfgs.linesearch == GGML_LINESEARCH_BACKTRACKING_WOLFE ||\n        params.lbfgs.linesearch == GGML_LINESEARCH_BACKTRACKING_STRONG_WOLFE) {\n        if (params.lbfgs.wolfe <= params.lbfgs.ftol || 1. <= params.lbfgs.wolfe) {\n            return GGML_OPT_INVALID_WOLFE;\n        }\n    }\n\n    gf->n_threads = params.n_threads;\n    gb->n_threads = params.n_threads;\n\n    const int m = params.lbfgs.m;\n\n    // these will store the parameters we want to optimize\n    struct ggml_tensor * ps[GGML_MAX_PARAMS];\n\n    int np = 0;\n    int nx = 0;\n    for (int i = 0; i < gf->n_nodes; ++i) {\n        if (gf->nodes[i]->is_param) {\n            GGML_PRINT_DEBUG(\"found param %d: grad->op = %d\\n\", np, gf->nodes[i]->grad->op);\n\n            assert(np < GGML_MAX_PARAMS);\n\n            ps[np++] = gf->nodes[i];\n            nx += ggml_nelements(gf->nodes[i]);\n        }\n    }\n\n    float * x  = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, nx)->data; // current parameters\n    float * xp = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, nx)->data; // previous parameters\n    float * g  = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, nx)->data; // current gradient\n    float * gp = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, nx)->data; // previous gradient\n    float * d  = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, nx)->data; // search direction\n\n    float * pf = params.past > 0 ? ggml_new_tensor_1d(ctx, GGML_TYPE_F32, params.past)->data : NULL; // past function values\n\n    float fx    = 0.0f; // cost function value\n    float xnorm = 0.0f; // ||x||\n    float gnorm = 0.0f; // ||g||\n    float step  = 0.0f;\n\n    // initialize x from the graph nodes\n    ggml_opt_get_params(np, ps, x);\n\n    // the L-BFGS memory\n    struct ggml_lbfgs_iteration_data * lm = alloca(sizeof(struct ggml_lbfgs_iteration_data)*m);\n\n    for (int i = 0; i < m; ++i) {\n        lm[i].alpha = 0.0f;\n        lm[i].ys    = 0.0f;\n        lm[i].s     = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, nx)->data;\n        lm[i].y     = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, nx)->data;\n    }\n\n    // evaluate the function value and its gradient\n    {\n        ggml_opt_set_params(np, ps, x);\n\n        ggml_graph_reset  (gf);\n        ggml_set_f32      (f->grad, 1.0f);\n        ggml_graph_compute(ctx, gb);\n\n        ggml_opt_get_grad(np, ps, g);\n\n        fx = ggml_get_f32_1d(f, 0);\n    }\n\n    if (pf) {\n        pf[0] = fx;\n    }\n\n    float fx_best = fx;\n\n    // search direction = -gradient\n    ggml_vec_neg_f32(nx, d, g);\n\n    // ||x||, ||g||\n    ggml_vec_norm_f32(nx, &xnorm, x);\n    ggml_vec_norm_f32(nx, &gnorm, g);\n\n    if (xnorm < 1.0f) {\n        xnorm = 1.0f;\n    }\n\n    // already optimized\n    if (gnorm/xnorm <= params.lbfgs.eps) {\n        return GGML_OPT_OK;\n    }\n\n    // initial step\n    ggml_vec_norm_inv_f32(nx, &step, d);\n\n    int j                = 0;\n    int k                = 1;\n    int ls               = 0;\n    int end              = 0;\n    int bound            = 0;\n    int n_no_improvement = 0;\n\n    float ys   = 0.0f;\n    float yy   = 0.0f;\n    float beta = 0.0f;\n\n    while (true) {\n        // store the current position and gradient vectors\n        ggml_vec_cpy_f32(nx, xp, x);\n        ggml_vec_cpy_f32(nx, gp, g);\n\n        ls = linesearch_backtracking(ctx, &params, nx, x, &fx, g, d, &step, xp, f, gf, gb, np, ps);\n\n        if (ls < 0) {\n            // linesearch failed - go back to the previous point and return\n            ggml_vec_cpy_f32(nx, x, xp);\n            ggml_vec_cpy_f32(nx, g, gp);\n\n            return ls;\n        }\n\n        ggml_vec_norm_f32(nx, &xnorm, x);\n        ggml_vec_norm_f32(nx, &gnorm, g);\n\n        GGML_PRINT_DEBUG(\"f = %10.6f\\n\", ggml_get_f32_1d(f, 0));\n\n        if (xnorm < 1.0) {\n            xnorm = 1.0;\n        }\n        if (gnorm/xnorm <= params.lbfgs.eps) {\n            // converged\n            return GGML_OPT_OK;\n        }\n\n        // delta-based convergence test\n        if (pf != NULL) {\n            // need at least params.past iterations to start checking for convergence\n            if (params.past <= k) {\n                const float rate = (pf[k%params.past] - fx)/fx;\n\n                if (fabs(rate) < params.delta) {\n                    return GGML_OPT_OK;\n                }\n            }\n\n            pf[k%params.past] = fx;\n        }\n\n        // check for improvement\n        if (params.max_no_improvement > 0) {\n            if (fx < fx_best) {\n                fx_best = fx;\n                n_no_improvement = 0;\n            } else {\n                n_no_improvement++;\n\n                if (n_no_improvement >= params.max_no_improvement) {\n                    return GGML_OPT_OK;\n                }\n            }\n        }\n\n        if (params.lbfgs.n_iter != 0 && params.lbfgs.n_iter < k + 1) {\n            // reached the maximum number of iterations\n            return GGML_OPT_DID_NOT_CONVERGE;\n        }\n\n        // update vectors s and y:\n        //   s_{k+1} = x_{k+1} - x_{k} = \\step * d_{k}.\n        //   y_{k+1} = g_{k+1} - g_{k}.\n        //\n        ggml_vec_sub_f32(nx, lm[end].s, x, xp);\n        ggml_vec_sub_f32(nx, lm[end].y, g, gp);\n\n        // compute scalars ys and yy:\n        //     ys = y^t \\cdot s    -> 1 / \\rho.\n        //     yy = y^t \\cdot y.\n        //\n        ggml_vec_dot_f32(nx, &ys, lm[end].y, lm[end].s);\n        ggml_vec_dot_f32(nx, &yy, lm[end].y, lm[end].y);\n\n        lm[end].ys = ys;\n\n        // find new search direction\n        //   ref: https://en.wikipedia.org/wiki/Limited-memory_BFGS\n\n        bound = (m <= k) ? m : k;\n        k++;\n        end = (end + 1)%m;\n\n        // initialize search direction with -g\n        ggml_vec_neg_f32(nx, d, g);\n\n        j = end;\n        for (int i = 0; i < bound; ++i) {\n            j = (j + m - 1) % m;\n            // \\alpha_{j} = \\rho_{j} s^{t}_{j} \\cdot q_{k+1}\n            ggml_vec_dot_f32(nx, &lm[j].alpha, lm[j].s, d);\n            lm[j].alpha /= lm[j].ys;\n            // q_{i} = q_{i+1} - \\alpha_{i} y_{i}\n            ggml_vec_mad_f32(nx, d, lm[j].y, -lm[j].alpha);\n        }\n\n        ggml_vec_scale_f32(nx, d, ys/yy);\n\n        for (int i = 0; i < bound; ++i) {\n            // \\beta_{j} = \\rho_{j} y^t_{j} \\cdot \\gamma_{i}\n            ggml_vec_dot_f32(nx, &beta, lm[j].y, d);\n            beta /= lm[j].ys;\n            // \\gamma_{i+1} = \\gamma_{i} + (\\alpha_{j} - \\beta_{j}) s_{j}\n            ggml_vec_mad_f32(nx, d, lm[j].s, lm[j].alpha - beta);\n            j = (j + 1)%m;\n        }\n\n        step = 1.0;\n    }\n\n    return GGML_OPT_DID_NOT_CONVERGE;\n}\n\nstruct ggml_opt_params ggml_opt_default_params(enum ggml_opt_type type) {\n    struct ggml_opt_params result;\n\n    switch (type) {\n        case GGML_OPT_ADAM:\n            {\n                result = (struct ggml_opt_params) {\n                    .type      = GGML_OPT_ADAM,\n                    .n_threads = 1,\n                    .past      = 0,\n                    .delta     = 1e-5f,\n\n                    .max_no_improvement = 100,\n\n                    .print_forward_graph  = true,\n                    .print_backward_graph = true,\n\n                    .adam = {\n                        .n_iter = 10000,\n                        .alpha  = 0.001f,\n                        .beta1  = 0.9f,\n                        .beta2  = 0.999f,\n                        .eps    = 1e-8f,\n                        .eps_f  = 1e-5f,\n                        .eps_g  = 1e-3f,\n                    },\n                };\n            } break;\n        case GGML_OPT_LBFGS:\n            {\n                result = (struct ggml_opt_params) {\n                    .type      = GGML_OPT_LBFGS,\n                    .n_threads = 1,\n                    .past      = 0,\n                    .delta     = 1e-5f,\n\n                    .max_no_improvement = 0,\n\n                    .print_forward_graph  = true,\n                    .print_backward_graph = true,\n\n                    .lbfgs = {\n                        .m              = 6,\n                        .n_iter         = 100,\n                        .max_linesearch = 20,\n\n                        .eps      = 1e-5f,\n                        .ftol     = 1e-4f,\n                        .wolfe    = 0.9f,\n                        .min_step = 1e-20f,\n                        .max_step = 1e+20f,\n\n                        .linesearch = GGML_LINESEARCH_DEFAULT,\n                    },\n                };\n            } break;\n    }\n\n    return result;\n}\n\nenum ggml_opt_result ggml_opt(\n        struct ggml_context * ctx,\n        struct ggml_opt_params params,\n        struct ggml_tensor * f) {\n    bool free_ctx = false;\n    if (ctx == NULL) {\n        struct ggml_init_params params_ctx = {\n            .mem_size   = 16*1024*1024,\n            .mem_buffer = NULL,\n        };\n\n        ctx = ggml_init(params_ctx);\n        if (ctx == NULL) {\n            return GGML_OPT_NO_CONTEXT;\n        }\n\n        free_ctx = true;\n    }\n\n    enum ggml_opt_result result = GGML_OPT_OK;\n\n    // build forward + backward compute graphs\n    struct ggml_cgraph gf = ggml_build_forward (f);\n    struct ggml_cgraph gb = ggml_build_backward(ctx, &gf, false);\n\n    switch (params.type) {\n        case GGML_OPT_ADAM:\n            {\n                result = ggml_opt_adam(ctx, params, f, &gf, &gb);\n            } break;\n        case GGML_OPT_LBFGS:\n            {\n                result = ggml_opt_lbfgs(ctx, params, f, &gf, &gb);\n            } break;\n    }\n\n    if (params.print_forward_graph) {\n        ggml_graph_print   (&gf);\n        ggml_graph_dump_dot(&gf, NULL, \"opt-forward.dot\");\n    }\n\n    if (params.print_backward_graph) {\n        ggml_graph_print   (&gb);\n        ggml_graph_dump_dot(&gb, &gf, \"opt-backward.dot\");\n    }\n\n    if (free_ctx) {\n        ggml_free(ctx);\n    }\n\n    return result;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n\nint ggml_cpu_has_avx(void) {\n#if defined(__AVX__)\n    return 1;\n#else\n    return 0;\n#endif\n}\n\nint ggml_cpu_has_avx2(void) {\n#if defined(__AVX2__)\n    return 1;\n#else\n    return 0;\n#endif\n}\n\nint ggml_cpu_has_avx512(void) {\n#if defined(__AVX512F__)\n    return 1;\n#else\n    return 0;\n#endif\n}\n\nint ggml_cpu_has_fma(void) {\n#if defined(__FMA__)\n    return 1;\n#else\n    return 0;\n#endif\n}\n\nint ggml_cpu_has_neon(void) {\n#if defined(__ARM_NEON)\n    return 1;\n#else\n    return 0;\n#endif\n}\n\nint ggml_cpu_has_arm_fma(void) {\n#if defined(__ARM_FEATURE_FMA)\n    return 1;\n#else\n    return 0;\n#endif\n}\n\nint ggml_cpu_has_f16c(void) {\n#if defined(__F16C__)\n    return 1;\n#else\n    return 0;\n#endif\n}\n\nint ggml_cpu_has_fp16_va(void) {\n#if defined(__ARM_FEATURE_FP16_VECTOR_ARITHMETIC)\n    return 1;\n#else\n    return 0;\n#endif\n}\n\nint ggml_cpu_has_wasm_simd(void) {\n#if defined(__wasm_simd128__)\n    return 1;\n#else\n    return 0;\n#endif\n}\n\nint ggml_cpu_has_blas(void) {\n#if defined(GGML_USE_ACCELERATE) || defined(GGML_USE_OPENBLAS)\n    return 1;\n#else\n    return 0;\n#endif\n}\n\n////////////////////////////////////////////////////////////////////////////////\n"
  },
  {
    "path": "Whisper/source/ggml.h",
    "content": "#pragma once\n\n//\n// GGML Tensor Library\n//\n// This documentation is still a work in progress.\n// If you wish some specific topics to be covered, feel free to drop a comment:\n//\n//   https://github.com/ggerganov/whisper.cpp/issues/40\n//\n// ## Overview\n//\n// This library implements:\n//\n//  - a set of tensor operations\n//  - automatic differentiation\n//  - basic optimization algorithms\n//\n// The aim of this library is to provide a minimalistic approach for various machine learning tasks. This includes,\n// but is not limited to, the following:\n//\n//  - linear regression\n//  - support vector machines\n//  - neural networks\n//\n// The library allows the user to define a certain function using the available tensor operations. This function\n// definition is represented internally via a computation graph. Each tensor operation in the function definition\n// corresponds to a node in the graph. Having the computation graph defined, the user can choose to compute the\n// function's value and/or its gradient with respect to the input variables. Optionally, the function can be optimized\n// using one of the available optimization algorithms.\n//\n// For example, here we define the function: f(x) = a*x^2 + b\n//\n//   {\n//       struct ggml_init_params params = {\n//           .mem_size   = 16*1024*1024,\n//           .mem_buffer = NULL,\n//       };\n//\n//       // memory allocation happens here\n//       struct ggml_context * ctx = ggml_init(params);\n//\n//       struct ggml_tensor * x = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, 1);\n//\n//       ggml_set_param(ctx, x); // x is an input variable\n//\n//       struct ggml_tensor * a  = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, 1);\n//       struct ggml_tensor * b  = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, 1);\n//       struct ggml_tensor * x2 = ggml_mul(ctx, x, x);\n//       struct ggml_tensor * f  = ggml_add(ctx, ggml_mul(ctx, a, x2), b);\n//\n//       ...\n//   }\n//\n// Notice that the function definition above does not involve any actual computation. The computation is performed only\n// when the user explicitly requests it. For example, to compute the function's value at x = 2.0:\n//\n//   {\n//       ...\n//\n//       struct ggml_cgraph gf = ggml_build_forward(f);\n//\n//       // set the input variable and parameter values\n//       ggml_set_f32(x, 2.0f);\n//       ggml_set_f32(a, 3.0f);\n//       ggml_set_f32(b, 4.0f);\n//\n//       ggml_graph_compute(ctx0, &gf);\n//\n//       printf(\"f = %f\\n\", ggml_get_f32_1d(f, 0));\n//\n//       ...\n//   }\n//\n// The actual computation is performed in the ggml_graph_compute() function.\n//\n// The ggml_new_tensor_...() functions create new tensors. They are allocated in the memory buffer provided to the\n// ggml_init() function. You have to be careful not to exceed the memory buffer size. Therefore, you have to know\n// in advance how much memory you need for your computation. Alternatively, you can allocate a large enough memory\n// and after defining the computation graph, call the ggml_used_mem() function to find out how much memory was\n// actually needed.\n//\n// The ggml_set_param() function marks a tensor as an input variable. This is used by the automatic\n// differentiation and optimization algorithms.\n//\n// The described approach allows to define the function graph once and then compute its forward or backward graphs\n// multiple times. All computations will use the same memory buffer allocated in the ggml_init() function. This way\n// the user can avoid the memory allocation overhead at runtime.\n//\n// The library supports multi-dimensional tensors - up to 4 dimensions. The FP16 and FP32 data types are first class\n// citizens, but in theory the library can be extended to support FP8 and integer data types.\n//\n// Each tensor operation produces a new tensor. Initially the library was envisioned to support only the use of unary\n// and binary operations. Most of the available operations fall into one of these two categories. With time, it became\n// clear that the library needs to support more complex operations. The way to support these operations is not clear\n// yet, but a few examples are demonstrated in the following operations:\n//\n//   - ggml_permute()\n//   - ggml_conv_1d_1s()\n//   - ggml_conv_1d_2s()\n//\n// For each tensor operator, the library implements a forward and backward computation function. The forward function\n// computes the output tensor value given the input tensor values. The backward function computes the adjoint of the\n// input tensors given the adjoint of the output tensor. For a detailed explanation of what this means, take a\n// calculus class, or watch the following video:\n//\n//   What is Automatic Differentiation?\n//   https://www.youtube.com/watch?v=wG_nF1awSSY\n//\n//\n// ## Tensor data (struct ggml_tensor)\n//\n// The tensors are stored in memory via the ggml_tensor struct. The structure provides information about the size of\n// the tensor, the data type, and the memory buffer where the tensor data is stored. Additionally, it contains\n// pointers to the \"source\" tensors - i.e. the tensors that were used to compute the current tensor. For example:\n//\n//   {\n//       struct ggml_tensor * c = ggml_add(ctx, a, b);\n//\n//       assert(c->src[0] == a);\n//       assert(c->src[1] == b);\n//   }\n//\n// The multi-dimensional tensors are stored in row-major order. The ggml_tensor struct contains fields for the\n// number of elements in each dimension (\"ne\") as well as the number of bytes (\"nb\", a.k.a. stride). This allows\n// to store tensors that are not contiguous in memory, which is useful for operations such as transposition and\n// permutation. All tensor operations have to take the stride into account and not assume that the tensor is\n// contiguous in memory.\n//\n// The data of the tensor is accessed via the \"data\" pointer. For example:\n//\n//   {\n//       struct ggml_tensor * a = ggml_new_tensor_2d(ctx, GGML_TYPE_F32, 2, 3);\n//\n//       // a[1, 2] = 1.0f;\n//       *(float *) ((char *) a->data + 2*a->nb[1] + 1*a->nb[0]) = 1.0f;\n//\n//       // a[2, 0] = 2.0f;\n//       *(float *) ((char *) a->data + 0*a->nb[1] + 2*a->nb[0]) = 2.0f;\n//\n//       ...\n//   }\n//\n// Alternatively, there are helper functions, such as ggml_get_f32_1d() and ggml_set_f32_1d() that can be used.\n//\n// ## The matrix multiplication operator (ggml_mul_mat)\n//\n// TODO\n//\n//\n// ## Multi-threading\n//\n// TODO\n//\n//\n// ## Overview of ggml.c\n//\n// TODO\n//\n//\n// ## SIMD optimizations\n//\n// TODO\n//\n//\n// ## Debugging ggml\n//\n// TODO\n//\n//\n\n#ifdef  __cplusplus\nextern \"C\" {\n#endif\n\n#include <stdint.h>\n#include <stddef.h>\n#include <stdbool.h>\n\n#define GGML_MAX_DIMS     4\n#define GGML_MAX_NODES    4096\n#define GGML_MAX_PARAMS   16\n#define GGML_MAX_CONTEXTS 64\n#define GGML_MAX_OPT      4\n\n#ifdef __ARM_NEON\n// we use the built-in 16-bit float type\ntypedef __fp16 ggml_fp16_t;\n#else\ntypedef uint16_t ggml_fp16_t;\n#endif\n\n// convert FP16 <-> FP32\nfloat       ggml_fp16_to_fp32(ggml_fp16_t x);\nggml_fp16_t ggml_fp32_to_fp16(float x);\n\nstruct ggml_object;\nstruct ggml_context;\n\nenum ggml_type {\n    GGML_TYPE_I8,\n    GGML_TYPE_I16,\n    GGML_TYPE_I32,\n    GGML_TYPE_F16,\n    GGML_TYPE_F32,\n    GGML_TYPE_COUNT,\n};\n\n// available tensor operations:\nenum ggml_op {\n    GGML_OP_NONE = 0,\n\n    GGML_OP_DUP,\n    GGML_OP_ADD,\n    GGML_OP_SUB,\n    GGML_OP_MUL,\n    GGML_OP_DIV,\n    GGML_OP_SQR,\n    GGML_OP_SQRT,\n    GGML_OP_SUM,\n    GGML_OP_MEAN,\n    GGML_OP_REPEAT,\n    GGML_OP_ABS,\n    GGML_OP_SGN,\n    GGML_OP_NEG,\n    GGML_OP_STEP,\n    GGML_OP_RELU,\n    GGML_OP_GELU,\n    GGML_OP_NORM, // normalize\n\n    GGML_OP_MUL_MAT,\n\n    GGML_OP_SCALE,\n    GGML_OP_CPY,\n    GGML_OP_RESHAPE,\n    GGML_OP_VIEW,\n    GGML_OP_PERMUTE,\n    GGML_OP_TRANSPOSE,\n    GGML_OP_GET_ROWS,\n    GGML_OP_DIAG_MASK_INF,\n    GGML_OP_SOFT_MAX,\n    GGML_OP_ROPE,\n    GGML_OP_CONV_1D_1S,\n    GGML_OP_CONV_1D_2S,\n\n    GGML_OP_FLASH_ATTN,\n    GGML_OP_FLASH_FF,\n\n    GGML_OP_COUNT,\n};\n\n// n-dimensional tensor\nstruct ggml_tensor {\n    enum ggml_type type;\n\n    int    n_dims;\n    int    ne[GGML_MAX_DIMS]; // number of elements\n    size_t nb[GGML_MAX_DIMS]; // stride in bytes:\n                              // nb[0] = sizeof(type)\n                              // nb[1] = nb[0]   * ne[0] + padding\n                              // nb[i] = nb[i-1] * ne[i-1]\n\n    // compute data\n    enum ggml_op op;\n\n    bool is_param;\n\n    struct ggml_tensor * grad;\n    struct ggml_tensor * src0;\n    struct ggml_tensor * src1;\n    struct ggml_tensor * opt[GGML_MAX_OPT];\n\n    // thread scheduling\n    int n_tasks;\n\n    // performance\n    int     perf_runs;\n    int64_t perf_cycles;\n    int64_t perf_time_us;\n\n    void * data;\n    char padding[8];\n};\n\n// computation graph\nstruct ggml_cgraph {\n    int n_nodes;\n    int n_leafs;\n    int n_threads;\n\n    size_t work_size;\n    struct ggml_tensor * work;\n\n    struct ggml_tensor * nodes[GGML_MAX_NODES];\n    struct ggml_tensor * grads[GGML_MAX_NODES];\n    struct ggml_tensor * leafs[GGML_MAX_NODES];\n\n    // performance\n    int     perf_runs;\n    int64_t perf_cycles;\n    int64_t perf_time_us;\n};\n\nstruct ggml_init_params {\n    // memory pool\n    size_t mem_size;   // bytes\n    void * mem_buffer; // if NULL, memory will be allocated internally\n};\n\nvoid    ggml_time_init(void); // call this once at the beginning of the program\nint64_t ggml_time_ms(void);\nint64_t ggml_time_us(void);\nint64_t ggml_cycles(void);\nint64_t ggml_cycles_per_ms(void);\n\nvoid ggml_print_object (const struct ggml_object * obj);\nvoid ggml_print_objects(const struct ggml_context * ctx);\n\nint    ggml_nelements(const struct ggml_tensor * tensor);\nsize_t ggml_nbytes   (const struct ggml_tensor * tensor);\n\nsize_t ggml_type_size   (enum ggml_type type);\nsize_t ggml_element_size(const struct ggml_tensor * tensor);\n\nstruct ggml_context * ggml_init(struct ggml_init_params params);\nvoid ggml_free(struct ggml_context * ctx);\n\nsize_t ggml_used_mem(const struct ggml_context * ctx);\n\nstruct ggml_tensor * ggml_new_tensor(\n        struct ggml_context * ctx,\n        enum   ggml_type type,\n        int    n_dims,\n        const int *ne);\n\nstruct ggml_tensor * ggml_new_tensor_1d(\n        struct ggml_context * ctx,\n        enum   ggml_type type,\n        int    ne0);\n\nstruct ggml_tensor * ggml_new_tensor_2d(\n        struct ggml_context * ctx,\n        enum   ggml_type type,\n        int    ne0,\n        int    ne1);\n\nstruct ggml_tensor * ggml_new_tensor_3d(\n        struct ggml_context * ctx,\n        enum   ggml_type type,\n        int    ne0,\n        int    ne1,\n        int    ne2);\n\nstruct ggml_tensor * ggml_new_tensor_4d(\n        struct ggml_context * ctx,\n        enum   ggml_type type,\n        int    ne0,\n        int    ne1,\n        int    ne2,\n        int    ne3);\n\nstruct ggml_tensor * ggml_new_i32(struct ggml_context * ctx, int32_t value);\nstruct ggml_tensor * ggml_new_f32(struct ggml_context * ctx, float value);\n\nstruct ggml_tensor * ggml_dup_tensor (struct ggml_context * ctx, const struct ggml_tensor * src);\nstruct ggml_tensor * ggml_view_tensor(struct ggml_context * ctx, const struct ggml_tensor * src);\n\nstruct ggml_tensor * ggml_set_zero(struct ggml_tensor * tensor);\nstruct ggml_tensor * ggml_set_i32 (struct ggml_tensor * tensor, int32_t value);\nstruct ggml_tensor * ggml_set_f32 (struct ggml_tensor * tensor, float value);\n\nint32_t ggml_get_i32_1d(const struct ggml_tensor * tensor, int i);\nvoid    ggml_set_i32_1d(const struct ggml_tensor * tensor, int i, int32_t value);\n\nfloat ggml_get_f32_1d(const struct ggml_tensor * tensor, int i);\nvoid  ggml_set_f32_1d(const struct ggml_tensor * tensor, int i, float value);\n\n void * ggml_get_data    (const struct ggml_tensor * tensor);\nfloat * ggml_get_data_f32(const struct ggml_tensor * tensor);\n\n//\n// operations on tensors with backpropagation\n//\n\nstruct ggml_tensor * ggml_dup(\n        struct ggml_context * ctx,\n        struct ggml_tensor  * a);\n\nstruct ggml_tensor * ggml_add(\n        struct ggml_context * ctx,\n        struct ggml_tensor  * a,\n        struct ggml_tensor  * b);\n\nstruct ggml_tensor * ggml_sub(\n        struct ggml_context * ctx,\n        struct ggml_tensor  * a,\n        struct ggml_tensor  * b);\n\nstruct ggml_tensor * ggml_mul(\n        struct ggml_context * ctx,\n        struct ggml_tensor  * a,\n        struct ggml_tensor  * b);\n\nstruct ggml_tensor * ggml_div(\n        struct ggml_context * ctx,\n        struct ggml_tensor  * a,\n        struct ggml_tensor  * b);\n\nstruct ggml_tensor * ggml_sqr(\n        struct ggml_context * ctx,\n        struct ggml_tensor  * a);\n\nstruct ggml_tensor * ggml_sqrt(\n        struct ggml_context * ctx,\n        struct ggml_tensor  * a);\n\n// return scalar\n// TODO: compute sum along rows\nstruct ggml_tensor * ggml_sum(\n        struct ggml_context * ctx,\n        struct ggml_tensor  * a);\n\n// mean along rows\nstruct ggml_tensor * ggml_mean(\n        struct ggml_context * ctx,\n        struct ggml_tensor  * a);\n\n// if a is the same shape as b, and a is not parameter, return a\n// otherwise, return a new tensor: repeat(a) to fit in b\nstruct ggml_tensor * ggml_repeat(\n        struct ggml_context * ctx,\n        struct ggml_tensor  * a,\n        struct ggml_tensor  * b);\n\nstruct ggml_tensor * ggml_abs(\n        struct ggml_context * ctx,\n        struct ggml_tensor  * a);\n\nstruct ggml_tensor * ggml_sgn(\n        struct ggml_context * ctx,\n        struct ggml_tensor  * a);\n\nstruct ggml_tensor * ggml_neg(\n        struct ggml_context * ctx,\n        struct ggml_tensor  * a);\n\nstruct ggml_tensor * ggml_step(\n        struct ggml_context * ctx,\n        struct ggml_tensor  * a);\n\nstruct ggml_tensor * ggml_relu(\n        struct ggml_context * ctx,\n        struct ggml_tensor  * a);\n\n// TODO: double-check this computation is correct\nstruct ggml_tensor * ggml_gelu(\n        struct ggml_context * ctx,\n        struct ggml_tensor  * a);\n\n// normalize along rows\n// TODO: eps is hardcoded to 1e-5 for now\nstruct ggml_tensor * ggml_norm(\n        struct ggml_context * ctx,\n        struct ggml_tensor  * a);\n\n// A: m rows, n columns\n// B: p rows, n columns (i.e. we transpose it internally)\n// result is m columns, p rows\nstruct ggml_tensor * ggml_mul_mat(\n        struct ggml_context * ctx,\n        struct ggml_tensor  * a,\n        struct ggml_tensor  * b);\n\n//\n// operations on tensors without backpropagation\n//\n\n// in-place, returns view(a)\nstruct ggml_tensor * ggml_scale(\n        struct ggml_context * ctx,\n        struct ggml_tensor  * a,\n        struct ggml_tensor  * b);\n\n// a -> b, return view(b)\nstruct ggml_tensor * ggml_cpy(\n        struct ggml_context * ctx,\n        struct ggml_tensor  * a,\n        struct ggml_tensor  * b);\n\n// return view(a), b specifies the new shape\n// TODO: when we start computing gradient, make a copy instead of view\nstruct ggml_tensor * ggml_reshape(\n        struct ggml_context * ctx,\n        struct ggml_tensor  * a,\n        struct ggml_tensor  * b);\n\n// return view(a)\n// TODO: when we start computing gradient, make a copy instead of view\nstruct ggml_tensor * ggml_reshape_2d(\n        struct ggml_context * ctx,\n        struct ggml_tensor  * a,\n        int                   ne0,\n        int                   ne1);\n\n// return view(a)\n// TODO: when we start computing gradient, make a copy instead of view\nstruct ggml_tensor * ggml_reshape_3d(\n        struct ggml_context * ctx,\n        struct ggml_tensor  * a,\n        int                   ne0,\n        int                   ne1,\n        int                   ne2);\n\n// offset in bytes\nstruct ggml_tensor * ggml_view_1d(\n        struct ggml_context * ctx,\n        struct ggml_tensor  * a,\n        int                   ne0,\n        size_t                offset);\n\nstruct ggml_tensor * ggml_view_2d(\n        struct ggml_context * ctx,\n        struct ggml_tensor  * a,\n        int                   ne0,\n        int                   ne1,\n        size_t                nb1, // row stride in bytes\n        size_t                offset);\n\nstruct ggml_tensor * ggml_permute(\n        struct ggml_context * ctx,\n        struct ggml_tensor  * a,\n        int                   axis0,\n        int                   axis1,\n        int                   axis2,\n        int                   axis3);\n\n// alias for ggml_permute(ctx, a, 1, 0, 2, 3)\nstruct ggml_tensor * ggml_transpose(\n        struct ggml_context * ctx,\n        struct ggml_tensor  * a);\n\nstruct ggml_tensor * ggml_get_rows(\n        struct ggml_context * ctx,\n        struct ggml_tensor  * a,\n        struct ggml_tensor  * b);\n\n// set elements above the diagonal to -INF\n// in-place, returns view(a)\nstruct ggml_tensor * ggml_diag_mask_inf(\n        struct ggml_context * ctx,\n        struct ggml_tensor  * a,\n        int                   n_past);\n\n// in-place, returns view(a)\nstruct ggml_tensor * ggml_soft_max(\n        struct ggml_context * ctx,\n        struct ggml_tensor  * a);\n\n// rotary position embedding\n// in-place, returns view(a)\n// if mode == 1, skip n_past elements\n// TODO: avoid creating a new tensor every time\nstruct ggml_tensor * ggml_rope(\n        struct ggml_context * ctx,\n        struct ggml_tensor  * a,\n        int                   n_past,\n        int                   n_dims,\n        int                   mode);\n\n// padding = 1\n// TODO: we don't support extra parameters for now\n//       that's why we are hard-coding the stride, padding, and dilation\n//       not great ..\nstruct ggml_tensor * ggml_conv_1d_1s(\n        struct ggml_context * ctx,\n        struct ggml_tensor  * a,\n        struct ggml_tensor  * b);\n\nstruct ggml_tensor * ggml_conv_1d_2s(\n        struct ggml_context * ctx,\n        struct ggml_tensor  * a,\n        struct ggml_tensor  * b);\n\nstruct ggml_tensor * ggml_flash_attn(\n        struct ggml_context * ctx,\n        struct ggml_tensor  * q,\n        struct ggml_tensor  * k,\n        struct ggml_tensor  * v,\n        bool                  masked);\n\nstruct ggml_tensor * ggml_flash_ff(\n        struct ggml_context * ctx,\n        struct ggml_tensor  * a,\n        struct ggml_tensor  * b0,\n        struct ggml_tensor  * b1,\n        struct ggml_tensor  * c0,\n        struct ggml_tensor  * c1);\n\n//\n// automatic differentiation\n//\n\nvoid ggml_set_param(\n        struct ggml_context * ctx,\n        struct ggml_tensor * tensor);\n\nvoid ggml_build_forward_expand(struct ggml_cgraph * cgraph, struct ggml_tensor * tensor);\n\nstruct ggml_cgraph ggml_build_forward (struct ggml_tensor * tensor);\nstruct ggml_cgraph ggml_build_backward(struct ggml_context * ctx, struct ggml_cgraph * gf, bool keep);\n\nvoid ggml_graph_compute(struct ggml_context * ctx, struct ggml_cgraph * cgraph);\nvoid ggml_graph_reset  (struct ggml_cgraph * cgraph);\n\n// print info and performance information for the graph\nvoid ggml_graph_print(const struct ggml_cgraph * cgraph);\n\n// dump the graph into a file using the dot format\nvoid ggml_graph_dump_dot(const struct ggml_cgraph * gb, const struct ggml_cgraph * gf, const char * filename);\n\n//\n// optimization\n//\n\n// optimization methods\nenum ggml_opt_type {\n    GGML_OPT_ADAM,\n    GGML_OPT_LBFGS,\n};\n\n// linesearch methods\nenum ggml_linesearch {\n    GGML_LINESEARCH_DEFAULT = 1,\n\n    GGML_LINESEARCH_BACKTRACKING_ARMIJO       = 0,\n    GGML_LINESEARCH_BACKTRACKING_WOLFE        = 1,\n    GGML_LINESEARCH_BACKTRACKING_STRONG_WOLFE = 2,\n};\n\n// optimization return values\nenum ggml_opt_result {\n    GGML_OPT_OK = 0,\n    GGML_OPT_DID_NOT_CONVERGE,\n    GGML_OPT_NO_CONTEXT,\n    GGML_OPT_INVALID_WOLFE,\n    GGML_OPT_FAIL,\n\n    GGML_LINESEARCH_FAIL = -128,\n    GGML_LINESEARCH_MINIMUM_STEP,\n    GGML_LINESEARCH_MAXIMUM_STEP,\n    GGML_LINESEARCH_MAXIMUM_ITERATIONS,\n    GGML_LINESEARCH_INVALID_PARAMETERS,\n};\n\n// optimization parameters\n//\n//   see ggml.c (ggml_opt_default_params) for default values\n//\nstruct ggml_opt_params {\n    enum ggml_opt_type type;\n\n    int n_threads;\n\n    // delta-based convergence test\n    //\n    //   if past == 0 - disabled\n    //   if past > 0:\n    //     stop if |f(x) - f(x_past)| < delta * max(1, |f(x)|)\n    //\n    int past;\n    float delta;\n\n    // maximum number of iterations without improvement\n    //\n    //   if 0 - disabled\n    //   if > 0:\n    //     assume convergence if no cost improvement in this number of iterations\n    //\n    int max_no_improvement;\n\n    bool print_forward_graph;\n    bool print_backward_graph;\n\n    // ADAM parameters\n    struct {\n        int n_iter;\n\n        float alpha; // learning rate\n        float beta1;\n        float beta2;\n        float eps;   // epsilon for numerical stability\n        float eps_f; // epsilon for convergence test\n        float eps_g; // epsilon for convergence test\n    } adam;\n\n    // LBFGS parameters\n    struct {\n        int m; // number of corrections to approximate the inv. Hessian\n        int n_iter;\n        int max_linesearch;\n\n        float eps;      // convergence tolerance\n        float ftol;     // line search tolerance\n        float wolfe;\n        float min_step;\n        float max_step;\n\n        enum ggml_linesearch linesearch;\n    } lbfgs;\n};\n\nstruct ggml_opt_params ggml_opt_default_params(enum ggml_opt_type type);\n\n// optimize the function defined by the tensor f\nenum ggml_opt_result ggml_opt(\n        struct ggml_context * ctx,\n        struct ggml_opt_params params,\n        struct ggml_tensor * f);\n\n//\n// system info\n//\n\nint ggml_cpu_has_avx(void);\nint ggml_cpu_has_avx2(void);\nint ggml_cpu_has_avx512(void);\nint ggml_cpu_has_fma(void);\nint ggml_cpu_has_neon(void);\nint ggml_cpu_has_arm_fma(void);\nint ggml_cpu_has_f16c(void);\nint ggml_cpu_has_fp16_va(void);\nint ggml_cpu_has_wasm_simd(void);\nint ggml_cpu_has_blas(void);\n\n#ifdef  __cplusplus\n}\n#endif\n"
  },
  {
    "path": "Whisper/source/whisper.cpp",
    "content": "﻿#define WHISPER_BUILD\n#include \"whisper.h\"\n\n#include \"ggml.h\"\n\n#include <algorithm>\n#include <cassert>\n#define _USE_MATH_DEFINES\n#include <cmath>\n#include <cstdio>\n#include <cstring>\n#include <fstream>\n#include <map>\n#include <string>\n#include <thread>\n#include <vector>\n#include <regex>\n#include \"Utils/Logger.h\"\n\n#define USE_FLASH_ATTN\n//#define USE_FLASH_FF\n\n// available whisper models\nenum e_model {\n    MODEL_UNKNOWN,\n    MODEL_TINY,\n    MODEL_BASE,\n    MODEL_SMALL,\n    MODEL_MEDIUM,\n    MODEL_LARGE,\n};\n\nstatic const std::map<std::string, std::pair<int, std::string>> g_lang = {\n    { \"en\",  { 0,  \"english\",         } },\n    { \"zh\",  { 1,  \"chinese\",         } },\n    { \"de\",  { 2,  \"german\",          } },\n    { \"es\",  { 3,  \"spanish\",         } },\n    { \"ru\",  { 4,  \"russian\",         } },\n    { \"ko\",  { 5,  \"korean\",          } },\n    { \"fr\",  { 6,  \"french\",          } },\n    { \"ja\",  { 7,  \"japanese\",        } },\n    { \"pt\",  { 8,  \"portuguese\",      } },\n    { \"tr\",  { 9,  \"turkish\",         } },\n    { \"pl\",  { 10, \"polish\",          } },\n    { \"ca\",  { 11,  \"catalan\",        } },\n    { \"nl\",  { 12,  \"dutch\",          } },\n    { \"ar\",  { 13,  \"arabic\",         } },\n    { \"sv\",  { 14,  \"swedish\",        } },\n    { \"it\",  { 15,  \"italian\",        } },\n    { \"id\",  { 16,  \"indonesian\",     } },\n    { \"hi\",  { 17,  \"hindi\",          } },\n    { \"fi\",  { 18,  \"finnish\",        } },\n    { \"vi\",  { 19,  \"vietnamese\",     } },\n    { \"iw\",  { 20,  \"hebrew\",         } },\n    { \"uk\",  { 21,  \"ukrainian\",      } },\n    { \"el\",  { 22,  \"greek\",          } },\n    { \"ms\",  { 23,  \"malay\",          } },\n    { \"cs\",  { 24,  \"czech\",          } },\n    { \"ro\",  { 25,  \"romanian\",       } },\n    { \"da\",  { 26,  \"danish\",         } },\n    { \"hu\",  { 27,  \"hungarian\",      } },\n    { \"ta\",  { 28,  \"tamil\",          } },\n    { \"no\",  { 29,  \"norwegian\",      } },\n    { \"th\",  { 30,  \"thai\",           } },\n    { \"ur\",  { 31,  \"urdu\",           } },\n    { \"hr\",  { 32,  \"croatian\",       } },\n    { \"bg\",  { 33,  \"bulgarian\",      } },\n    { \"lt\",  { 34,  \"lithuanian\",     } },\n    { \"la\",  { 35,  \"latin\",          } },\n    { \"mi\",  { 36,  \"maori\",          } },\n    { \"ml\",  { 37,  \"malayalam\",      } },\n    { \"cy\",  { 38,  \"welsh\",          } },\n    { \"sk\",  { 39,  \"slovak\",         } },\n    { \"te\",  { 40,  \"telugu\",         } },\n    { \"fa\",  { 41,  \"persian\",        } },\n    { \"lv\",  { 42,  \"latvian\",        } },\n    { \"bn\",  { 43,  \"bengali\",        } },\n    { \"sr\",  { 44,  \"serbian\",        } },\n    { \"az\",  { 45,  \"azerbaijani\",    } },\n    { \"sl\",  { 46,  \"slovenian\",      } },\n    { \"kn\",  { 47,  \"kannada\",        } },\n    { \"et\",  { 48,  \"estonian\",       } },\n    { \"mk\",  { 49,  \"macedonian\",     } },\n    { \"br\",  { 50,  \"breton\",         } },\n    { \"eu\",  { 51,  \"basque\",         } },\n    { \"is\",  { 52,  \"icelandic\",      } },\n    { \"hy\",  { 53,  \"armenian\",       } },\n    { \"ne\",  { 54,  \"nepali\",         } },\n    { \"mn\",  { 55,  \"mongolian\",      } },\n    { \"bs\",  { 56,  \"bosnian\",        } },\n    { \"kk\",  { 57,  \"kazakh\",         } },\n    { \"sq\",  { 58,  \"albanian\",       } },\n    { \"sw\",  { 59,  \"swahili\",        } },\n    { \"gl\",  { 60,  \"galician\",       } },\n    { \"mr\",  { 61,  \"marathi\",        } },\n    { \"pa\",  { 62,  \"punjabi\",        } },\n    { \"si\",  { 63,  \"sinhala\",        } },\n    { \"km\",  { 64,  \"khmer\",          } },\n    { \"sn\",  { 65,  \"shona\",          } },\n    { \"yo\",  { 66,  \"yoruba\",         } },\n    { \"so\",  { 67,  \"somali\",         } },\n    { \"af\",  { 68,  \"afrikaans\",      } },\n    { \"oc\",  { 69,  \"occitan\",        } },\n    { \"ka\",  { 70,  \"georgian\",       } },\n    { \"be\",  { 71,  \"belarusian\",     } },\n    { \"tg\",  { 72,  \"tajik\",          } },\n    { \"sd\",  { 73,  \"sindhi\",         } },\n    { \"gu\",  { 74,  \"gujarati\",       } },\n    { \"am\",  { 75,  \"amharic\",        } },\n    { \"yi\",  { 76,  \"yiddish\",        } },\n    { \"lo\",  { 77,  \"lao\",            } },\n    { \"uz\",  { 78,  \"uzbek\",          } },\n    { \"fo\",  { 79,  \"faroese\",        } },\n    { \"ht\",  { 80,  \"haitian creole\", } },\n    { \"ps\",  { 81,  \"pashto\",         } },\n    { \"tk\",  { 82,  \"turkmen\",        } },\n    { \"nn\",  { 83,  \"nynorsk\",        } },\n    { \"mt\",  { 84,  \"maltese\",        } },\n    { \"sa\",  { 85,  \"sanskrit\",       } },\n    { \"lb\",  { 86,  \"luxembourgish\",  } },\n    { \"my\",  { 87,  \"myanmar\",        } },\n    { \"bo\",  { 88,  \"tibetan\",        } },\n    { \"tl\",  { 89,  \"tagalog\",        } },\n    { \"mg\",  { 90,  \"malagasy\",       } },\n    { \"as\",  { 91,  \"assamese\",       } },\n    { \"tt\",  { 92,  \"tatar\",          } },\n    { \"haw\", { 93,  \"hawaiian\",       } },\n    { \"ln\",  { 94,  \"lingala\",        } },\n    { \"ha\",  { 95,  \"hausa\",          } },\n    { \"ba\",  { 96,  \"bashkir\",        } },\n    { \"jw\",  { 97,  \"javanese\",       } },\n    { \"su\",  { 98,  \"sundanese\",      } },\n};\n\nstatic const size_t MB = 1024*1024;\n\nstatic const std::map<e_model, size_t> MEM_REQ_MODEL = {\n    { MODEL_TINY,     74ull*MB },\n    { MODEL_BASE,    142ull*MB },\n    { MODEL_SMALL,   466ull*MB },\n    { MODEL_MEDIUM, 1464ull*MB },\n    { MODEL_LARGE,  2952ull*MB },\n};\n\nstatic const std::map<e_model, size_t> MEM_REQ_MEMORY = {\n    { MODEL_TINY,     12ull*MB },\n    { MODEL_BASE,     24ull*MB },\n    { MODEL_SMALL,    70ull*MB },\n    { MODEL_MEDIUM,  184ull*MB },\n    { MODEL_LARGE,   306ull*MB },\n};\n\nstatic const std::map<e_model, size_t> MEM_REQ_ENCODE = {\n    { MODEL_TINY,     80ull*MB },\n    { MODEL_BASE,    128ull*MB },\n    { MODEL_SMALL,   300ull*MB },\n    { MODEL_MEDIUM,  680ull*MB },\n    { MODEL_LARGE,  1100ull*MB },\n};\n\nstatic const std::map<e_model, size_t> MEM_REQ_ENCODE_LAYER = {\n    { MODEL_TINY,    104ull*MB },\n    { MODEL_BASE,    138ull*MB },\n    { MODEL_SMALL,   208ull*MB },\n    { MODEL_MEDIUM,  280ull*MB },\n    { MODEL_LARGE,   354ull*MB },\n};\n\nstatic const std::map<e_model, size_t> MEM_REQ_DECODE = {\n    { MODEL_TINY,    200ull*MB },\n    { MODEL_BASE,    202ull*MB },\n    { MODEL_SMALL,   204ull*MB },\n    { MODEL_MEDIUM,  206ull*MB },\n    { MODEL_LARGE,   208ull*MB },\n};\n\nstatic const std::map<e_model, size_t> MEM_REQ_DECODE_LAYER = {\n    { MODEL_TINY,     32ull*MB },\n    { MODEL_BASE,     44ull*MB },\n    { MODEL_SMALL,    64ull*MB },\n    { MODEL_MEDIUM,   84ull*MB },\n    { MODEL_LARGE,   110ull*MB },\n};\n\nstruct whisper_mel {\n    int n_len;\n    int n_mel;\n\n    std::vector<float> data;\n};\n\nstruct whisper_filters {\n    int32_t n_mel;\n    int32_t n_fft;\n\n    std::vector<float> data;\n};\n\nstruct whisper_vocab {\n    using id    = int32_t;\n    using token = std::string;\n\n    int n_vocab = 51864;\n\n    std::map<token, id> token_to_id;\n    std::map<id, token> id_to_token;\n\n    id token_eot  = 50256;\n    id token_sot  = 50257;\n    id token_prev = 50360;\n    id token_solm = 50361; // ??\n    id token_not  = 50362; // no timestamps\n    id token_beg  = 50363;\n\n    // available tasks\n    static const id token_translate  = 50358;\n    static const id token_transcribe = 50359;\n\n    bool is_multilingual() const {\n        return n_vocab == 51865;\n    }\n};\n\nstruct whisper_segment {\n    int64_t t0;\n    int64_t t1;\n\n    std::string text;\n\n    std::vector<whisper_token_data> tokens;\n};\n\n// medium\n// hparams: {\n// 'n_mels': 80,\n// 'n_vocab': 51864,\n// 'n_audio_ctx': 1500,\n// 'n_audio_state': 1024,\n// 'n_audio_head': 16,\n// 'n_audio_layer': 24,\n// 'n_text_ctx': 448,\n// 'n_text_state': 1024,\n// 'n_text_head': 16,\n// 'n_text_layer': 24\n// }\n//\n// default hparams (Whisper tiny)\nstruct whisper_hparams {\n    int32_t n_vocab       = 51864;\n    int32_t n_audio_ctx   = 1500;\n    int32_t n_audio_state = 384;\n    int32_t n_audio_head  = 6;\n    int32_t n_audio_layer = 4;\n    int32_t n_text_ctx    = 448;\n    int32_t n_text_state  = 384;\n    int32_t n_text_head   = 6;\n    int32_t n_text_layer  = 4;\n    int32_t n_mels        = 80;\n    int32_t f16           = 1;\n};\n\n// audio encoding layer\nstruct whisper_layer_encoder {\n    // encoder.blocks.*.attn_ln\n    struct ggml_tensor * attn_ln_0_w;\n    struct ggml_tensor * attn_ln_0_b;\n\n    // encoder.blocks.*.attn.out\n    struct ggml_tensor * attn_ln_1_w;\n    struct ggml_tensor * attn_ln_1_b;\n\n    // encoder.blocks.*.attn.query\n    struct ggml_tensor * attn_q_w;\n    struct ggml_tensor * attn_q_b;\n\n    // encoder.blocks.*.attn.key\n    struct ggml_tensor * attn_k_w;\n\n    // encoder.blocks.*.attn.value\n    struct ggml_tensor * attn_v_w;\n    struct ggml_tensor * attn_v_b;\n\n    // encoder.blocks.*.mlp_ln\n    struct ggml_tensor * mlp_ln_w;\n    struct ggml_tensor * mlp_ln_b;\n\n    // encoder.blocks.*.mlp.0\n    struct ggml_tensor * mlp_0_w;\n    struct ggml_tensor * mlp_0_b;\n\n    // encoder.blocks.*.mlp.2\n    struct ggml_tensor * mlp_1_w;\n    struct ggml_tensor * mlp_1_b;\n};\n\n// token decoding layer\nstruct whisper_layer_decoder {\n    // decoder.blocks.*.attn_ln\n    struct ggml_tensor * attn_ln_0_w;\n    struct ggml_tensor * attn_ln_0_b;\n\n    // decoder.blocks.*.attn.out\n    struct ggml_tensor * attn_ln_1_w;\n    struct ggml_tensor * attn_ln_1_b;\n\n    // decoder.blocks.*.attn.query\n    struct ggml_tensor * attn_q_w;\n    struct ggml_tensor * attn_q_b;\n\n    // decoder.blocks.*.attn.key\n    struct ggml_tensor * attn_k_w;\n\n    // decoder.blocks.*.attn.value\n    struct ggml_tensor * attn_v_w;\n    struct ggml_tensor * attn_v_b;\n\n    // decoder.blocks.*.cross_attn_ln\n    struct ggml_tensor * cross_attn_ln_0_w;\n    struct ggml_tensor * cross_attn_ln_0_b;\n\n    // decoder.blocks.*.cross_attn.out\n    struct ggml_tensor * cross_attn_ln_1_w;\n    struct ggml_tensor * cross_attn_ln_1_b;\n\n    // decoder.blocks.*.cross_attn.query\n    struct ggml_tensor * cross_attn_q_w;\n    struct ggml_tensor * cross_attn_q_b;\n\n    // decoder.blocks.*.cross_attn.key\n    struct ggml_tensor * cross_attn_k_w;\n\n    // decoder.blocks.*.cross_attn.value\n    struct ggml_tensor * cross_attn_v_w;\n    struct ggml_tensor * cross_attn_v_b;\n\n    // decoder.blocks.*.mlp_ln\n    struct ggml_tensor * mlp_ln_w;\n    struct ggml_tensor * mlp_ln_b;\n\n    // decoder.blocks.*.mlp.0\n    struct ggml_tensor * mlp_0_w;\n    struct ggml_tensor * mlp_0_b;\n\n    // decoder.blocks.*.mlp.2\n    struct ggml_tensor * mlp_1_w;\n    struct ggml_tensor * mlp_1_b;\n};\n\nstruct whisper_model {\n    e_model type = MODEL_UNKNOWN;\n\n    whisper_hparams hparams;\n    whisper_filters filters;\n\n    // encoder.positional_embedding\n    struct ggml_tensor * e_pe;\n\n    // encoder.conv1\n    struct ggml_tensor * e_conv_1_w;\n    struct ggml_tensor * e_conv_1_b;\n\n    // encoder.conv2\n    struct ggml_tensor * e_conv_2_w;\n    struct ggml_tensor * e_conv_2_b;\n\n    // encoder.ln_post\n    struct ggml_tensor * e_ln_w;\n    struct ggml_tensor * e_ln_b;\n\n    // decoder.positional_embedding\n    struct ggml_tensor * d_pe; // DD\n\n    // decoder.token_embedding\n    struct ggml_tensor * d_te; // DD\n\n    // decoder.ln\n    struct ggml_tensor * d_ln_w; // DD\n    struct ggml_tensor * d_ln_b; // DD\n\n    std::vector<whisper_layer_encoder> layers_encoder;\n    std::vector<whisper_layer_decoder> layers_decoder;\n\n    // key + value memory\n    struct ggml_tensor * memory_k;\n    struct ggml_tensor * memory_v;\n\n    struct ggml_tensor * memory_cross_k;\n    struct ggml_tensor * memory_cross_v;\n\n    // context\n    struct ggml_context * ctx;\n    struct ggml_context * ctx_mem;\n\n    // tensors\n    int n_loaded;\n    std::map<std::string, struct ggml_tensor *> tensors;\n};\n\nstruct whisper_context {\n    int64_t t_load_us   = 0;\n    int64_t t_mel_us    = 0;\n    int64_t t_sample_us = 0;\n    int64_t t_encode_us = 0;\n    int64_t t_decode_us = 0;\n    int64_t t_start_us  = 0;\n\n    std::vector<uint8_t> * buf_model; // the model buffer is read-only and can be shared between processors\n    std::vector<uint8_t>   buf_memory;\n    std::vector<uint8_t>   buf_compute;\n    std::vector<uint8_t>   buf_compute_layer;\n\n    whisper_model model;\n    whisper_vocab vocab;\n\n    whisper_mel mel;\n\n    std::vector<float> probs;\n    std::vector<float> logits;\n\n    std::vector<whisper_segment> result_all;\n\n    std::vector<whisper_token> prompt_past;\n\n    // [EXPERIMENTAL] token-level timestamps data\n    int64_t t_beg;\n    int64_t t_last;\n    whisper_token tid_last;\n    std::vector<float> energy; // PCM signal energy\n\n    // [EXPERIMENTAL] speed-up techniques\n    int32_t exp_n_audio_ctx; // 0 - use default\n};\n\ntemplate<typename T>\nstatic void read_safe(std::ifstream& fin, T& dest)\n{\n  fin.read((char*)& dest, sizeof(T));\n}\n\n// load the model from a ggml file\n//\n// file format:\n//\n//   - hparams\n//   - pre-computed mel filters\n//   - vocab\n//   - weights\n//\n// see the convert-pt-to-ggml.py script for details\n//\nstatic bool whisper_model_load(const std::string & fname, whisper_context & wctx) {\n\tlogDebug( u8\"%s: loading model from '%s'\", __func__, fname.c_str() );\n\n    auto & model = wctx.model;\n    auto & vocab = wctx.vocab;\n\n    auto fin = std::ifstream(fname, std::ios::binary);\n    if (!fin) {\n\t\tlogError( u8\"%s: failed to open '%s'\", __func__, fname.c_str() );\n        return false;\n    }\n\n    // verify magic\n    {\n        uint32_t magic;\n        read_safe(fin, magic);\n        if (magic != 0x67676d6c) {\n\t\t\tlogError( u8\"%s: invalid model file '%s' (bad magic)\", __func__, fname.c_str() );\n            return false;\n        }\n    }\n\n    //load hparams\n    {\n        auto & hparams = model.hparams;\n\n        read_safe(fin, hparams.n_vocab);\n        read_safe(fin, hparams.n_audio_ctx);\n        read_safe(fin, hparams.n_audio_state);\n        read_safe(fin, hparams.n_audio_head);\n        read_safe(fin, hparams.n_audio_layer);\n        read_safe(fin, hparams.n_text_ctx);\n        read_safe(fin, hparams.n_text_state);\n        read_safe(fin, hparams.n_text_head);\n        read_safe(fin, hparams.n_text_layer);\n        read_safe(fin, hparams.n_mels);\n        read_safe(fin, hparams.f16);\n\n        assert(hparams.n_text_state == hparams.n_audio_state);\n\n        if (hparams.n_audio_layer == 4) {\n            model.type = e_model::MODEL_TINY;\n        }\n\n        if (hparams.n_audio_layer == 6) {\n            model.type = e_model::MODEL_BASE;\n        }\n\n        if (hparams.n_audio_layer == 12) {\n            model.type = e_model::MODEL_SMALL;\n        }\n\n        if (hparams.n_audio_layer == 24) {\n            model.type = e_model::MODEL_MEDIUM;\n        }\n\n        if (hparams.n_audio_layer == 32) {\n            model.type = e_model::MODEL_LARGE;\n        }\n\n\t\tlogDebug( u8\"%s: n_vocab       = %d\", __func__, hparams.n_vocab);\n\t\tlogDebug( u8\"%s: n_audio_ctx   = %d\", __func__, hparams.n_audio_ctx);\n\t\tlogDebug( u8\"%s: n_audio_state = %d\", __func__, hparams.n_audio_state);\n\t\tlogDebug( u8\"%s: n_audio_head  = %d\", __func__, hparams.n_audio_head);\n\t\tlogDebug( u8\"%s: n_audio_layer = %d\", __func__, hparams.n_audio_layer);\n\t\tlogDebug( u8\"%s: n_text_ctx    = %d\", __func__, hparams.n_text_ctx);\n\t\tlogDebug( u8\"%s: n_text_state  = %d\", __func__, hparams.n_text_state);\n\t\tlogDebug( u8\"%s: n_text_head   = %d\", __func__, hparams.n_text_head);\n\t\tlogDebug( u8\"%s: n_text_layer  = %d\", __func__, hparams.n_text_layer);\n\t\tlogDebug( u8\"%s: n_mels        = %d\", __func__, hparams.n_mels);\n\t\tlogDebug( u8\"%s: f16           = %d\", __func__, hparams.f16);\n\t\tlogDebug( u8\"%s: type          = %d\", __func__, model.type);\n\n        wctx.buf_model = new std::vector<uint8_t>();\n        wctx.buf_model->resize(MEM_REQ_MODEL.at(model.type));\n        wctx.buf_memory.resize(MEM_REQ_MEMORY.at(model.type));\n        wctx.buf_compute.resize(std::max(MEM_REQ_ENCODE.at(model.type), MEM_REQ_DECODE.at(model.type)));\n        wctx.buf_compute_layer.resize(std::max(MEM_REQ_ENCODE_LAYER.at(model.type), MEM_REQ_DECODE_LAYER.at(model.type)));\n    }\n\n    // load mel filters\n    {\n        auto & filters = wctx.model.filters;\n\n        read_safe(fin, filters.n_mel);\n        read_safe(fin, filters.n_fft);\n\n        filters.data.resize(filters.n_mel * filters.n_fft);\n        fin.read((char *) filters.data.data(), filters.data.size() * sizeof(float));\n    }\n\n    // load vocab\n    {\n        int32_t n_vocab = 0;\n        read_safe(fin, n_vocab);\n\n        //if (n_vocab != model.hparams.n_vocab) {\n        //    fprintf(stderr, \"%s: invalid model file '%s' (bad vocab size %d != %d)\\n\",\n        //            __func__, fname.c_str(), n_vocab, model.hparams.n_vocab);\n        //    return false;\n        //}\n\n        std::string word;\n        std::vector<char> tmp;\n        for (int i = 0; i < n_vocab; i++) {\n            uint32_t len;\n            read_safe(fin, len);\n\n            if (len > 0) {\n                tmp.resize(len);\n                fin.read(&tmp[0], tmp.size()); // read to buffer\n                word.assign(&tmp[0], tmp.size());\n            } else {\n                // seems like we have an empty-string token in multi-language models (i = 50256)\n                //fprintf(stderr, \"%s: warning: empty-string token in vocab, i = %d\\n\", __func__, i);\n                word = \"\";\n            }\n\n            vocab.token_to_id[word] = i;\n            vocab.id_to_token[i] = word;\n\n            //printf(\"%s: vocab[%d] = '%s'\\n\", __func__, i, word.c_str());\n        }\n\n        vocab.n_vocab = model.hparams.n_vocab;\n        if (vocab.is_multilingual()) {\n            vocab.token_eot++;\n            vocab.token_sot++;\n            vocab.token_prev++;\n            vocab.token_solm++;\n            vocab.token_not++;\n            vocab.token_beg++;\n        }\n\n        if (n_vocab < model.hparams.n_vocab) {\n\t\t\tlogDebug( u8\"%s: adding %d extra tokens\", __func__, model.hparams.n_vocab - n_vocab );\n            for (int i = n_vocab; i < model.hparams.n_vocab; i++) {\n                if (i > vocab.token_beg) {\n                    word = \"[_TT_\" + std::to_string(i - vocab.token_beg) + \"]\";\n                } else if (i == vocab.token_eot) {\n                    word = \"[_EOT_]\";\n                } else if (i == vocab.token_sot) {\n                    word = \"[_SOT_]\";\n                } else if (i == vocab.token_prev) {\n                    word = \"[_PREV_]\";\n                } else if (i == vocab.token_not) {\n                    word = \"[_NOT_]\";\n                } else if (i == vocab.token_beg) {\n                    word = \"[_BEG_]\";\n                } else {\n                    word = \"[_extra_token_\" + std::to_string(i) + \"]\";\n                }\n                vocab.token_to_id[word] = i;\n                vocab.id_to_token[i] = word;\n            }\n        }\n    }\n\n    {\n        // this is the total memory required to run the inference\n        const size_t mem_required =\n                   wctx.buf_model->size() +\n                   wctx.buf_memory.size() +\n                   wctx.buf_compute.size() +\n                   wctx.buf_compute_layer.size();\n\n\t\tlogDebug( u8\"%s: mem_required  = %7.2f MB\", __func__, mem_required / 1024.0 / 1024.0 );\n    }\n\n    // for the big tensors, we have the option to store the data in 16-bit floats\n    // in order to save memory and also to speed up the computation\n    const ggml_type wtype = model.hparams.f16 ? GGML_TYPE_F16 : GGML_TYPE_F32;\n\n    size_t ctx_size = 0;\n\n    {\n        const auto & hparams = model.hparams;\n\n        const int n_vocab = hparams.n_vocab;\n\n        const int n_audio_ctx   = hparams.n_audio_ctx;\n        const int n_audio_state = hparams.n_audio_state;\n        const int n_audio_layer = hparams.n_audio_layer;\n\n        const int n_text_ctx   = hparams.n_text_ctx;\n        const int n_text_state = hparams.n_text_state;\n        const int n_text_layer = hparams.n_text_layer;\n\n        const int n_mels = hparams.n_mels;\n\n        // encoder\n        {\n            // TODO: F16 .. maybe not?\n            ctx_size += n_audio_ctx*n_audio_state*ggml_type_size(GGML_TYPE_F32); // e_pe;\n\n            ctx_size += 3*n_mels*n_audio_state*ggml_type_size(wtype);         // e_conv_1_w\n            ctx_size +=          n_audio_state*ggml_type_size(GGML_TYPE_F32); // e_conv_1_b\n\n            ctx_size += 3*n_audio_state*n_audio_state*ggml_type_size(wtype);         // e_conv_2_w\n            ctx_size +=                 n_audio_state*ggml_type_size(GGML_TYPE_F32); // e_conv_2_b\n\n            ctx_size += n_audio_state*ggml_type_size(GGML_TYPE_F32); // e_ln_w;\n            ctx_size += n_audio_state*ggml_type_size(GGML_TYPE_F32); // e_ln_b;\n        }\n\n        // decoder\n        {\n            // TODO: F16 .. maybe not?\n            ctx_size += n_text_ctx*n_text_state*ggml_type_size(GGML_TYPE_F32); // d_pe;\n\n            ctx_size += n_vocab*n_text_state*ggml_type_size(wtype); // d_te;\n\n            ctx_size += n_text_state*ggml_type_size(GGML_TYPE_F32); // d_ln_w;\n            ctx_size += n_text_state*ggml_type_size(GGML_TYPE_F32); // d_ln_b;\n        }\n\n        // encoder layers\n        {\n            ctx_size += n_audio_layer*(n_audio_state*ggml_type_size(GGML_TYPE_F32)); // mlp_ln_w\n            ctx_size += n_audio_layer*(n_audio_state*ggml_type_size(GGML_TYPE_F32)); // mlp_ln_b\n\n            ctx_size += n_audio_layer*(4*n_audio_state*n_audio_state*ggml_type_size(wtype));         // mlp_0_w\n            ctx_size += n_audio_layer*(              4*n_audio_state*ggml_type_size(GGML_TYPE_F32)); // mlp_0_b\n\n            ctx_size += n_audio_layer*(4*n_audio_state*n_audio_state*ggml_type_size(wtype));         // mlp_1_w\n            ctx_size += n_audio_layer*(                n_audio_state*ggml_type_size(GGML_TYPE_F32)); // mlp_1_b\n\n            ctx_size += n_audio_layer*(n_audio_state*ggml_type_size(GGML_TYPE_F32)); // attn_ln_0_w\n            ctx_size += n_audio_layer*(n_audio_state*ggml_type_size(GGML_TYPE_F32)); // attn_ln_0_b\n\n            ctx_size += n_audio_layer*(n_audio_state*n_audio_state*ggml_type_size(wtype));         // attn_q_w\n            ctx_size += n_audio_layer*(              n_audio_state*ggml_type_size(GGML_TYPE_F32)); // attn_q_b\n\n            ctx_size += n_audio_layer*(n_audio_state*n_audio_state*ggml_type_size(wtype)); // attn_k_w\n\n            ctx_size += n_audio_layer*(n_audio_state*n_audio_state*ggml_type_size(wtype));         // attn_v_w\n            ctx_size += n_audio_layer*(              n_audio_state*ggml_type_size(GGML_TYPE_F32)); // attn_v_b\n\n            ctx_size += n_audio_layer*(n_audio_state*n_audio_state*ggml_type_size(wtype));         // attn_ln_1_w\n            ctx_size += n_audio_layer*(              n_audio_state*ggml_type_size(GGML_TYPE_F32)); // attn_ln_1_b\n        }\n\n        // decoder layers\n        {\n            ctx_size += n_text_layer*(n_text_state*ggml_type_size(GGML_TYPE_F32)); // mlp_ln_w\n            ctx_size += n_text_layer*(n_text_state*ggml_type_size(GGML_TYPE_F32)); // mlp_ln_b\n\n            ctx_size += n_text_layer*(4*n_text_state*n_text_state*ggml_type_size(wtype));         // mlp_0_w\n            ctx_size += n_text_layer*(             4*n_text_state*ggml_type_size(GGML_TYPE_F32)); // mlp_0_b\n\n            ctx_size += n_text_layer*(4*n_text_state*n_text_state*ggml_type_size(wtype));         // mlp_1_w\n            ctx_size += n_text_layer*(               n_text_state*ggml_type_size(GGML_TYPE_F32)); // mlp_1_b\n\n            ctx_size += n_text_layer*(n_text_state*ggml_type_size(GGML_TYPE_F32)); // attn_ln_0_w\n            ctx_size += n_text_layer*(n_text_state*ggml_type_size(GGML_TYPE_F32)); // attn_ln_0_b\n\n            ctx_size += n_text_layer*(n_text_state*n_text_state*ggml_type_size(wtype));         // attn_q_w\n            ctx_size += n_text_layer*(             n_text_state*ggml_type_size(GGML_TYPE_F32)); // attn_q_b\n\n            ctx_size += n_text_layer*(n_text_state*n_text_state*ggml_type_size(wtype)); // attn_k_w\n\n            ctx_size += n_text_layer*(n_text_state*n_text_state*ggml_type_size(wtype));         // attn_v_w\n            ctx_size += n_text_layer*(             n_text_state*ggml_type_size(GGML_TYPE_F32)); // attn_v_b\n\n            ctx_size += n_text_layer*(n_text_state*n_text_state*ggml_type_size(wtype));         // attn_ln_1_w\n            ctx_size += n_text_layer*(             n_text_state*ggml_type_size(GGML_TYPE_F32)); // attn_ln_1_b\n                                                                                                //\n            ctx_size += n_text_layer*(n_text_state*ggml_type_size(GGML_TYPE_F32)); // cross_attn_ln_0_w\n            ctx_size += n_text_layer*(n_text_state*ggml_type_size(GGML_TYPE_F32)); // cross_attn_ln_0_b\n\n            ctx_size += n_text_layer*(n_text_state*n_text_state*ggml_type_size(wtype));         // cross_attn_q_w\n            ctx_size += n_text_layer*(             n_text_state*ggml_type_size(GGML_TYPE_F32)); // cross_attn_q_b\n\n            ctx_size += n_text_layer*(n_text_state*n_text_state*ggml_type_size(wtype)); // cross_attn_k_w\n\n            ctx_size += n_text_layer*(n_text_state*n_text_state*ggml_type_size(wtype));         // cross_attn_v_w\n            ctx_size += n_text_layer*(             n_text_state*ggml_type_size(GGML_TYPE_F32)); // cross_attn_v_b\n\n            ctx_size += n_text_layer*(n_text_state*n_text_state*ggml_type_size(wtype));         // cross_attn_ln_1_w\n            ctx_size += n_text_layer*(             n_text_state*ggml_type_size(GGML_TYPE_F32)); // cross_attn_ln_1_b\n        }\n\n        ctx_size += (15 + 15*n_audio_layer + 24*n_text_layer)*256; // object overhead\n\n\t\tlogDebug( u8\"%s: ggml ctx size = %7.2f MB\", __func__, ctx_size / ( 1024.0 * 1024.0 ) );\n    }\n\n    // create the ggml context\n    {\n        struct ggml_init_params params;\n        params.mem_size   = wctx.buf_model->size();\n        params.mem_buffer = wctx.buf_model->data();\n\n        model.ctx = ggml_init(params);\n        if (!model.ctx) {\n\t\t\tlogError( u8\"%s: ggml_init() failed\", __func__ );\n            return false;\n        }\n    }\n\n    // prepare memory for the weights\n    {\n        auto & ctx = model.ctx;\n\n        const auto & hparams = model.hparams;\n\n        const int n_vocab = hparams.n_vocab;\n\n        const int n_audio_ctx   = hparams.n_audio_ctx;\n        const int n_audio_state = hparams.n_audio_state;\n        const int n_audio_layer = hparams.n_audio_layer;\n\n        const int n_text_ctx   = hparams.n_text_ctx;\n        const int n_text_state = hparams.n_text_state;\n        const int n_text_layer = hparams.n_text_layer;\n\n        const int n_mels = hparams.n_mels;\n\n        model.layers_encoder.resize(n_audio_layer);\n        model.layers_decoder.resize(n_text_layer);\n\n        // encoder\n        {\n            model.e_pe = ggml_new_tensor_2d(ctx, GGML_TYPE_F32, n_audio_state, n_audio_ctx);\n\n            model.e_conv_1_w = ggml_new_tensor_3d(ctx, wtype,         3, n_mels, n_audio_state);\n            model.e_conv_1_b = ggml_new_tensor_2d(ctx, GGML_TYPE_F32, 1, n_audio_state);\n\n            model.e_conv_2_w = ggml_new_tensor_3d(ctx, wtype,         3, n_audio_state, n_audio_state);\n            model.e_conv_2_b = ggml_new_tensor_2d(ctx, GGML_TYPE_F32, 1, n_audio_state);\n\n            model.e_ln_w = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, n_audio_state);\n            model.e_ln_b = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, n_audio_state);\n\n            // map by name\n            model.tensors[\"encoder.positional_embedding\"] = model.e_pe;\n\n            model.tensors[\"encoder.conv1.weight\"] = model.e_conv_1_w;\n            model.tensors[\"encoder.conv1.bias\"]   = model.e_conv_1_b;\n\n            model.tensors[\"encoder.conv2.weight\"] = model.e_conv_2_w;\n            model.tensors[\"encoder.conv2.bias\"]   = model.e_conv_2_b;\n\n            model.tensors[\"encoder.ln_post.weight\"] = model.e_ln_w;\n            model.tensors[\"encoder.ln_post.bias\"]   = model.e_ln_b;\n\n            for (int i = 0; i < n_audio_layer; ++i) {\n                auto & layer = model.layers_encoder[i];\n\n                layer.mlp_ln_w = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, n_audio_state);\n                layer.mlp_ln_b = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, n_audio_state);\n\n                layer.mlp_0_w = ggml_new_tensor_2d(ctx, wtype,           n_audio_state, 4*n_audio_state);\n                layer.mlp_0_b = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, 4*n_audio_state);\n\n                layer.mlp_1_w = ggml_new_tensor_2d(ctx, wtype,         4*n_audio_state, n_audio_state);\n                layer.mlp_1_b = ggml_new_tensor_1d(ctx, GGML_TYPE_F32,   n_audio_state);\n\n                layer.attn_ln_0_w = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, n_audio_state);\n                layer.attn_ln_0_b = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, n_audio_state);\n\n                layer.attn_q_w = ggml_new_tensor_2d(ctx, wtype,         n_audio_state, n_audio_state);\n                layer.attn_q_b = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, n_audio_state);\n\n                layer.attn_k_w = ggml_new_tensor_2d(ctx, wtype,         n_audio_state, n_audio_state);\n\n                layer.attn_v_w = ggml_new_tensor_2d(ctx, wtype,         n_audio_state, n_audio_state);\n                layer.attn_v_b = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, n_audio_state);\n\n                layer.attn_ln_1_w = ggml_new_tensor_2d(ctx, wtype,         n_audio_state, n_audio_state);\n                layer.attn_ln_1_b = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, n_audio_state);\n\n                // map by name\n                model.tensors[\"encoder.blocks.\" + std::to_string(i) + \".mlp_ln.weight\"] = layer.mlp_ln_w;\n                model.tensors[\"encoder.blocks.\" + std::to_string(i) + \".mlp_ln.bias\"]   = layer.mlp_ln_b;\n\n                model.tensors[\"encoder.blocks.\" + std::to_string(i) + \".mlp.0.weight\"] = layer.mlp_0_w;\n                model.tensors[\"encoder.blocks.\" + std::to_string(i) + \".mlp.0.bias\"]   = layer.mlp_0_b;\n\n                model.tensors[\"encoder.blocks.\" + std::to_string(i) + \".mlp.2.weight\"] = layer.mlp_1_w;\n                model.tensors[\"encoder.blocks.\" + std::to_string(i) + \".mlp.2.bias\"]   = layer.mlp_1_b;\n\n                model.tensors[\"encoder.blocks.\" + std::to_string(i) + \".attn_ln.weight\"] = layer.attn_ln_0_w;\n                model.tensors[\"encoder.blocks.\" + std::to_string(i) + \".attn_ln.bias\"]   = layer.attn_ln_0_b;\n\n                model.tensors[\"encoder.blocks.\" + std::to_string(i) + \".attn.query.weight\"] = layer.attn_q_w;\n                model.tensors[\"encoder.blocks.\" + std::to_string(i) + \".attn.query.bias\"]   = layer.attn_q_b;\n\n                model.tensors[\"encoder.blocks.\" + std::to_string(i) + \".attn.key.weight\"] = layer.attn_k_w;\n\n                model.tensors[\"encoder.blocks.\" + std::to_string(i) + \".attn.value.weight\"] = layer.attn_v_w;\n                model.tensors[\"encoder.blocks.\" + std::to_string(i) + \".attn.value.bias\"]   = layer.attn_v_b;\n\n                model.tensors[\"encoder.blocks.\" + std::to_string(i) + \".attn.out.weight\"] = layer.attn_ln_1_w;\n                model.tensors[\"encoder.blocks.\" + std::to_string(i) + \".attn.out.bias\"]   = layer.attn_ln_1_b;\n            }\n        }\n\n        // decoder\n        {\n            model.d_pe = ggml_new_tensor_2d(ctx, GGML_TYPE_F32, n_text_state, n_text_ctx);\n\n            model.d_te = ggml_new_tensor_2d(ctx, wtype, n_text_state, n_vocab);\n\n            model.d_ln_w = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, n_text_state);\n            model.d_ln_b = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, n_text_state);\n\n            // map by name\n            model.tensors[\"decoder.positional_embedding\"] = model.d_pe;\n\n            model.tensors[\"decoder.token_embedding.weight\"] = model.d_te;\n\n            model.tensors[\"decoder.ln.weight\"] = model.d_ln_w;\n            model.tensors[\"decoder.ln.bias\"]   = model.d_ln_b;\n\n            for (int i = 0; i < n_text_layer; ++i) {\n                auto & layer = model.layers_decoder[i];\n\n                layer.mlp_ln_w = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, n_text_state);\n                layer.mlp_ln_b = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, n_text_state);\n\n                layer.mlp_0_w = ggml_new_tensor_2d(ctx, wtype,           n_text_state, 4*n_text_state);\n                layer.mlp_0_b = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, 4*n_text_state);\n\n                layer.mlp_1_w = ggml_new_tensor_2d(ctx, wtype,         4*n_text_state, n_text_state);\n                layer.mlp_1_b = ggml_new_tensor_1d(ctx, GGML_TYPE_F32,   n_text_state);\n\n                layer.attn_ln_0_w = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, n_text_state);\n                layer.attn_ln_0_b = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, n_text_state);\n\n                layer.attn_q_w = ggml_new_tensor_2d(ctx, wtype,         n_text_state, n_text_state);\n                layer.attn_q_b = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, n_text_state);\n\n                layer.attn_k_w = ggml_new_tensor_2d(ctx, wtype,         n_text_state, n_text_state);\n\n                layer.attn_v_w = ggml_new_tensor_2d(ctx, wtype,         n_text_state, n_text_state);\n                layer.attn_v_b = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, n_text_state);\n\n                layer.attn_ln_1_w = ggml_new_tensor_2d(ctx, wtype,         n_text_state, n_text_state);\n                layer.attn_ln_1_b = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, n_text_state);\n\n                layer.cross_attn_ln_0_w = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, n_text_state);\n                layer.cross_attn_ln_0_b = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, n_text_state);\n\n                layer.cross_attn_q_w = ggml_new_tensor_2d(ctx, wtype,         n_text_state, n_text_state);\n                layer.cross_attn_q_b = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, n_text_state);\n\n                layer.cross_attn_k_w = ggml_new_tensor_2d(ctx, wtype,         n_text_state, n_text_state);\n\n                layer.cross_attn_v_w = ggml_new_tensor_2d(ctx, wtype,         n_text_state, n_text_state);\n                layer.cross_attn_v_b = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, n_text_state);\n\n                layer.cross_attn_ln_1_w = ggml_new_tensor_2d(ctx, wtype,         n_text_state, n_text_state);\n                layer.cross_attn_ln_1_b = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, n_text_state);\n\n                // map by name\n                model.tensors[\"decoder.blocks.\" + std::to_string(i) + \".mlp_ln.weight\"] = layer.mlp_ln_w;\n                model.tensors[\"decoder.blocks.\" + std::to_string(i) + \".mlp_ln.bias\"]   = layer.mlp_ln_b;\n\n                model.tensors[\"decoder.blocks.\" + std::to_string(i) + \".mlp.0.weight\"] = layer.mlp_0_w;\n                model.tensors[\"decoder.blocks.\" + std::to_string(i) + \".mlp.0.bias\"]   = layer.mlp_0_b;\n\n                model.tensors[\"decoder.blocks.\" + std::to_string(i) + \".mlp.2.weight\"] = layer.mlp_1_w;\n                model.tensors[\"decoder.blocks.\" + std::to_string(i) + \".mlp.2.bias\"]   = layer.mlp_1_b;\n\n                model.tensors[\"decoder.blocks.\" + std::to_string(i) + \".attn_ln.weight\"] = layer.attn_ln_0_w;\n                model.tensors[\"decoder.blocks.\" + std::to_string(i) + \".attn_ln.bias\"]   = layer.attn_ln_0_b;\n\n                model.tensors[\"decoder.blocks.\" + std::to_string(i) + \".attn.query.weight\"] = layer.attn_q_w;\n                model.tensors[\"decoder.blocks.\" + std::to_string(i) + \".attn.query.bias\"]   = layer.attn_q_b;\n\n                model.tensors[\"decoder.blocks.\" + std::to_string(i) + \".attn.key.weight\"] = layer.attn_k_w;\n\n                model.tensors[\"decoder.blocks.\" + std::to_string(i) + \".attn.value.weight\"] = layer.attn_v_w;\n                model.tensors[\"decoder.blocks.\" + std::to_string(i) + \".attn.value.bias\"]   = layer.attn_v_b;\n\n                model.tensors[\"decoder.blocks.\" + std::to_string(i) + \".attn.out.weight\"] = layer.attn_ln_1_w;\n                model.tensors[\"decoder.blocks.\" + std::to_string(i) + \".attn.out.bias\"]   = layer.attn_ln_1_b;\n\n                model.tensors[\"decoder.blocks.\" + std::to_string(i) + \".cross_attn_ln.weight\"] = layer.cross_attn_ln_0_w;\n                model.tensors[\"decoder.blocks.\" + std::to_string(i) + \".cross_attn_ln.bias\"]   = layer.cross_attn_ln_0_b;\n\n                model.tensors[\"decoder.blocks.\" + std::to_string(i) + \".cross_attn.query.weight\"] = layer.cross_attn_q_w;\n                model.tensors[\"decoder.blocks.\" + std::to_string(i) + \".cross_attn.query.bias\"]   = layer.cross_attn_q_b;\n\n                model.tensors[\"decoder.blocks.\" + std::to_string(i) + \".cross_attn.key.weight\"] = layer.cross_attn_k_w;\n\n                model.tensors[\"decoder.blocks.\" + std::to_string(i) + \".cross_attn.value.weight\"] = layer.cross_attn_v_w;\n                model.tensors[\"decoder.blocks.\" + std::to_string(i) + \".cross_attn.value.bias\"]   = layer.cross_attn_v_b;\n\n                model.tensors[\"decoder.blocks.\" + std::to_string(i) + \".cross_attn.out.weight\"] = layer.cross_attn_ln_1_w;\n                model.tensors[\"decoder.blocks.\" + std::to_string(i) + \".cross_attn.out.bias\"]   = layer.cross_attn_ln_1_b;\n            }\n        }\n    }\n\n    // create the ggml memory context\n    {\n        struct ggml_init_params params;\n        params.mem_size   = wctx.buf_memory.size();\n        params.mem_buffer = wctx.buf_memory.data();\n\n        model.ctx_mem = ggml_init(params);\n        if (!model.ctx_mem) {\n\t\t\tlogError( u8\"%s: ggml_init() failed\", __func__ );\n            return false;\n        }\n    }\n\n    // key + value memory\n    {\n        auto & ctx = model.ctx_mem;\n\n        const auto & hparams = model.hparams;\n\n        const int n_text_state = hparams.n_text_state;\n        const int n_text_layer = hparams.n_text_layer;\n        const int n_text_ctx   = hparams.n_text_ctx;\n\n        // key/value memory for the self-attention layer\n        {\n            const int n_mem      = n_text_layer*n_text_ctx;\n            const int n_elements = n_text_state*n_mem;\n\n            model.memory_k = ggml_new_tensor_1d(ctx, GGML_TYPE_F16, n_elements);\n            model.memory_v = ggml_new_tensor_1d(ctx, GGML_TYPE_F16, n_elements);\n        }\n\n        // key/value memory for the cross-attention layer\n        {\n            const int n_audio_ctx = hparams.n_audio_ctx;\n\n            const int n_mem      = n_text_layer*n_audio_ctx;\n            const int n_elements = n_text_state*n_mem;\n\n            model.memory_cross_k = ggml_new_tensor_1d(ctx, GGML_TYPE_F16, n_elements);\n            model.memory_cross_v = ggml_new_tensor_1d(ctx, GGML_TYPE_F16, n_elements);\n        }\n\n        const size_t memory_size =\n            ggml_nbytes(model.memory_k)       + ggml_nbytes(model.memory_v) +\n            ggml_nbytes(model.memory_cross_k) + ggml_nbytes(model.memory_cross_v);\n\n\t\tlogDebug( u8\"%s: memory size   = %7.2f MB\", __func__, memory_size/1024.0/1024.0);\n    }\n\n    // load weights\n    {\n        size_t total_size = 0;\n\n        model.n_loaded = 0;\n\n        while (true) {\n            int32_t n_dims;\n            int32_t length;\n            int32_t ftype;\n\n            read_safe(fin, n_dims);\n            read_safe(fin, length);\n            read_safe(fin, ftype);\n\n            if (fin.eof()) {\n                break;\n            }\n\n            int32_t nelements = 1;\n            int32_t ne[3] = { 1, 1, 1 };\n            for (int i = 0; i < n_dims; ++i) {\n                read_safe(fin, ne[i]);\n                nelements *= ne[i];\n            }\n\n            std::string name;\n            std::vector<char> tmp(length); // create a buffer\n            fin.read( &tmp[0], tmp.size() ); // read to buffer\n            name.assign(&tmp[0], tmp.size());\n\n            if (model.tensors.find(name) == model.tensors.end()) {\n\t\t\t\tlogError( u8\"%s: unknown tensor '%s' in model file\", __func__, name.data() );\n                return false;\n            }\n\n            auto tensor = model.tensors[name.data()];\n            if (ggml_nelements(tensor) != nelements) {\n\t\t\t\tlogError( u8\"%s: tensor '%s' has wrong size in model file\", __func__, name.data());\n                return false;\n            }\n\n            if (tensor->ne[0] != ne[0] || tensor->ne[1] != ne[1] || tensor->ne[2] != ne[2]) {\n\t\t\t\tlogError( u8\"%s: tensor '%s' has wrong shape in model file: got [%d, %d, %d], expected [%d, %d, %d]\",\n\t\t\t\t\t__func__, name.data(), tensor->ne[ 0 ], tensor->ne[ 1 ], tensor->ne[ 2 ], ne[ 0 ], ne[ 1 ], ne[ 2 ] );\n                return false;\n            }\n\n            const size_t bpe = (ftype == 0) ? sizeof(float) : sizeof(ggml_fp16_t);\n\n            if (nelements*bpe != ggml_nbytes(tensor)) {\n\t\t\t\tlogError( u8\"%s: tensor '%s' has wrong size in model file: got %zu, expected %zu\\n\",\n\t\t\t\t\t__func__, name.data(), ggml_nbytes( tensor ), nelements* bpe );\n                return false;\n            }\n\n            fin.read(reinterpret_cast<char *>(tensor->data), ggml_nbytes(tensor));\n\n            //printf(\"%48s - [%5d, %5d, %5d], type = %6s, %6.2f MB\\n\", name.data(), ne[0], ne[1], ne[2], ftype == 0 ? \"float\" : \"f16\", ggml_nbytes(tensor)/1024.0/1024.0);\n            total_size += ggml_nbytes(tensor);\n            model.n_loaded++;\n        }\n\n\t\tlogDebug( u8\"%s: model size    = %7.2f MB\", __func__, total_size / 1024.0 / 1024.0 );\n\n        if (model.n_loaded == 0) {\n\t\t\tlogWarning( u8\"%s: WARN no tensors loaded from model file - assuming empty model for testing\", __func__);\n        } else if (model.n_loaded != (int) model.tensors.size()) {\n\t\t\tlogError( u8\"%s: ERROR not all tensors loaded from model file - expected %zu, got %d\", __func__, model.tensors.size(), model.n_loaded );\n            return false;\n        }\n    }\n\n    fin.close();\n\n    return true;\n}\n\n// evaluate the encoder\n//\n// given audio recording (more specifically, its log mel spectrogram), runs forward pass of the encoder\n// part of the transformer model and returns the encoded features\n//\n//   - model:      the model\n//   - n_threads:  number of threads to use\n//   - mel_offset: offset in the mel spectrogram (i.e. audio offset)\n//\nstatic bool whisper_encode(\n              whisper_context & wctx,\n        const int n_threads,\n        const int mel_offset) {\n    const auto & model   = wctx.model;\n    const auto & mel_inp = wctx.mel;\n    const auto & hparams = model.hparams;\n\n    const int n_ctx   = wctx.exp_n_audio_ctx > 0 ? wctx.exp_n_audio_ctx : hparams.n_audio_ctx;\n    const int n_state = hparams.n_audio_state;\n    const int n_head  = hparams.n_audio_head;\n    const int n_layer = hparams.n_audio_layer;\n\n    const int n_mels = hparams.n_mels;\n    assert(mel_inp.n_mel == n_mels);\n\n    struct ggml_init_params params;\n    params.mem_size   = wctx.buf_compute.size();\n    params.mem_buffer = wctx.buf_compute.data();\n\n    struct ggml_context * ctx0 = ggml_init(params);\n\n    struct ggml_tensor * mel = ggml_new_tensor_2d(ctx0, GGML_TYPE_F32, 2*n_ctx, n_mels);\n    assert(mel->type == GGML_TYPE_F32);\n    {\n        float * dst = (float *) mel->data;\n        memset(dst, 0, ggml_nbytes(mel));\n\n        const int i0 = std::min(mel_offset, mel_inp.n_len);\n        const int i1 = std::min(mel_offset + 2*n_ctx, mel_inp.n_len);\n\n        for (int j = 0; j < mel_inp.n_mel; ++j) {\n            for (int i = i0; i < i1; ++i) {\n                dst[j*2*n_ctx + (i - i0)] = mel_inp.data[j*mel_inp.n_len + i];\n            }\n        }\n    }\n\tTracing::delayTensor( \"enc.input\", mel );\n\n    struct ggml_tensor * cur;\n\n    // convolution + gelu\n    {\n        cur = ggml_conv_1d_1s(ctx0, model.e_conv_1_w, mel);\n\t\tTracing::delayTensor( \"enc.conv1\", cur );\n        cur = ggml_add(ctx0,\n                ggml_repeat(ctx0,\n                    model.e_conv_1_b,\n                    cur),\n                cur);\n\n        cur = ggml_gelu(ctx0, cur);\n\t\tTracing::delayTensor( \"enc.temp1\", cur );\n\n        cur = ggml_conv_1d_2s(ctx0, model.e_conv_2_w, cur);\n        cur = ggml_add(ctx0,\n                ggml_repeat(ctx0,\n                    model.e_conv_2_b,\n                    cur),\n                cur);\n\n        cur = ggml_gelu(ctx0, cur);\n    }\n\n    // ===================================================================\n    // NOTE: experimenting with partial evaluation of the encoder (ignore)\n    //static int iter = -1;\n    //const int n_iter = 1500/n_ctx;\n\n    //iter = (iter + 1) % n_iter;\n\n    //if (iter == 0) {\n    //    memset(model.memory_cross_k->data, 0, ggml_nbytes(model.memory_cross_k));\n    //    memset(model.memory_cross_v->data, 0, ggml_nbytes(model.memory_cross_v));\n    //}\n\n    static int iter = 0;\n\n    const size_t e_pe_stride = model.e_pe->ne[0]*ggml_element_size(model.e_pe);\n    const size_t e_pe_offset = model.e_pe->ne[0]*ggml_element_size(model.e_pe)*n_ctx*iter;\n\n    struct ggml_tensor * e_pe = ggml_view_2d(ctx0, model.e_pe, model.e_pe->ne[0], n_ctx, e_pe_stride, e_pe_offset);\n\n    cur = ggml_add(ctx0, e_pe, ggml_transpose(ctx0, cur));\n    // ===================================================================\n\n    // original:\n    //cur = ggml_add(ctx0, model.e_pe, ggml_transpose(ctx0, cur));\n\n    struct ggml_tensor * inpL = cur;\n\n    for (int il = 0; il < n_layer; ++il) {\n        const auto & layer = model.layers_encoder[il];\n\n        // create separate context for each layer to reduce memory usage\n\n        struct ggml_init_params paramsL;\n        paramsL.mem_size   = wctx.buf_compute_layer.size();\n        paramsL.mem_buffer = wctx.buf_compute_layer.data();\n\n        struct ggml_context * ctxL = ggml_init(paramsL);\n\n\t\tTracing::delayTensor( { \"enc.layer[ %i ].in\", il }, inpL );\n\n        // norm\n        {\n            cur = ggml_norm(ctxL, inpL);\n\t\t\tif( il == 0 )\n\t\t\t\tTracing::delayTensor( \"enc-norm\", cur );\n\n            // cur = ln_0_w*cur + ln_0_b\n            cur = ggml_add(ctxL,\n                    ggml_mul(ctxL,\n                        ggml_repeat(ctxL, layer.attn_ln_0_w, cur),\n                        cur),\n                    ggml_repeat(ctxL, layer.attn_ln_0_b, cur));\n        }\n\n        // self-attention\n        {\n            struct ggml_tensor * Qcur = ggml_mul_mat(ctxL,\n                    layer.attn_q_w,\n                    cur);\n\t\t\tif( il == 0 )\n\t\t\t\tTracing::delayTensor( \"enc-Qcur\", Qcur );\n\n            Qcur = ggml_add(ctxL,\n                    ggml_repeat(ctxL,\n                        layer.attn_q_b,\n                        Qcur),\n                    Qcur);\n\n            //Qcur = ggml_scale(ctxL, Qcur, ggml_new_f32(ctxL, pow(float(n_state)/n_head, -0.25)));\n\n            // note: no bias for Key\n            struct ggml_tensor * Kcur = ggml_mul_mat(ctxL,\n                    layer.attn_k_w,\n                    cur);\n\t\t\tif( il == 0 )\n\t\t\t\tTracing::delayTensor( \"enc-Kcur\", Kcur );\n\n            //Kcur = ggml_scale(ctxL, Kcur, ggml_new_f32(ctxL, pow(float(n_state)/n_head, -0.25)));\n\n            struct ggml_tensor * Vcur = ggml_mul_mat(ctxL,\n                    layer.attn_v_w,\n                    cur);\n\t\t\tif( il == 0 )\n\t\t\t\tTracing::delayTensor( \"enc-Vcur\", Vcur );\n\n            Vcur = ggml_add(ctxL,\n                    ggml_repeat(ctxL,\n                        layer.attn_v_b,\n                        Vcur),\n                    Vcur);\n\n            // ------\n\n#ifdef USE_FLASH_ATTN\n            struct ggml_tensor * Q =\n                ggml_permute(ctxL,\n                        ggml_cpy(ctxL,\n                            Qcur,\n                            ggml_new_tensor_3d(ctxL, GGML_TYPE_F16, n_state/n_head, n_head, n_ctx)),\n                        0, 2, 1, 3);\n\n            struct ggml_tensor * K =\n                ggml_permute(ctxL,\n                        ggml_cpy(ctxL,\n                            Kcur,\n                            ggml_new_tensor_3d(ctxL, GGML_TYPE_F16, n_state/n_head, n_head, n_ctx)),\n                        0, 2, 1, 3);\n\n            struct ggml_tensor * V =\n                ggml_cpy(ctxL,\n                        ggml_permute(ctxL,\n                            ggml_reshape_3d(ctxL,\n                                Vcur,\n                                n_state/n_head, n_head, n_ctx),\n                            1, 2, 0, 3),\n                        ggml_new_tensor_3d(ctxL, GGML_TYPE_F16, n_ctx, n_state/n_head, n_head)\n                        );\n\n            struct ggml_tensor * KQV = ggml_flash_attn(ctxL, Q, K, V, false);\n\t\t\tif( il == 0 )\n\t\t\t\tTracing::delayTensor( \"enc-KQV\", KQV );\n#else\n            struct ggml_tensor * Q =\n                ggml_permute(ctxL,\n                        ggml_cpy(ctxL,\n                            Qcur,\n                            ggml_new_tensor_3d(ctxL, GGML_TYPE_F32, n_state/n_head, n_head, n_ctx)),\n                        0, 2, 1, 3);\n\n            struct ggml_tensor * K =\n                ggml_permute(ctxL,\n                        ggml_cpy(ctxL,\n                            Kcur,\n                            ggml_new_tensor_3d(ctxL, GGML_TYPE_F16, n_state/n_head, n_head, n_ctx)),\n                        0, 2, 1, 3);\n\n            // K * Q\n            struct ggml_tensor * KQ = ggml_mul_mat(ctxL, K, Q);\n\n            struct ggml_tensor * KQ_scaled =\n                ggml_scale(ctxL,\n                        KQ,\n                        ggml_new_f32(ctxL, 1.0f/sqrt(float(n_state)/n_head))\n                        );\n\n            struct ggml_tensor * KQ_soft_max = ggml_soft_max(ctxL, KQ_scaled);\n\n            //struct ggml_tensor * V_trans =\n            //    ggml_permute(ctxL,\n            //            ggml_cpy(ctxL,\n            //                Vcur,\n            //                ggml_new_tensor_3d(ctxL, GGML_TYPE_F16, n_state/n_head, n_head, n_ctx)),\n            //            1, 2, 0, 3);\n\n            //struct ggml_tensor * KQV = ggml_mul_mat(ctxL, V_trans, KQ_soft_max);\n\n            struct ggml_tensor * V =\n                ggml_cpy(ctxL,\n                        ggml_permute(ctxL,\n                            ggml_reshape_3d(ctxL,\n                                Vcur,\n                                n_state/n_head, n_head, n_ctx),\n                            0, 2, 1, 3),\n                        ggml_new_tensor_3d(ctxL, GGML_TYPE_F16, n_state/n_head, n_ctx, n_head)\n                        );\n\n            struct ggml_tensor * KQV = ggml_mul_mat(ctxL, ggml_transpose(ctxL, V), KQ_soft_max);\n#endif\n\n            struct ggml_tensor * KQV_merged = ggml_permute(ctxL, KQV, 0, 2, 1, 3);\n\n            cur = ggml_cpy(ctxL,\n                    KQV_merged,\n                    ggml_new_tensor_2d(ctxL, GGML_TYPE_F32, n_state, n_ctx));\n        }\n\n        // projection\n        {\n            cur = ggml_mul_mat(ctxL,\n                    layer.attn_ln_1_w,\n                    cur);\n\n            cur = ggml_add(ctxL,\n                    ggml_repeat(ctxL, layer.attn_ln_1_b, cur),\n                    cur);\n        }\n\n        // add the input\n        cur = ggml_add(ctxL, cur, inpL);\n\n        struct ggml_tensor * inpFF = cur;\n\n        // feed-forward network\n        {\n            // norm\n            {\n                cur = ggml_norm(ctxL, inpFF);\n\n                // cur = mlp_ln_w*cur + mlp_ln_b\n                cur = ggml_add(ctxL,\n                        ggml_mul(ctxL,\n                            ggml_repeat(ctxL, layer.mlp_ln_w, cur),\n                            cur),\n                        ggml_repeat(ctxL, layer.mlp_ln_b, cur));\n            }\n\n#ifdef USE_FLASH_FF\n            cur = ggml_flash_ff(ctxL,\n                    ggml_cpy(ctxL, cur, ggml_new_tensor_2d(ctxL, GGML_TYPE_F16, n_state, N)),\n                    layer.mlp_0_w, layer.mlp_0_b, layer.mlp_1_w, layer.mlp_1_b);\n#else\n            // fully connected\n            cur = ggml_mul_mat(ctxL,\n                    layer.mlp_0_w,\n                    cur);\n\n            cur = ggml_add(ctxL,\n                    ggml_repeat(ctxL, layer.mlp_0_b, cur),\n                    cur);\n\n            // GELU activation\n            cur = ggml_gelu(ctxL, cur);\n\n            // projection\n            cur = ggml_mul_mat(ctxL,\n                    layer.mlp_1_w,\n                    cur);\n\n            cur = ggml_add(ctxL,\n                    ggml_repeat(ctxL, layer.mlp_1_b, cur),\n                    cur);\n#endif\n        }\n\n        // output from this layer\n        struct ggml_tensor * inpO = ggml_add(ctxL, cur, inpFF);\n\n        {\n            struct ggml_cgraph gf = {};\n            gf.n_threads = n_threads;\n\n            ggml_build_forward_expand(&gf, inpO);\n            ggml_graph_compute       (ctxL, &gf);\n\t\t\tTracing::writeDelayedTensors();\n            //ggml_graph_print(&gf);\n        }\n\n        // TODO: this is a hack to have per-layer computation graphs - need to come up with something better\n        // input for next layer (inpO -> inpL)\n        memcpy(inpL->data, inpO->data, ggml_nbytes(inpL));\n        inpL->op = GGML_OP_NONE;\n        inpL->src0 = nullptr;\n        inpL->src1 = nullptr;\n\n        //printf(\"%s: - used_mem(%d) = %f MB\\n\", __func__, il, ggml_used_mem(ctxL)/1024.0/1024.0);\n\n        ggml_free(ctxL);\n    }\n\tTracing::tensor( \"enc.layers\", inpL );\n\tcur = inpL;\n\n    // norm\n    {\n        cur = ggml_norm(ctx0, cur);\n\n        // cur = ln_f_g*cur + ln_f_b\n        cur = ggml_add(ctx0,\n                ggml_mul(ctx0,\n                    ggml_repeat(ctx0, model.e_ln_w, cur),\n                    cur),\n                ggml_repeat(ctx0, model.e_ln_b, cur));\n    }\n\n    // run the computation\n    {\n        struct ggml_cgraph gf = {};\n        gf.n_threads = n_threads;\n\n        ggml_build_forward_expand(&gf, cur);\n        ggml_graph_compute       (ctx0, &gf);\n\n        //ggml_graph_print(&gf);\n    }\n\n\tTracing::tensor( \"encode-out\", cur );\n\n    // cur\n    //{\n    //    printf(\"ne0 = %d\\n\", cur->ne[0]);\n    //    printf(\"ne1 = %d\\n\", cur->ne[1]);\n    //    for (int i = 0; i < 10; ++i) {\n    //        printf(\"%8.4f \", ((float *)(cur->data))[i]);\n    //    }\n    //    printf(\"... \");\n    //    for (int i = cur->ne[0] - 10; i < cur->ne[0]; ++i) {\n    //        printf(\"%8.4f \", ((float *)(cur->data))[i]);\n    //    }\n    //    printf(\"\\n\");\n    //}\n\n    // pre-compute cross-attention memory\n    {\n        struct ggml_cgraph gf = {};\n        gf.n_threads = n_threads;\n\n        // TODO: hack to disconnect the encoded features from the previous graph\n        cur->op = GGML_OP_NONE;\n        cur->src0 = nullptr;\n        cur->src1 = nullptr;\n\n        for (int il = 0; il < model.hparams.n_text_layer; ++il) {\n            auto & layer = model.layers_decoder[il];\n\n            struct ggml_tensor * Kcross = ggml_mul_mat(ctx0,\n                    layer.cross_attn_k_w,\n                    cur);\n\n            Kcross = ggml_scale(ctx0, Kcross, ggml_new_f32(ctx0, pow(float(n_state)/n_head, -0.25)));\n\n            struct ggml_tensor * Vcross = ggml_mul_mat(ctx0,\n                    layer.cross_attn_v_w,\n                    cur);\n\n            Vcross = ggml_add(ctx0,\n                    ggml_repeat(ctx0,\n                        layer.cross_attn_v_b,\n                        Vcross),\n                    Vcross);\n\n            //struct ggml_tensor * k = ggml_view_1d(ctx0, model.memory_cross_k, n_state*n_ctx, (ggml_element_size(model.memory_cross_k)*n_state)*(il*hparams.n_audio_ctx + iter*n_ctx));\n            //struct ggml_tensor * v = ggml_view_1d(ctx0, model.memory_cross_v, n_state*n_ctx, (ggml_element_size(model.memory_cross_v)*n_state)*(il*hparams.n_audio_ctx + iter*n_ctx));\n            struct ggml_tensor * k = ggml_view_1d(ctx0, model.memory_cross_k, n_state*n_ctx, (ggml_element_size(model.memory_cross_k)*n_state)*(il*n_ctx));\n            struct ggml_tensor * v = ggml_view_1d(ctx0, model.memory_cross_v, n_state*n_ctx, (ggml_element_size(model.memory_cross_v)*n_state)*(il*n_ctx));\n\n            ggml_build_forward_expand(&gf, ggml_cpy(ctx0, Kcross, k));\n            ggml_build_forward_expand(&gf, ggml_cpy(ctx0, Vcross, v));\n        }\n\n        ggml_graph_compute(ctx0, &gf);\n    }\n\n    ////////////////////////////////////////////////////////////////////////////\n\n    //printf(\"%s: used_mem = %f MB\\n\", __func__, ggml_used_mem(ctx0)/1024.0/1024.0);\n\n    ggml_free(ctx0);\n\n    return true;\n}\n\n// evaluate the decoder\n//\n// given text prompt + audio features -> predicts the probabilities for the next token\n//\n//   - model:      the model\n//   - n_threads:  number of threads to use\n//   - tokens:     text prompt\n//   - n_tokens:   number of tokens in the prompt\n//   - n_past:     number of past tokens to prefix the prompt with\n//\nstatic bool whisper_decode(\n              whisper_context & wctx,\n        const int n_threads,\n        const whisper_token * tokens,\n        const int n_tokens,\n        const int n_past) {\n    const auto & model   = wctx.model;\n    const auto & hparams = model.hparams;\n\n    auto & logits_out = wctx.logits;\n    auto & probs_out  = wctx.probs;\n\n    const int n_vocab = hparams.n_vocab;\n\n    const int n_ctx   = hparams.n_text_ctx;\n    const int n_state = hparams.n_text_state;\n    const int n_head  = hparams.n_text_head;\n    const int n_layer = hparams.n_text_layer;\n\n    const int N = n_tokens;\n    const int M = wctx.exp_n_audio_ctx > 0 ? wctx.exp_n_audio_ctx : hparams.n_audio_ctx;\n\n    struct ggml_init_params params;\n    params.mem_size   = wctx.buf_compute.size();\n    params.mem_buffer = wctx.buf_compute.data();\n\n    struct ggml_context * ctx0 = ggml_init(params);\n\n    struct ggml_tensor * embd = ggml_new_tensor_1d(ctx0, GGML_TYPE_I32, N);\n    memcpy(embd->data, tokens, N*ggml_element_size(embd));\n\n    struct ggml_tensor * position = ggml_new_tensor_1d(ctx0, GGML_TYPE_I32, N);\n    for (int i = 0; i < N; ++i) {\n        ((int32_t *) position->data)[i] = n_past + i;\n    }\n\n    // token encoding + position encoding\n    struct ggml_tensor * cur =\n        ggml_add(ctx0,\n                ggml_get_rows(ctx0, model.d_te, embd),\n                ggml_get_rows(ctx0, model.d_pe, position));\n\tTracing::delayTensor( \"dec-rows\", cur );\n\n    struct ggml_tensor * inpL = cur;\n\n    for (int il = 0; il < n_layer; ++il) {\n        const auto & layer = model.layers_decoder[il];\n\n        struct ggml_init_params paramsL;\n        paramsL.mem_size   = wctx.buf_compute_layer.size();\n        paramsL.mem_buffer = wctx.buf_compute_layer.data();\n\n        struct ggml_context * ctxL = ggml_init(paramsL);\n        struct ggml_cgraph gf = {};\n        gf.n_threads = n_threads;\n\n        // norm\n        {\n            cur = ggml_norm(ctxL, inpL);\n\n            // cur = ln_0_w*cur + ln_0_b\n            cur = ggml_add(ctxL,\n                    ggml_mul(ctxL,\n                        ggml_repeat(ctxL, layer.attn_ln_0_w, cur),\n                        cur),\n                    ggml_repeat(ctxL, layer.attn_ln_0_b, cur));\n        }\n\n        // self-attention\n        {\n            struct ggml_tensor * Qcur = ggml_mul_mat(ctxL,\n                    layer.attn_q_w,\n                    cur);\n\n            Qcur = ggml_add(ctxL,\n                    ggml_repeat(ctxL,\n                        layer.attn_q_b,\n                        Qcur),\n                    Qcur);\n\n            Qcur = ggml_scale(ctxL, Qcur, ggml_new_f32(ctxL, pow(float(n_state)/n_head, -0.25)));\n\n            // note: no bias for Key\n            struct ggml_tensor * Kcur = ggml_mul_mat(ctxL,\n                    layer.attn_k_w,\n                    cur);\n\n            Kcur = ggml_scale(ctxL, Kcur, ggml_new_f32(ctxL, pow(float(n_state)/n_head, -0.25)));\n\n            struct ggml_tensor * Vcur = ggml_mul_mat(ctxL,\n                    layer.attn_v_w,\n                    cur);\n\n            Vcur = ggml_add(ctxL,\n                    ggml_repeat(ctxL,\n                        layer.attn_v_b,\n                        Vcur),\n                    Vcur);\n\n            // store key and value to memory\n            {\n                struct ggml_tensor * k = ggml_view_1d(ctxL, model.memory_k, N*n_state, (ggml_element_size(model.memory_k)*n_state)*(il*n_ctx + n_past));\n                struct ggml_tensor * v = ggml_view_1d(ctxL, model.memory_v, N*n_state, (ggml_element_size(model.memory_v)*n_state)*(il*n_ctx + n_past));\n\n                ggml_build_forward_expand(&gf, ggml_cpy(ctxL, Kcur, k));\n                ggml_build_forward_expand(&gf, ggml_cpy(ctxL, Vcur, v));\n            }\n\n            // ------\n\n            struct ggml_tensor * Q =\n                ggml_permute(ctxL,\n                        ggml_cpy(ctxL,\n                            Qcur,\n                            ggml_new_tensor_3d(ctxL, GGML_TYPE_F32, n_state/n_head, n_head, N)),\n                        0, 2, 1, 3);\n\n            struct ggml_tensor * K =\n                ggml_permute(ctxL,\n                        ggml_reshape_3d(ctxL,\n                            ggml_view_1d(ctxL, model.memory_k, (n_past + N)*n_state, il*n_ctx*ggml_element_size(model.memory_k)*n_state),\n                            n_state/n_head, n_head, n_past + N),\n                        0, 2, 1, 3);\n\n            // K * Q\n            struct ggml_tensor * KQ = ggml_mul_mat(ctxL, K, Q);\n\n            //struct ggml_tensor * KQ_scaled =\n            //    ggml_scale(ctxL,\n            //            KQ,\n            //            ggml_new_f32(ctxL, 1.0f/sqrt(float(n_state)/n_head))\n            //            );\n\n            struct ggml_tensor * KQ_masked = ggml_diag_mask_inf(ctxL, KQ, n_past);\n\n            struct ggml_tensor * KQ_soft_max = ggml_soft_max(ctxL, KQ_masked);\n\t\t\tif( 0 == il ) Tracing::delayTensor( \"dec-KQ\", KQ_soft_max );\n\n            struct ggml_tensor * V_trans =\n                ggml_permute(ctxL,\n                        ggml_reshape_3d(ctxL,\n                            ggml_view_1d(ctxL, model.memory_v, (n_past + N)*n_state, il*n_ctx*ggml_element_size(model.memory_v)*n_state),\n                            n_state/n_head, n_head, n_past + N),\n                        1, 2, 0, 3);\n\n            struct ggml_tensor * KQV = ggml_mul_mat(ctxL, V_trans, KQ_soft_max);\n\t\t\tif( 0 == il ) Tracing::delayTensor( \"dec-KQV\", KQV );\n\n            struct ggml_tensor * KQV_merged = ggml_permute(ctxL, KQV, 0, 2, 1, 3);\n\n            cur = ggml_cpy(ctxL,\n                    KQV_merged,\n                    ggml_new_tensor_2d(ctxL, GGML_TYPE_F32, n_state, N));\n        }\n\n        {\n            cur = ggml_mul_mat(ctxL,\n                    layer.attn_ln_1_w,\n                    cur);\n\n            cur = ggml_add(ctxL,\n                    ggml_repeat(ctxL, layer.attn_ln_1_b, cur),\n                    cur);\n        }\n\n        // add the input\n        struct ggml_tensor * inpCA = ggml_add(ctxL, cur, inpL);\n\n        // norm\n        {\n            cur = ggml_norm(ctxL, inpCA); // note: we use inpCA here\n\n            // cur = ln_0_w*cur + ln_0_b\n            cur = ggml_add(ctxL,\n                    ggml_mul(ctxL,\n                        ggml_repeat(ctxL, layer.cross_attn_ln_0_w, cur),\n                        cur),\n                    ggml_repeat(ctxL, layer.cross_attn_ln_0_b, cur));\n        }\n\n        // cross-attention\n        {\n            struct ggml_tensor * Qcur = ggml_mul_mat(ctxL,\n                    layer.cross_attn_q_w,\n                    cur);\n\n            Qcur = ggml_add(ctxL,\n                    ggml_repeat(ctxL,\n                        layer.cross_attn_q_b,\n                        Qcur),\n                    Qcur);\n\n            Qcur = ggml_scale(ctxL, Qcur, ggml_new_f32(ctxL, pow(float(n_state)/n_head, -0.25)));\n\n            // Kcross is already scaled\n            struct ggml_tensor * Kcross =\n                ggml_reshape_3d(ctxL,\n                        ggml_view_1d(ctxL, model.memory_cross_k, M*n_state, il*M*ggml_element_size(model.memory_cross_k)*n_state),\n                        n_state/n_head, n_head, M);\n\n            struct ggml_tensor * Vcross =\n                ggml_reshape_3d(ctxL,\n                        ggml_view_1d(ctxL, model.memory_cross_v, M*n_state, il*M*ggml_element_size(model.memory_cross_v)*n_state),\n                        n_state/n_head, n_head, M);\n\n            // ------\n\n            struct ggml_tensor * Q =\n                ggml_permute(ctxL,\n                        ggml_cpy(ctxL,\n                            Qcur,\n                            ggml_new_tensor_3d(ctxL, GGML_TYPE_F32, n_state/n_head, n_head, N)),\n                        0, 2, 1, 3);\n\n            struct ggml_tensor * K = ggml_permute(ctxL, Kcross, 0, 2, 1, 3);\n\n            // K * Q\n            struct ggml_tensor * KQ = ggml_mul_mat(ctxL, K, Q);\n\n            //struct ggml_tensor * KQ_scaled =\n            //    ggml_scale(ctxL,\n            //            KQ,\n            //            ggml_new_f32(ctxL, 1.0f/sqrt(float(n_state)/n_head))\n            //            );\n\n            // no masking for cross-attention\n            //struct ggml_tensor * KQ_masked = ggml_diag_mask_inf(ctxL, KQ_scaled, n_past);\n\n            struct ggml_tensor * KQ_soft_max = ggml_soft_max(ctxL, KQ);\n\n            struct ggml_tensor * V_trans = ggml_permute(ctxL, Vcross, 1, 2, 0, 3);\n\n            struct ggml_tensor * KQV = ggml_mul_mat(ctxL, V_trans, KQ_soft_max);\n\t\t\tif( 0 == il ) Tracing::delayTensor( \"dec-KQV\", KQV );\n\n            struct ggml_tensor * KQV_merged = ggml_permute(ctxL, KQV, 0, 2, 1, 3);\n\n            // cur = KQV_merged.contiguous().view(n_state, N)\n            cur = ggml_cpy(ctxL,\n                    KQV_merged,\n                    ggml_new_tensor_2d(ctxL, GGML_TYPE_F32, n_state, N));\n        }\n\n        // projection\n        {\n            cur = ggml_mul_mat(ctxL,\n                    layer.cross_attn_ln_1_w,\n                    cur);\n\n            cur = ggml_add(ctxL,\n                    ggml_repeat(ctxL, layer.cross_attn_ln_1_b, cur),\n                    cur);\n        }\n\n        // add the input\n        cur = ggml_add(ctxL, cur, inpCA);\n\n        struct ggml_tensor * inpFF = cur;\n\n        // feed-forward network\n        {\n            // norm\n            {\n                cur = ggml_norm(ctxL, inpFF);\n\n                // cur = mlp_ln_w*cur + mlp_ln_b\n                cur = ggml_add(ctxL,\n                        ggml_mul(ctxL,\n                            ggml_repeat(ctxL, layer.mlp_ln_w, cur),\n                            cur),\n                        ggml_repeat(ctxL, layer.mlp_ln_b, cur));\n            }\n\n            // fully connected\n            cur = ggml_mul_mat(ctxL,\n                    layer.mlp_0_w,\n                    cur);\n\n            cur = ggml_add(ctxL,\n                    ggml_repeat(ctxL, layer.mlp_0_b, cur),\n                    cur);\n\n            // GELU activation\n            cur = ggml_gelu(ctxL, cur);\n\n            // projection\n            cur = ggml_mul_mat(ctxL,\n                    layer.mlp_1_w,\n                    cur);\n\n            cur = ggml_add(ctxL,\n                    ggml_repeat(ctxL, layer.mlp_1_b, cur),\n                    cur);\n        }\n\n        // output from this layer\n        struct ggml_tensor * inpO = ggml_add(ctxL, cur, inpFF);\n\n        {\n            ggml_build_forward_expand(&gf, inpO);\n            ggml_graph_compute       (ctxL, &gf);\n\t\t\tTracing::writeDelayedTensors();\n            //ggml_graph_print(&gf);\n        }\n\n        // TODO: this is a hack to have per-layer computation graphs - need to come up with something better\n        // input for next layer (inpO -> inpL)\n        memcpy(inpL->data, inpO->data, ggml_nbytes(inpL));\n        inpL->op = GGML_OP_NONE;\n        inpL->src0 = nullptr;\n        inpL->src1 = nullptr;\n\n        if (N > 1) {\n            //printf(\"%s: - used_mem(%d) = %f MB\\n\", __func__, il, ggml_used_mem(ctxL)/1024.0/1024.0);\n        }\n\n        ggml_free(ctxL);\n    }\n\n    cur = inpL;\n\n    // norm\n    {\n        cur = ggml_norm(ctx0, cur);\n\n        cur = ggml_add(ctx0,\n                ggml_mul(ctx0,\n                    ggml_repeat(ctx0, model.d_ln_w, cur),\n                    cur),\n                ggml_repeat(ctx0, model.d_ln_b, cur));\n    }\n\n    struct ggml_tensor * logits = ggml_mul_mat(ctx0, model.d_te, cur);\n\n    // logits -> probs\n    cur = ggml_dup(ctx0, logits);\n    cur = ggml_soft_max(ctx0, cur); // in-place\n\n    // run the computation\n    {\n        struct ggml_cgraph gf = {};\n        gf.n_threads = n_threads;\n\n        ggml_build_forward_expand(&gf, cur);\n        ggml_graph_compute       (ctx0, &gf);\n    }\n\n    logits_out.resize(N*n_vocab);\n    memcpy(logits_out.data(), ggml_get_data(logits), sizeof(float)*N*n_vocab);\n\n    probs_out.resize(N*n_vocab);\n    memcpy(probs_out.data(), ggml_get_data(cur), sizeof(float)*N*n_vocab);\n\n    if (N > 1) {\n        //const float mem_per_token = ggml_used_mem(ctx0)/1024.0/1024.0/N;\n        //printf(\"%s: used_mem = %f MB / %f per token\\n\", __func__, ggml_used_mem(ctx0)/1024.0/1024.0, mem_per_token);\n        //printf(\"%s: max mem = %f MB\\n\", __func__, mem_per_token*model.hparams.n_text_ctx);\n    }\n\n    ggml_free(ctx0);\n\t// Hash::vector( \"probs\", probs_out );\n\tTracing::vector( \"probs\", probs_out );\n\n    return true;\n}\n\n// the most basic sampling scheme - select the top token\nstatic whisper_token_data whisper_sample_best(\n        const whisper_vocab & vocab,\n        const float * probs,\n              bool force_timestamp,\n              bool is_initial) {\n    whisper_token_data result = {\n        0, 0, 0.0f, 0.0f, 0.0f, -1, -1, 0.0f,\n    };\n\n    int n_logits = vocab.id_to_token.size();\n\n    std::vector<std::pair<double, whisper_vocab::id>> probs_id;\n    probs_id.reserve(n_logits);\n\n    for (int i = 0; i < n_logits; i++) {\n        probs_id.emplace_back(probs[i], i);\n    }\n\n    {\n        double sum_ts =  0.0;\n        double max_ts = -1.0;\n        double max_tx = -1.0;\n\n        for (int i = 0; i < vocab.token_beg; i++) {\n            max_tx = std::max(max_tx, probs_id[i].first);\n        }\n\n        const auto i0 = is_initial ? vocab.token_beg + 101 : vocab.token_beg;\n        const auto i1 = is_initial ? vocab.token_beg + 101 : n_logits;\n\n        // the initial timestamp cannot be larger than 100\n        // ref: https://github.com/openai/whisper/blob/0b1ba3d46ebf7fe6f953acfd8cad62a4f851b49f/whisper/decoding.py#L426-L429\n        if (is_initial) {\n            for (int i = i0; i < n_logits; ++ i) {\n                probs_id[i].first = -INFINITY;\n            }\n        }\n\n        for (int i = vocab.token_beg; i < i1; i++) {\n            sum_ts += probs_id[i].first;\n            if  (probs_id[i].first > max_ts) {\n                max_ts = probs_id[i].first;\n                result.tid = probs_id[i].second;\n            }\n        }\n\n        // if the probability sum of all timestamp tokens is higher than the max probability of the text tokens - sample a\n        // timestamp token\n        if (sum_ts > max_tx || force_timestamp) {\n            // ref: https://github.com/openai/whisper/blob/0b1ba3d46ebf7fe6f953acfd8cad62a4f851b49f/whisper/decoding.py#L430-L438\n            for (int i = 0; i < vocab.token_beg; i++) {\n                probs_id[i].first = -INFINITY;\n            }\n        }\n\n        result.pt = max_ts/(sum_ts + 1e-10);\n        result.ptsum = sum_ts;\n    }\n\n    // find the top K tokens\n    const int top_k = 4;\n\n    std::partial_sort(\n            probs_id.begin(),\n            probs_id.begin() + top_k, probs_id.end(),\n            [](const std::pair<double, whisper_vocab::id> & a, const std::pair<double, whisper_vocab::id> & b) {\n        return a.first > b.first;\n    });\n\n    probs_id.resize(top_k);\n\n    //printf(\"\\n\");\n    //for (int i = 0; i < (int) probs_id.size(); i++) {\n    //    printf(\"%d: '%s' %f, %d\\n\", i, vocab.id_to_token.at(probs_id[i].second).c_str(), probs_id[i].first, probs_id[i].second);\n    //}\n\n    int res = 0;\n    while ((probs_id[res].second == vocab.token_sot ||\n            probs_id[res].second == vocab.token_solm ||\n            probs_id[res].second == vocab.token_not) &&\n            res < (int) probs_id.size() - 1) {\n        res++;\n    }\n\n    result.id = probs_id[res].second;\n    result.p  = probs_id[res].first;\n\n    return result;\n}\n\n//  500 -> 00:05.000\n// 6000 -> 01:00.000\nstatic std::string to_timestamp(int64_t t, bool comma = false) {\n    int64_t msec = t * 10;\n    int64_t hr = msec / (1000 * 60 * 60);\n    msec = msec - hr * (1000 * 60 * 60);\n    int64_t min = msec / (1000 * 60);\n    msec = msec - min * (1000 * 60);\n    int64_t sec = msec / 1000;\n    msec = msec - sec * 1000;\n\n    char buf[32];\n    snprintf(buf, sizeof(buf), \"%02d:%02d:%02d%s%03d\", (int) hr, (int) min, (int) sec, comma ? \",\" : \".\", (int) msec);\n\n    return std::string(buf);\n}\n\n// naive Discrete Fourier Transform\n// input is real-valued\n// output is complex-valued\nstatic void dft(const std::vector<float> & in, std::vector<float> & out) {\n    int N = in.size();\n\n    out.resize(N*2);\n\n    for (int k = 0; k < N; k++) {\n        float re = 0;\n        float im = 0;\n\n        for (int n = 0; n < N; n++) {\n            float angle = 2*M_PI*k*n/N;\n            re += in[n]*cos(angle);\n            im -= in[n]*sin(angle);\n        }\n\n        out[k*2 + 0] = re;\n        out[k*2 + 1] = im;\n    }\n}\n\n// Cooley-Tukey FFT\n// poor man's implementation - use something better\n// input is real-valued\n// output is complex-valued\nstatic void fft(const std::vector<float> & in, std::vector<float> & out) {\n    out.resize(in.size()*2);\n\n    int N = in.size();\n\n    if (N == 1) {\n        out[0] = in[0];\n        out[1] = 0;\n        return;\n    }\n\n    if (N%2 == 1) {\n        dft(in, out);\n        return;\n    }\n\n    std::vector<float> even;\n    std::vector<float> odd;\n\n    for (int i = 0; i < N; i++) {\n        if (i % 2 == 0) {\n            even.push_back(in[i]);\n        } else {\n            odd.push_back(in[i]);\n        }\n    }\n\n    std::vector<float> even_fft;\n    std::vector<float> odd_fft;\n\n    fft(even, even_fft);\n    fft(odd, odd_fft);\n\n    for (int k = 0; k < N/2; k++) {\n        float theta = 2*M_PI*k/N;\n\n        float re = cos(theta);\n        float im = -sin(theta);\n\n        float re_odd = odd_fft[2*k + 0];\n        float im_odd = odd_fft[2*k + 1];\n\n        out[2*k + 0] = even_fft[2*k + 0] + re*re_odd - im*im_odd;\n        out[2*k + 1] = even_fft[2*k + 1] + re*im_odd + im*re_odd;\n\n        out[2*(k + N/2) + 0] = even_fft[2*k + 0] - re*re_odd + im*im_odd;\n        out[2*(k + N/2) + 1] = even_fft[2*k + 1] - re*im_odd - im*re_odd;\n    }\n}\n\n// ref: https://github.com/openai/whisper/blob/main/whisper/audio.py#L92-L124\nstatic bool log_mel_spectrogram(\n    const float * samples,\n    const int n_samples,\n    const int /*sample_rate*/,\n    const int fft_size,\n    const int fft_step,\n    const int n_mel,\n    const int n_threads,\n    const whisper_filters & filters,\n    const bool speed_up,\n    whisper_mel & mel) {\n\n    // Hanning window\n    std::vector<float> hann;\n    hann.resize(fft_size);\n    for (int i = 0; i < fft_size; i++) {\n        hann[i] = 0.5*(1.0 - cos((2.0*M_PI*i)/(fft_size)));\n    }\n\n    mel.n_mel = n_mel;\n    mel.n_len = (n_samples)/fft_step;\n    mel.data.resize(mel.n_mel*mel.n_len);\n\n    const int n_fft = 1 + (speed_up ? fft_size/4 : fft_size/2);\n\n    //printf(\"%s: n_samples = %d, n_len = %d\\n\", __func__, n_samples, mel.n_len);\n    //printf(\"%s: recording length: %f s\\n\", __func__, (float) n_samples/sample_rate);\n\n    std::vector<std::thread> workers(n_threads);\n    for (int iw = 0; iw < n_threads; ++iw) {\n        workers[iw] = std::thread([&](int ith) {\n            std::vector<float> fft_in;\n            fft_in.resize(fft_size);\n            for (int i = 0; i < fft_size; i++) {\n                fft_in[i] = 0.0;\n            }\n\n            std::vector<float> fft_out;\n            fft_out.resize(2*fft_size);\n\n            for (int i = ith; i < mel.n_len; i += n_threads) {\n                const int offset = i*fft_step;\n\n                // apply Hanning window\n                for (int j = 0; j < fft_size; j++) {\n                    if (offset + j < n_samples) {\n                        fft_in[j] = hann[j]*samples[offset + j];\n                    } else {\n                        fft_in[j] = 0.0;\n                    }\n                }\n\n                // FFT -> mag^2\n                fft(fft_in, fft_out);\n\n                for (int j = 0; j < fft_size; j++) {\n                    fft_out[j] = (fft_out[2*j + 0]*fft_out[2*j + 0] + fft_out[2*j + 1]*fft_out[2*j + 1]);\n                }\n                for (int j = 1; j < fft_size/2; j++) {\n                    //if (i == 0) {\n                    //    printf(\"%d: %f %f\\n\", j, fft_out[j], fft_out[fft_size - j]);\n                    //}\n                    fft_out[j] += fft_out[fft_size - j];\n                }\n                if (i == 0) {\n                    //for (int j = 0; j < fft_size; j++) {\n                    //    printf(\"%d: %e\\n\", j, fft_out[j]);\n                    //}\n                }\n\n                if (speed_up) {\n                    // scale down in the frequency domain results in a speed up in the time domain\n                    for (int j = 0; j < n_fft; j++) {\n                        fft_out[j] = 0.5*(fft_out[2*j] + fft_out[2*j + 1]);\n                    }\n                }\n\n                // mel spectrogram\n                for (int j = 0; j < mel.n_mel; j++) {\n                    double sum = 0.0;\n\n                    for (int k = 0; k < n_fft; k++) {\n                        sum += fft_out[k]*filters.data[j*n_fft + k];\n                    }\n                    if (sum < 1e-10) {\n                        sum = 1e-10;\n                    }\n\n                    sum = log10(sum);\n\n                    mel.data[j*mel.n_len + i] = sum;\n                }\n            }\n        }, iw);\n    }\n\n    for (int iw = 0; iw < n_threads; ++iw) {\n        workers[iw].join();\n    }\n\n    // clamping and normalization\n    double mmax = -1e20;\n    for (int i = 0; i < mel.n_mel*mel.n_len; i++) {\n        if (mel.data[i] > mmax) {\n            mmax = mel.data[i];\n        }\n    }\n    //printf(\"%s: max = %f\\n\", __func__, mmax);\n\n    mmax -= 8.0;\n\n    for (int i = 0; i < mel.n_mel*mel.n_len; i++) {\n        if (mel.data[i] < mmax) {\n            mel.data[i] = mmax;\n        }\n\n        mel.data[i] = (mel.data[i] + 4.0)/4.0;\n    }\n\n    return true;\n}\n\n// split text into tokens\n//\n// ref: https://github.com/openai/gpt-2/blob/a74da5d99abaaba920de8131d64da2862a8f213b/src/encoder.py#L53\n//\n// Regex (Python):\n// r\"\"\"'s|'t|'re|'ve|'m|'ll|'d| ?\\p{L}+| ?\\p{N}+| ?[^\\s\\p{L}\\p{N}]+|\\s+(?!\\S)|\\s+\"\"\"\n//\n// Regex (C++):\n// R\"('s|'t|'re|'ve|'m|'ll|'d| ?[[:alpha:]]+| ?[[:digit:]]+| ?[^\\s[:alpha:][:digit:]]+|\\s+(?!\\S)|\\s+)\"\n//\nstatic std::vector<whisper_vocab::id> tokenize(const whisper_vocab & vocab, const std::string & text) {\n    std::vector<std::string> words;\n\n    // first split the text into words\n    {\n        std::string str = text;\n        std::string pat = R\"('s|'t|'re|'ve|'m|'ll|'d| ?[[:alpha:]]+| ?[[:digit:]]+| ?[^\\s[:alpha:][:digit:]]+|\\s+(?!\\S)|\\s+)\";\n\n        std::regex re(pat);\n        std::smatch m;\n\n        while (std::regex_search(str, m, re)) {\n            for (auto x : m) {\n                words.push_back(x);\n            }\n            str = m.suffix();\n        }\n    }\n\n    // find the longest tokens that form the words:\n    std::vector<whisper_vocab::id> tokens;\n    for (const auto & word : words) {\n        if (word.empty()) continue;\n\n        int i = 0;\n        int n = word.size();\n        while (i < n) {\n            int j = n;\n            while (j > i) {\n                auto it = vocab.token_to_id.find(word.substr(i, j-i));\n                if (it != vocab.token_to_id.end()) {\n                    tokens.push_back(it->second);\n                    i = j;\n                    break;\n                }\n                --j;\n            }\n            if (i == n) {\n                break;\n            }\n            if (j == i) {\n                auto sub = word.substr(i, 1);\n                if (vocab.token_to_id.find(sub) != vocab.token_to_id.end()) {\n                    tokens.push_back(vocab.token_to_id.at(sub));\n                } else {\n\t\t\t\t\tlogWarning( u8\"%s: unknown token '%s'\", __func__, sub.data() );\n                }\n                ++i;\n            }\n        }\n    }\n\n    return tokens;\n}\n\n//\n// interface implementation\n//\n\nstruct whisper_context * whisper_init(const char * path_model) {\n    ggml_time_init();\n\n    whisper_context * ctx = new whisper_context;\n\n    const int64_t t_start_us = ggml_time_us();\n\n    ctx->t_start_us = t_start_us;\n\n    if (!whisper_model_load(path_model, *ctx)) {\n\t\tlogError( u8\"%s: failed to load model from '%s'\", __func__, path_model );\n        delete ctx;\n        return nullptr;\n    }\n\n    ctx->t_load_us = ggml_time_us() - t_start_us;\n\n    return ctx;\n}\n\nvoid whisper_free(struct whisper_context * ctx) {\n    if (ctx) {\n        if (ctx->model.ctx) {\n            ggml_free(ctx->model.ctx);\n        }\n        if (ctx->model.ctx_mem) {\n            ggml_free(ctx->model.ctx_mem);\n        }\n        if (ctx->buf_model) {\n            delete ctx->buf_model;\n        }\n        delete ctx;\n    }\n}\n\nint whisper_pcm_to_mel(struct whisper_context * ctx, const float * samples, int n_samples, int n_threads) {\n    const int64_t t_start_us = ggml_time_us();\n\n    if (!log_mel_spectrogram(samples, n_samples, WHISPER_SAMPLE_RATE, WHISPER_N_FFT, WHISPER_HOP_LENGTH, WHISPER_N_MEL, n_threads, ctx->model.filters, false, ctx->mel)) {\n\t\tlogError( u8\"%s: failed to compute mel spectrogram\", __func__ );\n        return -1;\n    }\n\n    ctx->t_mel_us = ggml_time_us() - t_start_us;\n\n    return 0;\n}\n\n// same as whisper_pcm_to_mel, but applies a Phase Vocoder to speed up the audio x2\nint whisper_pcm_to_mel_phase_vocoder(struct whisper_context * ctx, const float * samples, int n_samples, int n_threads) {\n    const int64_t t_start_us = ggml_time_us();\n\n    if (!log_mel_spectrogram(samples, n_samples, WHISPER_SAMPLE_RATE, 2*WHISPER_N_FFT, 2*WHISPER_HOP_LENGTH, WHISPER_N_MEL, n_threads, ctx->model.filters, true, ctx->mel)) {\n\t\tlogError( u8\"%s: failed to compute mel spectrogram\", __func__ );\n        return -1;\n    }\n\n    ctx->t_mel_us = ggml_time_us() - t_start_us;\n\n    return 0;\n}\n\nint whisper_set_mel(\n        struct whisper_context * ctx,\n        const float * data,\n        int n_len,\n        int n_mel) {\n    if (n_mel != WHISPER_N_MEL) {\n\t\tlogError( u8\"%s: invalid number of mel bands: %d (expected %d)\", __func__, n_mel, WHISPER_N_MEL );\n        return -1;\n    }\n\n    ctx->mel.n_len = n_len;\n    ctx->mel.n_mel = n_mel;\n\n    ctx->mel.data.resize(n_len*n_mel);\n    memcpy(ctx->mel.data.data(), data, n_len*n_mel*sizeof(float));\n\n    return 0;\n}\n\nint whisper_encode(struct whisper_context * ctx, int offset, int n_threads) {\n    const int64_t t_start_us = ggml_time_us();\n\n    if (!whisper_encode(*ctx, n_threads, offset)) {\n\t\tlogError( u8\"%s: failed to eval\", __func__ );\n        return -1;\n    }\n\n    ctx->t_encode_us += ggml_time_us() - t_start_us;\n\n    return 0;\n}\n\nint whisper_decode(struct whisper_context * ctx, const whisper_token * tokens, int n_tokens, int n_past, int n_threads) {\n    const int64_t t_start_us = ggml_time_us();\n\n    if (!whisper_decode(*ctx, n_threads, tokens, n_tokens, n_past)) {\n\t\tlogError( u8\"%s: failed to eval\", __func__ );\n        return 1;\n    }\n\n    ctx->t_decode_us += ggml_time_us() - t_start_us;\n\n    return 0;\n}\n\nstruct whisper_token_data whisper_sample_best(struct whisper_context * ctx) {\n    const int64_t t_start_sample_us = ggml_time_us();\n\n    const auto res = whisper_sample_best(ctx->vocab, ctx->probs.data() + (ctx->probs.size() - ctx->vocab.n_vocab), false, false);\n\n    ctx->t_sample_us += ggml_time_us() - t_start_sample_us;\n\n    return res;\n}\n\nstruct whisper_token_data whisper_sample_timestamp(struct whisper_context * ctx, bool is_initial) {\n    const int64_t t_start_sample_us = ggml_time_us();\n\n    const auto res = whisper_sample_best(ctx->vocab, ctx->probs.data() + (ctx->probs.size() - ctx->vocab.n_vocab), true, is_initial);\n\n    ctx->t_sample_us += ggml_time_us() - t_start_sample_us;\n\n    return res;\n}\n\nint whisper_tokenize(struct whisper_context * ctx, const char * text, whisper_token * tokens, int n_max_tokens) {\n    const auto res = tokenize(ctx->vocab, text);\n\n    if (n_max_tokens < (int) res.size()) {\n\t\tlogError( u8\"%s: too many resulting tokens: %d (max %d)\", __func__, (int)res.size(), n_max_tokens );\n        return -1;\n    }\n\n    for (int i = 0; i < (int) res.size(); i++) {\n        tokens[i] = res[i];\n    }\n\n    return res.size();\n}\n\nint whisper_lang_max_id() {\n    auto max_id = 0;\n    for (const auto & kv : g_lang) {\n        max_id = std::max(max_id, kv.second.first);\n    }\n\n    return max_id;\n}\n\nint whisper_lang_id(const char * lang) {\n    if (!g_lang.count(lang)) {\n        for (const auto & kv : g_lang) {\n            if (kv.second.second == lang) {\n                return kv.second.first;\n            }\n        }\n\n\t\tlogError( u8\"%s: unknown language '%s'\", __func__, lang );\n        return -1;\n    }\n\n    return g_lang.at(lang).first;\n}\n\nconst char * whisper_lang_str(int id) {\n    for (const auto & kv : g_lang) {\n        if (kv.second.first == id) {\n            return kv.first.c_str();\n        }\n    }\n\n\tlogError( u8\"%s: unknown language id %d\", __func__, id );\n    return nullptr;\n}\n\nint whisper_lang_auto_detect(\n        struct whisper_context * ctx,\n        int offset_ms,\n        int n_threads,\n        float * lang_probs) {\n    const int seek = offset_ms/10;\n\n    if (seek < 0) {\n\t\tlogError( u8\"%s: offset %dms is before the start of the audio\", __func__, offset_ms );\n        return -1;\n    }\n\n    if (seek >= ctx->mel.n_len) {\n\t\tlogError( u8\"%s: offset %dms is past the end of the audio (%dms)\", __func__, offset_ms, ctx->mel.n_len * 10 );\n        return -2;\n    }\n\n    // run the encoder\n    if (whisper_encode(ctx, seek, n_threads) != 0) {\n\t\tlogError( u8\"%s: failed to encode\", __func__ );\n        return -6;\n    }\n\n    const std::vector<whisper_token> prompt = { whisper_token_sot(ctx) };\n\n    if (whisper_decode(ctx, prompt.data(), prompt.size(), 0, n_threads) != 0) {\n\t\tlogError( u8\"%s: failed to decode\", __func__ );\n        return -7;\n    }\n\n    std::vector<std::pair<float, int>> probs_id;\n    for (const auto & kv : g_lang) {\n        const auto token_lang = whisper_token_lang(ctx, kv.second.first);\n        probs_id.emplace_back( ctx->probs[token_lang], kv.second.first );\n    }\n\n    // sort descending\n    {\n        using pair_type = decltype(probs_id)::value_type;\n        std::sort(probs_id.begin(), probs_id.end(), [](const pair_type & a, const pair_type & b) {\n            return a.first > b.first;\n        });\n    }\n\n    // softmax\n    {\n        float sum = 0;\n        for (const auto & kv : probs_id) {\n            sum += exp(kv.first);\n        }\n\n        for (auto & kv : probs_id) {\n            kv.first = exp(kv.first) / sum;\n        }\n    }\n\n    {\n        for (int i = 0; i < (int) probs_id.size(); i++) {\n            if (lang_probs) {\n                lang_probs[probs_id[i].second] = probs_id[i].first;\n            }\n\n            //printf(\"%s: lang %2d (%3s): %f\\n\", __func__, probs_id[i].second, whisper_lang_str(probs_id[i].second), probs_id[i].first);\n        }\n    }\n\n    return probs_id[0].second;\n}\n\nint whisper_n_len(struct whisper_context * ctx) {\n    return ctx->mel.n_len;\n}\n\nint whisper_n_vocab(struct whisper_context * ctx) {\n    return ctx->vocab.n_vocab;\n}\n\nint whisper_n_text_ctx(struct whisper_context * ctx) {\n    return ctx->model.hparams.n_text_ctx;\n}\n\nint whisper_is_multilingual(struct whisper_context * ctx) {\n    return ctx->vocab.is_multilingual() ? 1 : 0;\n}\n\nfloat * whisper_get_probs(struct whisper_context * ctx) {\n    return ctx->probs.data();\n}\n\nconst char * whisper_token_to_str(struct whisper_context * ctx, whisper_token token) {\n    return ctx->vocab.id_to_token.at(token).c_str();\n}\n\nwhisper_token whisper_token_eot(struct whisper_context * ctx) {\n    return ctx->vocab.token_eot;\n}\n\nwhisper_token whisper_token_sot(struct whisper_context * ctx) {\n    return ctx->vocab.token_sot;\n}\n\nwhisper_token whisper_token_prev(struct whisper_context * ctx) {\n    return ctx->vocab.token_prev;\n}\n\nwhisper_token whisper_token_solm(struct whisper_context * ctx) {\n    return ctx->vocab.token_solm;\n}\n\nwhisper_token whisper_token_not(struct whisper_context * ctx) {\n    return ctx->vocab.token_not;\n}\n\nwhisper_token whisper_token_beg(struct whisper_context * ctx) {\n    return ctx->vocab.token_beg;\n}\n\nwhisper_token whisper_token_lang(struct whisper_context * ctx, int lang_id) {\n    return whisper_token_sot(ctx) + 1 + lang_id;\n}\n\nwhisper_token whisper_token_translate(void) {\n    return whisper_vocab::token_translate;\n}\n\nwhisper_token whisper_token_transcribe(void) {\n    return whisper_vocab::token_transcribe;\n}\n\nvoid whisper_print_timings(struct whisper_context * ctx) {\n    const int64_t t_end_us = ggml_time_us();\n\n\tlogInfo( u8\"%s:     load time = %8.2f ms\", __func__, ctx->t_load_us / 1000.0f );\n\tlogInfo( u8\"%s:      mel time = %8.2f ms\", __func__, ctx->t_mel_us / 1000.0f );\n\tlogInfo( u8\"%s:   sample time = %8.2f ms\", __func__, ctx->t_sample_us / 1000.0f );\n\tlogInfo( u8\"%s:   encode time = %8.2f ms / %.2f ms per layer\", __func__,\n\t\tctx->t_encode_us / 1000.0f, ctx->t_encode_us / 1000.0f / ctx->model.hparams.n_audio_layer );\n\tlogInfo( u8\"%s:   decode time = %8.2f ms / %.2f ms per layer\", __func__,\n\t\tctx->t_decode_us / 1000.0f, ctx->t_decode_us / 1000.0f / ctx->model.hparams.n_text_layer );\n\tlogInfo( u8\"%s:    total time = %8.2f ms\", __func__, ( t_end_us - ctx->t_start_us ) / 1000.0f );\n}\n\nvoid whisper_reset_timings(struct whisper_context * ctx) {\n    ctx->t_sample_us = 0;\n    ctx->t_encode_us = 0;\n    ctx->t_decode_us = 0;\n}\n\nconst char * whisper_print_system_info(void) {\n    static std::string s;\n\n    s  = \"\";\n    s += \"AVX = \"       + std::to_string(ggml_cpu_has_avx())       + \" | \";\n    s += \"AVX2 = \"      + std::to_string(ggml_cpu_has_avx2())      + \" | \";\n    s += \"AVX512 = \"    + std::to_string(ggml_cpu_has_avx512())    + \" | \";\n    s += \"FMA = \"       + std::to_string(ggml_cpu_has_fma())       + \" | \";\n    s += \"NEON = \"      + std::to_string(ggml_cpu_has_neon())      + \" | \";\n    s += \"ARM_FMA = \"   + std::to_string(ggml_cpu_has_arm_fma())   + \" | \";\n    s += \"F16C = \"      + std::to_string(ggml_cpu_has_f16c())      + \" | \";\n    s += \"FP16_VA = \"   + std::to_string(ggml_cpu_has_fp16_va())   + \" | \";\n    s += \"WASM_SIMD = \" + std::to_string(ggml_cpu_has_wasm_simd()) + \" | \";\n    s += \"BLAS = \"      + std::to_string(ggml_cpu_has_blas())      + \" | \";\n\n    return s.c_str();\n}\n\n////////////////////////////////////////////////////////////////////////////\n\nstruct whisper_full_params whisper_full_default_params(enum whisper_sampling_strategy strategy) {\n    struct whisper_full_params result;\n\n    switch (strategy) {\n        case WHISPER_SAMPLING_GREEDY:\n            {\n                result = {\n                    /*.strategy         =*/ WHISPER_SAMPLING_GREEDY,\n\n                    /*.n_threads        =*/ std::min(4, (int32_t) std::thread::hardware_concurrency()),\n                    /*.n_max_text_ctx   =*/ 16384,\n                    /*.offset_ms        =*/ 0,\n                    /*.duration_ms      =*/ 0,\n\n                    /*.translate        =*/ false,\n                    /*.no_context       =*/ false,\n                    /*.single_segment   =*/ false,\n                    /*.print_special    =*/ false,\n                    /*.print_progress   =*/ true,\n                    /*.print_realtime   =*/ false,\n                    /*.print_timestamps =*/ true,\n\n                    /*.token_timestamps =*/ false,\n                    /*.thold_pt         =*/ 0.01f,\n                    /*.thold_ptsum      =*/ 0.01f,\n                    /*.max_len          =*/ 0,\n                    /*.max_tokens       =*/ 0,\n\n                    /*.speed_up         =*/ false,\n                    /*.audio_ctx        =*/ 0,\n\n                    /*.prompt_tokens    =*/ nullptr,\n                    /*.prompt_n_tokens  =*/ 0,\n\n                    /*.language         =*/ \"en\",\n\n                    /*.greedy           =*/ {\n                        /*.n_past =*/ 0,\n                    },\n\n                    /*.beam_search      =*/ {\n                        /*.n_past     =*/ -1,\n                        /*.beam_width =*/ -1,\n                        /*.n_best     =*/ -1,\n                    },\n\n                    /*.new_segment_callback           =*/ nullptr,\n                    /*.new_segment_callback_user_data =*/ nullptr,\n\n                    /*.encoder_begin_callback           =*/ nullptr,\n                    /*.encoder_begin_callback_user_data =*/ nullptr,\n                };\n            } break;\n        case WHISPER_SAMPLING_BEAM_SEARCH:\n            {\n                result = {\n                    /*.strategy         =*/ WHISPER_SAMPLING_BEAM_SEARCH,\n\n                    /*.n_threads        =*/ std::min(4, (int32_t) std::thread::hardware_concurrency()),\n                    /*.n_max_text_ctx   =*/ 16384,\n                    /*.offset_ms        =*/ 0,\n                    /*.duration_ms      =*/ 0,\n\n                    /*.translate        =*/ false,\n                    /*.no_context       =*/ false,\n                    /*.single_segment   =*/ false,\n                    /*.print_special    =*/ false,\n                    /*.print_progress   =*/ true,\n                    /*.print_realtime   =*/ false,\n                    /*.print_timestamps =*/ true,\n\n                    /*.token_timestamps =*/ false,\n                    /*.thold_pt         =*/ 0.01f,\n                    /*.thold_ptsum      =*/ 0.01f,\n                    /*.max_len          =*/ 0,\n                    /*.max_tokens       =*/ 0,\n\n                    /*.speed_up         =*/ false,\n                    /*.audio_ctx        =*/ 0,\n\n                    /*.prompt_tokens    =*/ nullptr,\n                    /*.prompt_n_tokens  =*/ 0,\n\n                    /*.language         =*/ \"en\",\n\n                    /*.greedy           =*/ {\n                        /*.n_past =*/ -1,\n                    },\n\n                    /*.beam_search      =*/ {\n                        /*.n_past     =*/ 0,\n                        /*.beam_width =*/ 10,\n                        /*.n_best     =*/ 5,\n                    },\n\n                    /*.new_segment_callback           =*/ nullptr,\n                    /*.new_segment_callback_user_data =*/ nullptr,\n\n                    /*.encoder_begin_callback           =*/ nullptr,\n                    /*.encoder_begin_callback_user_data =*/ nullptr,\n                };\n            } break;\n    }\n\n    return result;\n}\n\n// forward declarations\nstatic std::vector<float> get_signal_energy(const float * signal, int n_samples, int n_samples_per_half_window);\nstatic void whisper_exp_compute_token_level_timestamps(\n        struct whisper_context * ctx,\n        int   i_segment,\n        float thold_pt,\n        float thold_ptsum);\n\n// wrap the last segment to max_len characters\n// returns the number of new segments\nstatic int whisper_wrap_segment(struct whisper_context * ctx, int max_len) {\n    auto segment = ctx->result_all.back();\n\n    int res = 1;\n    int acc = 0;\n\n    std::string text;\n\n    for (int i = 0; i < (int) segment.tokens.size(); i++) {\n        const auto & token = segment.tokens[i];\n        if (token.id >= whisper_token_eot(ctx)) {\n            continue;\n        }\n\n        const auto txt = whisper_token_to_str(ctx, token.id);\n\n        const int cur = strlen(txt);\n\n        if (acc + cur > max_len && i > 0) {\n            // split here\n            ctx->result_all.back().text = std::move(text);\n            ctx->result_all.back().t1 = token.t0;\n            ctx->result_all.back().tokens.resize(i);\n\n            ctx->result_all.push_back({});\n            ctx->result_all.back().t0 = token.t0;\n            ctx->result_all.back().t1 = segment.t1;\n\n            // add tokens [i, end] to the new segment\n            ctx->result_all.back().tokens.insert(\n                    ctx->result_all.back().tokens.end(),\n                    segment.tokens.begin() + i,\n                    segment.tokens.end());\n\n            acc = 0;\n            text = \"\";\n\n            segment = ctx->result_all.back();\n            i = -1;\n\n            res++;\n        } else {\n            acc += cur;\n            text += txt;\n        }\n    }\n\n    ctx->result_all.back().text = std::move(text);\n\n    return res;\n}\n\nint whisper_full(\n        struct whisper_context * ctx,\n        struct whisper_full_params params,\n        const float * samples,\n        int n_samples) {\n    // clear old results\n    auto & result_all = ctx->result_all;\n\n    result_all.clear();\n\n    // compute log mel spectrogram\n    if (params.speed_up) {\n        if (whisper_pcm_to_mel_phase_vocoder(ctx, samples, n_samples, params.n_threads) != 0) {\n\t\t\tlogError( u8\"%s: failed to compute log mel spectrogram\", __func__ );\n            return -1;\n        }\n    } else {\n        if (whisper_pcm_to_mel(ctx, samples, n_samples, params.n_threads) != 0) {\n\t\t\tlogError( u8\"%s: failed to compute log mel spectrogram\", __func__ );\n            return -2;\n        }\n    }\n\n    // auto-detect language if not specified\n    if (params.language == nullptr || strlen(params.language) == 0 || strcmp(params.language, \"auto\") == 0) {\n        std::vector<float> probs(whisper_lang_max_id() + 1, 0.0f);\n\n        const auto lang_id = whisper_lang_auto_detect(ctx, 0, params.n_threads, probs.data());\n        if (lang_id < 0) {\n\t\t\tlogError( u8\"%s: failed to auto-detect language\", __func__ );\n            return -3;\n        }\n\n        params.language = whisper_lang_str(lang_id);\n\n\t\tlogInfo( u8\"%s: auto-detected language: %s (p = %f)\", __func__, params.language, probs[ whisper_lang_id( params.language ) ] );\n    }\n\n    if (params.token_timestamps) {\n        ctx->t_beg = 0;\n        ctx->t_last = 0;\n        ctx->tid_last = 0;\n        ctx->energy = get_signal_energy(samples, n_samples, 32);\n    }\n\n    const int seek_start = params.offset_ms/10;\n    const int seek_end = seek_start + (params.duration_ms == 0 ? whisper_n_len(ctx) : params.duration_ms/10);\n\n    // if length of spectrogram is less than 1s (100 samples), then return\n    // basically don't process anything that is less than 1s\n    // see issue #39: https://github.com/ggerganov/whisper.cpp/issues/39\n    if (seek_end < 100 + seek_start) {\n        return 0;\n    }\n\n    // the accumulated text context so far\n    auto & prompt_past = ctx->prompt_past;\n    if (params.no_context) {\n        prompt_past.clear();\n    }\n\n    // prepend the prompt tokens to the prompt_past\n    if (params.prompt_tokens && params.prompt_n_tokens > 0) {\n        // parse tokens from the pointer\n        for (int i = 0; i < params.prompt_n_tokens; i++) {\n            prompt_past.push_back(params.prompt_tokens[i]);\n        }\n        std::rotate(prompt_past.begin(), prompt_past.end() - params.prompt_n_tokens, prompt_past.end());\n    }\n\n    // overwrite audio_ctx\n    ctx->exp_n_audio_ctx = params.audio_ctx;\n\n    // these tokens determine the task that will be performed\n    std::vector<whisper_token> prompt_init = { whisper_token_sot(ctx) };\n    if (whisper_is_multilingual(ctx)) {\n        const int lang_id = whisper_lang_id(params.language);\n        prompt_init.push_back(whisper_token_lang(ctx, lang_id));\n        if (params.translate) {\n            prompt_init.push_back(whisper_token_translate());\n        } else {\n            prompt_init.push_back(whisper_token_transcribe());\n        }\n    }\n\n    int progress_prev = 0;\n    int progress_step = 5;\n\n    std::vector<whisper_token_data> tokens_cur;\n    tokens_cur.reserve(whisper_n_text_ctx(ctx));\n\n    std::vector<whisper_token> prompt;\n    prompt.reserve(whisper_n_text_ctx(ctx));\n\n    // main loop\n    int seek = seek_start;\n    while (true) {\n        const int progress_cur = (100*(seek - seek_start))/(seek_end - seek_start);\n        while (progress_cur >= progress_prev + progress_step) {\n            progress_prev += progress_step;\n            if (params.print_progress) {\n\t\t\t\tlogInfo( u8\"%s: progress = %3d%%\", __func__, progress_prev );\n            }\n        }\n\n        // of only 1 second left, then stop\n        if (seek + 100 >= seek_end) {\n            break;\n        }\n\n        // if there is a very short audio segment left to process, we remove any past prompt since it tends\n        // to confuse the decoder and often make it repeat or hallucinate stuff\n        if (seek > seek_start && seek + 500 >= seek_end) {\n            prompt_past.clear();\n        }\n\n        if (params.encoder_begin_callback) {\n            if (params.encoder_begin_callback(ctx, params.encoder_begin_callback_user_data) == false) {\n\t\t\t\tlogDebug( u8\"%s: encoder_begin_callback returned false - aborting\", __func__ );\n                break;\n            }\n        }\n\n        // encode audio features starting at offset seek\n        if (whisper_encode(ctx, seek, params.n_threads) != 0) {\n\t\t\tlogError( u8\"%s: failed to encode\", __func__ );\n            return -4;\n        }\n\n        int n_past = 0;\n        prompt.clear();\n\n        // if we have already generated some text, use it as a prompt to condition the next generation\n        if (!prompt_past.empty()) {\n            int n_take = std::min(std::min(params.n_max_text_ctx, whisper_n_text_ctx(ctx)/2), int(prompt_past.size()));\n\n            prompt = { whisper_token_prev(ctx) };\n            prompt.insert(prompt.begin() + 1, prompt_past.end() - n_take, prompt_past.end());\n\n            prompt_past.clear();\n            prompt_past.insert(prompt_past.end(), prompt.begin() + 1, prompt.end());\n        }\n\n        prompt.insert(prompt.end(), prompt_init.begin(), prompt_init.end());\n\n        int seek_delta = 100*WHISPER_CHUNK_SIZE;\n\n        // print the prompt\n        //printf(\"\\n\\n\");\n        //for (int i = 0; i < prompt.size(); i++) {\n        //    printf(\"%s: prompt[%d] = %s\\n\", __func__, i, ctx->vocab.id_to_token[prompt[i]].c_str());\n        //}\n        //printf(\"\\n\\n\");\n\n        // the accumulated transcription in the current interation\n        int result_len = 0;\n        tokens_cur.clear();\n\n        bool failed = false;\n        bool has_ts = false; // have we already sampled a non-beg timestamp token for the current segment?\n\n        for (int i = 0, n_max = whisper_n_text_ctx(ctx)/2 - 4; i < n_max; ++i) {\n            if (whisper_decode(ctx, prompt.data(), prompt.size(), n_past, params.n_threads) != 0) {\n\t\t\t\tlogError( u8\"%s: failed to decode\", __func__ );\n                return -5;\n            }\n\n            n_past += prompt.size();\n            prompt.clear();\n\n            // very basic greedy sampling strategy:\n            //\n            //   - always take the most probable token\n            //\n            // more sophisticated sampling strategies could be implemented here, but we keep it simple\n            // feel free to experiment!\n            //\n            {\n                const auto token = (i == 0) ? whisper_sample_timestamp(ctx, true) : whisper_sample_best(ctx);\n\n                // timestamp token - update sliding window\n                if (token.id > whisper_token_beg(ctx)) {\n                    const int seek_delta_new = 2*(token.id - whisper_token_beg(ctx));\n\n                    // do not allow to go back in time\n                    if (has_ts && seek_delta > seek_delta_new && result_len < i) {\n                        break;\n                    }\n\n                    seek_delta = seek_delta_new;\n                    result_len = i + 1;\n                    has_ts = true;\n                }\n\n                // add it to the context\n                prompt.push_back(token.id);\n                tokens_cur.push_back(token);\n\n                //{\n                //    const auto tt = token.pt > 0.10 ? ctx->vocab.id_to_token[token.tid] : \"[?]\";\n                //    printf(\"%s: %3d %10s %6d %6.3f '%s'\\n\", __func__, i, tt.c_str(), token.id, token.pt, ctx->vocab.id_to_token[token.id].c_str());\n                //}\n\n                // end of segment\n                if (token.id == whisper_token_eot(ctx) ||                // end of text token\n                    (params.max_tokens > 0 && i >= params.max_tokens) || // max tokens per segment reached\n                    (has_ts && seek + seek_delta + 100 >= seek_end)      // end of audio reached\n                    ) {\n                    if (result_len == 0) {\n                        if (seek + seek_delta + 100 >= seek_end) {\n                            result_len = i + 1;\n                        } else {\n                            failed = true;\n                            break;\n                        }\n                    }\n\n                    if (params.single_segment) {\n                        result_len = i + 1;\n                        seek_delta = 100*WHISPER_CHUNK_SIZE;\n                    }\n\n                    break;\n                }\n\n                // TESTS: if no tensors are loaded, it means we are running tests\n                if (ctx->model.n_loaded == 0) {\n                    seek_delta = 100*WHISPER_CHUNK_SIZE;\n                    break;\n                }\n            }\n\n            // sometimes, the decoding can get stuck in a repetition loop\n            // this is a simple strategy to avoid such cases - we simply flag the decoding as failed and advance\n            // the sliding window by 1 second\n            if (i == n_max - 1 && (result_len == 0 || seek_delta < 100*WHISPER_CHUNK_SIZE/2)) {\n                failed = true;\n                break;\n            }\n        }\n\n        if (failed) {\n            // when we fail to sample timestamp token, retry by clearing the past prompt\n            // if it fails again, then we advance the window by 1 second\n            if (!prompt_past.empty()) {\n                prompt_past.clear();\n            } else {\n\t\t\t\tlogWarning( u8\"%s: failed to generate timestamp token - skipping one second\", __func__ );\n                seek += 100;\n            }\n            continue;\n        }\n\n        // shrink down to result_len\n        tokens_cur.resize(result_len);\n\n        for (const auto & r : tokens_cur) {\n            prompt_past.push_back(r.id);\n        }\n\n        // store the text from this iteration\n        if (!tokens_cur.empty()) {\n            int  i0 = 0;\n            auto t0 = seek + 2*(tokens_cur.front().tid - whisper_token_beg(ctx));\n\n            std::string text;\n\n            for (int i = 0; i < (int) tokens_cur.size(); i++) {\n                //printf(\"%s: %18s %6.3f %18s %6.3f\\n\", __func__,\n                //        ctx->vocab.id_to_token[tokens_cur[i].id].c_str(), tokens_cur[i].p,\n                //        ctx->vocab.id_to_token[tokens_cur[i].tid].c_str(), tokens_cur[i].pt);\n\n                if (params.print_special == false && tokens_cur[i].id >= whisper_token_eot(ctx)) {\n                } else {\n                    text += whisper_token_to_str(ctx, tokens_cur[i].id);\n                }\n                if (tokens_cur[i].id > whisper_token_beg(ctx) && !params.single_segment) {\n                    const auto t1 = seek + 2*(tokens_cur[i].tid - whisper_token_beg(ctx));\n                    if (!text.empty()) {\n                        const auto tt0 = params.speed_up ? 2*t0 : t0;\n                        const auto tt1 = params.speed_up ? 2*t1 : t1;\n\n                        if (params.print_realtime) {\n                            if (params.print_timestamps) {\n                                printf(\"[%s --> %s]  %s\\n\", to_timestamp(tt0).c_str(), to_timestamp(tt1).c_str(), text.c_str());\n                            } else {\n                                printf(\"%s\", text.c_str());\n                                fflush(stdout);\n                            }\n                        }\n\n                        result_all.push_back({ tt0, tt1, text, {} });\n                        for (int j = i0; j <= i; j++) {\n                            result_all.back().tokens.push_back(tokens_cur[j]);\n                        }\n\n                        int n_new = 1;\n\n                        if (params.token_timestamps) {\n                            whisper_exp_compute_token_level_timestamps(\n                                    ctx, result_all.size() - 1, params.thold_pt, params.thold_ptsum);\n\n                            if (params.max_len > 0) {\n                                n_new = whisper_wrap_segment(ctx, params.max_len);\n                            }\n                        }\n                        if (params.new_segment_callback) {\n                            params.new_segment_callback(ctx, n_new, params.new_segment_callback_user_data);\n                        }\n                    }\n                    text = \"\";\n                    while (i < (int) tokens_cur.size() && tokens_cur[i].id > whisper_token_beg(ctx)) {\n                        i++;\n                    }\n                    i--;\n                    t0 = t1;\n                    i0 = i + 1;\n                }\n            }\n\n            if (!text.empty()) {\n                const auto t1 = seek + seek_delta;\n\n                const auto tt0 = params.speed_up ? 2*t0 : t0;\n                const auto tt1 = params.speed_up ? 2*t1 : t1;\n\n                if (params.print_realtime) {\n                    if (params.print_timestamps) {\n                        printf(\"[%s --> %s]  %s\\n\", to_timestamp(tt0).c_str(), to_timestamp(tt1).c_str(), text.c_str());\n                    } else {\n                        printf(\"%s\", text.c_str());\n                        fflush(stdout);\n                    }\n                }\n\n                result_all.push_back({ tt0, tt1, text, {} });\n                for (int j = i0; j < (int) tokens_cur.size(); j++) {\n                    result_all.back().tokens.push_back(tokens_cur[j]);\n                }\n\n                int n_new = 1;\n\n                if (params.token_timestamps) {\n                    whisper_exp_compute_token_level_timestamps(\n                            ctx, result_all.size() - 1, params.thold_pt, params.thold_ptsum);\n\n                    if (params.max_len > 0) {\n                        n_new = whisper_wrap_segment(ctx, params.max_len);\n                    }\n                }\n                if (params.new_segment_callback) {\n                    params.new_segment_callback(ctx, n_new, params.new_segment_callback_user_data);\n                }\n            }\n        }\n\n        seek += seek_delta;\n    }\n\n    return 0;\n}\n\nint whisper_full_parallel(\n        struct whisper_context * ctx,\n        struct whisper_full_params params,\n        const float * samples,\n        int n_samples,\n        int n_processors) {\n    if (n_processors == 1) {\n        return whisper_full(ctx, params, samples, n_samples);\n    }\n\n    int ret = 0;\n\n    // prepare separate contexts for each thread\n    std::vector<struct whisper_context> ctxs(n_processors - 1);\n\n    for (int i = 0; i < n_processors - 1; ++i) {\n        ctxs[i] = *ctx;\n\n        auto & model = ctxs[i].model;\n\n        // create the ggml memory context\n        {\n            struct ggml_init_params params;\n            params.mem_size   = ctxs[i].buf_memory.size();\n            params.mem_buffer = ctxs[i].buf_memory.data();\n\n            model.ctx_mem = ggml_init(params);\n            if (!model.ctx_mem) {\n\t\t\t\tlogError( u8\"%s: ggml_init() failed\", __func__ );\n                return false;\n            }\n        }\n\n        // separate key + value memory for each processor\n        {\n            auto & ctx = model.ctx_mem;\n\n            const auto & hparams = model.hparams;\n\n            const int n_text_state = hparams.n_text_state;\n            const int n_text_layer = hparams.n_text_layer;\n            const int n_text_ctx   = hparams.n_text_ctx;\n\n            // key/value memory for the self-attention layer\n            {\n                const int n_mem      = n_text_layer*n_text_ctx;\n                const int n_elements = n_text_state*n_mem;\n\n                model.memory_k = ggml_new_tensor_1d(ctx, GGML_TYPE_F16, n_elements);\n                model.memory_v = ggml_new_tensor_1d(ctx, GGML_TYPE_F16, n_elements);\n            }\n\n            // key/value memory for the cross-attention layer\n            {\n                const int n_audio_ctx = hparams.n_audio_ctx;\n\n                const int n_mem      = n_text_layer*n_audio_ctx;\n                const int n_elements = n_text_state*n_mem;\n\n                model.memory_cross_k = ggml_new_tensor_1d(ctx, GGML_TYPE_F16, n_elements);\n                model.memory_cross_v = ggml_new_tensor_1d(ctx, GGML_TYPE_F16, n_elements);\n            }\n        }\n    }\n\n    const int offset_samples = (WHISPER_SAMPLE_RATE*params.offset_ms)/1000;\n    const int n_samples_per_processor = (n_samples - offset_samples)/n_processors;\n\n    // the calling thread will process the first chunk\n    // while the other threads will process the remaining chunks\n\n    std::vector<std::thread> workers(n_processors - 1);\n    for (int i = 0; i < n_processors - 1; ++i) {\n        const int start_samples = offset_samples + (i + 1)*n_samples_per_processor;\n        const int n_samples_cur = (i == n_processors - 2) ? n_samples - start_samples : n_samples_per_processor;\n\n        auto params_cur = params;\n\n        params_cur.offset_ms = 0;\n        params_cur.print_progress = false;\n        params_cur.print_realtime = false;\n\n        params_cur.new_segment_callback = nullptr;\n        params_cur.new_segment_callback_user_data = nullptr;\n\n        workers[i] = std::thread(whisper_full, &ctxs[i], std::move(params_cur), samples + start_samples, n_samples_cur);\n    }\n\n    {\n        auto params_cur = params;\n\n        ret = whisper_full(ctx, std::move(params_cur), samples, offset_samples + n_samples_per_processor);\n    }\n\n    for (int i = 0; i < n_processors - 1; ++i) {\n        workers[i].join();\n    }\n\n    const int64_t offset_t = (int64_t) params.offset_ms/10.0;\n\n    // combine results into ctx->result_all\n    for (int i = 0; i < n_processors - 1; ++i) {\n        auto & results_i = ctxs[i].result_all;\n\n        for (int j = 0; j < (int) results_i.size(); ++j) {\n            // correct the segment timestamp taking into account the offset\n            results_i[j].t0 += 100*((i + 1)*n_samples_per_processor)/WHISPER_SAMPLE_RATE + offset_t;\n            results_i[j].t1 += 100*((i + 1)*n_samples_per_processor)/WHISPER_SAMPLE_RATE + offset_t;\n\n            // make sure that segments are not overlapping\n            if (!ctx->result_all.empty()) {\n                results_i[j].t0 = std::max(results_i[j].t0, ctx->result_all.back().t1);\n            }\n\n            ctx->result_all.push_back(std::move(results_i[j]));\n\n            // call the new_segment_callback for each segment\n            if (params.new_segment_callback) {\n                params.new_segment_callback(ctx, 1, params.new_segment_callback_user_data);\n            }\n        }\n\n        ctx->t_mel_us    += ctxs[i].t_mel_us;\n        ctx->t_sample_us += ctxs[i].t_sample_us;\n        ctx->t_encode_us += ctxs[i].t_encode_us;\n        ctx->t_decode_us += ctxs[i].t_decode_us;\n    }\n\n    // average the timings\n    ctx->t_mel_us    /= n_processors;\n    ctx->t_sample_us /= n_processors;\n    ctx->t_encode_us /= n_processors;\n    ctx->t_decode_us /= n_processors;\n\n    // print information about the audio boundaries\n\tlogDebug( u8\"%s: the audio has been split into %d chunks at the following times:\", __func__, n_processors );\n\tfor( int i = 0; i < n_processors - 1; ++i )\n\t\tlogDebug( u8\"%s: split %d - %s\", __func__, ( i + 1 ), to_timestamp( 100 * ( ( i + 1 ) * n_samples_per_processor ) / WHISPER_SAMPLE_RATE + offset_t ).c_str() );\n\tlogDebug( u8\"%s: the transcription quality may be degraded near these boundaries\", __func__ );\n\n    return ret;\n}\n\nint whisper_full_n_segments(struct whisper_context * ctx) {\n    return ctx->result_all.size();\n}\n\nint64_t whisper_full_get_segment_t0(struct whisper_context * ctx, int i_segment) {\n    return ctx->result_all[i_segment].t0;\n}\n\nint64_t whisper_full_get_segment_t1(struct whisper_context * ctx, int i_segment) {\n    return ctx->result_all[i_segment].t1;\n}\n\nconst char * whisper_full_get_segment_text(struct whisper_context * ctx, int i_segment) {\n    return ctx->result_all[i_segment].text.c_str();\n}\n\nint whisper_full_n_tokens(struct whisper_context * ctx, int i_segment) {\n    return ctx->result_all[i_segment].tokens.size();\n}\n\nconst char * whisper_full_get_token_text(struct whisper_context * ctx, int i_segment, int i_token) {\n    return ctx->vocab.id_to_token[ctx->result_all[i_segment].tokens[i_token].id].c_str();\n}\n\nwhisper_token whisper_full_get_token_id(struct whisper_context * ctx, int i_segment, int i_token) {\n    return ctx->result_all[i_segment].tokens[i_token].id;\n}\n\nstruct whisper_token_data whisper_full_get_token_data(struct whisper_context * ctx, int i_segment, int i_token) {\n    return ctx->result_all[i_segment].tokens[i_token];\n}\n\nfloat whisper_full_get_token_p(struct whisper_context * ctx, int i_segment, int i_token) {\n    return ctx->result_all[i_segment].tokens[i_token].p;\n}\n\n// =================================================================================================\n\n//\n// Experimental stuff below\n//\n// Not sure if these should be part of the library at all, because the quality of the results is not\n// guaranteed. Might get removed at some point unless a robust algorithm implementation is found\n//\n\n// =================================================================================================\n\n//\n// token-level timestamps\n//\n\nstatic int timestamp_to_sample(int64_t t, int n_samples) {\n    return std::max(0, std::min((int) n_samples - 1, (int) ((t*WHISPER_SAMPLE_RATE)/100)));\n}\n\nstatic int64_t sample_to_timestamp(int i_sample) {\n    return (100*i_sample)/WHISPER_SAMPLE_RATE;\n}\n\n// a cost-function / heuristic that is high for text that takes longer to pronounce\n// obviously, can be improved\nstatic float voice_length(const std::string & text) {\n    float res = 0.0f;\n\n    for (size_t i = 0; i < text.size(); ++i) {\n        if (text[i] == ' ') {\n            res += 0.01f;\n        } else if (text[i] == ',') {\n            res += 2.00f;\n        } else if (text[i] == '.') {\n            res += 3.00f;\n        } else if (text[i] == '!') {\n            res += 3.00f;\n        } else if (text[i] == '?') {\n            res += 3.00f;\n        } else if (text[i] >= '0' && text[i] <= '9') {\n            res += 3.00f;\n        } else {\n            res += 1.00f;\n        }\n    }\n\n    return res;\n}\n\n// average the fabs of the signal\nstatic std::vector<float> get_signal_energy(const float * signal, int n_samples, int n_samples_per_half_window) {\n    const int hw = n_samples_per_half_window;\n\n    std::vector<float> result(n_samples);\n\n    for (int i = 0; i < n_samples; i++) {\n        float sum = 0;\n        for (int j = -hw; j <= hw; j++) {\n            if (i + j >= 0 && i + j < n_samples) {\n                sum += fabs(signal[i + j]);\n            }\n        }\n        result[i] = sum/(2*hw + 1);\n    }\n\n    return result;\n}\n\nstatic void whisper_exp_compute_token_level_timestamps(\n        struct whisper_context * ctx,\n        int   i_segment,\n        float thold_pt,\n        float thold_ptsum) {\n    auto & segment = ctx->result_all[i_segment];\n    auto & tokens  = segment.tokens;\n\n    const int n_samples = ctx->energy.size();\n\n    if (n_samples == 0) {\n\t\tlogWarning( u8\"%s: no signal data available\", __func__ );\n        return;\n    }\n\n    const int64_t t0 = segment.t0;\n    const int64_t t1 = segment.t1;\n\n    const int n = tokens.size();\n\n    if (n == 0) {\n        return;\n    }\n\n    if (n == 1) {\n        tokens[0].t0 = t0;\n        tokens[0].t1 = t1;\n\n        return;\n    }\n\n    auto & t_beg    = ctx->t_beg;\n    auto & t_last   = ctx->t_last;\n    auto & tid_last = ctx->tid_last;\n\n    for (int j = 0; j < n; ++j) {\n        auto & token = tokens[j];\n\n        if (j == 0) {\n            if (token.id == whisper_token_beg(ctx)) {\n                tokens[j    ].t0 = t0;\n                tokens[j    ].t1 = t0;\n                tokens[j + 1].t0 = t0;\n\n                t_beg    = t0;\n                t_last   = t0;\n                tid_last = whisper_token_beg(ctx);\n            } else {\n                tokens[j    ].t0 = t_last;\n            }\n        }\n\n        const int64_t tt = t_beg + 2*(token.tid - whisper_token_beg(ctx));\n\n        tokens[j].id    = token.id;\n        tokens[j].tid   = token.tid;\n        tokens[j].p     = token.p;\n        tokens[j].pt    = token.pt;\n        tokens[j].ptsum = token.ptsum;\n\n        tokens[j].vlen = voice_length(whisper_token_to_str(ctx, token.id));\n\n        if (token.pt > thold_pt && token.ptsum > thold_ptsum && token.tid > tid_last && tt <= t1) {\n            if (j > 0) {\n                tokens[j - 1].t1 = tt;\n            }\n            tokens[j].t0 = tt;\n            tid_last = token.tid;\n        }\n    }\n\n    tokens[n - 2].t1 = t1;\n    tokens[n - 1].t0 = t1;\n    tokens[n - 1].t1 = t1;\n\n    t_last = t1;\n\n    // find intervals of tokens with unknown timestamps\n    // fill the timestamps by proportionally splitting the interval based on the token voice lengths\n    {\n        int p0 = 0;\n        int p1 = 0;\n\n        while (true) {\n            while (p1 < n && tokens[p1].t1 < 0) {\n                p1++;\n            }\n\n            if (p1 >= n) {\n                p1--;\n            }\n\n            if (p1 > p0) {\n                double psum = 0.0;\n                for (int j = p0; j <= p1; j++) {\n                    psum += tokens[j].vlen;\n                }\n\n                //printf(\"analyzing %d - %d, psum = %f\\n\", p0, p1, psum);\n\n                const double dt = tokens[p1].t1 - tokens[p0].t0;\n\n                // split the time proportionally to the voice length\n                for (int j = p0 + 1; j <= p1; j++) {\n                    const double ct = tokens[j - 1].t0 + dt*tokens[j - 1].vlen/psum;\n\n                    tokens[j - 1].t1 = ct;\n                    tokens[j    ].t0 = ct;\n                }\n            }\n\n            p1++;\n            p0 = p1;\n            if (p1 >= n) {\n                break;\n            }\n        }\n    }\n\n    // fix up (just in case)\n    for (int j = 0; j < n - 1; j++) {\n        if (tokens[j].t1 < 0) {\n            tokens[j + 1].t0 = tokens[j].t1;\n        }\n\n        if (j > 0) {\n            if (tokens[j - 1].t1 > tokens[j].t0) {\n                tokens[j].t0 = tokens[j - 1].t1;\n                tokens[j].t1 = std::max(tokens[j].t0, tokens[j].t1);\n            }\n        }\n    }\n\n    // VAD\n    // expand or contract tokens based on voice activity\n    {\n        const int hw = WHISPER_SAMPLE_RATE/8;\n\n        for (int j = 0; j < n; j++) {\n            if (tokens[j].id >= whisper_token_eot(ctx)) {\n                continue;\n            }\n\n            int s0 = timestamp_to_sample(tokens[j].t0, n_samples);\n            int s1 = timestamp_to_sample(tokens[j].t1, n_samples);\n\n            const int ss0 = std::max(s0 - hw, 0);\n            const int ss1 = std::min(s1 + hw, n_samples);\n\n            const int ns = ss1 - ss0;\n\n            float sum = 0.0f;\n\n            for (int k = ss0; k < ss1; k++) {\n                sum += ctx->energy[k];\n            }\n\n            const float thold = 0.5*sum/ns;\n\n            {\n                int k = s0;\n                if (ctx->energy[k] > thold && j > 0) {\n                    while (k > 0 && ctx->energy[k] > thold) {\n                        k--;\n                    }\n                    tokens[j].t0 = sample_to_timestamp(k);\n                    if (tokens[j].t0 < tokens[j - 1].t1) {\n                        tokens[j].t0 = tokens[j - 1].t1;\n                    } else {\n                        s0 = k;\n                    }\n                } else {\n                    while (ctx->energy[k] < thold && k < s1) {\n                        k++;\n                    }\n                    s0 = k;\n                    tokens[j].t0 = sample_to_timestamp(k);\n                }\n            }\n\n            {\n                int k = s1;\n                if (ctx->energy[k] > thold) {\n                    while (k < n_samples - 1 && ctx->energy[k] > thold) {\n                        k++;\n                    }\n                    tokens[j].t1 = sample_to_timestamp(k);\n                    if (j < ns - 1 && tokens[j].t1 > tokens[j + 1].t0) {\n                        tokens[j].t1 = tokens[j + 1].t0;\n                    } else {\n                        s1 = k;\n                    }\n                } else {\n                    while (ctx->energy[k] < thold && k > s0) {\n                        k--;\n                    }\n                    s1 = k;\n                    tokens[j].t1 = sample_to_timestamp(k);\n                }\n            }\n        }\n    }\n\n    // fixed token expand (optional)\n    //{\n    //    const int t_expand = 0;\n\n    //    for (int j = 0; j < n; j++) {\n    //        if (j > 0) {\n    //            tokens[j].t0 = std::max(0, (int) (tokens[j].t0 - t_expand));\n    //        }\n    //        if (j < n - 1) {\n    //            tokens[j].t1 = tokens[j].t1 + t_expand;\n    //        }\n    //    }\n    //}\n\n    // debug info\n    //for (int j = 0; j < n; ++j) {\n    //    const auto & token = tokens[j];\n    //    const auto tt = token.pt > thold_pt && token.ptsum > 0.01 ? whisper_token_to_str(ctx, token.tid) : \"[?]\";\n    //    printf(\"%s: %10s %6.3f %6.3f %6.3f %6.3f %5d %5d '%s'\\n\", __func__,\n    //            tt, token.p, token.pt, token.ptsum, token.vlen, (int) token.t0, (int) token.t1, whisper_token_to_str(ctx, token.id));\n\n    //    if (tokens[j].id >= whisper_token_eot(ctx)) {\n    //        continue;\n    //    }\n    //}\n}\n"
  },
  {
    "path": "Whisper/source/whisper.h",
    "content": "#ifndef WHISPER_H\n#define WHISPER_H\n\n#include <stdint.h>\n#include <stdbool.h>\n\n#ifdef WHISPER_SHARED\n#    ifdef _WIN32\n#        ifdef WHISPER_BUILD\n#            define WHISPER_API __declspec(dllexport)\n#        else\n#            define WHISPER_API __declspec(dllimport)\n#        endif\n#    else\n#        define WHISPER_API __attribute__ ((visibility (\"default\")))\n#    endif\n#else\n#    define WHISPER_API\n#endif\n\n#define WHISPER_SAMPLE_RATE 16000\n#define WHISPER_N_FFT       400\n#define WHISPER_N_MEL       80\n#define WHISPER_HOP_LENGTH  160\n#define WHISPER_CHUNK_SIZE  30\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n    //\n    // C interface\n    //\n    // The following interface is thread-safe as long as the sample whisper_context is not used by multiple threads\n    // concurrently.\n    //\n    // Basic usage:\n    //\n    //     #include \"whisper.h\"\n    //\n    //     ...\n    //\n    //     struct whisper_context * ctx = whisper_init(\"/path/to/ggml-base.en.bin\");\n    //\n    //     if (whisper_full(ctx, wparams, pcmf32.data(), pcmf32.size()) != 0) {\n    //         fprintf(stderr, \"failed to process audio\\n\");\n    //         return 7;\n    //     }\n    //\n    //     const int n_segments = whisper_full_n_segments(ctx);\n    //     for (int i = 0; i < n_segments; ++i) {\n    //         const char * text = whisper_full_get_segment_text(ctx, i);\n    //         printf(\"%s\", text);\n    //     }\n    //\n    //     whisper_free(ctx);\n    //\n    //     ...\n    //\n    // This is a demonstration of the most straightforward usage of the library.\n    // \"pcmf32\" contains the RAW audio data in 32-bit floating point format.\n    //\n    // The interface also allows for more fine-grained control over the computation, but it requires a deeper\n    // understanding of how the model works.\n    //\n\n    struct whisper_context;\n\n    typedef int whisper_token;\n\n    typedef struct whisper_token_data {\n        whisper_token id;  // token id\n        whisper_token tid; // forced timestamp token id\n\n        float p;           // probability of the token\n        float pt;          // probability of the timestamp token\n        float ptsum;       // sum of probabilities of all timestamp tokens\n\n        // token-level timestamp data\n        // do not use if you haven't computed token-level timestamps\n        int64_t t0;        // start time of the token\n        int64_t t1;        //   end time of the token\n\n        float vlen;        // voice length of the token\n    } whisper_token_data;\n\n    // Allocates all memory needed for the model and loads the model from the given file.\n    // Returns NULL on failure.\n    WHISPER_API struct whisper_context * whisper_init(const char * path_model);\n\n    // Frees all memory allocated by the model.\n    WHISPER_API void whisper_free(struct whisper_context * ctx);\n\n    // Convert RAW PCM audio to log mel spectrogram.\n    // The resulting spectrogram is stored inside the provided whisper context.\n    // Returns 0 on success\n    WHISPER_API int whisper_pcm_to_mel(\n            struct whisper_context * ctx,\n                       const float * samples,\n                               int   n_samples,\n                               int   n_threads);\n\n    // This can be used to set a custom log mel spectrogram inside the provided whisper context.\n    // Use this instead of whisper_pcm_to_mel() if you want to provide your own log mel spectrogram.\n    // n_mel must be 80\n    // Returns 0 on success\n    WHISPER_API int whisper_set_mel(\n            struct whisper_context * ctx,\n                       const float * data,\n                               int   n_len,\n                               int   n_mel);\n\n    // Run the Whisper encoder on the log mel spectrogram stored inside the provided whisper context.\n    // Make sure to call whisper_pcm_to_mel() or whisper_set_mel() first.\n    // offset can be used to specify the offset of the first frame in the spectrogram.\n    // Returns 0 on success\n    WHISPER_API int whisper_encode(\n            struct whisper_context * ctx,\n                               int   offset,\n                               int   n_threads);\n\n    // Run the Whisper decoder to obtain the logits and probabilities for the next token.\n    // Make sure to call whisper_encode() first.\n    // tokens + n_tokens is the provided context for the decoder.\n    // n_past is the number of tokens to use from previous decoder calls.\n    // Returns 0 on success\n    WHISPER_API int whisper_decode(\n            struct whisper_context * ctx,\n               const whisper_token * tokens,\n                               int   n_tokens,\n                               int   n_past,\n                               int   n_threads);\n\n    // Token sampling methods.\n    // These are provided for convenience and can be used after each call to whisper_decode().\n    // You can also implement your own sampling method using the whisper_get_probs() function.\n    // whisper_sample_best() returns the token with the highest probability\n    // whisper_sample_timestamp() returns the most probable timestamp token\n    WHISPER_API whisper_token_data whisper_sample_best(struct whisper_context * ctx);\n    WHISPER_API whisper_token_data whisper_sample_timestamp(struct whisper_context * ctx, bool is_initial);\n\n    // Convert the provided text into tokens.\n    // The tokens pointer must be large enough to hold the resulting tokens.\n    // Returns the number of tokens on success, no more than n_max_tokens\n    // Returns -1 on failure\n    // TODO: not sure if correct\n    WHISPER_API int whisper_tokenize(\n            struct whisper_context * ctx,\n                        const char * text,\n                     whisper_token * tokens,\n\t                           int   n_max_tokens);\n\n    // Largest language id (i.e. number of available languages - 1)\n    WHISPER_API int whisper_lang_max_id();\n\n    // Return the id of the specified language, returns -1 if not found\n    // Examples:\n    //   \"de\" -> 2\n    //   \"german\" -> 2\n    WHISPER_API int whisper_lang_id(const char * lang);\n\n    // Return the short string of the specified language id (e.g. 2 -> \"de\"), returns nullptr if not found\n    WHISPER_API const char * whisper_lang_str(int id);\n\n    // Use mel data at offset_ms to try and auto-detect the spoken language\n    // Make sure to call whisper_pcm_to_mel() or whisper_set_mel() first\n    // Returns the top language id or negative on failure\n    // If not null, fills the lang_probs array with the probabilities of all languages\n    // The array must be whispe_lang_max_id() + 1 in size\n    // ref: https://github.com/openai/whisper/blob/main/whisper/decoding.py#L18-L69\n    WHISPER_API int whisper_lang_auto_detect(\n            struct whisper_context * ctx,\n                               int   offset_ms,\n                               int   n_threads,\n                             float * lang_probs);\n\n    WHISPER_API int whisper_n_len          (struct whisper_context * ctx); // mel length\n    WHISPER_API int whisper_n_vocab        (struct whisper_context * ctx);\n    WHISPER_API int whisper_n_text_ctx     (struct whisper_context * ctx);\n    WHISPER_API int whisper_is_multilingual(struct whisper_context * ctx);\n\n    // The probabilities for the next token\n    WHISPER_API float * whisper_get_probs(struct whisper_context * ctx);\n\n    // Token Id -> String. Uses the vocabulary in the provided context\n    WHISPER_API const char * whisper_token_to_str(struct whisper_context * ctx, whisper_token token);\n\n    // Special tokens\n    WHISPER_API whisper_token whisper_token_eot (struct whisper_context * ctx);\n    WHISPER_API whisper_token whisper_token_sot (struct whisper_context * ctx);\n    WHISPER_API whisper_token whisper_token_prev(struct whisper_context * ctx);\n    WHISPER_API whisper_token whisper_token_solm(struct whisper_context * ctx);\n    WHISPER_API whisper_token whisper_token_not (struct whisper_context * ctx);\n    WHISPER_API whisper_token whisper_token_beg (struct whisper_context * ctx);\n    WHISPER_API whisper_token whisper_token_lang(struct whisper_context * ctx, int lang_id);\n\n    // Task tokens\n    WHISPER_API whisper_token whisper_token_translate (void);\n    WHISPER_API whisper_token whisper_token_transcribe(void);\n\n    // Performance information\n    WHISPER_API void whisper_print_timings(struct whisper_context * ctx);\n    WHISPER_API void whisper_reset_timings(struct whisper_context * ctx);\n\n    // Print system information\n    WHISPER_API const char * whisper_print_system_info(void);\n\n    ////////////////////////////////////////////////////////////////////////////\n\n    // Available sampling strategies\n    enum whisper_sampling_strategy {\n        WHISPER_SAMPLING_GREEDY,      // Always select the most probable token\n        WHISPER_SAMPLING_BEAM_SEARCH, // TODO: not implemented yet!\n    };\n\n    // Text segment callback\n    // Called on every newly generated text segment\n    // Use the whisper_full_...() functions to obtain the text segments\n    typedef void (*whisper_new_segment_callback)(struct whisper_context * ctx, int n_new, void * user_data);\n\n    // Encoder begin callback\n    // If not NULL, called before the encoder starts\n    // If it returns false, the computation is aborted\n    typedef bool (*whisper_encoder_begin_callback)(struct whisper_context * ctx, void * user_data);\n\n    // Parameters for the whisper_full() function\n    // If you chnage the order or add new parameters, make sure to update the default values in whisper.cpp:\n    // whisper_full_default_params()\n    struct whisper_full_params {\n        enum whisper_sampling_strategy strategy;\n\n        int n_threads;\n        int n_max_text_ctx;\n        int offset_ms;          // start offset in ms\n        int duration_ms;        // audio duration to process in ms\n\n        bool translate;\n        bool no_context;\n        bool single_segment;    // force single segment output (useful for streaming)\n        bool print_special;\n        bool print_progress;\n        bool print_realtime;\n        bool print_timestamps;\n\n        // [EXPERIMENTAL] token-level timestamps\n        bool  token_timestamps; // enable token-level timestamps\n        float thold_pt;         // timestamp token probability threshold (~0.01)\n        float thold_ptsum;      // timestamp token sum probability threshold (~0.01)\n        int   max_len;          // max segment length in characters\n        int   max_tokens;       // max tokens per segment (0 = no limit)\n\n        // [EXPERIMENTAL] speed-up techniques\n        bool speed_up;          // speed-up the audio by 2x using Phase Vocoder\n        int  audio_ctx;         // overwrite the audio context size (0 = use default)\n\n        // tokens to provide the whisper model as initial prompt\n        // these are prepended to any existing text context from a previous call\n        const whisper_token * prompt_tokens;\n        int prompt_n_tokens;\n\n        // for auto-detection, set to nullptr, \"\" or \"auto\"\n        const char * language;\n\n        struct {\n            int n_past;\n        } greedy;\n\n        struct {\n            int n_past;\n            int beam_width;\n            int n_best;\n        } beam_search;\n\n        whisper_new_segment_callback new_segment_callback;\n        void * new_segment_callback_user_data;\n\n        whisper_encoder_begin_callback encoder_begin_callback;\n        void * encoder_begin_callback_user_data;\n    };\n\n    WHISPER_API struct whisper_full_params whisper_full_default_params(enum whisper_sampling_strategy strategy);\n\n    // Run the entire model: PCM -> log mel spectrogram -> encoder -> decoder -> text\n    // Uses the specified decoding strategy to obtain the text.\n    WHISPER_API int whisper_full(\n                struct whisper_context * ctx,\n            struct whisper_full_params   params,\n                           const float * samples,\n                                   int   n_samples);\n\n    // Split the input audio in chunks and process each chunk separately using whisper_full()\n    // It seems this approach can offer some speedup in some cases.\n    // However, the transcription accuracy can be worse at the beginning and end of each chunk.\n    WHISPER_API int whisper_full_parallel(\n                struct whisper_context * ctx,\n            struct whisper_full_params   params,\n                           const float * samples,\n                                   int   n_samples,\n                                   int   n_processors);\n\n    // Number of generated text segments.\n    // A segment can be a few words, a sentence, or even a paragraph.\n    WHISPER_API int whisper_full_n_segments(struct whisper_context * ctx);\n\n    // Get the start and end time of the specified segment.\n    WHISPER_API int64_t whisper_full_get_segment_t0(struct whisper_context * ctx, int i_segment);\n    WHISPER_API int64_t whisper_full_get_segment_t1(struct whisper_context * ctx, int i_segment);\n\n    // Get the text of the specified segment.\n    WHISPER_API const char * whisper_full_get_segment_text(struct whisper_context * ctx, int i_segment);\n\n    // Get number of tokens in the specified segment.\n    WHISPER_API int whisper_full_n_tokens(struct whisper_context * ctx, int i_segment);\n\n    // Get the token text of the specified token in the specified segment.\n    WHISPER_API const char * whisper_full_get_token_text(struct whisper_context * ctx, int i_segment, int i_token);\n    WHISPER_API whisper_token whisper_full_get_token_id (struct whisper_context * ctx, int i_segment, int i_token);\n\n    // Get token data for the specified token in the specified segment.\n    // This contains probabilities, timestamps, etc.\n    WHISPER_API whisper_token_data whisper_full_get_token_data(struct whisper_context * ctx, int i_segment, int i_token);\n\n    // Get the probability of the specified token in the specified segment.\n    WHISPER_API float whisper_full_get_token_p(struct whisper_context * ctx, int i_segment, int i_token);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "Whisper/source.compat/Readme.txt",
    "content": "﻿The code in this folder is dropped by the linker’s dead code elimination optimization pass, unless you change BUILD_BOTH_VERSIONS macro in stdafx.h"
  },
  {
    "path": "Whisper/source.compat/convertThings.cpp",
    "content": "#include \"stdafx.h\"\n#if BUILD_BOTH_VERSIONS\n#include \"../API/iContext.cl.h\"\n#include \"convertThings.h\"\nusing namespace Whisper;\n\nsFullParams makeNewParams( const whisper_full_params& wfp )\n{\n\tassert( nullptr == wfp.encoder_begin_callback );\n\tassert( nullptr == wfp.new_segment_callback );\n\n\tsFullParams res;\n\tmemset( &res, 0, sizeof( res ) );\n\n\tres.strategy = (eSamplingStrategy)wfp.strategy;\n\tres.cpuThreads = wfp.n_threads;\n\tres.n_max_text_ctx = wfp.n_max_text_ctx;\n\tres.offset_ms = wfp.offset_ms;\n\tres.duration_ms = wfp.duration_ms;\n\n\t// flags\n\tuint32_t flags = 0;\n\tif( wfp.translate ) flags |= (uint32_t)eFullParamsFlags::Translate;\n\tif( wfp.no_context ) flags |= (uint32_t)eFullParamsFlags::NoContext;\n\tif( wfp.single_segment ) flags |= (uint32_t)eFullParamsFlags::SingleSegment;\n\tif( wfp.print_special ) flags |= (uint32_t)eFullParamsFlags::PrintSpecial;\n\tif( wfp.print_progress ) flags |= (uint32_t)eFullParamsFlags::PrintProgress;\n\tif( wfp.print_realtime ) flags |= (uint32_t)eFullParamsFlags::PrintRealtime;\n\tif( wfp.print_timestamps ) flags |= (uint32_t)eFullParamsFlags::PrintTimestamps;\n\tif( wfp.token_timestamps ) flags |= (uint32_t)eFullParamsFlags::TokenTimestamps;\n\tif( wfp.speed_up ) flags |= (uint32_t)eFullParamsFlags::SpeedupAudio;\n\tres.flags = (eFullParamsFlags)flags;\n\n\tres.language = findLanguageKeyA( wfp.language );\n\tres.thold_pt = wfp.thold_pt;\n\tres.thold_ptsum = wfp.thold_ptsum;\n\tres.max_len = wfp.max_len;\n\tres.greedy.n_past = wfp.greedy.n_past;\n\tres.beam_search.n_past = wfp.beam_search.n_past;\n\tres.beam_search.beam_width = wfp.beam_search.beam_width;\n\tres.beam_search.n_best = wfp.beam_search.n_best;\n\tres.audio_ctx = wfp.audio_ctx;\n\tres.prompt_tokens = wfp.prompt_tokens;\n\tres.prompt_n_tokens = wfp.prompt_n_tokens;\n\n\treturn res;\n}\n\nnamespace\n{\n\tclass NewParamsTemp\n\t{\n\t\tchar language[ 5 ];\n\t\tiContext* newContext;\n\t\tpfnNewSegment newSegment;\n\t\tpfnEncoderBegin encoderBegin;\n\n\t\tstatic bool encBegin( struct whisper_context* ctx, void* user_data );\n\t\tstatic void newSeg( struct whisper_context* ctx, int n_new, void* user_data );\n\n\tpublic:\n\n\t\tvoid initialize( whisper_full_params& res, const Whisper::sFullParams& rsi, Whisper::iContext* context )\n\t\t{\n\t\t\t*(uint32_t*)( &language[ 0 ] ) = rsi.language;\n\t\t\tlanguage[ 4 ] = '\\0';\n\t\t\tres.language = language;\n\n\t\t\tnewContext = context;\n\n\t\t\tif( nullptr != rsi.encoder_begin_callback )\n\t\t\t{\n\t\t\t\tencoderBegin = rsi.encoder_begin_callback;\n\t\t\t\tres.encoder_begin_callback = &encBegin;\n\t\t\t\tres.encoder_begin_callback_user_data = rsi.encoder_begin_callback_user_data;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tencoderBegin = nullptr;\n\t\t\t\tres.encoder_begin_callback = nullptr;\n\t\t\t\tres.encoder_begin_callback_user_data = nullptr;\n\t\t\t}\n\n\t\t\tif( nullptr != rsi.new_segment_callback )\n\t\t\t{\n\t\t\t\tnewSegment = rsi.new_segment_callback;\n\t\t\t\tres.new_segment_callback = &newSeg;\n\t\t\t\tres.new_segment_callback_user_data = rsi.new_segment_callback_user_data;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tnewSegment = nullptr;\n\t\t\t\tres.new_segment_callback = nullptr;\n\t\t\t\tres.new_segment_callback_user_data = nullptr;\n\t\t\t}\n\t\t}\n\t};\n\n\tstatic thread_local NewParamsTemp npTemp;\n\n\tbool NewParamsTemp::encBegin( struct whisper_context* ctx, void* user_data )\n\t{\n\t\tconst NewParamsTemp& tmp = npTemp;\n\t\tHRESULT hr = tmp.encoderBegin( tmp.newContext, user_data );\n\t\tif( SUCCEEDED( hr ) )\n\t\t\treturn S_OK == hr;\n\t\tthrow hr;\n\t}\n\n\tvoid NewParamsTemp::newSeg( struct whisper_context* ctx, int n_new, void* user_data )\n\t{\n\t\tassert( n_new >= 0 );\n\t\tconst NewParamsTemp& tmp = npTemp;\n\t\tHRESULT hr = tmp.newSegment( tmp.newContext, (uint32_t)n_new, user_data );\n\t\tif( SUCCEEDED( hr ) )\n\t\t\treturn;\n\t\tthrow hr;\n\t}\n}\n\nwhisper_full_params makeOldParams( const Whisper::sFullParams& rsi, Whisper::iContext* context )\n{\n\twhisper_full_params res;\n\tmemset( &res, 0, sizeof( res ) );\n\n\tres.strategy = (whisper_sampling_strategy)rsi.strategy;\n\tres.n_threads = rsi.cpuThreads;\n\tres.n_max_text_ctx = rsi.n_max_text_ctx;\n\tres.offset_ms = rsi.offset_ms;\n\tres.duration_ms = rsi.duration_ms;\n\n\t// flags\n\tconst uint32_t flags = (uint32_t)rsi.flags;\n\tauto hasFlag = [ = ]( eFullParamsFlags bit ) { return 0 != ( flags & (uint32_t)bit ); };\n\n\tres.translate = hasFlag( eFullParamsFlags::Translate );\n\tres.no_context = hasFlag( eFullParamsFlags::NoContext );\n\tres.single_segment = hasFlag( eFullParamsFlags::SingleSegment );\n\tres.print_special = hasFlag( eFullParamsFlags::PrintSpecial );\n\tres.print_progress = hasFlag( eFullParamsFlags::PrintProgress );\n\tres.print_realtime = hasFlag( eFullParamsFlags::PrintRealtime );\n\tres.print_timestamps = hasFlag( eFullParamsFlags::PrintTimestamps );\n\tres.token_timestamps = hasFlag( eFullParamsFlags::TokenTimestamps );\n\tres.speed_up = hasFlag( eFullParamsFlags::SpeedupAudio );\n\n\tres.thold_pt = rsi.thold_pt;\n\tres.thold_ptsum = rsi.thold_ptsum;\n\tres.max_len = rsi.max_len;\n\tres.greedy.n_past = rsi.greedy.n_past;\n\tres.beam_search.n_past = rsi.beam_search.n_past;\n\tres.beam_search.beam_width = rsi.beam_search.beam_width;\n\tres.beam_search.n_best = rsi.beam_search.n_best;\n\tres.audio_ctx = rsi.audio_ctx;\n\tres.prompt_tokens = rsi.prompt_tokens;\n\tres.prompt_n_tokens = rsi.prompt_n_tokens;\n\n\tNewParamsTemp& tmp = npTemp;\n\ttmp.initialize( res, rsi, context );\n\treturn res;\n}\n\n#include \"../Whisper/TranscribeResult.h\"\n#include <mfapi.h>\n\nnamespace\n{\n\tinline sTimeSpan time( int64_t wt )\n\t{\n\t\tint64_t ticks = MFllMulDiv( wt, 10'000'000, 100, 0 );\n\t\treturn sTimeSpan{ (uint64_t)ticks };\n\t}\n\n\tvoid makeNewResults( whisper_context* ctx, Whisper::eResultFlags flags, TranscribeResult& res )\n\t{\n\t\tconst bool makeTokens = 0 != ( flags & eResultFlags::Tokens );\n\t\tres.segments.clear();\n\t\tres.tokens.clear();\n\n\t\tconst int countSegments = whisper_full_n_segments( ctx );\n\t\tres.segments.resize( countSegments );\n\t\tconst int tokenEot = whisper_token_eot( ctx );\n\t\tfor( int i = 0; i < countSegments; i++ )\n\t\t{\n\t\t\tsSegment& seg = res.segments[ i ];\n\t\t\tseg.text = whisper_full_get_segment_text( ctx, i );\n\t\t\tseg.time.begin = time( whisper_full_get_segment_t0( ctx, i ) );\n\t\t\tseg.time.end = time( whisper_full_get_segment_t1( ctx, i ) );\n\n\t\t\tseg.firstToken = (uint32_t)res.tokens.size();\n\t\t\tseg.countTokens = 0;\n\t\t\tif( !makeTokens )\n\t\t\t\tcontinue;\n\n\t\t\tconst int countTokens = whisper_full_n_tokens( ctx, i );\n\t\t\tseg.countTokens = countTokens;\n\t\t\tres.tokens.resize( res.tokens.size() + countTokens );\n\t\t\tfor( int t = 0; t < countTokens; t++ )\n\t\t\t{\n\t\t\t\tsToken& tok = res.tokens[ seg.firstToken + t ];\n\t\t\t\ttok.text = whisper_full_get_token_text( ctx, i, t );\n\n\t\t\t\tconst whisper_token_data src = whisper_full_get_token_data( ctx, i, t );\n\t\t\t\ttok.time.begin = time( src.t0 );\n\t\t\t\ttok.time.end = time( src.t1 );\n\t\t\t\ttok.probability = src.p;\n\t\t\t\ttok.probabilityTimestamp = src.pt;\n\t\t\t\ttok.ptsum = src.ptsum;\n\t\t\t\ttok.vlen = src.vlen;\n\t\t\t\ttok.id = src.id;\n\t\t\t\tuint32_t flags = 0;\n\t\t\t\tif( src.id >= tokenEot )\n\t\t\t\t\tflags |= eTokenFlags::Special;\n\t\t\t\ttok.flags = (eTokenFlags)flags;\n\t\t\t}\n\t\t}\n\t}\n}\n\nHRESULT makeNewResults( whisper_context* ctx, Whisper::eResultFlags flags, Whisper::iTranscribeResult** pp )\n{\n\tstatic TranscribeResultStatic trs;\n\tif( flags & eResultFlags::NewObject )\n\t{\n\t\treturn E_NOTIMPL;\n\t}\n\telse\n\t{\n\t\tmakeNewResults( ctx, flags, trs );\n\t\t*pp = &trs;\n\t\t( *pp )->AddRef();\n\t\treturn S_OK;\n\t}\n}\n#endif"
  },
  {
    "path": "Whisper/source.compat/convertThings.h",
    "content": "#pragma once\n#include \"../source/whisper.h\"\n#include \"../API/sFullParams.h\"\n#include \"../API/iTranscribeResult.cl.h\"\n\nWhisper::sFullParams makeNewParams( const whisper_full_params& rsi );\n\nwhisper_full_params makeOldParams( const Whisper::sFullParams& rsi, Whisper::iContext* context );\n\nHRESULT makeNewResults( whisper_context* ctx, Whisper::eResultFlags flags, Whisper::iTranscribeResult** pp );"
  },
  {
    "path": "Whisper/source.compat/ggmlMsvc.c",
    "content": "#include <stdint.h>\n#include <immintrin.h>\n#include <stdio.h>\n#include <assert.h>\n#include \"../source/ggml.h\"\n\n__forceinline float _cvtsh_ss( uint16_t f16 )\n{\n\t__m128i i = _mm_cvtsi32_si128( f16 );\n\t__m128 f = _mm_cvtph_ps( i );\n\treturn _mm_cvtss_f32( f );\n}\n\n__forceinline uint16_t _cvtss_sh( float f, int rounding )\n{\n\tassert( 0 == rounding );\n\t__m128 v = _mm_set_ss( f );\n\t__m128i i = _mm_cvtps_ph( v, 0 );\n\treturn (uint16_t)(uint32_t)_mm_cvtsi128_si32( i );\n}\n\nFILE* fopen_msvc( const char* filename, const char* mode )\n{\n\tFILE* stream;\n\terrno_t err = fopen_s( &stream, filename, mode );\n\tif( err == 0 )\n\t\treturn stream;\n\treturn NULL;\n}\n\n#define fopen fopen_msvc\n\n#include \"../ML/testUtilsC.h\"\n\n#define __F16C__\n#define __FMA__\n#include \"../source/ggml.c\""
  },
  {
    "path": "Whisper/stdafx.cpp",
    "content": "#include \"stdafx.h\""
  },
  {
    "path": "Whisper/stdafx.h",
    "content": "#pragma once\n#define _USE_MATH_DEFINES\n#include <stdint.h>\n#include <assert.h>\n#include <array>\n#include <vector>\n#include <algorithm>\n#include <emmintrin.h>\t// SSE 2\n#include <smmintrin.h>\t// SSE 4.1\n\n#define WIN32_LEAN_AND_MEAN\n#define NOMINMAX\n// Setup Windows SDK to only enable features available since Windows 8.0\n#include <WinSDKVer.h>\n#define _WIN32_WINNT _WIN32_WINNT_WIN8\n#define NTDDI_VERSION NTDDI_WIN8\n#include <sdkddkver.h>\n\n#include <windows.h>\n\n#define _XM_SSE4_INTRINSICS_\n#include <d3d11.h>\n#include <DirectXMath.h>\n\n#include <atlcomcli.h>\n#include \"Utils/Logger.h\"\n#include \"Utils/miscUtils.h\"\n\n// Build both legacy and DirectCompute implementations\n#define BUILD_BOTH_VERSIONS 0\n\n// Build hybrid model which uses DirectCompute only for the encode step of the algorithm, and decodes on CPU, using AVX SIMD and the Windows' built-in thread pool.\n// Disabled because on all computers I have in this house that hybrid model performed worse than D3D11 GPGPU model\n#define BUILD_HYBRID_VERSION 0\n\n// Enable debug traces. Should be disabled in production, the feature comes with a huge performance overhead.\n// When enabled, while computing things it streams gigabytes of data into that binary file.\n// See Tools / compareTraces project for a command-line app to compare these traces.\n#define SAVE_DEBUG_TRACE 0\n\n// Initialize tensors with NaN numbers, and test for NaN values in the outputs tensors\n// Incompatible with SAVE_DEBUG_TRACE macro\n// When enabled, it stalls GPU pipelining pretty often. This is not great for performance, should be disabled in production.\n#define DBG_TEST_NAN 0\n\n// In addition to collecting total GPU times per compute shader, also collect and print performance data about individual invocations of some of the most expensive shaders\n// The feature is relatively cheap in terms of performance overhead, but pretty much useless in production, and clutters debug console with all these numbers\n#define PROFILER_COLLECT_TAGS 0"
  },
  {
    "path": "Whisper/whisper.def",
    "content": "LIBRARY\nEXPORTS setupLogger\nEXPORTS loadModel\nEXPORTS initMediaFoundation\nEXPORTS findLanguageKeyW\nEXPORTS findLanguageKeyA\nEXPORTS getSupportedLanguages\nEXPORTS listGPUs"
  },
  {
    "path": "Whisper/whisperCom.cpp",
    "content": "﻿#include \"stdafx.h\"\n#include \"ML/Tensor.h\"\n#include \"API/iMediaFoundation.cl.h\"\n#include \"API/iContext.cl.h\"\n#include \"API/sFullParams.h\"\n#include \"Utils/ReadStream.h\"\n#include \"ML/testUtils.h\"\n#include \"Utils/Trace/tracing.h\"\n#include \"modelFactory.h\"\n#if BUILD_BOTH_VERSIONS\n#ifndef __AVX__\n#error Reference version requires AVX build, and AVX2 CPU\n#endif // !__AVX__\n\nnamespace\n{\n\tLPCTSTR traceFilePath = LR\"(C:\\Temp\\2remove\\Whisper\\ref.bin)\";\n\tusing ComLight::iReadStream;\n}\n\nstruct whisper_context;\nstruct ggml_tensor;\n\nclass GpuEncTest\n{\n\tDirectCompute::Tensor mel, gpuResult;\n\n\tDirectCompute::Tensor tempGpu;\n\tconst ggml_tensor* tempRef = nullptr;\npublic:\n\tGpuEncTest( const whisper_context& wctx, const int mel_offset );\n\tvoid compare( const ggml_tensor* expected ) const;\n\tvoid compareMel( const ggml_tensor* expected ) const;\n};\n\nclass GpuDecTest\n{\n\tstd::vector<float> logits, probs;\n\tconst ggml_tensor* tempRef = nullptr;\n\npublic:\n\n\tGpuDecTest( const whisper_context& wctx, const int* tokens, const int n_tokens, const int n_past );\n\n\tvoid postpone( const ggml_tensor* t );\n\tvoid comparePostponed();\n\tvoid compare( const std::vector<float>& cpuLogits, const std::vector<float>& cpuProbs ) const;\n};\n\nstatic DirectCompute::Tensor gpuEncode( const whisper_context& wctx, const int mel_offset );\n\n#include \"source/whisper.cpp\"\n#include \"API/iContext.cl.h\"\n#include \"../ComLightLib/comLightServer.h\"\n#include \"Whisper/WhisperContext.h\"\n#include \"Whisper/ModelLoader.h\"\n#include \"Whisper/WhisperModel.h\"\n#include \"source.compat/convertThings.h\"\n\nnamespace Whisper\n{\n\tinline HRESULT isZero( int i )\n\t{\n\t\treturn ( 0 == i ) ? S_OK : E_FAIL;\n\t}\n\n\tclass Context : public ComLight::ObjectRoot<iContext>,\n\t\tpublic iModel\n\t{\n\t\tvirtual HRESULT COMLIGHTCALL isMultilingual() override final\n\t\t{\n\t\t\treturn whisper_is_multilingual( &ctx ) ? S_OK : S_FALSE;\n\t\t}\n\t\tvirtual const char* COMLIGHTCALL stringFromToken( whisper_token token ) override final\n\t\t{\n\t\t\treturn whisper_token_to_str( &ctx, token );\n\t\t}\n\t\tvirtual HRESULT COMLIGHTCALL getSpecialTokens( SpecialTokens& rdi )\n\t\t{\n\t\t\trdi.TranscriptionEnd = whisper_token_eot( &ctx );\n\t\t\trdi.TranscriptionStart = whisper_token_sot( &ctx );\n\t\t\trdi.PreviousWord = whisper_token_prev( &ctx );\n\t\t\trdi.SentenceStart = whisper_token_solm( &ctx );\n\t\t\trdi.Not = whisper_token_not( &ctx );\n\t\t\trdi.TranscriptionBegin = whisper_token_beg( &ctx );\n\t\t\trdi.TaskTranslate = whisper_token_translate();\n\t\t\trdi.TaskTranscribe = whisper_token_transcribe();\n\t\t\treturn S_OK;\n\t\t}\n\t\tHRESULT COMLIGHTCALL tokenize( const char* text, pfnDecodedTokens pfn, void* pv ) override final\n\t\t{\n\t\t\tconst auto res = ::tokenize( ctx.vocab, text );\n\t\t\tif( !res.empty() )\n\t\t\t\tpfn( res.data(), res.size(), pv );\n\t\t\treturn S_OK;\n\t\t}\n\t\tHRESULT COMLIGHTCALL clone( iModel** rdi ) override final\n\t\t{\n\t\t\tlogError( u8\"Reference CPU model doesn’t support clone()\" );\n\t\t\treturn E_NOTIMPL;\n\t\t}\n\t\tHRESULT COMLIGHTCALL detectSpeaker( const sTimeInterval& time, eSpeakerChannel& result ) const override final\n\t\t{\n\t\t\tlogError( u8\"Reference CPU model doesn’t support speaker detection\" );\n\t\t\treturn E_NOTIMPL;\n\t\t}\n\n\t\t// Performance information\n\t\tvirtual HRESULT COMLIGHTCALL timingsPrint() override final\n\t\t{\n\t\t\twhisper_print_timings( &ctx );\n\t\t\treturn S_OK;\n\t\t}\n\t\tvirtual HRESULT COMLIGHTCALL timingsReset() override final\n\t\t{\n\t\t\twhisper_reset_timings( &ctx );\n\t\t\treturn S_OK;\n\t\t}\n\n\t\tvirtual HRESULT COMLIGHTCALL fullDefaultParams( eSamplingStrategy strategy, sFullParams* rdi )\n\t\t{\n\t\t\tstatic_assert( (int)eSamplingStrategy::Greedy == whisper_sampling_strategy::WHISPER_SAMPLING_GREEDY );\n\t\t\tstatic_assert( (int)eSamplingStrategy::BeamSearch == whisper_sampling_strategy::WHISPER_SAMPLING_BEAM_SEARCH );\n\t\t\tconst whisper_sampling_strategy wss = (whisper_sampling_strategy)(int)strategy;\n\t\t\twhisper_full_params wfp = whisper_full_default_params( wss );\n\n\t\t\t*rdi = makeNewParams( wfp );\n\t\t\treturn S_OK;\n\t\t}\n\n\t\tHRESULT COMLIGHTCALL runFull( const sFullParams& params, const iAudioBuffer* buffer ) override final\n\t\t{\n\t\t\twhisper_full_params wfp = makeOldParams( params, this );\n\t\t\tconst float* const samples = buffer->getPcmMono();\n\t\t\tconst uint32_t n_samples = buffer->countSamples();\n\t\t\treturn isZero( whisper_full( &ctx, wfp, samples, (int)n_samples ) );\n\t\t}\n\n\t\tHRESULT COMLIGHTCALL runStreamed( const sFullParams& params, const sProgressSink& progress, const iAudioReader* reader ) override final\n\t\t{\n\t\t\tlogError( u8\"The CPU reference implementation doesn’t support streaming\" );\n\t\t\treturn E_NOTIMPL;\n\t\t}\n\t\tHRESULT COMLIGHTCALL runCapture( const sFullParams& params, const sCaptureCallbacks& callbacks, const iAudioCapture* reader ) override final\n\t\t{\n\t\t\tlogError( u8\"The CPU reference implementation doesn’t support audio capture\" );\n\t\t\treturn E_NOTIMPL;\n\t\t}\n\n\t\tHRESULT COMLIGHTCALL getResults( eResultFlags flags, iTranscribeResult** pp ) const override final\n\t\t{\n\t\t\tmakeNewResults( &ctx, flags, pp );\n\t\t\treturn S_OK;\n\t\t}\n\n\t\tHRESULT loadImpl( iReadStream* stm );\n\n\t\tvirtual HRESULT COMLIGHTCALL createContext( iContext** pp ) override final\n\t\t{\n\t\t\tif( nullptr == pp )\n\t\t\t\treturn E_POINTER;\n\t\t\t*pp = this;\n\t\t\t( *pp )->AddRef();\n\t\t\treturn S_OK;\n\t\t}\n\n\t\tvirtual HRESULT COMLIGHTCALL getModel( iModel** pp ) override final\n\t\t{\n\t\t\tif( nullptr == pp )\n\t\t\t\treturn E_POINTER;\n\t\t\t*pp = this;\n\t\t\t( *pp )->AddRef();\n\t\t\treturn S_OK;\n\t\t}\n\n\tpublic:\n\n\t\tContext()\n\t\t{\n\t\t\tif( nullptr != traceFilePath )\n\t\t\t\tTracing::traceCreate( traceFilePath );\n\t\t}\n\n\t\tmutable whisper_context ctx;\n\n\t\tHRESULT load( iReadStream* stm );\n\n\t\t~Context()\n\t\t{\n\t\t\tTracing::traceClose();\n\n\t\t\tif( ctx.model.ctx )\n\t\t\t{\n\t\t\t\tggml_free( ctx.model.ctx );\n\t\t\t\tctx.model.ctx = nullptr;\n\t\t\t}\n\t\t\tif( ctx.model.ctx_mem )\n\t\t\t{\n\t\t\t\tggml_free( ctx.model.ctx_mem );\n\t\t\t\tctx.model.ctx_mem = nullptr;\n\t\t\t}\n\t\t\tif( ctx.buf_model )\n\t\t\t{\n\t\t\t\tdelete ctx.buf_model;\n\t\t\t\tctx.buf_model = nullptr;\n\t\t\t}\n\t\t}\n\n\t\tBEGIN_COM_MAP()\n\t\t\tCOM_INTERFACE_ENTRY( iModel );\n\t\tEND_COM_MAP()\n\t};\n\n\tinline HRESULT readBytes( iReadStream* stm, void* rdi, size_t cb )\n\t{\n\t\tif( cb > INT_MAX )\n\t\t\treturn DISP_E_OVERFLOW;\n\t\tif( cb == 0 )\n\t\t\treturn S_FALSE;\n\t\tint n;\n\t\tCHECK( stm->read( rdi, (int)cb, n ) );\n\t\tif( n != (int)cb )\n\t\t\treturn E_EOF;\n\t\treturn S_OK;\n\t}\n\n\ttemplate<typename T>\n\tinline HRESULT readStruct( iReadStream* stm, T& dest )\n\t{\n\t\treturn readBytes( stm, &dest, sizeof( T ) );\n\t}\n\ttemplate<typename E>\n\tinline HRESULT readVector( iReadStream* stm, std::vector<E>& vec )\n\t{\n\t\tconst size_t cb = sizeof( E ) * vec.size();\n\t\tif( cb > 0 )\n\t\t\treturn readBytes( stm, vec.data(), cb );\n\t\treturn S_FALSE;\n\t}\n\n\tinline HRESULT readString( iReadStream* stm, std::string& str )\n\t{\n\t\tuint32_t len;\n\t\tCHECK( readStruct( stm, len ) );\n\t\tif( len > 0 )\n\t\t{\n\t\t\tstr.resize( len );\n\t\t\treturn readBytes( stm, str.data(), len );\n\t\t}\n\t\telse\n\t\t{\n\t\t\tstr.clear();\n\t\t\treturn S_FALSE;\n\t\t}\n\t}\n\n\t// load the model from a ggml file\n\t// file format:\n\t//   - hparams\n\t//   - pre-computed mel filters\n\t//   - vocab\n\t//   - weights\n\t// see the convert-pt-to-ggml.py script for details\n\tHRESULT Context::loadImpl( iReadStream* stm )\n\t{\n\t\t// WhisperModel wm;\n\t\t// return wm.load( stm );\n\n\t\t// Copy-pasted from whisper_model_load() function\n\t\tauto& model = ctx.model;\n\t\tauto& vocab = ctx.vocab;\n\n\t\t// verify magic\n\t\t{\n\t\t\tuint32_t magic;\n\t\t\tint cbRead;\n\t\t\tCHECK( stm->read( &magic, 4, cbRead ) );\n\t\t\tif( magic != 0x67676d6c )\n\t\t\t{\n\t\t\t\tlogError( u8\"Invalid model file, bad magic\" );\n\t\t\t\treturn E_INVALIDARG;\n\t\t\t}\n\t\t}\n\n\t\t//load hparams\n\t\t{\n\t\t\tauto& hparams = model.hparams;\n\t\t\tCHECK( readStruct( stm, hparams ) );\n\t\t\tassert( hparams.n_text_state == hparams.n_audio_state );\n\n\t\t\tif( hparams.n_audio_layer == 4 )\n\t\t\t\tmodel.type = e_model::MODEL_TINY;\n\t\t\tif( hparams.n_audio_layer == 6 )\n\t\t\t\tmodel.type = e_model::MODEL_BASE;\n\t\t\tif( hparams.n_audio_layer == 12 )\n\t\t\t\tmodel.type = e_model::MODEL_SMALL;\n\t\t\tif( hparams.n_audio_layer == 24 )\n\t\t\t\tmodel.type = e_model::MODEL_MEDIUM;\n\t\t\tif( hparams.n_audio_layer == 32 )\n\t\t\t\tmodel.type = e_model::MODEL_LARGE;\n\n\t\t\tlogDebug( u8\"%s: n_vocab       = %d\", __func__, hparams.n_vocab );\n\t\t\tlogDebug( u8\"%s: n_audio_ctx   = %d\", __func__, hparams.n_audio_ctx );\n\t\t\tlogDebug( u8\"%s: n_audio_state = %d\", __func__, hparams.n_audio_state );\n\t\t\tlogDebug( u8\"%s: n_audio_head  = %d\", __func__, hparams.n_audio_head );\n\t\t\tlogDebug( u8\"%s: n_audio_layer = %d\", __func__, hparams.n_audio_layer );\n\t\t\tlogDebug( u8\"%s: n_text_ctx    = %d\", __func__, hparams.n_text_ctx );\n\t\t\tlogDebug( u8\"%s: n_text_state  = %d\", __func__, hparams.n_text_state );\n\t\t\tlogDebug( u8\"%s: n_text_head   = %d\", __func__, hparams.n_text_head );\n\t\t\tlogDebug( u8\"%s: n_text_layer  = %d\", __func__, hparams.n_text_layer );\n\t\t\tlogDebug( u8\"%s: n_mels        = %d\", __func__, hparams.n_mels );\n\t\t\tlogDebug( u8\"%s: f16           = %d\", __func__, hparams.f16 );\n\t\t\tlogDebug( u8\"%s: type          = %d\", __func__, model.type );\n\n\t\t\tctx.buf_model = new std::vector<uint8_t>();\n\t\t\tctx.buf_model->resize( MEM_REQ_MODEL.at( model.type ) );\n\t\t\tctx.buf_memory.resize( MEM_REQ_MEMORY.at( model.type ) );\n\t\t\tctx.buf_compute.resize( std::max( MEM_REQ_ENCODE.at( model.type ), MEM_REQ_DECODE.at( model.type ) ) );\n\t\t\tctx.buf_compute_layer.resize( std::max( MEM_REQ_ENCODE_LAYER.at( model.type ), MEM_REQ_DECODE_LAYER.at( model.type ) ) );\n\t\t}\n\n\t\t// load mel filters\n\t\t{\n\t\t\tauto& filters = ctx.model.filters;\n\t\t\tCHECK( readStruct( stm, filters.n_mel ) );\n\t\t\tCHECK( readStruct( stm, filters.n_fft ) );\n\t\t\tfilters.data.resize( filters.n_mel * filters.n_fft );\n\t\t\tCHECK( readVector( stm, filters.data ) );\n\t\t}\n\n\t\t// load vocab\n\t\t{\n\t\t\tint32_t n_vocab = 0;\n\t\t\tCHECK( readStruct( stm, n_vocab ) );\n\n\t\t\t//if (n_vocab != model.hparams.n_vocab) {\n\t\t\t//    fprintf(stderr, \"%s: invalid model file '%s' (bad vocab size %d != %d)\\n\",\n\t\t\t//            __func__, fname.c_str(), n_vocab, model.hparams.n_vocab);\n\t\t\t//    return false;\n\t\t\t//}\n\n\t\t\tstd::string word;\n\t\t\tfor( int i = 0; i < n_vocab; i++ )\n\t\t\t{\n\t\t\t\tCHECK( readString( stm, word ) );\n\t\t\t\tvocab.token_to_id[ word ] = i;\n\t\t\t\tvocab.id_to_token[ i ] = word;\n\t\t\t}\n\n\t\t\tvocab.n_vocab = model.hparams.n_vocab;\n\t\t\tif( vocab.is_multilingual() )\n\t\t\t{\n\t\t\t\tvocab.token_eot++;\n\t\t\t\tvocab.token_sot++;\n\t\t\t\tvocab.token_prev++;\n\t\t\t\tvocab.token_solm++;\n\t\t\t\tvocab.token_not++;\n\t\t\t\tvocab.token_beg++;\n\t\t\t}\n\n\t\t\tif( n_vocab < model.hparams.n_vocab )\n\t\t\t{\n\t\t\t\tlogDebug( u8\"%s: adding %d extra tokens\", __func__, model.hparams.n_vocab - n_vocab );\n\t\t\t\tfor( int i = n_vocab; i < model.hparams.n_vocab; i++ )\n\t\t\t\t{\n\t\t\t\t\tif( i > vocab.token_beg )\n\t\t\t\t\t\tword = \"[_TT_\" + std::to_string( i - vocab.token_beg ) + \"]\";\n\t\t\t\t\telse if( i == vocab.token_eot )\n\t\t\t\t\t\tword = \"[_EOT_]\";\n\t\t\t\t\telse if( i == vocab.token_sot )\n\t\t\t\t\t\tword = \"[_SOT_]\";\n\t\t\t\t\telse if( i == vocab.token_prev )\n\t\t\t\t\t\tword = \"[_PREV_]\";\n\t\t\t\t\telse if( i == vocab.token_not )\n\t\t\t\t\t\tword = \"[_NOT_]\";\n\t\t\t\t\telse if( i == vocab.token_beg )\n\t\t\t\t\t\tword = \"[_BEG_]\";\n\t\t\t\t\telse\n\t\t\t\t\t\tword = \"[_extra_token_\" + std::to_string( i ) + \"]\";\n\n\t\t\t\t\tvocab.token_to_id[ word ] = i;\n\t\t\t\t\tvocab.id_to_token[ i ] = word;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t{\n\t\t\t// this is the total memory required to run the inference\n\t\t\tconst size_t mem_required =\n\t\t\t\tctx.buf_model->size() +\n\t\t\t\tctx.buf_memory.size() +\n\t\t\t\tctx.buf_compute.size() +\n\t\t\t\tctx.buf_compute_layer.size();\n\t\t\tlogDebug( u8\"%s: mem_required  = %7.2f MB\", __func__, mem_required / 1024.0 / 1024.0 );\n\t\t}\n\n\t\t// for the big tensors, we have the option to store the data in 16-bit floats\n\t\t// in order to save memory and also to speed up the computation\n\t\tconst ggml_type wtype = model.hparams.f16 ? GGML_TYPE_F16 : GGML_TYPE_F32;\n\n\t\tsize_t ctx_size = 0;\n\t\tsize_t ctx_mem_size = 0;\n\n\t\t{\n\t\t\tconst auto& hparams = model.hparams;\n\n\t\t\tconst int n_vocab = hparams.n_vocab;\n\n\t\t\tconst int n_audio_ctx = hparams.n_audio_ctx;\n\t\t\tconst int n_audio_state = hparams.n_audio_state;\n\t\t\tconst int n_audio_layer = hparams.n_audio_layer;\n\n\t\t\tconst int n_text_ctx = hparams.n_text_ctx;\n\t\t\tconst int n_text_state = hparams.n_text_state;\n\t\t\tconst int n_text_layer = hparams.n_text_layer;\n\n\t\t\tconst int n_mels = hparams.n_mels;\n\n\t\t\t// encoder\n\t\t\t{\n\t\t\t\t// TODO: F16 .. maybe not?\n\t\t\t\tctx_size += n_audio_ctx * n_audio_state * ggml_type_size( GGML_TYPE_F32 ); // e_pe;\n\n\t\t\t\tctx_size += 3 * n_mels * n_audio_state * ggml_type_size( wtype );         // e_conv_1_w\n\t\t\t\tctx_size += n_audio_state * ggml_type_size( GGML_TYPE_F32 ); // e_conv_1_b\n\n\t\t\t\tctx_size += 3 * n_audio_state * n_audio_state * ggml_type_size( wtype );         // e_conv_2_w\n\t\t\t\tctx_size += n_audio_state * ggml_type_size( GGML_TYPE_F32 ); // e_conv_2_b\n\n\t\t\t\tctx_size += n_audio_state * ggml_type_size( GGML_TYPE_F32 ); // e_ln_w;\n\t\t\t\tctx_size += n_audio_state * ggml_type_size( GGML_TYPE_F32 ); // e_ln_b;\n\t\t\t}\n\n\t\t\t// decoder\n\t\t\t{\n\t\t\t\t// TODO: F16 .. maybe not?\n\t\t\t\tctx_size += n_text_ctx * n_text_state * ggml_type_size( GGML_TYPE_F32 ); // d_pe;\n\n\t\t\t\tctx_size += n_vocab * n_text_state * ggml_type_size( wtype ); // d_te;\n\n\t\t\t\tctx_size += n_text_state * ggml_type_size( GGML_TYPE_F32 ); // d_ln_w;\n\t\t\t\tctx_size += n_text_state * ggml_type_size( GGML_TYPE_F32 ); // d_ln_b;\n\t\t\t}\n\n\t\t\t// encoder layers\n\t\t\t{\n\t\t\t\tctx_size += n_audio_layer * ( n_audio_state * ggml_type_size( GGML_TYPE_F32 ) ); // mlp_ln_w\n\t\t\t\tctx_size += n_audio_layer * ( n_audio_state * ggml_type_size( GGML_TYPE_F32 ) ); // mlp_ln_b\n\n\t\t\t\tctx_size += n_audio_layer * ( 4 * n_audio_state * n_audio_state * ggml_type_size( wtype ) );         // mlp_0_w\n\t\t\t\tctx_size += n_audio_layer * ( 4 * n_audio_state * ggml_type_size( GGML_TYPE_F32 ) ); // mlp_0_b\n\n\t\t\t\tctx_size += n_audio_layer * ( 4 * n_audio_state * n_audio_state * ggml_type_size( wtype ) );         // mlp_1_w\n\t\t\t\tctx_size += n_audio_layer * ( n_audio_state * ggml_type_size( GGML_TYPE_F32 ) ); // mlp_1_b\n\n\t\t\t\tctx_size += n_audio_layer * ( n_audio_state * ggml_type_size( GGML_TYPE_F32 ) ); // attn_ln_0_w\n\t\t\t\tctx_size += n_audio_layer * ( n_audio_state * ggml_type_size( GGML_TYPE_F32 ) ); // attn_ln_0_b\n\n\t\t\t\tctx_size += n_audio_layer * ( n_audio_state * n_audio_state * ggml_type_size( wtype ) );         // attn_q_w\n\t\t\t\tctx_size += n_audio_layer * ( n_audio_state * ggml_type_size( GGML_TYPE_F32 ) ); // attn_q_b\n\n\t\t\t\tctx_size += n_audio_layer * ( n_audio_state * n_audio_state * ggml_type_size( wtype ) ); // attn_k_w\n\n\t\t\t\tctx_size += n_audio_layer * ( n_audio_state * n_audio_state * ggml_type_size( wtype ) );         // attn_v_w\n\t\t\t\tctx_size += n_audio_layer * ( n_audio_state * ggml_type_size( GGML_TYPE_F32 ) ); // attn_v_b\n\n\t\t\t\tctx_size += n_audio_layer * ( n_audio_state * n_audio_state * ggml_type_size( wtype ) );         // attn_ln_1_w\n\t\t\t\tctx_size += n_audio_layer * ( n_audio_state * ggml_type_size( GGML_TYPE_F32 ) ); // attn_ln_1_b\n\t\t\t}\n\n\t\t\t// decoder layers\n\t\t\t{\n\t\t\t\tctx_size += n_text_layer * ( n_text_state * ggml_type_size( GGML_TYPE_F32 ) ); // mlp_ln_w\n\t\t\t\tctx_size += n_text_layer * ( n_text_state * ggml_type_size( GGML_TYPE_F32 ) ); // mlp_ln_b\n\n\t\t\t\tctx_size += n_text_layer * ( 4 * n_text_state * n_text_state * ggml_type_size( wtype ) );         // mlp_0_w\n\t\t\t\tctx_size += n_text_layer * ( 4 * n_text_state * ggml_type_size( GGML_TYPE_F32 ) ); // mlp_0_b\n\n\t\t\t\tctx_size += n_text_layer * ( 4 * n_text_state * n_text_state * ggml_type_size( wtype ) );         // mlp_1_w\n\t\t\t\tctx_size += n_text_layer * ( n_text_state * ggml_type_size( GGML_TYPE_F32 ) ); // mlp_1_b\n\n\t\t\t\tctx_size += n_text_layer * ( n_text_state * ggml_type_size( GGML_TYPE_F32 ) ); // attn_ln_0_w\n\t\t\t\tctx_size += n_text_layer * ( n_text_state * ggml_type_size( GGML_TYPE_F32 ) ); // attn_ln_0_b\n\n\t\t\t\tctx_size += n_text_layer * ( n_text_state * n_text_state * ggml_type_size( wtype ) );         // attn_q_w\n\t\t\t\tctx_size += n_text_layer * ( n_text_state * ggml_type_size( GGML_TYPE_F32 ) ); // attn_q_b\n\n\t\t\t\tctx_size += n_text_layer * ( n_text_state * n_text_state * ggml_type_size( wtype ) ); // attn_k_w\n\n\t\t\t\tctx_size += n_text_layer * ( n_text_state * n_text_state * ggml_type_size( wtype ) );         // attn_v_w\n\t\t\t\tctx_size += n_text_layer * ( n_text_state * ggml_type_size( GGML_TYPE_F32 ) ); // attn_v_b\n\n\t\t\t\tctx_size += n_text_layer * ( n_text_state * n_text_state * ggml_type_size( wtype ) );         // attn_ln_1_w\n\t\t\t\tctx_size += n_text_layer * ( n_text_state * ggml_type_size( GGML_TYPE_F32 ) ); // attn_ln_1_b\n\t\t\t\t//\n\t\t\t\tctx_size += n_text_layer * ( n_text_state * ggml_type_size( GGML_TYPE_F32 ) ); // cross_attn_ln_0_w\n\t\t\t\tctx_size += n_text_layer * ( n_text_state * ggml_type_size( GGML_TYPE_F32 ) ); // cross_attn_ln_0_b\n\n\t\t\t\tctx_size += n_text_layer * ( n_text_state * n_text_state * ggml_type_size( wtype ) );         // cross_attn_q_w\n\t\t\t\tctx_size += n_text_layer * ( n_text_state * ggml_type_size( GGML_TYPE_F32 ) ); // cross_attn_q_b\n\n\t\t\t\tctx_size += n_text_layer * ( n_text_state * n_text_state * ggml_type_size( wtype ) ); // cross_attn_k_w\n\n\t\t\t\tctx_size += n_text_layer * ( n_text_state * n_text_state * ggml_type_size( wtype ) );         // cross_attn_v_w\n\t\t\t\tctx_size += n_text_layer * ( n_text_state * ggml_type_size( GGML_TYPE_F32 ) ); // cross_attn_v_b\n\n\t\t\t\tctx_size += n_text_layer * ( n_text_state * n_text_state * ggml_type_size( wtype ) );         // cross_attn_ln_1_w\n\t\t\t\tctx_size += n_text_layer * ( n_text_state * ggml_type_size( GGML_TYPE_F32 ) ); // cross_attn_ln_1_b\n\t\t\t}\n\n\t\t\tctx_mem_size += n_text_layer * n_text_ctx * n_text_state * ggml_type_size( GGML_TYPE_F16 ); // memory_k\n\t\t\tctx_mem_size += n_text_layer * n_text_ctx * n_text_state * ggml_type_size( GGML_TYPE_F16 ); // memory_v\n\n\t\t\tctx_mem_size += n_text_layer * n_audio_ctx * n_text_state * ggml_type_size( GGML_TYPE_F16 ); // memory_cross_k\n\t\t\tctx_mem_size += n_text_layer * n_audio_ctx * n_text_state * ggml_type_size( GGML_TYPE_F16 ); // memory_cross_v\n\n\t\t\tctx_size += ( 15 + 15 * n_audio_layer + 24 * n_text_layer ) * 256; // object overhead\n\n\t\t\tlogDebug( u8\"%s: ggml ctx size = %7.2f MB\", __func__, ctx_size / ( 1024.0 * 1024.0 ) );\n\t\t}\n\n\t\t// create the ggml context\n\t\t{\n\t\t\tstruct ggml_init_params params;\n\t\t\tparams.mem_size = ctx.buf_model->size();\n\t\t\tparams.mem_buffer = ctx.buf_model->data();\n\n\t\t\tmodel.ctx = ggml_init( params );\n\t\t\tif( !model.ctx )\n\t\t\t{\n\t\t\t\tlogError( u8\"%s: ggml_init() failed\", __func__ );\n\t\t\t\treturn E_INVALIDARG;\n\t\t\t}\n\t\t}\n\n\t\tstd::map<std::string, struct ggml_tensor*> tensors;\n\t\tDirectCompute::ModelLoader loader{ model.hparams.n_audio_layer, model.hparams.n_text_layer };\n\n\t\t// prepare memory for the weights\n\t\t{\n\t\t\tauto& ctx = model.ctx;\n\t\t\tconst auto& hparams = model.hparams;\n\t\t\tconst int n_vocab = hparams.n_vocab;\n\n\t\t\tconst int n_audio_ctx = hparams.n_audio_ctx;\n\t\t\tconst int n_audio_state = hparams.n_audio_state;\n\t\t\tconst int n_audio_layer = hparams.n_audio_layer;\n\n\t\t\tconst int n_text_ctx = hparams.n_text_ctx;\n\t\t\tconst int n_text_state = hparams.n_text_state;\n\t\t\tconst int n_text_layer = hparams.n_text_layer;\n\n\t\t\tconst int n_mels = hparams.n_mels;\n\n\t\t\tmodel.layers_encoder.resize( n_audio_layer );\n\t\t\tmodel.layers_decoder.resize( n_text_layer );\n\n\t\t\t// encoder\n\t\t\t{\n\t\t\t\tmodel.e_pe = ggml_new_tensor_2d( ctx, GGML_TYPE_F32, n_audio_state, n_audio_ctx );\n\t\t\t\tloader.add( model.e_pe, loader.model.enc.positionalEmbedding );\n\n\t\t\t\tmodel.e_conv_1_w = ggml_new_tensor_3d( ctx, wtype, 3, n_mels, n_audio_state );\n\t\t\t\tmodel.e_conv_1_b = ggml_new_tensor_2d( ctx, GGML_TYPE_F32, 1, n_audio_state );\n\t\t\t\tloader.add( model.e_conv_1_w, model.e_conv_1_b, loader.model.enc.conv1 );\n\n\t\t\t\tmodel.e_conv_2_w = ggml_new_tensor_3d( ctx, wtype, 3, n_audio_state, n_audio_state );\n\t\t\t\tmodel.e_conv_2_b = ggml_new_tensor_2d( ctx, GGML_TYPE_F32, 1, n_audio_state );\n\t\t\t\tloader.add( model.e_conv_2_w, model.e_conv_2_b, loader.model.enc.conv2 );\n\n\t\t\t\tmodel.e_ln_w = ggml_new_tensor_1d( ctx, GGML_TYPE_F32, n_audio_state );\n\t\t\t\tmodel.e_ln_b = ggml_new_tensor_1d( ctx, GGML_TYPE_F32, n_audio_state );\n\t\t\t\tloader.add( model.e_ln_w, model.e_ln_b, loader.model.enc.lnPost );\n\n\t\t\t\t// map by name\n\t\t\t\ttensors[ \"encoder.positional_embedding\" ] = model.e_pe;\n\n\t\t\t\ttensors[ \"encoder.conv1.weight\" ] = model.e_conv_1_w;\n\t\t\t\ttensors[ \"encoder.conv1.bias\" ] = model.e_conv_1_b;\n\n\t\t\t\ttensors[ \"encoder.conv2.weight\" ] = model.e_conv_2_w;\n\t\t\t\ttensors[ \"encoder.conv2.bias\" ] = model.e_conv_2_b;\n\n\t\t\t\ttensors[ \"encoder.ln_post.weight\" ] = model.e_ln_w;\n\t\t\t\ttensors[ \"encoder.ln_post.bias\" ] = model.e_ln_b;\n\n\t\t\t\tfor( int i = 0; i < n_audio_layer; ++i )\n\t\t\t\t{\n\t\t\t\t\tauto& layer = model.layers_encoder[ i ];\n\t\t\t\t\tauto& gpu = loader.model.enc.layers[ i ];\n\n\t\t\t\t\tlayer.mlp_ln_w = ggml_new_tensor_1d( ctx, GGML_TYPE_F32, n_audio_state );\n\t\t\t\t\tlayer.mlp_ln_b = ggml_new_tensor_1d( ctx, GGML_TYPE_F32, n_audio_state );\n\t\t\t\t\tloader.add( layer.mlp_ln_w, layer.mlp_ln_b, gpu.mlpLn );\n\n\t\t\t\t\tlayer.mlp_0_w = ggml_new_tensor_2d( ctx, wtype, n_audio_state, 4 * n_audio_state );\n\t\t\t\t\tlayer.mlp_0_b = ggml_new_tensor_1d( ctx, GGML_TYPE_F32, 4 * n_audio_state );\n\t\t\t\t\tloader.add( layer.mlp_0_w, layer.mlp_0_b, gpu.mlp0 );\n\n\t\t\t\t\tlayer.mlp_1_w = ggml_new_tensor_2d( ctx, wtype, 4 * n_audio_state, n_audio_state );\n\t\t\t\t\tlayer.mlp_1_b = ggml_new_tensor_1d( ctx, GGML_TYPE_F32, n_audio_state );\n\t\t\t\t\tloader.add( layer.mlp_1_w, layer.mlp_1_b, gpu.mlp1 );\n\n\t\t\t\t\tlayer.attn_ln_0_w = ggml_new_tensor_1d( ctx, GGML_TYPE_F32, n_audio_state );\n\t\t\t\t\tlayer.attn_ln_0_b = ggml_new_tensor_1d( ctx, GGML_TYPE_F32, n_audio_state );\n\t\t\t\t\tloader.add( layer.attn_ln_0_w, layer.attn_ln_0_b, gpu.attnLn0 );\n\n\t\t\t\t\tlayer.attn_q_w = ggml_new_tensor_2d( ctx, wtype, n_audio_state, n_audio_state );\n\t\t\t\t\tlayer.attn_q_b = ggml_new_tensor_1d( ctx, GGML_TYPE_F32, n_audio_state );\n\t\t\t\t\tloader.add( layer.attn_q_w, layer.attn_q_b, gpu.attnQuery );\n\n\t\t\t\t\tlayer.attn_k_w = ggml_new_tensor_2d( ctx, wtype, n_audio_state, n_audio_state );\n\t\t\t\t\tloader.add( layer.attn_k_w, gpu.attnKey );\n\n\t\t\t\t\tlayer.attn_v_w = ggml_new_tensor_2d( ctx, wtype, n_audio_state, n_audio_state );\n\t\t\t\t\tlayer.attn_v_b = ggml_new_tensor_1d( ctx, GGML_TYPE_F32, n_audio_state );\n\t\t\t\t\tloader.add( layer.attn_v_w, layer.attn_v_b, gpu.attnValue );\n\n\t\t\t\t\tlayer.attn_ln_1_w = ggml_new_tensor_2d( ctx, wtype, n_audio_state, n_audio_state );\n\t\t\t\t\tlayer.attn_ln_1_b = ggml_new_tensor_1d( ctx, GGML_TYPE_F32, n_audio_state );\n\t\t\t\t\tloader.add( layer.attn_ln_1_w, layer.attn_ln_1_b, gpu.attnLn1 );\n\n\t\t\t\t\t// map by name\n\t\t\t\t\ttensors[ \"encoder.blocks.\" + std::to_string( i ) + \".mlp_ln.weight\" ] = layer.mlp_ln_w;\n\t\t\t\t\ttensors[ \"encoder.blocks.\" + std::to_string( i ) + \".mlp_ln.bias\" ] = layer.mlp_ln_b;\n\n\t\t\t\t\ttensors[ \"encoder.blocks.\" + std::to_string( i ) + \".mlp.0.weight\" ] = layer.mlp_0_w;\n\t\t\t\t\ttensors[ \"encoder.blocks.\" + std::to_string( i ) + \".mlp.0.bias\" ] = layer.mlp_0_b;\n\n\t\t\t\t\ttensors[ \"encoder.blocks.\" + std::to_string( i ) + \".mlp.2.weight\" ] = layer.mlp_1_w;\n\t\t\t\t\ttensors[ \"encoder.blocks.\" + std::to_string( i ) + \".mlp.2.bias\" ] = layer.mlp_1_b;\n\n\t\t\t\t\ttensors[ \"encoder.blocks.\" + std::to_string( i ) + \".attn_ln.weight\" ] = layer.attn_ln_0_w;\n\t\t\t\t\ttensors[ \"encoder.blocks.\" + std::to_string( i ) + \".attn_ln.bias\" ] = layer.attn_ln_0_b;\n\n\t\t\t\t\ttensors[ \"encoder.blocks.\" + std::to_string( i ) + \".attn.query.weight\" ] = layer.attn_q_w;\n\t\t\t\t\ttensors[ \"encoder.blocks.\" + std::to_string( i ) + \".attn.query.bias\" ] = layer.attn_q_b;\n\n\t\t\t\t\ttensors[ \"encoder.blocks.\" + std::to_string( i ) + \".attn.key.weight\" ] = layer.attn_k_w;\n\n\t\t\t\t\ttensors[ \"encoder.blocks.\" + std::to_string( i ) + \".attn.value.weight\" ] = layer.attn_v_w;\n\t\t\t\t\ttensors[ \"encoder.blocks.\" + std::to_string( i ) + \".attn.value.bias\" ] = layer.attn_v_b;\n\n\t\t\t\t\ttensors[ \"encoder.blocks.\" + std::to_string( i ) + \".attn.out.weight\" ] = layer.attn_ln_1_w;\n\t\t\t\t\ttensors[ \"encoder.blocks.\" + std::to_string( i ) + \".attn.out.bias\" ] = layer.attn_ln_1_b;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// decoder\n\t\t\t{\n\t\t\t\tmodel.d_pe = ggml_new_tensor_2d( ctx, GGML_TYPE_F32, n_text_state, n_text_ctx );\n\t\t\t\tloader.add( model.d_pe, loader.model.dec.positionalEmbedding );\n\n\t\t\t\tmodel.d_te = ggml_new_tensor_2d( ctx, wtype, n_text_state, n_vocab );\n\t\t\t\tloader.add( model.d_te, loader.model.dec.tokenEmbedding );\n\n\t\t\t\tmodel.d_ln_w = ggml_new_tensor_1d( ctx, GGML_TYPE_F32, n_text_state );\n\t\t\t\tmodel.d_ln_b = ggml_new_tensor_1d( ctx, GGML_TYPE_F32, n_text_state );\n\t\t\t\tloader.add( model.d_ln_w, model.d_ln_b, loader.model.dec.ln );\n\n\t\t\t\t// map by name\n\t\t\t\ttensors[ \"decoder.positional_embedding\" ] = model.d_pe;\n\n\t\t\t\ttensors[ \"decoder.token_embedding.weight\" ] = model.d_te;\n\n\t\t\t\ttensors[ \"decoder.ln.weight\" ] = model.d_ln_w;\n\t\t\t\ttensors[ \"decoder.ln.bias\" ] = model.d_ln_b;\n\n\t\t\t\tfor( int i = 0; i < n_text_layer; ++i ) {\n\t\t\t\t\tauto& layer = model.layers_decoder[ i ];\n\t\t\t\t\tauto& gpu = loader.model.dec.layers[ i ];\n\n\t\t\t\t\tlayer.mlp_ln_w = ggml_new_tensor_1d( ctx, GGML_TYPE_F32, n_text_state );\n\t\t\t\t\tlayer.mlp_ln_b = ggml_new_tensor_1d( ctx, GGML_TYPE_F32, n_text_state );\n\t\t\t\t\tloader.add( layer.mlp_ln_w, layer.mlp_ln_b, gpu.mlpLn );\n\n\t\t\t\t\tlayer.mlp_0_w = ggml_new_tensor_2d( ctx, wtype, n_text_state, 4 * n_text_state );\n\t\t\t\t\tlayer.mlp_0_b = ggml_new_tensor_1d( ctx, GGML_TYPE_F32, 4 * n_text_state );\n\t\t\t\t\tloader.add( layer.mlp_0_w, layer.mlp_0_b, gpu.mlp0 );\n\n\t\t\t\t\tlayer.mlp_1_w = ggml_new_tensor_2d( ctx, wtype, 4 * n_text_state, n_text_state );\n\t\t\t\t\tlayer.mlp_1_b = ggml_new_tensor_1d( ctx, GGML_TYPE_F32, n_text_state );\n\t\t\t\t\tloader.add( layer.mlp_1_w, layer.mlp_1_b, gpu.mlp1 );\n\n\t\t\t\t\tlayer.attn_ln_0_w = ggml_new_tensor_1d( ctx, GGML_TYPE_F32, n_text_state );\n\t\t\t\t\tlayer.attn_ln_0_b = ggml_new_tensor_1d( ctx, GGML_TYPE_F32, n_text_state );\n\t\t\t\t\tloader.add( layer.attn_ln_0_w, layer.attn_ln_0_b, gpu.attnLn0 );\n\n\t\t\t\t\tlayer.attn_q_w = ggml_new_tensor_2d( ctx, wtype, n_text_state, n_text_state );\n\t\t\t\t\tlayer.attn_q_b = ggml_new_tensor_1d( ctx, GGML_TYPE_F32, n_text_state );\n\t\t\t\t\tloader.add( layer.attn_q_w, layer.attn_q_b, gpu.attnQuery );\n\n\t\t\t\t\tlayer.attn_k_w = ggml_new_tensor_2d( ctx, wtype, n_text_state, n_text_state );\n\t\t\t\t\tloader.add( layer.attn_k_w, gpu.attnKey );\n\n\t\t\t\t\tlayer.attn_v_w = ggml_new_tensor_2d( ctx, wtype, n_text_state, n_text_state );\n\t\t\t\t\tlayer.attn_v_b = ggml_new_tensor_1d( ctx, GGML_TYPE_F32, n_text_state );\n\t\t\t\t\tloader.add( layer.attn_v_w, layer.attn_v_b, gpu.attnValue );\n\n\t\t\t\t\tlayer.attn_ln_1_w = ggml_new_tensor_2d( ctx, wtype, n_text_state, n_text_state );\n\t\t\t\t\tlayer.attn_ln_1_b = ggml_new_tensor_1d( ctx, GGML_TYPE_F32, n_text_state );\n\t\t\t\t\tloader.add( layer.attn_ln_1_w, layer.attn_ln_1_b, gpu.attnLn1 );\n\n\t\t\t\t\tlayer.cross_attn_ln_0_w = ggml_new_tensor_1d( ctx, GGML_TYPE_F32, n_text_state );\n\t\t\t\t\tlayer.cross_attn_ln_0_b = ggml_new_tensor_1d( ctx, GGML_TYPE_F32, n_text_state );\n\t\t\t\t\tloader.add( layer.cross_attn_ln_0_w, layer.cross_attn_ln_0_b, gpu.crossAttnLn0 );\n\n\t\t\t\t\tlayer.cross_attn_q_w = ggml_new_tensor_2d( ctx, wtype, n_text_state, n_text_state );\n\t\t\t\t\tlayer.cross_attn_q_b = ggml_new_tensor_1d( ctx, GGML_TYPE_F32, n_text_state );\n\t\t\t\t\tloader.add( layer.cross_attn_q_w, layer.cross_attn_q_b, gpu.crossAttnQuery );\n\n\t\t\t\t\tlayer.cross_attn_k_w = ggml_new_tensor_2d( ctx, wtype, n_text_state, n_text_state );\n\t\t\t\t\tloader.add( layer.cross_attn_k_w, gpu.crossAttnKey );\n\n\t\t\t\t\tlayer.cross_attn_v_w = ggml_new_tensor_2d( ctx, wtype, n_text_state, n_text_state );\n\t\t\t\t\tlayer.cross_attn_v_b = ggml_new_tensor_1d( ctx, GGML_TYPE_F32, n_text_state );\n\t\t\t\t\tloader.add( layer.cross_attn_v_w, layer.cross_attn_v_b, gpu.crossAttnValue );\n\n\t\t\t\t\tlayer.cross_attn_ln_1_w = ggml_new_tensor_2d( ctx, wtype, n_text_state, n_text_state );\n\t\t\t\t\tlayer.cross_attn_ln_1_b = ggml_new_tensor_1d( ctx, GGML_TYPE_F32, n_text_state );\n\t\t\t\t\tloader.add( layer.cross_attn_ln_1_w, layer.cross_attn_ln_1_b, gpu.crossAttnLn1 );\n\n\t\t\t\t\t// map by name\n\t\t\t\t\ttensors[ \"decoder.blocks.\" + std::to_string( i ) + \".mlp_ln.weight\" ] = layer.mlp_ln_w;\n\t\t\t\t\ttensors[ \"decoder.blocks.\" + std::to_string( i ) + \".mlp_ln.bias\" ] = layer.mlp_ln_b;\n\n\t\t\t\t\ttensors[ \"decoder.blocks.\" + std::to_string( i ) + \".mlp.0.weight\" ] = layer.mlp_0_w;\n\t\t\t\t\ttensors[ \"decoder.blocks.\" + std::to_string( i ) + \".mlp.0.bias\" ] = layer.mlp_0_b;\n\n\t\t\t\t\ttensors[ \"decoder.blocks.\" + std::to_string( i ) + \".mlp.2.weight\" ] = layer.mlp_1_w;\n\t\t\t\t\ttensors[ \"decoder.blocks.\" + std::to_string( i ) + \".mlp.2.bias\" ] = layer.mlp_1_b;\n\n\t\t\t\t\ttensors[ \"decoder.blocks.\" + std::to_string( i ) + \".attn_ln.weight\" ] = layer.attn_ln_0_w;\n\t\t\t\t\ttensors[ \"decoder.blocks.\" + std::to_string( i ) + \".attn_ln.bias\" ] = layer.attn_ln_0_b;\n\n\t\t\t\t\ttensors[ \"decoder.blocks.\" + std::to_string( i ) + \".attn.query.weight\" ] = layer.attn_q_w;\n\t\t\t\t\ttensors[ \"decoder.blocks.\" + std::to_string( i ) + \".attn.query.bias\" ] = layer.attn_q_b;\n\n\t\t\t\t\ttensors[ \"decoder.blocks.\" + std::to_string( i ) + \".attn.key.weight\" ] = layer.attn_k_w;\n\n\t\t\t\t\ttensors[ \"decoder.blocks.\" + std::to_string( i ) + \".attn.value.weight\" ] = layer.attn_v_w;\n\t\t\t\t\ttensors[ \"decoder.blocks.\" + std::to_string( i ) + \".attn.value.bias\" ] = layer.attn_v_b;\n\n\t\t\t\t\ttensors[ \"decoder.blocks.\" + std::to_string( i ) + \".attn.out.weight\" ] = layer.attn_ln_1_w;\n\t\t\t\t\ttensors[ \"decoder.blocks.\" + std::to_string( i ) + \".attn.out.bias\" ] = layer.attn_ln_1_b;\n\n\t\t\t\t\ttensors[ \"decoder.blocks.\" + std::to_string( i ) + \".cross_attn_ln.weight\" ] = layer.cross_attn_ln_0_w;\n\t\t\t\t\ttensors[ \"decoder.blocks.\" + std::to_string( i ) + \".cross_attn_ln.bias\" ] = layer.cross_attn_ln_0_b;\n\n\t\t\t\t\ttensors[ \"decoder.blocks.\" + std::to_string( i ) + \".cross_attn.query.weight\" ] = layer.cross_attn_q_w;\n\t\t\t\t\ttensors[ \"decoder.blocks.\" + std::to_string( i ) + \".cross_attn.query.bias\" ] = layer.cross_attn_q_b;\n\n\t\t\t\t\ttensors[ \"decoder.blocks.\" + std::to_string( i ) + \".cross_attn.key.weight\" ] = layer.cross_attn_k_w;\n\n\t\t\t\t\ttensors[ \"decoder.blocks.\" + std::to_string( i ) + \".cross_attn.value.weight\" ] = layer.cross_attn_v_w;\n\t\t\t\t\ttensors[ \"decoder.blocks.\" + std::to_string( i ) + \".cross_attn.value.bias\" ] = layer.cross_attn_v_b;\n\n\t\t\t\t\ttensors[ \"decoder.blocks.\" + std::to_string( i ) + \".cross_attn.out.weight\" ] = layer.cross_attn_ln_1_w;\n\t\t\t\t\ttensors[ \"decoder.blocks.\" + std::to_string( i ) + \".cross_attn.out.bias\" ] = layer.cross_attn_ln_1_b;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// create the ggml memory context\n\t\t{\n\t\t\tstruct ggml_init_params params;\n\t\t\tparams.mem_size = ctx.buf_memory.size();\n\t\t\tparams.mem_buffer = ctx.buf_memory.data();\n\t\t\tmodel.ctx_mem = ggml_init( params );\n\t\t\tif( !model.ctx_mem )\n\t\t\t{\n\t\t\t\tlogError( u8\"%s: ggml_init() failed\", __func__ );\n\t\t\t\treturn E_INVALIDARG;\n\t\t\t}\n\t\t}\n\n\t\t// key + value memory\n\t\t{\n\t\t\tauto& ctx = model.ctx_mem;\n\n\t\t\tconst auto& hparams = model.hparams;\n\n\t\t\tconst int n_text_state = hparams.n_text_state;\n\t\t\tconst int n_text_layer = hparams.n_text_layer;\n\t\t\tconst int n_text_ctx = hparams.n_text_ctx;\n\n\t\t\t// key/value memory for the self-attention layer\n\t\t\t{\n\t\t\t\tconst int n_mem = n_text_layer * n_text_ctx;\n\t\t\t\tconst int n_elements = n_text_state * n_mem;\n\n\t\t\t\tmodel.memory_k = ggml_new_tensor_1d( ctx, GGML_TYPE_F16, n_elements );\n\t\t\t\tmodel.memory_v = ggml_new_tensor_1d( ctx, GGML_TYPE_F16, n_elements );\n\t\t\t}\n\n\t\t\t// key/value memory for the cross-attention layer\n\t\t\t{\n\t\t\t\tconst int n_audio_ctx = hparams.n_audio_ctx;\n\n\t\t\t\tconst int n_mem = n_text_layer * n_audio_ctx;\n\t\t\t\tconst int n_elements = n_text_state * n_mem;\n\n\t\t\t\tmodel.memory_cross_k = ggml_new_tensor_1d( ctx, GGML_TYPE_F16, n_elements );\n\t\t\t\tmodel.memory_cross_v = ggml_new_tensor_1d( ctx, GGML_TYPE_F16, n_elements );\n\t\t\t}\n\n\t\t\tconst size_t memory_size =\n\t\t\t\tggml_nbytes( model.memory_k ) + ggml_nbytes( model.memory_v ) +\n\t\t\t\tggml_nbytes( model.memory_cross_k ) + ggml_nbytes( model.memory_cross_v );\n\n\t\t\tlogDebug( u8\"%s: memory size   = %7.2f MB\", __func__, memory_size / 1024.0 / 1024.0 );\n\t\t}\n\n\t\t// load weights\n\t\t{\n\t\t\tsize_t total_size = 0;\n\t\t\tint n_loaded = 0;\n\t\t\tstd::string name;\n\n\t\t\twhile( true )\n\t\t\t{\n\t\t\t\tint32_t n_dims;\n\t\t\t\tint32_t length;\n\t\t\t\tint32_t ftype;\n\n\t\t\t\tHRESULT hr = readStruct( stm, n_dims );\n\t\t\t\tif( hr == E_EOF )\n\t\t\t\t\tbreak;\n\t\t\t\tCHECK( hr );\n\t\t\t\tCHECK( readStruct( stm, length ) );\n\t\t\t\tCHECK( readStruct( stm, ftype ) );\n\n\t\t\t\tint32_t nelements = 1;\n\t\t\t\tint32_t ne[ 3 ] = { 1, 1, 1 };\n\t\t\t\tfor( int i = 0; i < n_dims; ++i )\n\t\t\t\t{\n\t\t\t\t\tCHECK( readStruct( stm, ne[ i ] ) );\n\t\t\t\t\tnelements *= ne[ i ];\n\t\t\t\t}\n\n\t\t\t\tname.resize( length );\n\t\t\t\tCHECK( readBytes( stm, name.data(), length ) );\n\n\t\t\t\tif( tensors.find( name.data() ) == tensors.end() )\n\t\t\t\t{\n\t\t\t\t\tlogError( u8\"%s: unknown tensor '%s' in model file\", __func__, name.data() );\n\t\t\t\t\treturn E_INVALIDARG;\n\t\t\t\t}\n\n\t\t\t\tauto tensor = tensors[ name.data() ];\n\t\t\t\tif( ggml_nelements( tensor ) != nelements )\n\t\t\t\t{\n\t\t\t\t\tlogError( u8\"%s: tensor '%s' has wrong size in model file\", __func__, name.data() );\n\t\t\t\t\treturn E_INVALIDARG;\n\t\t\t\t}\n\n\t\t\t\tif( tensor->ne[ 0 ] != ne[ 0 ] || tensor->ne[ 1 ] != ne[ 1 ] || tensor->ne[ 2 ] != ne[ 2 ] )\n\t\t\t\t{\n\t\t\t\t\tlogError( u8\"%s: tensor '%s' has wrong shape in model file: got [%d, %d, %d], expected [%d, %d, %d]\",\n\t\t\t\t\t\t__func__, name.data(), tensor->ne[ 0 ], tensor->ne[ 1 ], tensor->ne[ 2 ], ne[ 0 ], ne[ 1 ], ne[ 2 ] );\n\t\t\t\t\treturn E_INVALIDARG;\n\t\t\t\t}\n\n\t\t\t\tconst size_t bpe = ( ftype == 0 ) ? sizeof( float ) : sizeof( ggml_fp16_t );\n\n\t\t\t\tif( nelements * bpe != ggml_nbytes( tensor ) )\n\t\t\t\t{\n\t\t\t\t\tlogError( u8\"%s: tensor '%s' has wrong size in model file: got %zu, expected %zu\",\n\t\t\t\t\t\t__func__, name.data(), ggml_nbytes( tensor ), nelements * bpe );\n\t\t\t\t\treturn E_INVALIDARG;\n\t\t\t\t}\n\n\t\t\t\tCHECK( readBytes( stm, tensor->data, ggml_nbytes( tensor ) ) );\n\n\t\t\t\t//printf(\"%48s - [%5d, %5d, %5d], type = %6s, %6.2f MB\\n\", name.data(), ne[0], ne[1], ne[2], ftype == 0 ? \"float\" : \"f16\", ggml_nbytes(tensor)/1024.0/1024.0);\n\t\t\t\ttotal_size += ggml_nbytes( tensor );\n\t\t\t\tn_loaded++;\n\t\t\t\t// loader.tryLoad( tensor );\n\t\t\t}\n\n\t\t\tlogDebug( u8\"%s: model size    = %7.2f MB\", __func__, total_size / 1024.0 / 1024.0 );\n\t\t\tif( n_loaded == 0 )\n\t\t\t{\n\t\t\t\tlogError( u8\"%s: no tensors loaded from model file\", __func__ );\n\t\t\t\treturn E_INVALIDARG;\n\t\t\t}\n\t\t\telse if( n_loaded != (int)tensors.size() )\n\t\t\t{\n\t\t\t\tlogError( u8\"%s: not all tensors loaded from model file - expected %zu, got %d\", __func__, tensors.size(), n_loaded );\n\t\t\t\treturn E_INVALIDARG;\n\t\t\t}\n\t\t\tmodel.n_loaded = n_loaded;\n\t\t}\n\n\t\treturn S_OK;\n\t}\n\n\tHRESULT Context::load( iReadStream* stm )\n\t{\n\t\tconst int64_t t_start_us = ggml_time_us();\n\t\tctx.t_start_us = t_start_us;\n\t\tHRESULT hr = loadImpl( stm );\n\t\tctx.t_load_us = ggml_time_us() - t_start_us;\n\t\treturn hr;\n\t}\n\n\tHRESULT __stdcall loadReferenceCpuModel( const wchar_t* path, iModel** pp )\n\t{\n\t\tif( nullptr == path || nullptr == pp )\n\t\t\treturn E_POINTER;\n\n\t\tComLight::Object<ReadStream> stream;\n\t\tCHECK( stream.open( path ) );\n\n\t\tggml_time_init();\n\t\tComLight::CComPtr<ComLight::Object<Context>> obj;\n\t\tCHECK( ComLight::Object<Context>::create( obj ) );\n\t\tCHECK( obj->load( &stream ) );\n\t\tobj.detach( pp );\n\t\treturn S_OK;\n\t}\n}\n\n#include \"Whisper/WhisperContext.h\"\n#include \"Whisper/ModelBuffers.h\"\n#include \"ML/testUtils.h\"\nusing namespace DirectCompute;\n\nstatic DirectCompute::Tensor gpuEncode( const whisper_context& wctx, const int mel_offset )\n{\n\treturn DirectCompute::Tensor{};\n#if 0\n\tusing namespace DirectCompute;\n\tWhisperContext& ctx = WhisperContext::current();\n\n\tTensor cur;\n\tsEncodeParams whisperParams;\n\tconst auto& mel_inp = wctx.mel;\n\t{\n\t\tconst auto& model = wctx.model;\n\t\tconst auto& hparams = model.hparams;\n\t\twhisperParams.n_len = (uint32_t)mel_inp.n_len;\n\t\twhisperParams.n_mel = (uint32_t)mel_inp.n_mel;\n\n\t\tconst int n_ctx = wctx.exp_n_audio_ctx > 0 ? wctx.exp_n_audio_ctx : wctx.model.hparams.n_audio_ctx;\n\t\tassert( n_ctx > 0 );\n\t\twhisperParams.n_ctx = (uint32_t)n_ctx;\n\n\t\tconst int n_mels = hparams.n_mels;\n\t\tassert( n_mels > 0 );\n\t\twhisperParams.n_mels = (uint32_t)n_mels;\n\n\t\tassert( mel_offset >= 0 );\n\t\twhisperParams.mel_offset = (uint32_t)mel_offset;\n\n\t\tconst int layersCount = hparams.n_audio_layer;\n\t\tassert( layersCount > 0 );\n\t\twhisperParams.layersCount = (uint32_t)layersCount;\n\n\t\tconst int n_state = hparams.n_audio_state;\n\t\tconst int n_head = hparams.n_audio_head;\n\t\tassert( n_state >= 0 );\n\t\tassert( n_head >= 0 );\n\n\t\twhisperParams.n_state = (uint32_t)n_state;\n\t\twhisperParams.n_head = (uint32_t)n_head;\n\n\t\tint n_audio_ctx = hparams.n_audio_ctx;\n\t\tassert( n_audio_ctx > 0 );\n\t\twhisperParams.n_audio_ctx = (uint32_t)n_audio_ctx;\n\n\t\tint n_text_state = hparams.n_text_state;\n\t\tassert( n_text_state > 0 );\n\t\twhisperParams.n_text_state = (uint32_t)n_text_state;\n\n\t\tint n_text_layer = hparams.n_text_layer;\n\t\tassert( n_text_layer > 0 );\n\t\twhisperParams.n_text_layer = (uint32_t)n_text_layer;\n\n\t\tint n_text_ctx = hparams.n_text_ctx;\n\t\tassert( n_text_ctx > 0 );\n\t\twhisperParams.n_text_ctx = (uint32_t)n_text_ctx;\n\t}\n\n\treturn ctx.encode( mel_inp.data, whisperParams );\n#endif\n}\n\nGpuEncTest::GpuEncTest( const whisper_context& wctx, const int mel_offset )\n{\n\treturn;\n\tgpuResult = gpuEncode( wctx, mel_offset );\n}\n\nvoid GpuEncTest::compare( const ggml_tensor* expected ) const\n{\n\treturn;\n\tWhisperContext& ctx = WhisperContext::current();\n\tctx.dbgPrintDifference( expected, gpuResult, \"GpuEncTest.compare\", false );\n}\n\nvoid GpuEncTest::compareMel( const ggml_tensor* expected ) const\n{\n\treturn;\n\tWhisperContext& ctx = WhisperContext::current();\n\tctx.dbgPrintDifference( expected, mel, \"GpuEncTest.compareMel\", false );\n}\n\n/*\nvoid GpuEncTest::comparePostponed()\n{\n\tif( nullptr == tempRef )\n\t\treturn;\n\n\tWhisperContext& ctx = WhisperContext::current();\n\tctx.dbgPrintDifference( tempRef, tempGpu, \"comparePostponed\" );\n\ttempRef = nullptr;\n} */\n\n__declspec( noinline ) GpuDecTest::GpuDecTest( const whisper_context& wctx, const int* tokens, const int n_tokens, const int n_past )\n{\n#if 1\n\treturn;\n#else\n\tsDecodeParams dp;\n\t{\n\t\tWhisperContext& ctx = WhisperContext::current();\n\t\tconst auto& model = wctx.model;\n\t\tconst auto& hparams = model.hparams;\n\t\tdp.n_state = hparams.n_text_state;\n\t\tdp.n_head = hparams.n_text_head;\n\t\tdp.n_ctx = hparams.n_text_ctx;\n\t\tdp.n_past = n_past;\n\t\tdp.M = wctx.exp_n_audio_ctx > 0 ? wctx.exp_n_audio_ctx : hparams.n_audio_ctx;\n\t\tdp.n_text_layer = hparams.n_text_layer;\n\t\tdp.n_vocab = hparams.n_vocab;\n\t}\n\n\tWhisperContext& ctx = WhisperContext::current();\n\tctx.decode( tokens, n_tokens, dp, logits, probs );\n#endif\n}\n\nvoid __declspec( noinline ) GpuDecTest::compare( const std::vector<float>& cpuLogits, const std::vector<float>& cpuProbs ) const\n{\n\treturn;\n\n\tif( cpuLogits.size() != logits.size() )\n\t{\n\t\tprintf( \"GpuDecTest.compare fail, size different\\n\" );\n\t\treturn;\n\t}\n\n\tcomputeDiff( logits.data(), cpuLogits.data(), logits.size() ).print( \"GpuDecTest.compare logits\" );\n\tcomputeDiff( probs.data(), cpuProbs.data(), probs.size() ).print( \"GpuDecTest.compare probs\" );\n}\n\nvoid __declspec( noinline ) GpuDecTest::postpone( const ggml_tensor* t )\n{\n\treturn;\n\n\tif( nullptr != tempRef )\n\t\treturn;\n\ttempRef = t;\n}\n\nvoid __declspec( noinline ) GpuDecTest::comparePostponed()\n{\n#if 1\n\treturn;\n#else\n\tif( nullptr == tempRef )\n\t\treturn;\n\tWhisperContext& ctx = WhisperContext::current();\n\tID3D11ShaderResourceView* srv = ctx.dbgDecodeTest;\n\tif( nullptr == srv )\n\t\treturn;\n\n\tctx.dbgPrintDifference( tempRef, ctx.dbgDecodeTest, \"GpuDecTest.comparePostponed\" );\n\ttempRef = nullptr;\n#endif\n}\n#else\nHRESULT __stdcall Whisper::loadReferenceCpuModel( const wchar_t* path, Whisper::iModel** pp )\n{\n\tlogError( u8\"This build of the DLL doesn’t implement the reference CPU-running Whisper model.\" );\n\treturn E_NOTIMPL;\n}\n#endif"
  },
  {
    "path": "WhisperCpp.sln",
    "content": "﻿\nMicrosoft Visual Studio Solution File, Format Version 12.00\n# Visual Studio Version 17\nVisualStudioVersion = 17.4.33122.133\nMinimumVisualStudioVersion = 10.0.40219.1\nProject(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = \"ComLightLib\", \"ComLightLib\\ComLightLib.vcxproj\", \"{52F486E7-830C-45D8-BE47-E76B5AAB2772}\"\nEndProject\nProject(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = \"Whisper\", \"Whisper\\Whisper.vcxproj\", \"{701DF8C8-E4A5-43EC-9C6B-747BBF4D8E71}\"\nEndProject\nProject(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = \"ComputeShaders\", \"ComputeShaders\\ComputeShaders.vcxproj\", \"{1C39D386-96D0-47A1-BBFA-68BBDB24439C}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"WhisperNet\", \"WhisperNet\\WhisperNet.csproj\", \"{F213558F-FEA2-4F66-A07F-69727E9EC81D}\"\n\tProjectSection(ProjectDependencies) = postProject\n\t\t{701DF8C8-E4A5-43EC-9C6B-747BBF4D8E71} = {701DF8C8-E4A5-43EC-9C6B-747BBF4D8E71}\n\tEndProjectSection\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"TranscribeCS\", \"Examples\\TranscribeCS\\TranscribeCS.csproj\", \"{0533B86C-D0E8-4190-9717-7DBD9EC8C11F}\"\nEndProject\nProject(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = \"OldMain\", \"Examples\\OldMain\\OldMain.vcxproj\", \"{596F9770-9AEB-49D3-86CA-4200197DF12B}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"CompressShaders\", \"Tools\\CompressShaders\\CompressShaders.csproj\", \"{4E85005E-D2C7-4B28-A5F2-8BC92DAF6BA2}\"\n\tProjectSection(ProjectDependencies) = postProject\n\t\t{1C39D386-96D0-47A1-BBFA-68BBDB24439C} = {1C39D386-96D0-47A1-BBFA-68BBDB24439C}\n\tEndProjectSection\nEndProject\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"Tools\", \"Tools\", \"{90D16EBB-08A4-4C9B-9991-B1B2E036838C}\"\nEndProject\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"Examples\", \"Examples\", \"{B988C132-115D-4157-99FE-0D891CE45A82}\"\nEndProject\nProject(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = \"main\", \"Examples\\main\\main.vcxproj\", \"{4CCA7042-EB15-4F7A-B77B-5CAFD2DF47B2}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"MicrophoneCS\", \"Examples\\MicrophoneCS\\MicrophoneCS.csproj\", \"{A49305C0-7022-45A6-89B4-4BD33138C98A}\"\n\tProjectSection(ProjectDependencies) = postProject\n\t\t{701DF8C8-E4A5-43EC-9C6B-747BBF4D8E71} = {701DF8C8-E4A5-43EC-9C6B-747BBF4D8E71}\n\tEndProjectSection\nEndProject\nProject(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = \"compareTraces\", \"Tools\\compareTraces\\compareTraces.vcxproj\", \"{8478A77C-D851-4C63-9511-1770CC82D33E}\"\nEndProject\nProject(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = \"WhisperDesktop\", \"Examples\\WhisperDesktop\\WhisperDesktop.vcxproj\", \"{CD9E49F0-75A3-4F91-AC71-336109EE39C6}\"\nEndProject\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"Solution Items\", \"Solution Items\", \"{17835CA3-D7F6-4BEF-9471-12C015764A2C}\"\n\tProjectSection(SolutionItems) = preProject\n\t\tReadme.md = Readme.md\n\tEndProjectSection\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"PerfSummary\", \"Tools\\PerfSummary\\PerfSummary.csproj\", \"{8AC301F0-FEC9-4F26-83DD-DB32969CD510}\"\nEndProject\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"WhisperPS\", \"WhisperPS\\WhisperPS.csproj\", \"{D6D188BB-237E-43F0-AFE3-8947FFD88FC7}\"\n\tProjectSection(ProjectDependencies) = postProject\n\t\t{701DF8C8-E4A5-43EC-9C6B-747BBF4D8E71} = {701DF8C8-E4A5-43EC-9C6B-747BBF4D8E71}\n\tEndProjectSection\nEndProject\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"CompressTables\", \"Tools\\CompressTables\\CompressTables.csproj\", \"{61CA4055-77F4-47DA-933E-175FEC28C6FA}\"\nEndProject\nGlobal\n\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n\t\tDebug|x64 = Debug|x64\n\t\tRelease|x64 = Release|x64\n\tEndGlobalSection\n\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n\t\t{52F486E7-830C-45D8-BE47-E76B5AAB2772}.Debug|x64.ActiveCfg = Debug|x64\n\t\t{52F486E7-830C-45D8-BE47-E76B5AAB2772}.Debug|x64.Build.0 = Debug|x64\n\t\t{52F486E7-830C-45D8-BE47-E76B5AAB2772}.Release|x64.ActiveCfg = Release|x64\n\t\t{52F486E7-830C-45D8-BE47-E76B5AAB2772}.Release|x64.Build.0 = Release|x64\n\t\t{701DF8C8-E4A5-43EC-9C6B-747BBF4D8E71}.Debug|x64.ActiveCfg = Debug|x64\n\t\t{701DF8C8-E4A5-43EC-9C6B-747BBF4D8E71}.Debug|x64.Build.0 = Debug|x64\n\t\t{701DF8C8-E4A5-43EC-9C6B-747BBF4D8E71}.Release|x64.ActiveCfg = Release|x64\n\t\t{701DF8C8-E4A5-43EC-9C6B-747BBF4D8E71}.Release|x64.Build.0 = Release|x64\n\t\t{1C39D386-96D0-47A1-BBFA-68BBDB24439C}.Debug|x64.ActiveCfg = Debug|x64\n\t\t{1C39D386-96D0-47A1-BBFA-68BBDB24439C}.Debug|x64.Build.0 = Debug|x64\n\t\t{1C39D386-96D0-47A1-BBFA-68BBDB24439C}.Release|x64.ActiveCfg = Release|x64\n\t\t{1C39D386-96D0-47A1-BBFA-68BBDB24439C}.Release|x64.Build.0 = Release|x64\n\t\t{F213558F-FEA2-4F66-A07F-69727E9EC81D}.Debug|x64.ActiveCfg = Debug|Any CPU\n\t\t{F213558F-FEA2-4F66-A07F-69727E9EC81D}.Debug|x64.Build.0 = Debug|Any CPU\n\t\t{F213558F-FEA2-4F66-A07F-69727E9EC81D}.Release|x64.ActiveCfg = Release|Any CPU\n\t\t{F213558F-FEA2-4F66-A07F-69727E9EC81D}.Release|x64.Build.0 = Release|Any CPU\n\t\t{0533B86C-D0E8-4190-9717-7DBD9EC8C11F}.Debug|x64.ActiveCfg = Debug|x64\n\t\t{0533B86C-D0E8-4190-9717-7DBD9EC8C11F}.Debug|x64.Build.0 = Debug|x64\n\t\t{0533B86C-D0E8-4190-9717-7DBD9EC8C11F}.Release|x64.ActiveCfg = Release|x64\n\t\t{0533B86C-D0E8-4190-9717-7DBD9EC8C11F}.Release|x64.Build.0 = Release|x64\n\t\t{596F9770-9AEB-49D3-86CA-4200197DF12B}.Debug|x64.ActiveCfg = Debug|x64\n\t\t{596F9770-9AEB-49D3-86CA-4200197DF12B}.Debug|x64.Build.0 = Debug|x64\n\t\t{596F9770-9AEB-49D3-86CA-4200197DF12B}.Release|x64.ActiveCfg = Release|x64\n\t\t{596F9770-9AEB-49D3-86CA-4200197DF12B}.Release|x64.Build.0 = Release|x64\n\t\t{4E85005E-D2C7-4B28-A5F2-8BC92DAF6BA2}.Debug|x64.ActiveCfg = Debug|Any CPU\n\t\t{4E85005E-D2C7-4B28-A5F2-8BC92DAF6BA2}.Debug|x64.Build.0 = Debug|Any CPU\n\t\t{4E85005E-D2C7-4B28-A5F2-8BC92DAF6BA2}.Release|x64.ActiveCfg = Release|Any CPU\n\t\t{4E85005E-D2C7-4B28-A5F2-8BC92DAF6BA2}.Release|x64.Build.0 = Release|Any CPU\n\t\t{4CCA7042-EB15-4F7A-B77B-5CAFD2DF47B2}.Debug|x64.ActiveCfg = Debug|x64\n\t\t{4CCA7042-EB15-4F7A-B77B-5CAFD2DF47B2}.Debug|x64.Build.0 = Debug|x64\n\t\t{4CCA7042-EB15-4F7A-B77B-5CAFD2DF47B2}.Release|x64.ActiveCfg = Release|x64\n\t\t{4CCA7042-EB15-4F7A-B77B-5CAFD2DF47B2}.Release|x64.Build.0 = Release|x64\n\t\t{A49305C0-7022-45A6-89B4-4BD33138C98A}.Debug|x64.ActiveCfg = Debug|x64\n\t\t{A49305C0-7022-45A6-89B4-4BD33138C98A}.Debug|x64.Build.0 = Debug|x64\n\t\t{A49305C0-7022-45A6-89B4-4BD33138C98A}.Release|x64.ActiveCfg = Release|x64\n\t\t{A49305C0-7022-45A6-89B4-4BD33138C98A}.Release|x64.Build.0 = Release|x64\n\t\t{8478A77C-D851-4C63-9511-1770CC82D33E}.Debug|x64.ActiveCfg = Debug|x64\n\t\t{8478A77C-D851-4C63-9511-1770CC82D33E}.Debug|x64.Build.0 = Debug|x64\n\t\t{8478A77C-D851-4C63-9511-1770CC82D33E}.Release|x64.ActiveCfg = Release|x64\n\t\t{8478A77C-D851-4C63-9511-1770CC82D33E}.Release|x64.Build.0 = Release|x64\n\t\t{CD9E49F0-75A3-4F91-AC71-336109EE39C6}.Debug|x64.ActiveCfg = Debug|x64\n\t\t{CD9E49F0-75A3-4F91-AC71-336109EE39C6}.Debug|x64.Build.0 = Debug|x64\n\t\t{CD9E49F0-75A3-4F91-AC71-336109EE39C6}.Release|x64.ActiveCfg = Release|x64\n\t\t{CD9E49F0-75A3-4F91-AC71-336109EE39C6}.Release|x64.Build.0 = Release|x64\n\t\t{8AC301F0-FEC9-4F26-83DD-DB32969CD510}.Debug|x64.ActiveCfg = Debug|Any CPU\n\t\t{8AC301F0-FEC9-4F26-83DD-DB32969CD510}.Debug|x64.Build.0 = Debug|Any CPU\n\t\t{8AC301F0-FEC9-4F26-83DD-DB32969CD510}.Release|x64.ActiveCfg = Release|Any CPU\n\t\t{8AC301F0-FEC9-4F26-83DD-DB32969CD510}.Release|x64.Build.0 = Release|Any CPU\n\t\t{D6D188BB-237E-43F0-AFE3-8947FFD88FC7}.Debug|x64.ActiveCfg = Debug|Any CPU\n\t\t{D6D188BB-237E-43F0-AFE3-8947FFD88FC7}.Debug|x64.Build.0 = Debug|Any CPU\n\t\t{D6D188BB-237E-43F0-AFE3-8947FFD88FC7}.Release|x64.ActiveCfg = Release|Any CPU\n\t\t{D6D188BB-237E-43F0-AFE3-8947FFD88FC7}.Release|x64.Build.0 = Release|Any CPU\n\t\t{61CA4055-77F4-47DA-933E-175FEC28C6FA}.Debug|x64.ActiveCfg = Debug|Any CPU\n\t\t{61CA4055-77F4-47DA-933E-175FEC28C6FA}.Debug|x64.Build.0 = Debug|Any CPU\n\t\t{61CA4055-77F4-47DA-933E-175FEC28C6FA}.Release|x64.ActiveCfg = Release|Any CPU\n\t\t{61CA4055-77F4-47DA-933E-175FEC28C6FA}.Release|x64.Build.0 = Release|Any CPU\n\tEndGlobalSection\n\tGlobalSection(SolutionProperties) = preSolution\n\t\tHideSolutionNode = FALSE\n\tEndGlobalSection\n\tGlobalSection(NestedProjects) = preSolution\n\t\t{0533B86C-D0E8-4190-9717-7DBD9EC8C11F} = {B988C132-115D-4157-99FE-0D891CE45A82}\n\t\t{596F9770-9AEB-49D3-86CA-4200197DF12B} = {B988C132-115D-4157-99FE-0D891CE45A82}\n\t\t{4E85005E-D2C7-4B28-A5F2-8BC92DAF6BA2} = {90D16EBB-08A4-4C9B-9991-B1B2E036838C}\n\t\t{4CCA7042-EB15-4F7A-B77B-5CAFD2DF47B2} = {B988C132-115D-4157-99FE-0D891CE45A82}\n\t\t{A49305C0-7022-45A6-89B4-4BD33138C98A} = {B988C132-115D-4157-99FE-0D891CE45A82}\n\t\t{8478A77C-D851-4C63-9511-1770CC82D33E} = {90D16EBB-08A4-4C9B-9991-B1B2E036838C}\n\t\t{CD9E49F0-75A3-4F91-AC71-336109EE39C6} = {B988C132-115D-4157-99FE-0D891CE45A82}\n\t\t{8AC301F0-FEC9-4F26-83DD-DB32969CD510} = {90D16EBB-08A4-4C9B-9991-B1B2E036838C}\n\t\t{61CA4055-77F4-47DA-933E-175FEC28C6FA} = {90D16EBB-08A4-4C9B-9991-B1B2E036838C}\n\tEndGlobalSection\n\tGlobalSection(ExtensibilityGlobals) = postSolution\n\t\tSolutionGuid = {07D5F1CF-1FAD-4F40-806A-B148CD609961}\n\tEndGlobalSection\nEndGlobal\n"
  },
  {
    "path": "WhisperNet/API/CaptureDeviceId.cs",
    "content": "﻿using System;\nusing Whisper.Internal;\n\nnamespace Whisper\n{\n\t/// <summary>Identifiers for an audio capture device</summary>\n\tpublic struct CaptureDeviceId\n\t{\n\t\t/// <summary>The display name is suitable for showing to the user, but might not be unique.</summary>\n\t\tpublic string displayName;\n\n\t\t/// <summary>Endpoint ID for an audio capture device.<br/>\n\t\t/// It uniquely identifies the device on the system, but is not a readable string.</summary>\n\t\tpublic string endpoint;\n\n\t\tinternal CaptureDeviceId( in sCaptureDevice rsi )\n\t\t{\n\t\t\tdisplayName = rsi.displayName ?? \"<display name unavailable>\";\n\t\t\tendpoint = rsi.endpoint ?? throw new ApplicationException( \"The device has no endpoint ID\" );\n\t\t}\n\n\t\t/// <summary>Returns a String which represents the object instance</summary>\n\t\tpublic override string ToString() => $\"Capture device: \\\"{displayName}\\\"\";\n\t}\n}"
  },
  {
    "path": "WhisperNet/API/Parameters.cs",
    "content": "﻿// Missing XML comment for publicly visible type or member\n// TODO: remove this line and document them.\n#pragma warning disable CS1591\n\nusing System;\n\nnamespace Whisper\n{\n\t/// <summary>Available sampling strategies</summary>\n\tpublic enum eSamplingStrategy: int\n\t{\n\t\t/// <summary>Always select the most probable token</summary>\n\t\tGreedy,\n\t\t/// <summary>TODO: not implemented yet!</summary>\n\t\tBeamSearch,\n\t};\n\n\t[Flags]\n\tpublic enum eFullParamsFlags: uint\n\t{\n\t\tNone = 0,\n\t\tTranslate = 1,\n\t\tNoContext = 2,\n\t\tSingleSegment = 4,\n\t\tPrintSpecial = 8,\n\t\tPrintProgress = 0x10,\n\t\tPrintRealtime = 0x20,\n\t\tPrintTimestamps = 0x40,\n\n\t\t// Experimental\n\t\tTokenTimestamps = 0x100,\n\t\tSpeedupAudio = 0x200,\n\t};\n\n\t/// <summary>Transcribe parameters</summary>\n\tpublic struct Parameters\n\t{\n\t\t/// <summary>Sampling strategy</summary>\n\t\tpublic eSamplingStrategy strategy;\n\n\t\t/// <summary>Count of CPU worker threads to use</summary>\n\t\t/// <remarks>So far, the GPU model only uses CPU threads for MEL spectrograms</remarks>\n\t\tpublic int cpuThreads;\n\n\t\tpublic int n_max_text_ctx;\n\t\t/// <summary>start offset in ms</summary>\n\t\tpublic int offset_ms;\n\t\t/// <summary>audio duration to process in ms</summary>\n\t\tpublic int duration_ms;\n\t\tpublic eFullParamsFlags flags;\n\n\t\t/// <summary>Set or clear the specified flag in the <see cref=\"flags\" /> field of this structure</summary>\n\t\tpublic void setFlag( eFullParamsFlags flag, bool set )\n\t\t{\n\t\t\tif( flag != eFullParamsFlags.None )\n\t\t\t{\n\t\t\t\tif( set )\n\t\t\t\t\tflags |= flag;\n\t\t\t\telse\n\t\t\t\t\tflags &= ~flag;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthrow new ArgumentException();\n\t\t}\n\n\t\t/// <summary>Language</summary>\n\t\tpublic eLanguage language;\n\n\t\t// [EXPERIMENTAL] token-level timestamps\n\t\t/// <summary>timestamp token probability threshold (~0.01)</summary>\n\t\tpublic float thold_pt;\n\t\t/// <summary>timestamp token sum probability threshold (~0.01)</summary>\n\t\tpublic float thold_ptsum;\n\t\t/// <summary>max segment length in characters</summary>\n\t\tpublic int max_len;\n\t\t/// <summary>max tokens per segment (0 = no limit)</summary>\n\t\tpublic int max_tokens;\n\n\t\tpublic struct sGreedy\n\t\t{\n\t\t\tpublic int n_past;\n\t\t}\n\t\tpublic sGreedy greedy;\n\n\t\tpublic struct sBeamSearch\n\t\t{\n\t\t\tpublic int n_past;\n\t\t\tpublic int beam_width;\n\t\t\tpublic int n_best;\n\t\t}\n\t\tpublic sBeamSearch beamSearch;\n\n\t\t// [EXPERIMENTAL] speed-up techniques\n\t\t/// <summary>overwrite the audio context size (0 = use default)</summary>\n\t\tpublic int audioContextSize;\n\t}\n}"
  },
  {
    "path": "WhisperNet/API/SpecialTokens.cs",
    "content": "﻿namespace Whisper\n{\n\t/// <summary>Special tokens defined in the model</summary>\n\tpublic readonly struct SpecialTokens\n\t{\n\t\t/// <summary>The end of a transcription</summary>\n\t\tpublic readonly int TranscriptionEnd; // token_eot\n\t\t/// <summary>Start of a transcription</summary>\n\t\tpublic readonly int TranscriptionStart;   // token_sot\n\t\t/// <summary>Represents the previous word in the transcription. It is used to help the model predict the current word based on the context of the words that came before it.</summary>\n\t\tpublic readonly int PreviousWord;   // token_prev\n\t\t/// <summary>Start of a sentence</summary>\n\t\tpublic readonly int SentenceStart;   // token_solm\n\t\t/// <summary>Represents the word \"not\" in the transcription</summary>\n\t\tpublic readonly int Not;    // token_not\n\t\t/// <summary>New transcription</summary>\n\t\tpublic readonly int TranscriptionBegin;    // token_beg\n\t\t/// <summary>token_translate</summary>\n\t\tpublic readonly int TaskTranslate;\n\t\t/// <summary>token_transcribe</summary>\n\t\tpublic readonly int TaskTranscribe;\n\t}\n}"
  },
  {
    "path": "WhisperNet/API/eCaptureStatus.cs",
    "content": "﻿using System;\n\nnamespace Whisper\n{\n\t/// <summary>Status of the voice capture</summary>\n\t[Flags]\n\tpublic enum eCaptureStatus: byte\n\t{\n\t\t/// <summary>Doing nothing</summary>\n\t\tNone = 0,\n\t\t/// <summary>Capturing the audio</summary>\n\t\tListening = 1,\n\t\t/// <summary>A voice is detected in the captured audio, recording</summary>\n\t\tVoice = 2,\n\t\t/// <summary>Transcribing a recorded piece of the audio</summary>\n\t\tTranscribing = 4,\n\t\t/// <summary>The computer is unable to transcribe the audio quickly enough,<br/>\n\t\t/// and the capture is dropping the incoming audio samples.</summary>\n\t\tStalled = 0x80,\n\t}\n}"
  },
  {
    "path": "WhisperNet/API/eGpuModelFlags.cs",
    "content": "﻿using System;\n\nnamespace Whisper\n{\n\t/// <summary>\n\t/// <para type=\"synopsis\">These flags affect compute shaders performance (which ones are faster depends on GPU model),<br/>\n\t/// and VRAM memory usage (UseReshapedMatMul needs slightly more VRAM).</para>\n\t/// </summary>\n\t[Flags]\n\tpublic enum eGpuModelFlags: uint\n\t{\n\t\t/// <summary>Equivalent to <c>Wave32 | NoReshapedMatMul</c> on Intel and nVidia GPUs,<br/>\n\t\t/// and <c>Wave64 | UseReshapedMatMul</c> on AMD GPUs</summary>\n\t\tNone = 0,\n\n\t\t/// <summary>Use Wave32 version of compute shaders even on AMD GPUs</summary>\n\t\t/// <remarks>Incompatible with <see cref=\"Wave64\" /></remarks>\n\t\tWave32 = 1,\n\n\t\t/// <summary>Use Wave64 version of compute shaders even on nVidia and Intel GPUs</summary>\n\t\t/// <remarks>Incompatible with <see cref=\"Wave32\" /></remarks>\n\t\tWave64 = 2,\n\n\t\t/// <summary>Do not use reshaped matrix multiplication shaders on AMD GPUs</summary>\n\t\t/// <remarks>Incompatible with <see cref=\"UseReshapedMatMul\" /></remarks>\n\t\tNoReshapedMatMul = 4,\n\n\t\t/// <summary>Use reshaped matrix multiplication shaders even on nVidia and Intel GPUs</summary>\n\t\t/// <remarks>Incompatible with <see cref=\"NoReshapedMatMul\" /></remarks>\n\t\tUseReshapedMatMul = 8,\n\n\t\t/// <summary>Create GPU tensors in a way which allows sharing across D3D devices</summary>\n\t\tCloneable = 0x10,\n\t}\n}"
  },
  {
    "path": "WhisperNet/API/eLanguage.cs",
    "content": "// This file is generated by a tool, from the `languageCodez.tsv` file in this repository\nnamespace Whisper\n{\n\t/// <summary>Supported languages</summary>\n\t/// <remarks>The values of this enum are zero-padded ASCII strings.<br/>\n\t/// It seems OpenAI tried to implement ISO 639-1, but they used the version of the standard from 1988.</remarks>\n\tpublic enum eLanguage: uint\n\t{\n\t\t/// <summary>Afrikaans</summary>\n\t\tAfrikaans = 0x6661,  // \"af\"\n\t\t/// <summary>Albanian</summary>\n\t\tAlbanian = 0x7173,  // \"sq\"\n\t\t/// <summary>Amharic</summary>\n\t\tAmharic = 0x6D61,  // \"am\"\n\t\t/// <summary>Arabic</summary>\n\t\tArabic = 0x7261,  // \"ar\"\n\t\t/// <summary>Armenian</summary>\n\t\tArmenian = 0x7968,  // \"hy\"\n\t\t/// <summary>Assamese</summary>\n\t\tAssamese = 0x7361,  // \"as\"\n\t\t/// <summary>Azerbaijani</summary>\n\t\tAzerbaijani = 0x7A61,  // \"az\"\n\t\t/// <summary>Bashkir</summary>\n\t\tBashkir = 0x6162,  // \"ba\"\n\t\t/// <summary>Basque</summary>\n\t\tBasque = 0x7565,  // \"eu\"\n\t\t/// <summary>Belarusian</summary>\n\t\tBelarusian = 0x6562,  // \"be\"\n\t\t/// <summary>Bengali</summary>\n\t\tBengali = 0x6E62,  // \"bn\"\n\t\t/// <summary>Bosnian</summary>\n\t\tBosnian = 0x7362,  // \"bs\"\n\t\t/// <summary>Breton</summary>\n\t\tBreton = 0x7262,  // \"br\"\n\t\t/// <summary>Bulgarian</summary>\n\t\tBulgarian = 0x6762,  // \"bg\"\n\t\t/// <summary>Catalan</summary>\n\t\tCatalan = 0x6163,  // \"ca\"\n\t\t/// <summary>Chinese</summary>\n\t\tChinese = 0x687A,  // \"zh\"\n\t\t/// <summary>Croatian</summary>\n\t\tCroatian = 0x7268,  // \"hr\"\n\t\t/// <summary>Czech</summary>\n\t\tCzech = 0x7363,  // \"cs\"\n\t\t/// <summary>Danish</summary>\n\t\tDanish = 0x6164,  // \"da\"\n\t\t/// <summary>Dutch</summary>\n\t\tDutch = 0x6C6E,  // \"nl\"\n\t\t/// <summary>English</summary>\n\t\tEnglish = 0x6E65,  // \"en\"\n\t\t/// <summary>Estonian</summary>\n\t\tEstonian = 0x7465,  // \"et\"\n\t\t/// <summary>Faroese</summary>\n\t\tFaroese = 0x6F66,  // \"fo\"\n\t\t/// <summary>Finnish</summary>\n\t\tFinnish = 0x6966,  // \"fi\"\n\t\t/// <summary>French</summary>\n\t\tFrench = 0x7266,  // \"fr\"\n\t\t/// <summary>Galician</summary>\n\t\tGalician = 0x6C67,  // \"gl\"\n\t\t/// <summary>Georgian</summary>\n\t\tGeorgian = 0x616B,  // \"ka\"\n\t\t/// <summary>German</summary>\n\t\tGerman = 0x6564,  // \"de\"\n\t\t/// <summary>Greek</summary>\n\t\tGreek = 0x6C65,  // \"el\"\n\t\t/// <summary>Gujarati</summary>\n\t\tGujarati = 0x7567,  // \"gu\"\n\t\t/// <summary>Haitian Creole</summary>\n\t\tHaitianCreole = 0x7468,  // \"ht\"\n\t\t/// <summary>Hausa</summary>\n\t\tHausa = 0x6168,  // \"ha\"\n\t\t/// <summary>Hawaiian</summary>\n\t\tHawaiian = 0x776168,  // \"haw\"\n\t\t/// <summary>Hebrew</summary>\n\t\tHebrew = 0x7769,  // \"iw\"\n\t\t/// <summary>Hindi</summary>\n\t\tHindi = 0x6968,  // \"hi\"\n\t\t/// <summary>Hungarian</summary>\n\t\tHungarian = 0x7568,  // \"hu\"\n\t\t/// <summary>Icelandic</summary>\n\t\tIcelandic = 0x7369,  // \"is\"\n\t\t/// <summary>Indonesian</summary>\n\t\tIndonesian = 0x6469,  // \"id\"\n\t\t/// <summary>Italian</summary>\n\t\tItalian = 0x7469,  // \"it\"\n\t\t/// <summary>Japanese</summary>\n\t\tJapanese = 0x616A,  // \"ja\"\n\t\t/// <summary>Javanese</summary>\n\t\tJavanese = 0x776A,  // \"jw\"\n\t\t/// <summary>Kannada</summary>\n\t\tKannada = 0x6E6B,  // \"kn\"\n\t\t/// <summary>Kazakh</summary>\n\t\tKazakh = 0x6B6B,  // \"kk\"\n\t\t/// <summary>Khmer</summary>\n\t\tKhmer = 0x6D6B,  // \"km\"\n\t\t/// <summary>Korean</summary>\n\t\tKorean = 0x6F6B,  // \"ko\"\n\t\t/// <summary>Lao</summary>\n\t\tLao = 0x6F6C,  // \"lo\"\n\t\t/// <summary>Latin</summary>\n\t\tLatin = 0x616C,  // \"la\"\n\t\t/// <summary>Latvian</summary>\n\t\tLatvian = 0x766C,  // \"lv\"\n\t\t/// <summary>Lingala</summary>\n\t\tLingala = 0x6E6C,  // \"ln\"\n\t\t/// <summary>Lithuanian</summary>\n\t\tLithuanian = 0x746C,  // \"lt\"\n\t\t/// <summary>Luxembourgish</summary>\n\t\tLuxembourgish = 0x626C,  // \"lb\"\n\t\t/// <summary>Macedonian</summary>\n\t\tMacedonian = 0x6B6D,  // \"mk\"\n\t\t/// <summary>Malagasy</summary>\n\t\tMalagasy = 0x676D,  // \"mg\"\n\t\t/// <summary>Malay</summary>\n\t\tMalay = 0x736D,  // \"ms\"\n\t\t/// <summary>Malayalam</summary>\n\t\tMalayalam = 0x6C6D,  // \"ml\"\n\t\t/// <summary>Maltese</summary>\n\t\tMaltese = 0x746D,  // \"mt\"\n\t\t/// <summary>Maori</summary>\n\t\tMaori = 0x696D,  // \"mi\"\n\t\t/// <summary>Marathi</summary>\n\t\tMarathi = 0x726D,  // \"mr\"\n\t\t/// <summary>Mongolian</summary>\n\t\tMongolian = 0x6E6D,  // \"mn\"\n\t\t/// <summary>Myanmar</summary>\n\t\tMyanmar = 0x796D,  // \"my\"\n\t\t/// <summary>Nepali</summary>\n\t\tNepali = 0x656E,  // \"ne\"\n\t\t/// <summary>Norwegian</summary>\n\t\tNorwegian = 0x6F6E,  // \"no\"\n\t\t/// <summary>Nynorsk</summary>\n\t\tNynorsk = 0x6E6E,  // \"nn\"\n\t\t/// <summary>Occitan</summary>\n\t\tOccitan = 0x636F,  // \"oc\"\n\t\t/// <summary>Pashto</summary>\n\t\tPashto = 0x7370,  // \"ps\"\n\t\t/// <summary>Persian</summary>\n\t\tPersian = 0x6166,  // \"fa\"\n\t\t/// <summary>Polish</summary>\n\t\tPolish = 0x6C70,  // \"pl\"\n\t\t/// <summary>Portuguese</summary>\n\t\tPortuguese = 0x7470,  // \"pt\"\n\t\t/// <summary>Punjabi</summary>\n\t\tPunjabi = 0x6170,  // \"pa\"\n\t\t/// <summary>Romanian</summary>\n\t\tRomanian = 0x6F72,  // \"ro\"\n\t\t/// <summary>Russian</summary>\n\t\tRussian = 0x7572,  // \"ru\"\n\t\t/// <summary>Sanskrit</summary>\n\t\tSanskrit = 0x6173,  // \"sa\"\n\t\t/// <summary>Serbian</summary>\n\t\tSerbian = 0x7273,  // \"sr\"\n\t\t/// <summary>Shona</summary>\n\t\tShona = 0x6E73,  // \"sn\"\n\t\t/// <summary>Sindhi</summary>\n\t\tSindhi = 0x6473,  // \"sd\"\n\t\t/// <summary>Sinhala</summary>\n\t\tSinhala = 0x6973,  // \"si\"\n\t\t/// <summary>Slovak</summary>\n\t\tSlovak = 0x6B73,  // \"sk\"\n\t\t/// <summary>Slovenian</summary>\n\t\tSlovenian = 0x6C73,  // \"sl\"\n\t\t/// <summary>Somali</summary>\n\t\tSomali = 0x6F73,  // \"so\"\n\t\t/// <summary>Spanish</summary>\n\t\tSpanish = 0x7365,  // \"es\"\n\t\t/// <summary>Sundanese</summary>\n\t\tSundanese = 0x7573,  // \"su\"\n\t\t/// <summary>Swahili</summary>\n\t\tSwahili = 0x7773,  // \"sw\"\n\t\t/// <summary>Swedish</summary>\n\t\tSwedish = 0x7673,  // \"sv\"\n\t\t/// <summary>Tagalog</summary>\n\t\tTagalog = 0x6C74,  // \"tl\"\n\t\t/// <summary>Tajik</summary>\n\t\tTajik = 0x6774,  // \"tg\"\n\t\t/// <summary>Tamil</summary>\n\t\tTamil = 0x6174,  // \"ta\"\n\t\t/// <summary>Tatar</summary>\n\t\tTatar = 0x7474,  // \"tt\"\n\t\t/// <summary>Telugu</summary>\n\t\tTelugu = 0x6574,  // \"te\"\n\t\t/// <summary>Thai</summary>\n\t\tThai = 0x6874,  // \"th\"\n\t\t/// <summary>Tibetan</summary>\n\t\tTibetan = 0x6F62,  // \"bo\"\n\t\t/// <summary>Turkish</summary>\n\t\tTurkish = 0x7274,  // \"tr\"\n\t\t/// <summary>Turkmen</summary>\n\t\tTurkmen = 0x6B74,  // \"tk\"\n\t\t/// <summary>Ukrainian</summary>\n\t\tUkrainian = 0x6B75,  // \"uk\"\n\t\t/// <summary>Urdu</summary>\n\t\tUrdu = 0x7275,  // \"ur\"\n\t\t/// <summary>Uzbek</summary>\n\t\tUzbek = 0x7A75,  // \"uz\"\n\t\t/// <summary>Vietnamese</summary>\n\t\tVietnamese = 0x6976,  // \"vi\"\n\t\t/// <summary>Welsh</summary>\n\t\tWelsh = 0x7963,  // \"cy\"\n\t\t/// <summary>Yiddish</summary>\n\t\tYiddish = 0x6979,  // \"yi\"\n\t\t/// <summary>Yoruba</summary>\n\t\tYoruba = 0x6F79,  // \"yo\"\n\t}\n}"
  },
  {
    "path": "WhisperNet/API/eLogLevel.cs",
    "content": "﻿using System;\n\nnamespace Whisper\n{\n\t/// <summary>Message log level</summary>\n\tpublic enum eLogLevel: byte\n\t{\n\t\t/// <summary>Error message</summary>\n\t\tError = 0,\n\t\t/// <summary>Warning message</summary>\n\t\tWarning = 1,\n\t\t/// <summary>Informational message</summary>\n\t\tInfo = 2,\n\t\t/// <summary>Debug message</summary>\n\t\tDebug = 3\n\t}\n\n\t/// <summary>A delegate to receive log messages from the library</summary>\n\tpublic delegate void pfnLogMessage( eLogLevel level, string message );\n\n\t/// <summary>Log destination flags</summary>\n\t[Flags]\n\tpublic enum eLoggerFlags: byte\n\t{\n\t\t/// <summary>No special flags</summary>\n\t\tNone = 0,\n\n\t\t/// <summary>In addition to calling the delegate, print messaged to standard error</summary>\n\t\tUseStandardError = 1,\n\n\t\t/// <summary>Don’t format error codes into messages</summary>\n\t\t/// <remarks>It’s recommended to use this flag in .NET.<br/>\n\t\t/// The standard library already formats these messages automatically, as needed.</remarks>\n\t\tSkipFormatMessage = 2,\n\t}\n}"
  },
  {
    "path": "WhisperNet/API/eModelImplementation.cs",
    "content": "﻿namespace Whisper\n{\n\t/// <summary>Implementation value for the <see cref=\"Library.loadModel(string, eGpuModelFlags, string?, eModelImplementation)\" /> factory function</summary>\n\tpublic enum eModelImplementation: uint\n\t{\n\t\t/// <summary>GPGPU implementation based on Direct3D 11.0 compute shaders</summary>\n\t\tGPU = 1,\n\n\t\t/// <summary>A hybrid implementation which uses DirectCompute for encode, and decodes on CPU</summary>\n\t\t/// <remarks>\n\t\t/// <para>The build of the native DLL included into this nuget package doesn’t implement this version.<br/>\n\t\t/// To enable, edit <c>stdafx.h</c> in Whisper project, change the value of <c>BUILD_HYBRID_VERSION</c> macro from zero to one, and build.</para>\n\t\t/// <para>This implementation requires a CPU with AVX1, FMA3, F16C and BMI1 instruction set extensions.</para>\n\t\t/// </remarks>\n\t\tHybrid = 2,\n\n\t\t/// <summary>A reference implementation which uses the original GGML CPU-running code.</summary>\n\t\t/// <remarks>\n\t\t/// <para>The build of the native DLL included into this nuget package doesn’t implement this version either.<br/>\n\t\t/// To enable, edit <c>stdafx.h</c> in Whisper project, change the value of <c>BUILD_BOTH_VERSIONS</c> macro from zero to one, and build the project.</para>\n\t\t/// <para>This implementation requires a CPU with AVX1, FMA3, and F16C instruction set extensions.</para>\n\t\t/// </remarks>\n\t\tReference = 3,\n\t}\n}"
  },
  {
    "path": "WhisperNet/API/eResultFlags.cs",
    "content": "﻿using System;\n\nnamespace Whisper\n{\n\t/// <summary>Flags for <see cref=\"Context.results(eResultFlags)\" /> method</summary>\n\t[Flags]\n\tpublic enum eResultFlags: uint\n\t{\n\t\t/// <summary>No flags</summary>\n\t\tNone = 0,\n\n\t\t/// <summary>Return individual tokens in addition to the segments</summary>\n\t\tTokens = 1,\n\n\t\t/// <summary>Return timestamps</summary>\n\t\tTimestamps = 2,\n\n\t\t/// <summary>Create a new COM object for the results.</summary>\n\t\t/// <remarks>Without this flag, the context returns a pointer to the COM object stored in the context.<br/>\n\t\t/// The content of that object is replaced every time you call <see cref=\"Internal.iContext.getResults(eResultFlags)\" /> method.</remarks>\n\t\tNewObject = 0x100,\n\t}\n}"
  },
  {
    "path": "WhisperNet/API/eSpeakerChannel.cs",
    "content": "﻿namespace Whisper\n{\n\t/// <summary>Output value for iContext.detectSpeaker method</summary>\n\tpublic enum eSpeakerChannel: byte\n\t{\n\t\t/// <summary>Unable to detect</summary>\n\t\tUnsure = 0,\n\t\t/// <summary>The speech was mostly in the left channel</summary>\n\t\tLeft = 1,\n\t\t/// <summary>The speech was mostly in the right channel</summary>\n\t\tRight = 2,\n\t\t/// <summary>The audio only has 1 channel</summary>\n\t\tNoStereoData = 0xFF,\n\t}\n}"
  },
  {
    "path": "WhisperNet/API/iAudioBuffer.cs",
    "content": "﻿using ComLight;\nusing System;\nusing System.Runtime.InteropServices;\n\nnamespace Whisper\n{\n\t/// <summary>A buffer with a chunk of audio.</summary>\n\t/// <remarks>Note the interface supports both marshaling directions.<br/>\n\t/// You can implement this interface in C#, to supply PCM audio data to the native code.</remarks>\n\t/// <seealso href=\"https://gist.github.com/Const-me/082c8d96eb10b76058c5dd9c68b5bfe1\" />\n\t[ComInterface( \"013583aa-c9eb-42bc-83db-633c2c317051\", eMarshalDirection.BothWays )]\n\tpublic interface iAudioBuffer: IDisposable\n\t{\n\t\t/// <summary>Count of samples in the buffer, equal to ( length in seconds ) * 16000</summary>\n\t\tint countSamples();\n\n\t\t/// <summary>Unmanaged pointer to the internal buffer with single-channel <c>float</c> PCM samples @ 16 kHz sample rate.</summary>\n\t\t/// <remarks>If you implementing this interface in C# and your audio data is on the managed heap, use <see cref=\"GCHandle\" /> to make sure it doesn't move.<br/>\n\t\t/// Or better yet, move the data to unmanaged buffer allocated with <see cref=\"Marshal.AllocHGlobal(int)\" /> or <see cref=\"Marshal.AllocCoTaskMem(int)\" /> method.</remarks>\n\t\tIntPtr getPcmMono();\n\n\t\t/// <summary>Unmanaged pointer to the internal buffer with interleaved stereo <c>float</c> PCM samples @ 16 kHz sample rate.</summary>\n\t\t/// <remarks>When the buffer doesn’t have stereo data, the method should return <see cref=\"IntPtr.Zero\" />.</remarks>\n\t\tIntPtr getPcmStereo();\n\n\t\t/// <summary>Start time of the buffer, relative to the start of the media.</summary>\n\t\t/// <remarks>The value is used to produce timestamps in <see cref=\"sSegment.time\" /> and <see cref=\"sToken.time\" /> fields.</remarks>\n\t\tvoid getTime( out TimeSpan time );\n\t}\n}"
  },
  {
    "path": "WhisperNet/API/iAudioReader.cs",
    "content": "﻿using ComLight;\nusing System;\n\nnamespace Whisper\n{\n\t/// <summary>Audio stream reader object</summary>\n\t/// <remarks>The implementation is forward-only, and these objects aren’t reusable.<br/>\n\t/// To read an audio file multiple time, dispose this object, and create a new one from the same source file.</remarks>\n\t[ComInterface( \"35b988da-04a6-476a-a193-d8891d5dc390\", eMarshalDirection.ToManaged )]\n\tpublic interface iAudioReader: IDisposable\n\t{\n\t\t/// <summary>Get duration of the media file</summary>\n\t\t[RetValIndex]\n\t\tTimeSpan getDuration();\n\t}\n\n\t/// <summary>Audio capture reader object</summary>\n\t/// <remarks>This interface has no public methods callable from C#.<br/>\n\t/// It’s only here to pass data between different functions implemented in C++.</remarks>\n\t[ComInterface( \"747752c2-d9fd-40df-8847-583c781bf013\", eMarshalDirection.ToManaged )]\n\tpublic interface iAudioCapture: IDisposable\n\t{\n\t}\n}"
  },
  {
    "path": "WhisperNet/API/iMediaFoundation.cs",
    "content": "﻿using ComLight;\nusing System;\nusing System.ComponentModel;\nusing System.Runtime.InteropServices;\nusing Whisper.Internal;\n\nnamespace Whisper\n{\n\t/// <summary>Exposes a small subset of MS Media Foundation framework.</summary>\n\t/// <remarks>That framework is a part of Windows OS, since Vista.</remarks>\n\t/// <seealso href=\"https://learn.microsoft.com/en-us/windows/win32/medfound/microsoft-media-foundation-sdk\" />\n\t[ComInterface( \"fb9763a5-d77d-4b6e-aff8-f494813cebd8\", eMarshalDirection.ToManaged ), CustomConventions( typeof( NativeLogger ) )]\n\tpublic interface iMediaFoundation: IDisposable\n\t{\n\t\t/// <summary>Decode complete audio file into a new memory buffer.</summary>\n\t\t/// <returns>\n\t\t/// The method asks MF to resample and convert audio into the suitable type for the Whisper model.<br/>\n\t\t/// If the path is a video file, the method will decode the first audio track.\n\t\t/// </returns>\n\t\t[RetValIndex( 2 )]\n\t\tiAudioBuffer loadAudioFile( [MarshalAs( UnmanagedType.LPWStr )] string path, [MarshalAs( UnmanagedType.U1 )] bool stereo = false );\n\n\t\t/// <summary>Create a reader to stream the audio file from disk</summary>\n\t\t/// <returns>\n\t\t/// The method returns an object which can be used to decode the audio file incrementally.<br/>\n\t\t/// For long audio files, this saves both memory (no need for large uncompressed PCM buffer), and time (decode and transcribe run concurrently on different CPU threads).<br/>\n\t\t/// If the path is a video file, the implementation will use the first audio track.\n\t\t/// </returns>\n\t\t[RetValIndex( 2 )]\n\t\tiAudioReader openAudioFile( [MarshalAs( UnmanagedType.LPWStr )] string path, [MarshalAs( UnmanagedType.U1 )] bool stereo = false );\n\n\t\t/// <summary>Create a reader to decode audio file in memory</summary>\n\t\t/// <remarks>The method first copies the content into another internal buffer, then creates a streaming decoder</remarks>\n\t\t[RetValIndex( 3 ), EditorBrowsable( EditorBrowsableState.Never )]\n\t\tiAudioReader loadAudioFileData( IntPtr data, long size, [MarshalAs( UnmanagedType.U1 )] bool stereo );\n\n\t\t/// <summary>List capture devices</summary>\n\t\tvoid listCaptureDevices( [MarshalAs( UnmanagedType.FunctionPtr )] pfnFoundCaptureDevices pfn, IntPtr pv );\n\n\t\t/// <summary>Open audio capture device</summary>\n\t\t[RetValIndex( 2 )]\n\t\tiAudioCapture openCaptureDevice( [MarshalAs( UnmanagedType.LPWStr )] string endpoint, [In] ref sCaptureParams captureParams );\n\t}\n\n\t/// <summary>Extension methods for <see cref=\"iMediaFoundation\" /> interface</summary>\n\tpublic static class MediaFoundationExt\n\t{\n\t\t/// <summary>Create a reader to decode audio file in memory</summary>\n\t\t/// <remarks>The method first copies the content into another internal buffer, then creates a streaming decoder</remarks>\n\t\tpublic static iAudioReader loadAudioFileData( this iMediaFoundation mf, ReadOnlySpan<byte> span, bool stereo = false )\n\t\t{\n\t\t\tunsafe\n\t\t\t{\n\t\t\t\tfixed( byte* rsi = span )\n\t\t\t\t{\n\t\t\t\t\treturn mf.loadAudioFileData( (IntPtr)rsi, span.Length, stereo );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}"
  },
  {
    "path": "WhisperNet/API/iModel.cs",
    "content": "﻿using ComLight;\nusing System;\nusing System.ComponentModel;\nusing System.Runtime.InteropServices;\n\nnamespace Whisper\n{\n\t/// <summary>Function pointer to receive array of tokens from <see cref=\"iModel.tokenize\" /></summary>\n\t[UnmanagedFunctionPointer( CallingConvention.StdCall )]\n\tpublic delegate void pfnDecodedTokens( [In, MarshalAs( UnmanagedType.LPArray, SizeParamIndex = 1 )] int[] arr,\n\t\tint length, IntPtr pv );\n\n\t/// <summary>A model in VRAM, loaded from GGML file.</summary>\n\t/// <remarks>This object doesn't keep any mutable state, and can be safely used from multiple threads concurrently</remarks>\n\t[ComInterface( \"abefb4c9-e8d8-46a3-8747-5afbadef1adb\", eMarshalDirection.ToManaged ), CustomConventions( typeof( Internal.NativeLogger ) )]\n\tpublic interface iModel: IDisposable\n\t{\n\t\t/// <summary>Create a context to transcribe audio with this model</summary>\n\t\t/// <remarks>Don't call this method, use <see cref=\"ExtensionMethods.createContext(iModel)\" /> instead.</remarks>\n\t\t[RetValIndex, EditorBrowsable( EditorBrowsableState.Never )]\n\t\tInternal.iContext createContextInternal();\n\n\t\t/// <summary>Convert the provided text into tokens</summary>\n\t\t[EditorBrowsable( EditorBrowsableState.Never )]\n\t\tvoid tokenize( [MarshalAs( UnmanagedType.LPUTF8Str )] string text,\n\t\t\t[MarshalAs( UnmanagedType.FunctionPtr )] pfnDecodedTokens pfn, IntPtr pv );\n\n\t\t/// <summary>True if this model is multi-lingual</summary>\n\t\tbool isMultilingual();\n\n\t\t/// <summary>Retrieve integer IDs of the special tokens defined by the model</summary>\n\t\t[RetValIndex]\n\t\tSpecialTokens getSpecialTokens();\n\n\t\t/// <summary>Try to resolve integer token ID into string.</summary>\n\t\t/// <remarks>Don't call this method, use <see cref=\"ExtensionMethods.stringFromToken(iModel, int)\" /> instead.</remarks>\n\t\t[EditorBrowsable( EditorBrowsableState.Never )]\n\t\tIntPtr stringFromTokenInternal( int id );\n\n\t\t/// <summary>Clone the model</summary>\n\t\t/// <remarks>You must pass <see cref=\"eGpuModelFlags.Cloneable\" /> bit when creating this model, otherwise this method will throw an exception.</remarks>\n\t\t[RetValIndex]\n\t\tiModel clone();\n\t}\n}"
  },
  {
    "path": "WhisperNet/API/sCaptureParams.cs",
    "content": "﻿using System;\n\nnamespace Whisper\n{\n\t/// <summary>Flags for the audio capture</summary>\n\t[Flags]\n\tpublic enum eCaptureFlags: uint\n\t{\n\t\t/// <summary>No special flags</summary>\n\t\tNone = 0,\n\t\t/// <summary>When the capture device supports stereo, keep stereo PCM samples in addition to mono</summary>\n\t\tStereo = 1,\n\t}\n\n\t/// <summary>Parameters for audio capture</summary>\n\tpublic struct sCaptureParams\n\t{\n\t\t/// <summary>Minimum transcribe duration in seconds</summary>\n\t\tpublic float minDuration;\n\t\t/// <summary>Maximum transcribe duration in seconds</summary>\n\t\tpublic float maxDuration;\n\t\t/// <summary></summary>\n\t\tpublic float dropStartSilence;\n\t\t/// <summary></summary>\n\t\tpublic float pauseDuration;\n\t\t/// <summary>Flags for the audio capture</summary>\n\t\tpublic eCaptureFlags flags;\n\n\t\t/// <summary>Initialize the structure with some reasonable default values</summary>\n\t\tpublic sCaptureParams( bool unused )\n\t\t{\n\t\t\tminDuration = 7.0f;         // 7 seconds\n\t\t\tmaxDuration = 11.0f;        // 11 seconds\n\t\t\tdropStartSilence = 0.25f;   // 250 ms\n\t\t\tpauseDuration = 0.333f;     // 333 ms\n\t\t\tflags = eCaptureFlags.None;\n\t\t}\n\t}\n}"
  },
  {
    "path": "WhisperNet/AssemblyInfo.cs",
    "content": "﻿using System.Reflection;\nusing System.Runtime.InteropServices;\n\n[assembly: AssemblyCopyright( \"Copyright © const.me, 2022-2023\" )]\n[assembly: ComVisible( false )]\n[assembly: AssemblyVersion( \"1.12.0.0\" )]"
  },
  {
    "path": "WhisperNet/AssemblyTitle.cs",
    "content": "﻿using System.Reflection;\nusing System.Runtime.InteropServices;\n\n[assembly: AssemblyTitle( \"WhisperNet\" )]\n[assembly: AssemblyDescription( \"DirectCompute port of whisper.cpp library, C# bindings\" )]\n[assembly: Guid( \"ced6cdb7-e040-4398-bae8-3417e5fa35f1\" )]"
  },
  {
    "path": "WhisperNet/Callbacks.cs",
    "content": "﻿using Whisper.Internal;\n\nnamespace Whisper\n{\n\t/// <summary>Implement this abstract class to receive callbacks from the native code</summary>\n\tpublic abstract class Callbacks\n\t{\n\t\t/// <summary>The callback is called before every encoder run.</summary>\n\t\t/// <remarks>If it returns false, the processing is aborted.</remarks>\n\t\tprotected virtual bool onEncoderBegin( Context sender ) { return true; }\n\n\t\t/// <summary>This callback is called on each new segment</summary>\n\t\tprotected virtual void onNewSegment( Context sender, int countNew ) { }\n\n\t\tconst int S_OK = 0;\n\t\tconst int S_FALSE = 1;\n\t\tinternal int encoderBegin( Context sender )\n\t\t{\n\t\t\ttry\n\t\t\t{\n\t\t\t\treturn onEncoderBegin( sender ) ? S_OK : S_FALSE;\n\t\t\t}\n\t\t\tcatch( Exception ex )\n\t\t\t{\n\t\t\t\tNativeLogger.captureException( ex );\n\t\t\t\treturn ex.HResult;\n\t\t\t}\n\t\t}\n\n\t\tinternal int newSegment( Context sender, int countNew )\n\t\t{\n\t\t\ttry\n\t\t\t{\n\t\t\t\tonNewSegment( sender, countNew );\n\t\t\t\treturn S_OK;\n\t\t\t}\n\t\t\tcatch( Exception ex )\n\t\t\t{\n\t\t\t\tNativeLogger.captureException( ex );\n\t\t\t\treturn ex.HResult;\n\t\t\t}\n\t\t}\n\t}\n}"
  },
  {
    "path": "WhisperNet/CaptureCallbacks.cs",
    "content": "﻿using Whisper.Internal;\n\nnamespace Whisper\n{\n\t/// <summary>Implement this abstract class to provide callbacks for audio capture method</summary>\n\tpublic abstract class CaptureCallbacks\n\t{\n\t\t/// <summary>Override this method to support cancellation</summary>\n\t\tprotected virtual bool shouldCancel( Context sender ) { return false; }\n\n\t\t/// <summary>Override this method to get notified about status changes</summary>\n\t\tprotected virtual void captureStatusChanged( Context sender, eCaptureStatus status ) { }\n\n\t\tinternal pfnShouldCancel cancel( Context sender )\n\t\t{\n\t\t\tconst int S_OK = 0;\n\t\t\tconst int S_FALSE = 1;\n\t\t\treturn delegate ( IntPtr pv )\n\t\t\t{\n\t\t\t\ttry\n\t\t\t\t{\n\t\t\t\t\treturn shouldCancel( sender ) ? S_FALSE : S_OK;\n\t\t\t\t}\n\t\t\t\tcatch( Exception ex )\n\t\t\t\t{\n\t\t\t\t\tNativeLogger.captureException( ex );\n\t\t\t\t\treturn ex.HResult;\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\n\t\tinternal pfnCaptureStatus status( Context sender )\n\t\t{\n\t\t\treturn delegate ( IntPtr pv, eCaptureStatus status )\n\t\t\t{\n\t\t\t\ttry\n\t\t\t\t{\n\t\t\t\t\tcaptureStatusChanged( sender, status );\n\t\t\t\t\treturn 0;\n\t\t\t\t}\n\t\t\t\tcatch( Exception ex )\n\t\t\t\t{\n\t\t\t\t\tNativeLogger.captureException( ex );\n\t\t\t\t\treturn ex.HResult;\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\t}\n}"
  },
  {
    "path": "WhisperNet/Context.cs",
    "content": "﻿using System.Diagnostics;\nusing Whisper.Internal;\nusing Whisper.Internals;\n\nnamespace Whisper\n{\n\t/// <summary>Stateful context, contains methods to transcribe audio</summary>\n\tpublic sealed class Context: IDisposable\n\t{\n\t\tiContext context;\n\t\t// Caching the results object here saves time spent in ComLight library creating these callable proxies over and over again for the same underlying C++ object\n\t\treadonly iTranscribeResult transcribeResult;\n\t\tsFullParams fullParams;\n\t\tsProgressSink progressSink;\n\t\tbool disposed = false;\n\t\treadonly Action<object> pfnBuffer, pfnStream;\n\n\t\tinternal Context( Internal.iContext context )\n\t\t{\n\t\t\tthis.context = context;\n\t\t\ttranscribeResult = context.getResults( eResultFlags.None );\n\t\t\tfullParams = context.fullDefaultParams( eSamplingStrategy.Greedy );\n\t\t\tpfnBuffer = processBuffer;\n\t\t\tpfnStream = processStream;\n\t\t\tprogressSink = default;\n\t\t}\n\n\t\tvoid IDisposable.Dispose()\n\t\t{\n\t\t\tif( disposed )\n\t\t\t\treturn;\n\t\t\tdisposed = true;\n\t\t\tcontext?.Dispose();\n\t\t\tGC.SuppressFinalize( this );\n\t\t}\n\n\t\t/// <summary>Adjustable parameters</summary>\n\t\tpublic ref Parameters parameters => ref fullParams.publicParams;\n\n\t\tvoid processBuffer( object buffer )\n\t\t{\n\t\t\tcontext.runFull( ref fullParams, (iAudioBuffer)buffer );\n\t\t}\n\t\tvoid processStream( object reader )\n\t\t{\n\t\t\tcontext.runStreamed( ref fullParams, ref progressSink, (iAudioReader)reader );\n\t\t}\n\n\t\tvoid runImpl( object source, Callbacks? callbacks, ReadOnlySpan<int> promptTokens, Action<object> pfn )\n\t\t{\n\t\t\tif( null != callbacks )\n\t\t\t{\n\t\t\t\t// TODO [very low, performance]: the following code creates 2 new GC-allocated objects on each call.\n\t\t\t\t// Possible to optimize by caching these function pointers in static readonly fields, and use another [ThreadStatic] field for the callbacks object\n\t\t\t\tfullParams.newSegmentCallback = delegate ( IntPtr ctx, int countNew, IntPtr userData )\n\t\t\t\t{\n\t\t\t\t\treturn callbacks.newSegment( this, countNew );\n\t\t\t\t};\n\n\t\t\t\tfullParams.encoderBeginCallback = delegate ( IntPtr ctx, IntPtr userData )\n\t\t\t\t{\n\t\t\t\t\treturn callbacks.encoderBegin( this );\n\t\t\t\t};\n\t\t\t}\n\n\t\t\ttry\n\t\t\t{\n\t\t\t\tif( promptTokens.IsEmpty )\n\t\t\t\t{\n\t\t\t\t\tpfn( source );\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tunsafe\n\t\t\t\t{\n\t\t\t\t\tfixed( int* tokens = promptTokens )\n\t\t\t\t\t{\n\t\t\t\t\t\tfullParams.prompt_tokens = (IntPtr)tokens;\n\t\t\t\t\t\tfullParams.prompt_n_tokens = promptTokens.Length;\n\t\t\t\t\t\tpfn( source );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tfinally\n\t\t\t{\n\t\t\t\t// Reset these delegates.\n\t\t\t\t// Otherwise, this class will retain the callbacks object preventing it from being garbage collected.\n\t\t\t\tfullParams.newSegmentCallback = null;\n\t\t\t\tfullParams.encoderBeginCallback = null;\n\n\t\t\t\tfullParams.prompt_tokens = IntPtr.Zero;\n\t\t\t\tfullParams.prompt_n_tokens = 0;\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>Run the entire model: PCM -> log mel spectrogram -> encoder -> decoder -> text</summary>\n\t\tpublic void runFull( iAudioBuffer buffer, Callbacks? callbacks, ReadOnlySpan<int> promptTokens )\n\t\t{\n\t\t\trunImpl( buffer, callbacks, promptTokens, pfnBuffer );\n\t\t}\n\t\t/// <summary>Run the entire model: PCM -> log mel spectrogram -> encoder -> decoder -> text</summary>\n\t\tpublic void runFull( iAudioBuffer buffer, Callbacks? callbacks = null ) =>\n\t\t\trunFull( buffer, callbacks, ReadOnlySpan<int>.Empty );\n\t\t/// <summary>Run the entire model: PCM -> log mel spectrogram -> encoder -> decoder -> text</summary>\n\t\tpublic void runFull( iAudioBuffer buffer, Callbacks? callbacks, int[]? promptTokens ) =>\n\t\t\trunFull( buffer, callbacks, promptTokens ?? ReadOnlySpan<int>.Empty );\n\n\t\t/// <summary>Run the entire model, streaming audio from the provided reader object</summary>\n\t\tpublic void runFull( iAudioReader reader, Callbacks? callbacks, Action<double>? pfnProgress, ReadOnlySpan<int> promptTokens )\n\t\t{\n\t\t\tif( null != pfnProgress )\n\t\t\t{\n\t\t\t\tprogressSink.pfn = delegate ( double value, IntPtr context, IntPtr pv )\n\t\t\t\t{\n\t\t\t\t\ttry\n\t\t\t\t\t{\n\t\t\t\t\t\tpfnProgress.Invoke( value );\n\t\t\t\t\t\treturn 0;\n\t\t\t\t\t}\n\t\t\t\t\tcatch( Exception ex )\n\t\t\t\t\t{\n\t\t\t\t\t\treturn ex.HResult;\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t}\n\t\t\ttry\n\t\t\t{\n\t\t\t\trunImpl( reader, callbacks, promptTokens, pfnStream );\n\t\t\t}\n\t\t\tfinally\n\t\t\t{\n\t\t\t\tprogressSink.pfn = null;\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>Run the entire model, streaming audio from the provided reader object</summary>\n\t\tpublic void runFull( iAudioReader reader, Action<double>? pfnProgress = null, Callbacks? callbacks = null ) =>\n\t\t\trunFull( reader, callbacks, pfnProgress, ReadOnlySpan<int>.Empty );\n\n\t\t/// <summary>Run the entire model, streaming audio from the provided reader object</summary>\n\t\tpublic void runFull( iAudioReader reader, Callbacks? callbacks, Action<double>? pfnProgress, int[]? promptTokens ) =>\n\t\t\trunFull( reader, callbacks, pfnProgress, promptTokens ?? ReadOnlySpan<int>.Empty );\n\n\t\t/// <summary>Get text results out of the context</summary>\n\t\tpublic TranscribeResult results( eResultFlags flags = eResultFlags.None )\n\t\t{\n\t\t\tif( flags.HasFlag( eResultFlags.NewObject ) )\n\t\t\t\tthrow new ArgumentException();\n\n\t\t\tiTranscribeResult res = context.getResults( flags );\n\t\t\tDebug.Assert( ReferenceEquals( res, transcribeResult ) );\n\t\t\treturn new TranscribeResult( res );\n\t\t}\n\n\t\t/// <summary>Print timing data</summary>\n\t\tpublic void timingsPrint() => context.timingsPrint();\n\n\t\t/// <summary>Reset timing data</summary>\n\t\tpublic void timingsReset() => context.timingsReset();\n\n\t\t/// <summary>Continuously process audio from microphone or a similar capture device</summary>\n\t\t/// <remarks>It’s recommended to call this method on a background thread.</remarks>\n\t\tpublic void runCapture( iAudioCapture capture, Callbacks? callbacks, CaptureCallbacks? captureCallbacks )\n\t\t{\n\t\t\tif( null != callbacks )\n\t\t\t{\n\t\t\t\t// TODO [very low, performance]: the following code creates 2 new GC-allocated objects on each call.\n\t\t\t\t// Possible to optimize by caching these function pointers in static readonly fields, and use another [ThreadStatic] field for the callbacks object\n\t\t\t\tfullParams.newSegmentCallback = delegate ( IntPtr ctx, int countNew, IntPtr userData )\n\t\t\t\t{\n\t\t\t\t\treturn callbacks.newSegment( this, countNew );\n\t\t\t\t};\n\n\t\t\t\tfullParams.encoderBeginCallback = delegate ( IntPtr ctx, IntPtr userData )\n\t\t\t\t{\n\t\t\t\t\treturn callbacks.encoderBegin( this );\n\t\t\t\t};\n\t\t\t}\n\n\t\t\ttry\n\t\t\t{\n\t\t\t\tsCaptureCallbacks cc = default;\n\t\t\t\tif( captureCallbacks != null )\n\t\t\t\t{\n\t\t\t\t\tcc.shouldCancel = captureCallbacks.cancel( this );\n\t\t\t\t\tcc.captureStatus = captureCallbacks.status( this );\n\t\t\t\t}\n\t\t\t\tcontext.runCapture( ref fullParams, ref cc, capture );\n\t\t\t}\n\t\t\tfinally\n\t\t\t{\n\t\t\t\t// Reset these delegates.\n\t\t\t\t// Otherwise, this class will retain the callbacks object preventing it from being garbage collected.\n\t\t\t\tfullParams.newSegmentCallback = null;\n\t\t\t\tfullParams.encoderBeginCallback = null;\n\n\t\t\t\tfullParams.prompt_tokens = IntPtr.Zero;\n\t\t\t\tfullParams.prompt_n_tokens = 0;\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>Try to detect speaker by comparing channels of the stereo PCM data</summary>\n\t\t/// <remarks>\n\t\t/// <para>The feature requires stereo PCM data.<br/>Pass <c>stereo=true</c> to <see cref=\"iMediaFoundation.loadAudioFile\" /> or <see cref=\"iMediaFoundation.openAudioFile\"/> methods,<br/>\n\t\t/// or <see cref=\"eCaptureFlags.Stereo\" /> to <see cref=\"iMediaFoundation.openCaptureDevice\" /> method.</para>\n\t\t/// <para>It seems to work fine with <a href=\"https://www.bluemic.com/en-us/products/yeti/\">Blue Yeti</a> microphone,\n\t\t/// after switched the microphone to Stereo pattern.<br/> With recorded sounds however, the performance varies depending on the recording.</para>\n\t\t/// </remarks>\n\t\tpublic eSpeakerChannel detectSpeaker( sTimeInterval interval ) =>\n\t\t\tcontext.detectSpeaker( ref interval );\n\t}\n}"
  },
  {
    "path": "WhisperNet/ExtensionMethods.cs",
    "content": "﻿using System.Diagnostics;\nusing System.Runtime.CompilerServices;\nusing System.Runtime.InteropServices;\nusing Whisper.Internal;\n\nnamespace Whisper\n{\n\t/// <summary>Extension methods of these COM interfaces</summary>\n\tpublic static class ExtensionMethods\n\t{\n\t\t/// <summary>Create a context to transcribe audio with this model</summary>\n\t\tpublic static Context createContext( this iModel model )\n\t\t{\n\t\t\tiContext ctx = model.createContextInternal();\n\t\t\treturn new Context( ctx );\n\t\t}\n\n\t\t/// <summary>Convert language into a short ID string, like <c>\"en\"</c></summary>\n\t\t[SkipLocalsInit]\n\t\tpublic static string getCode( this eLanguage lang )\n\t\t{\n\t\t\tunsafe\n\t\t\t{\n\t\t\t\tsbyte* ptr = stackalloc sbyte[ 5 ];\n\t\t\t\t*(uint*)ptr = (uint)lang;\n\t\t\t\tptr[ 4 ] = 0;\n\t\t\t\treturn new string( ptr );\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>Resolve integer token ID into string.</summary>\n\t\t/// <remarks>If the token ID was not found in the model, the method returns null without raising exceptions.</remarks>\n\t\tpublic static string? stringFromToken( this iModel model, int idToken ) =>\n\t\t\tMarshal.PtrToStringUTF8( model.stringFromTokenInternal( idToken ) );\n\n\t\t/// <summary>List capture devices</summary>\n\t\tpublic static CaptureDeviceId[]? listCaptureDevices( this iMediaFoundation mf )\n\t\t{\n\t\t\tCaptureDeviceId[]? result = null;\n\n\t\t\tpfnFoundCaptureDevices pfn = delegate ( int len, sCaptureDevice[]? arr, IntPtr pv )\n\t\t\t{\n\t\t\t\ttry\n\t\t\t\t{\n\t\t\t\t\tif( len == 0 || arr == null )\n\t\t\t\t\t\treturn 1;\n\t\t\t\t\tDebug.Assert( len == arr.Length );\n\n\t\t\t\t\tresult = new CaptureDeviceId[ len ];\n\t\t\t\t\tfor( int i = 0; i < len; i++ )\n\t\t\t\t\t\tresult[ i ] = new CaptureDeviceId( arr[ i ] );\n\t\t\t\t\treturn 0;\n\t\t\t\t}\n\t\t\t\tcatch( Exception ex )\n\t\t\t\t{\n\t\t\t\t\tNativeLogger.captureException( ex );\n\t\t\t\t\treturn ex.HResult;\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tmf.listCaptureDevices( pfn, IntPtr.Zero );\n\n\t\t\treturn result;\n\t\t}\n\n\t\t/// <summary>Open audio capture device</summary>\n\t\tpublic static iAudioCapture openCaptureDevice( this iMediaFoundation mf, in CaptureDeviceId id, in sCaptureParams? cp = null )\n\t\t{\n\t\t\tsCaptureParams captureParams = cp ?? new sCaptureParams();\n\t\t\treturn mf.openCaptureDevice( id.endpoint, ref captureParams );\n\t\t}\n\n\t\t/// <summary>Convert the provided text into tokens</summary>\n\t\tpublic static int[]? tokenize( this iModel model, string text )\n\t\t{\n\t\t\tint[]? result = null;\n\t\t\tpfnDecodedTokens pfn = delegate ( int[] arr, int length, IntPtr pv )\n\t\t\t{\n\t\t\t\tresult = arr;\n\t\t\t};\n\t\t\tmodel.tokenize( text, pfn, IntPtr.Zero );\n\t\t\treturn result;\n\t\t}\n\t}\n}"
  },
  {
    "path": "WhisperNet/Internal/NativeLogger.cs",
    "content": "﻿using System.Runtime.CompilerServices;\nusing System.Runtime.ExceptionServices;\nusing System.Runtime.InteropServices;\n\nnamespace Whisper.Internal\n{\n\t/// <summary>Utility class to supply logging function pointer to the C++ library,<br/>\n\t/// and provide custom calling conventions to ComLight runtime to convert error messages printed in C++ into .NET exception messages</summary>\n\tpublic static class NativeLogger\n\t{\n\t\tinternal static void startup() { }\n\n\t\tstatic NativeLogger()\n\t\t{\n\t\t\tsink = logSink;\n\t\t\tsLoggerSetup setup = default;\n\t\t\tsetup.sink = sink;\n\t\t\tsetup.level = eLogLevel.Warning;\n\t\t\tLibrary.setupLogger( ref setup );\n\t\t}\n\n\t\tinternal static void setup( eLogLevel lvl, eLoggerFlags flags, pfnLogMessage? pfn )\n\t\t{\n\t\t\tlogMessage = pfn;\n\n\t\t\tsLoggerSetup setup = default;\n\t\t\tsetup.sink = sink;\n\t\t\tsetup.level = lvl;\n\t\t\tsetup.flags = flags;\n\t\t\tLibrary.setupLogger( ref setup );\n\t\t}\n\n\t\t// This field is here to protect the function pointer from being collected by the GC\n\t\tstatic readonly pfnLoggerSink sink;\n\n\t\tstatic void logSink( IntPtr context, eLogLevel lvl, string message )\n\t\t{\n\t\t\tif( lvl == eLogLevel.Error )\n\t\t\t\tstate.setText( message );\n\t\t\tlogMessage?.Invoke( lvl, message );\n\t\t}\n\n\t\tsealed class ThreadState\n\t\t{\n\t\t\tstring? errorText = null;\n\t\t\tExceptionDispatchInfo? dispatchInfo = null;\n\n\t\t\tpublic void setText( string text ) => errorText = text;\n\t\t\tpublic void capture( Exception ex ) => dispatchInfo = ExceptionDispatchInfo.Capture( ex );\n\n\t\t\tpublic void clear()\n\t\t\t{\n\t\t\t\terrorText = null;\n\t\t\t\tdispatchInfo = null;\n\t\t\t}\n\n\t\t\tpublic void Deconstruct( out string? text, out ExceptionDispatchInfo? edi )\n\t\t\t{\n\t\t\t\ttext = errorText;\n\t\t\t\tedi = dispatchInfo;\n\t\t\t\terrorText = null;\n\t\t\t\tdispatchInfo = null;\n\t\t\t}\n\t\t}\n\n\t\t[ThreadStatic]\n\t\tstatic ThreadState state = new ThreadState();\n\n\t\tinternal static void captureException( Exception ex ) =>\n\t\t\tstate.capture( ex );\n\n\t\tstatic pfnLogMessage? logMessage = null;\n\n\t\t/// <summary>Called internally by ComLight runtime</summary>\n\t\t[MethodImpl( MethodImplOptions.AggressiveInlining )]\n\t\tpublic static void prologue()\n\t\t{\n\t\t\t// https://stackoverflow.com/a/2043505/126995\n\t\t\tif( null != state )\n\t\t\t\tstate.clear();\n\t\t\telse\n\t\t\t\tcreateState();\n\t\t}\n\n\t\t[MethodImpl( MethodImplOptions.NoInlining )]\n\t\tstatic void createState()\n\t\t{\n\t\t\tstate = new ThreadState();\n\t\t}\n\n\t\t/// <summary>Epilogue implementation for unsuccessful status codes</summary>\n\t\t[MethodImpl( MethodImplOptions.NoInlining )]\n\t\tstatic void throwException( int hr )\n\t\t{\n\t\t\t// Move state from the thread local object into local variables, and clear that object\n\t\t\t(string? text, ExceptionDispatchInfo? edi) = state;\n\n\t\t\tif( null != edi && edi.SourceException.HResult == hr )\n\t\t\t{\n\t\t\t\t// The error comes from a callback, and we have original context of that exception.\n\t\t\t\t// Re-throw the original exception.\n\t\t\t\t// This uses the original error message, and even correctly deals with the stack trace.\n\t\t\t\tedi.Throw();\n\t\t\t}\n\n\t\t\tif( null != text )\n\t\t\t{\n\t\t\t\t// C++ code has printed an error on the current thread, between prologue and epilogue.\n\t\t\t\t// Use that text for the exception message.\n\t\t\t\tException? ex = Marshal.GetExceptionForHR( hr );\n\t\t\t\tthrow new ApplicationException( text, ex );\n\t\t\t}\n\n\t\t\t// We don’t have any additional info about the exception.\n\t\t\t// Throw an exception from just the HRESULT code.\n\t\t\tMarshal.ThrowExceptionForHR( hr );\n\t\t}\n\n\t\t/// <summary>Called internally by ComLight runtime</summary>\n\t\t[MethodImpl( MethodImplOptions.AggressiveInlining )]\n\t\tpublic static void throwForHR( int hr )\n\t\t{\n\t\t\tif( hr >= 0 )\n\t\t\t\treturn; // SUCCEEDED\n\t\t\tthrowException( hr );\n\t\t}\n\n\t\t/// <summary>Called internally by ComLight runtime</summary>\n\t\t[MethodImpl( MethodImplOptions.AggressiveInlining )]\n\t\tpublic static bool throwAndReturnBool( int hr )\n\t\t{\n\t\t\tif( hr >= 0 )\n\t\t\t\treturn 0 == hr;\n\t\t\tthrowException( hr );\n\t\t\treturn false;\n\t\t}\n\t}\n}"
  },
  {
    "path": "WhisperNet/Internal/iContext.cs",
    "content": "﻿using ComLight;\nusing System;\nusing System.Runtime.InteropServices;\nusing Whisper.Internals;\n\nnamespace Whisper.Internal\n{\n    /// <summary>Stateful context, contains methods to transcribe audio</summary>\n    [ComInterface( \"b9956374-3b18-4943-90f2-2ab18a404537\", eMarshalDirection.ToManaged ), CustomConventions( typeof( NativeLogger ) )]\n\tpublic interface iContext: IDisposable\n\t{\n\t\t/// <summary>Run the entire model: PCM -> log mel spectrogram -> encoder -> decoder -> text</summary>\n\t\tvoid runFull( [In] ref sFullParams @params, iAudioBuffer buffer );\n\n\t\t/// <summary>Run the entire model, streaming audio from the provided reader object</summary>\n\t\tvoid runStreamed( [In] ref sFullParams @params, [In] ref sProgressSink progressSink, iAudioReader reader );\n\n\t\t/// <summary>Continuously process audio from microphone or a similar capture device</summary>\n\t\tvoid runCapture( [In] ref sFullParams @params, [In] ref sCaptureCallbacks callbacks, iAudioCapture reader );\n\n\t\t/// <summary>Get text results out of the context</summary>\n\t\t[RetValIndex( 1 )]\n\t\tiTranscribeResult getResults( eResultFlags flags );\n\n\t\t/// <summary>Try to detect speaker by comparing channels of the stereo PCM data</summary>\n\t\t[RetValIndex( 1 )]\n\t\teSpeakerChannel detectSpeaker( [In] ref sTimeInterval interval );\n\n\t\t/// <summary>Get the model which was used to create this context</summary>\n\t\t[RetValIndex]\n\t\tiModel getModel();\n\n\t\t/// <summary>Full the default parameters of the model, for the specified sampling strategy</summary>\n\t\t[RetValIndex( 1 )]\n\t\tsFullParams fullDefaultParams( eSamplingStrategy strategy );\n\n\t\t/// <summary>Print timing data</summary>\n\t\tvoid timingsPrint();\n\t\t/// <summary>Reset timing data</summary>\n\t\tvoid timingsReset();\n\t}\n}"
  },
  {
    "path": "WhisperNet/Internal/iTranscribeResult.cs",
    "content": "﻿#pragma warning disable CS0649 // Field is never assigned to\nusing ComLight;\nusing System.ComponentModel;\nusing System.Runtime.InteropServices;\n\nnamespace Whisper.Internal\n{\n\t/// <summary>Size of the buffers owned by the <see cref=\"iTranscribeResult\" /> object</summary>\n\tpublic readonly struct sTranscribeLength\n\t{\n\t\t/// <summary>Count of segments</summary>\n\t\tpublic readonly int countSegments;\n\t\t/// <summary>Total count of tokens, for all segments combined</summary>\n\t\tpublic readonly int countTokens;\n\t}\n\n\t/// <summary>Output data from the model</summary>\n\t[ComInterface( \"2871a73f-5ce3-48f8-8779-6582ee11935e\", eMarshalDirection.ToManaged ), CustomConventions( typeof( NativeLogger ) )]\n\tpublic interface iTranscribeResult\n\t{\n\t\t/// <summary>Get size of the buffers</summary>\n\t\t[RetValIndex, EditorBrowsable( EditorBrowsableState.Never )]\n\t\tpublic sTranscribeLength getSize();\n\n\t\t/// <summary>Pointer to segment data, a vector of <see cref=\"sSegment\" /> structures</summary>\n\t\t[EditorBrowsable( EditorBrowsableState.Never )]\n\t\tpublic IntPtr getSegments();\n\n\t\t/// <summary>Pointer to tokens data, a vector of <see cref=\"sToken\" /> structures</summary>\n\t\t[EditorBrowsable( EditorBrowsableState.Never )]\n\t\tpublic IntPtr getTokens();\n\t}\n}\n\nnamespace Whisper\n{\n\t/// <summary>Start and end times of a segment or token</summary>\n\t/// <remarks>The times are relative to the start of the media</remarks>\n\tpublic readonly struct sTimeInterval\n\t{\n\t\t/// <summary>Start time</summary>\n\t\tpublic readonly TimeSpan begin;\n\t\t/// <summary>End time</summary>\n\t\tpublic readonly TimeSpan end;\n\t}\n\n\t/// <summary>Segment data</summary>\n\tpublic readonly struct sSegment\n\t{\n\t\tinternal readonly IntPtr m_text;\n\t\t/// <summary>Segment text</summary>\n\t\tpublic string? text => Marshal.PtrToStringUTF8( m_text );\n\t\t/// <summary>Start and end times of the segment</summary>\n\t\tpublic readonly sTimeInterval time;\n\t\t/// <summary>Slice of the tokens</summary>\n\t\tpublic readonly int firstToken, countTokens;\n\t}\n\n\t/// <summary>Token flags</summary>\n\t[Flags]\n\tpublic enum eTokenFlags: uint\n\t{\n\t\t/// <summary>The token is special</summary>\n\t\tSpecial = 1,\n\t}\n\n\t/// <summary>Token data</summary>\n\tpublic readonly struct sToken\n\t{\n\t\tinternal readonly IntPtr m_text;\n\t\t/// <summary>Token text</summary>\n\t\tpublic string? text => Marshal.PtrToStringUTF8( m_text );\n\t\t/// <summary>Start and end times of the token</summary>\n\t\tpublic readonly sTimeInterval time;\n\t\t/// <summary>Probability of the token</summary>\n\t\tpublic readonly float probability;\n\t\t/// <summary>Probability of the timestamp token</summary>\n\t\tpublic readonly float probabilityTimestamp;\n\t\t/// <summary>Sum of probabilities of all timestamp tokens</summary>\n\t\tpublic readonly float ptsum;\n\t\t/// <summary>Voice length of the token</summary>\n\t\tpublic readonly float vlen;\n\t\t/// <summary>Token id</summary>\n\t\tpublic readonly int id;\n\t\t/// <summary>Token flags</summary>\n\t\treadonly eTokenFlags flags;\n\t\t/// <summary>True if the token flags has the specified bit set</summary>\n\t\tpublic bool hasFlag( eTokenFlags bit ) => flags.HasFlag( bit );\n\t}\n\n\t/// <summary>Output data from the model</summary>\n\tpublic readonly ref struct TranscribeResult\n\t{\n\t\t/// <summary>Segments in the results</summary>\n\t\tpublic readonly ReadOnlySpan<sSegment> segments;\n\t\t/// <summary>Tokens in the results, for all segments</summary>\n\t\tpublic readonly ReadOnlySpan<sToken> tokens;\n\n\t\tinternal TranscribeResult( Internal.iTranscribeResult i )\n\t\t{\n\t\t\tInternal.sTranscribeLength len = i.getSize();\n\t\t\tunsafe\n\t\t\t{\n\t\t\t\t// This does not copy the buffers to managed memory.\n\t\t\t\t// Instead, the C# spans directly reference the native memory stored in these std::vectors\n\t\t\t\tif( len.countSegments > 0 )\n\t\t\t\t\tsegments = new ReadOnlySpan<sSegment>( (void*)i.getSegments(), len.countSegments );\n\t\t\t\telse\n\t\t\t\t\tsegments = ReadOnlySpan<sSegment>.Empty;\n\n\t\t\t\tif( len.countTokens > 0 )\n\t\t\t\t\ttokens = new ReadOnlySpan<sToken>( (void*)i.getTokens(), len.countTokens );\n\t\t\t\telse\n\t\t\t\t\ttokens = ReadOnlySpan<sToken>.Empty;\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>Get tokens for the specified segment</summary>\n\t\tpublic ReadOnlySpan<sToken> getTokens( in sSegment seg ) =>\n\t\t\ttokens.Slice( seg.firstToken, seg.countTokens );\n\t}\n}"
  },
  {
    "path": "WhisperNet/Internal/sCaptureCallbacks.cs",
    "content": "﻿using System;\nusing System.Runtime.InteropServices;\n\nnamespace Whisper.Internal\n{\n\t/// <summary>Unmanaged code calls this to check for cancellation</summary>\n\t/// <remarks>Return 0 to proceed, or 1 to stop the process and return from Context.runCapture method</remarks>\n\t[UnmanagedFunctionPointer( CallingConvention.StdCall )]\n\tpublic delegate int pfnShouldCancel( IntPtr pv );\n\n\t/// <summary>Unmanaged code calls this to notify about the status</summary>\n\t[UnmanagedFunctionPointer( CallingConvention.StdCall )]\n\tpublic delegate int pfnCaptureStatus( IntPtr pv, eCaptureStatus status );\n\n\t/// <summary>Capture callbacks for unmanaged code</summary>\n\tpublic struct sCaptureCallbacks\n\t{\n\t\t/// <summary>Cancellation function pointer</summary>\n\t\tpublic pfnShouldCancel shouldCancel;\n\t\t/// <summary>Capture status function pointer</summary>\n\t\tpublic pfnCaptureStatus captureStatus;\n\t\t/// <summary>Context pointer, only needed for C++ compatibility</summary>\n\t\tpublic IntPtr pv;\n\t}\n}"
  },
  {
    "path": "WhisperNet/Internal/sCaptureDevice.cs",
    "content": "﻿#pragma warning disable CS0649 // Field is never assigned to\nusing System.Runtime.InteropServices;\n\nnamespace Whisper.Internal\n{\n\t/// <summary>Identifiers for an audio capture device</summary>\n\tpublic struct sCaptureDevice\n\t{\n\t\treadonly IntPtr m_displayName;\n\t\t/// <summary>The display name is suitable for showing to the user, but might not be unique.</summary>\n\t\tpublic string? displayName => Marshal.PtrToStringUni( m_displayName );\n\n\t\treadonly IntPtr m_endpoint;\n\t\t/// <summary>Endpoint ID for an audio capture device.<br/>\n\t\t/// It uniquely identifies the device on the system, but is not a readable string.</summary>\n\t\tpublic string? endpoint => Marshal.PtrToStringUni( m_endpoint );\n\t}\n\n\t/// <summary>Function pointer to consume a list of audio capture device IDs</summary>\n\t[UnmanagedFunctionPointer( CallingConvention.StdCall )]\n\tpublic delegate int pfnFoundCaptureDevices( int len, [In, MarshalAs( UnmanagedType.LPArray, SizeParamIndex = 0 )] sCaptureDevice[]? arr, IntPtr pv );\n}"
  },
  {
    "path": "WhisperNet/Internal/sFullParams.cs",
    "content": "﻿#pragma warning disable CS0649  // Field is never assigned to\n\n// Missing XML comment for publicly visible type or member\n// TODO: remove this line and document them.\n#pragma warning disable CS1591\n\nusing System.Runtime.InteropServices;\n\nnamespace Whisper.Internals\n{\n\t/// <summary>This callback is called on each new segment</summary>\n\t[UnmanagedFunctionPointer( CallingConvention.Cdecl )]\n\tdelegate int pfnNewSegment( IntPtr ctx, int countNew, IntPtr userData );\n\n\t/// <summary>The callback is called before every encoder run. If it returns S_FALSE, the processing is aborted.</summary>\n\t[UnmanagedFunctionPointer( CallingConvention.Cdecl )]\n\tdelegate int pfnEncoderBegin( IntPtr ctx, IntPtr userData );\n\n\t/// <summary>Transcribe parameters</summary>\n\tpublic struct sFullParams\n\t{\n\t\tinternal Parameters publicParams;\n\t\t// The rest of these parameters are not exposed to the user-friendly public API of this DLL\n\n\t\tinternal IntPtr prompt_tokens;\n\t\tinternal int prompt_n_tokens;\n\n\t\t/// <summary>This callback is called on each new segment</summary>\n\t\t[MarshalAs( UnmanagedType.FunctionPtr )]\n\t\tinternal pfnNewSegment? newSegmentCallback;\n\t\t/// <summary>Parameter for the above, not needed in C#</summary>\n\t\tinternal IntPtr newSegmentCallbackData;\n\n\t\t/// <summary>The callback is called before every encoder run. If it returns false, the processing is aborted</summary>\n\t\t[MarshalAs( UnmanagedType.FunctionPtr )]\n\t\tinternal pfnEncoderBegin? encoderBeginCallback;\n\t\t/// <summary>Parameter for the above, not needed in C#</summary>\n\t\tinternal IntPtr encoderBeginCallbackData;\n\t}\n}"
  },
  {
    "path": "WhisperNet/Internal/sLoadModelCallbacks.cs",
    "content": "﻿using System.Runtime.InteropServices;\n\nnamespace Whisper.Internal\n{\n\t/// <summary>Function pointer to report model loading progress</summary>\n\t[UnmanagedFunctionPointer( CallingConvention.StdCall )]\n\tdelegate int pfnLoadProgress( double progress, IntPtr pv );\n\n\t/// <summary>Function pointer to implement cooperative cancellation</summary>\n\t[UnmanagedFunctionPointer( CallingConvention.StdCall )]\n\tdelegate int pfnCancel( IntPtr pv );\n\n\t/// <summary>Callback functions for loading models</summary>\n\tpublic struct sLoadModelCallbacks\n\t{\n\t\t/// <summary>Function pointer to report model loading progress</summary>\n\t\t[MarshalAs( UnmanagedType.FunctionPtr )]\n\t\tpfnLoadProgress? progress;\n\n\t\t/// <summary>Function pointer to implement cooperative cancellation</summary>\n\t\t[MarshalAs( UnmanagedType.FunctionPtr )]\n\t\tpfnCancel? cancel;\n\n\t\t// Not needed in C#, delegates can capture things\n\t\tIntPtr pv;\n\n\t\t/// <summary>Wrap idiomatic C# things into these low-level C callbacks</summary>\n\t\tinternal sLoadModelCallbacks( CancellationToken cancelToken, Action<double>? pfnProgress )\n\t\t{\n\t\t\tif( cancelToken != CancellationToken.None )\n\t\t\t{\n\t\t\t\tcancel = delegate ( IntPtr pv )\n\t\t\t\t{\n\t\t\t\t\tif( cancelToken.IsCancellationRequested )\n\t\t\t\t\t\treturn 1;   // S_FALSE\n\t\t\t\t\treturn 0;   // S_OK\n\t\t\t\t};\n\t\t\t}\n\t\t\telse\n\t\t\t\tcancel = null;\n\n\t\t\tif( null != pfnProgress )\n\t\t\t{\n\t\t\t\tprogress = delegate ( double val, IntPtr pv )\n\t\t\t\t{\n\t\t\t\t\ttry\n\t\t\t\t\t{\n\t\t\t\t\t\tpfnProgress( val );\n\t\t\t\t\t\treturn 0;   // S_OK\n\t\t\t\t\t}\n\t\t\t\t\tcatch( Exception ex )\n\t\t\t\t\t{\n\t\t\t\t\t\tNativeLogger.captureException( ex );\n\t\t\t\t\t\treturn ex.HResult;\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t}\n\t\t\telse\n\t\t\t\tprogress = null;\n\n\t\t\tpv = IntPtr.Zero;\n\t\t}\n\t}\n}"
  },
  {
    "path": "WhisperNet/Internal/sLoggerSetup.cs",
    "content": "﻿using System;\nusing System.Runtime.InteropServices;\n\nnamespace Whisper.Internal\n{\n\t[UnmanagedFunctionPointer( CallingConvention.StdCall )]\n\tdelegate void pfnLoggerSink( IntPtr context, eLogLevel lvl, [MarshalAs( UnmanagedType.LPUTF8Str )] string message );\n\n\tstruct sLoggerSetup\n\t{\n\t\t[MarshalAs( UnmanagedType.FunctionPtr )]\n\t\tpublic pfnLoggerSink sink;\n\t\tIntPtr context;\n\t\tpublic eLogLevel level;\n\t\tpublic eLoggerFlags flags;\n\t}\n}"
  },
  {
    "path": "WhisperNet/Internal/sModelSetup.cs",
    "content": "﻿using System.Runtime.InteropServices;\n\nnamespace Whisper.Internal\n{\n\tstruct sModelSetup\n\t{\n\t\teModelImplementation impl;\n\t\teGpuModelFlags flags;\n\t\t[MarshalAs( UnmanagedType.LPWStr )]\n\t\tstring? adapter;\n\n\t\tpublic sModelSetup( eGpuModelFlags flags, eModelImplementation impl, string? adapter )\n\t\t{\n\t\t\tthis.impl = impl;\n\t\t\tthis.flags = flags;\n\t\t\tthis.adapter = adapter;\n\t\t}\n\t}\n\n\t[UnmanagedFunctionPointer( CallingConvention.StdCall )]\n\tdelegate void pfnListAdapters( [In, MarshalAs( UnmanagedType.LPWStr )] string name, IntPtr pv );\n}"
  },
  {
    "path": "WhisperNet/Internal/sProgressSink.cs",
    "content": "﻿#pragma warning disable CS0649  // Field is never assigned to\nusing System.Runtime.InteropServices;\n\nnamespace Whisper.Internal\n{\n\t/// <summary>A callback to get notified about the progress</summary>\n\t[UnmanagedFunctionPointer( CallingConvention.StdCall )]\n\tdelegate int pfnReportProgress( double value, IntPtr context, IntPtr pv );\n\n\t/// <summary>C structure with a progress reporting function pointer</summary>\n\tpublic struct sProgressSink\n\t{\n\t\t/// <summary>A callback to get notified about the progress</summary>\n\t\t[MarshalAs( UnmanagedType.FunctionPtr )]\n\t\tinternal pfnReportProgress? pfn;\n\n\t\t/// <summary>Last parameter to the callback</summary>\n\t\tinternal IntPtr pv;\n\t}\n}"
  },
  {
    "path": "WhisperNet/Library.cs",
    "content": "﻿using ComLight;\nusing System.Diagnostics;\nusing System.Runtime.InteropServices;\nusing System.Runtime.Intrinsics.X86;\nusing Whisper.Internal;\n\nnamespace Whisper\n{\n\t/// <summary>Factory methods implemented by the C++ DLL</summary>\n\tpublic static class Library\n\t{\n\t\tstatic Library()\n\t\t{\n\t\t\tif( Environment.OSVersion.Platform != PlatformID.Win32NT )\n\t\t\t\tthrow new ApplicationException( \"This library requires Windows OS\" );\n\t\t\tif( !Environment.Is64BitProcess )\n\t\t\t\tthrow new ApplicationException( \"This library only works in 64-bit processes\" );\n\t\t\tif( RuntimeInformation.ProcessArchitecture != Architecture.X64 )\n\t\t\t\tthrow new ApplicationException( \"This library requires a processor with AMD64 instruction set\" );\n\t\t\tif( !( Sse41.IsSupported && Avx.IsSupported ) )\n\t\t\t\tthrow new ApplicationException( \"This library requires a CPU with AVX support\" );\n\t\t\tNativeLogger.startup();\n\t\t}\n\n\t\tconst string dll = \"Whisper.dll\";\n\n\t\t[DllImport( dll, CallingConvention = RuntimeClass.defaultCallingConvention, PreserveSig = false )]\n\t\tinternal static extern void setupLogger( [In] ref sLoggerSetup setup );\n\n\t\t[DllImport( dll, CallingConvention = RuntimeClass.defaultCallingConvention, PreserveSig = true )]\n\t\tstatic extern int loadModel( [MarshalAs( UnmanagedType.LPWStr )] string path,\n\t\t\t[In] ref sModelSetup setup, [In] ref sLoadModelCallbacks callbacks,\n\t\t\t[MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Marshaler<iModel> ) )] out iModel model );\n\n\t\t/// <summary>Load Whisper model from GGML file on disk</summary>\n\t\t/// <remarks>Models are large, depending on user’s disk speed this might take a while, and this function blocks the calling thread.<br/>\n\t\t/// Consider <see cref=\"loadModelAsync\" /> instead.</remarks>\n\t\t/// <seealso href=\"https://huggingface.co/datasets/ggerganov/whisper.cpp\" />\n\t\tpublic static iModel loadModel( string path, eGpuModelFlags flags = eGpuModelFlags.None,\n\t\t\tstring? adapter = null, eModelImplementation impl = eModelImplementation.GPU )\n\t\t{\n\t\t\tiModel model;\n\t\t\tsModelSetup setup = new sModelSetup( flags, impl, adapter );\n\t\t\tsLoadModelCallbacks callbacks = default;\n\t\t\tNativeLogger.prologue();\n\t\t\tint hr = loadModel( path, ref setup, ref callbacks, out model );\n\t\t\tNativeLogger.throwForHR( hr );\n\t\t\treturn model;\n\t\t}\n\n\t\t/// <summary>Load Whisper model on a background thread, with optional progress reporting and cancellation</summary>\n\t\tpublic static Task<iModel> loadModelAsync( string path, CancellationToken cancelToken,\n\t\t\teGpuModelFlags flags = eGpuModelFlags.None, string? adapter = null,\n\t\t\tAction<double>? pfnProgress = null, eModelImplementation impl = eModelImplementation.GPU )\n\t\t{\n\t\t\tTaskCompletionSource<iModel> tcs = new TaskCompletionSource<iModel>();\n\n\t\t\tWaitCallback wcb = delegate ( object? state )\n\t\t\t{\n\t\t\t\ttry\n\t\t\t\t{\n\t\t\t\t\tsModelSetup setup = new sModelSetup( flags, impl, adapter );\n\t\t\t\t\tsLoadModelCallbacks callbacks = new sLoadModelCallbacks( cancelToken, pfnProgress );\n\n\t\t\t\t\tiModel model;\n\t\t\t\t\tNativeLogger.prologue();\n\t\t\t\t\tint hr = loadModel( path, ref setup, ref callbacks, out model );\n\t\t\t\t\tNativeLogger.throwForHR( hr );\n\n\t\t\t\t\ttcs.SetResult( model );\n\t\t\t\t}\n\t\t\t\tcatch( Exception ex )\n\t\t\t\t{\n\t\t\t\t\ttcs.SetException( ex );\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tThreadPool.QueueUserWorkItem( wcb );\n\t\t\treturn tcs.Task;\n\t\t}\n\n\t\t[DllImport( dll, CallingConvention = RuntimeClass.defaultCallingConvention, PreserveSig = true )]\n\t\tstatic extern int initMediaFoundation( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Marshaler<iMediaFoundation> ) )] out iMediaFoundation mf );\n\n\t\t/// <summary>Initialize Media Foundation runtime</summary>\n\t\tpublic static iMediaFoundation initMediaFoundation()\n\t\t{\n\t\t\tiMediaFoundation mf;\n\t\t\tNativeLogger.prologue();\n\t\t\tint hr = initMediaFoundation( out mf );\n\t\t\tNativeLogger.throwForHR( hr );\n\t\t\treturn mf;\n\t\t}\n\n\t\t// The .NET runtime uses UTF-16 for the strings, so we only need the Unicode version of this function.\n\t\t// The native DLL exports both Unicode and ASCII versions.\n\t\t[DllImport( dll, CallingConvention = RuntimeClass.defaultCallingConvention, PreserveSig = true )]\n\t\tstatic extern uint findLanguageKeyW( [MarshalAs( UnmanagedType.LPWStr )] string lang );\n\n\t\t/// <summary>Try to resolve language code string like <c>\"en\"</c>, <c>\"pl\"</c> or <c>\"uk\"</c> into the strongly-typed enum.</summary>\n\t\t/// <remarks>The function is case-sensitive, <c>\"EN\"</c> or <c>\"UK\"</c> gonna fail.</remarks>\n\t\tpublic static eLanguage? languageFromCode( string lang )\n\t\t{\n\t\t\tuint key = findLanguageKeyW( lang );\n\t\t\tif( key != uint.MaxValue )\n\t\t\t\treturn (eLanguage)key;\n\t\t\treturn null;\n\t\t}\n\n\t\t/// <summary>Set up delegate to receive log messages from the C++ library</summary>\n\t\tpublic static void setLogSink( eLogLevel lvl, eLoggerFlags flags = eLoggerFlags.SkipFormatMessage, pfnLogMessage? pfn = null )\n\t\t{\n\t\t\tNativeLogger.setup( lvl, flags, pfn );\n\t\t}\n\n\t\t[DllImport( dll, CallingConvention = RuntimeClass.defaultCallingConvention, PreserveSig = true )]\n\t\tstatic extern int listGPUs( [MarshalAs( UnmanagedType.FunctionPtr )] pfnListAdapters pfn, IntPtr pv );\n\n\t\t/// <summary>Enumerate graphics adapters on this computer, and return their names.</summary>\n\t\t/// <remarks>To manually select the GPU to use for the inference, pass one of these names to<br/>\n\t\t/// <see cref=\"loadModel(string, eGpuModelFlags, string?, eModelImplementation)\" /> or <br/>\n\t\t/// <see cref=\"loadModelAsync(string, CancellationToken, eGpuModelFlags, string?, Action{double}?, eModelImplementation)\" /> factory function.</remarks>\n\t\tpublic static string[] listGraphicAdapters()\n\t\t{\n\t\t\tList<string> list = new List<string>();\n\t\t\tpfnListAdapters pfn = delegate ( string name, IntPtr pv )\n\t\t\t{\n\t\t\t\tDebug.Assert( pv == IntPtr.Zero );\n\t\t\t\tlist.Add( name );\n\t\t\t};\n\n\t\t\tNativeLogger.prologue();\n\t\t\tint hr = listGPUs( pfn, IntPtr.Zero );\n\t\t\tNativeLogger.throwForHR( hr );\n\n\t\t\treturn list.ToArray();\n\t\t}\n\t}\n}"
  },
  {
    "path": "WhisperNet/Readme.md",
    "content": "﻿This library implements high-performance GPGPU inference of OpenAI's Whisper automatic speech recognition (ASR) model.\n\nThe library requires a hardware GPU which supports Direct3D 11.0, a 64-bit Windows OS, only works within 64-bit processes, and requires a 64 bit CPU which supports [AVX1](https://en.wikipedia.org/wiki/Advanced_Vector_Extensions) and [F16C](https://en.wikipedia.org/wiki/F16C) extensions.\n\nThe main entry point of the llibrary is `Whisper.Library` static class.\nCall `loadModel` function from that class to load an ML model from a binary file.\n\nThese binary files are available for free download on [Hugging Face](https://huggingface.co/ggerganov/whisper.cpp/tree/main).\nI recommend `ggml-medium.bin` (1.42GB in size, but that web page says 1.53 GB), because I’ve mostly tested the software with that model.\nCompressed models in ZIP format with `mlmodelc` in the file name are not supported.\n\nOnce the model is loaded, create a context by calling `createContext` extension method,\nthen use that object to transcribe or translate multimedia files or realtime audio captured from microphones."
  },
  {
    "path": "WhisperNet/WhisperNet.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\t<PropertyGroup>\n\t\t<TargetFramework>net6.0-windows</TargetFramework>\n\t\t<ImplicitUsings>enable</ImplicitUsings>\n\t\t<Nullable>enable</Nullable>\n\t\t<CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>\n\t\t<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>\n\t\t<GenerateDocumentationFile>True</GenerateDocumentationFile>\n\t\t<AllowUnsafeBlocks>True</AllowUnsafeBlocks>\n\t\t<RootNamespace>Whisper</RootNamespace>\n\t\t<GenerateAssemblyInfo>false</GenerateAssemblyInfo>\n\t\t<PlatformTarget>x64</PlatformTarget>\n\t</PropertyGroup>\n\t<PropertyGroup Condition=\"'$(Configuration)'=='Release'\">\n\t\t<GeneratePackageOnBuild>True</GeneratePackageOnBuild>\n\t\t<NuspecFile>WhisperNet.nuspec</NuspecFile>\n\t</PropertyGroup>\n\t<ItemGroup>\n\t\t<Content Include=\"..\\x64\\Release\\Whisper.dll\" Link=\"Whisper.dll\" />\n\t</ItemGroup>\n\t<ItemGroup>\n\t\t<PackageReference Include=\"ComLightInterop\" Version=\"1.3.7\" />\n\t</ItemGroup>\n</Project>"
  },
  {
    "path": "WhisperNet/WhisperNet.nuspec",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<package xmlns=\"http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd\">\n\t<metadata>\n\t\t<id>WhisperNet</id>\n\t\t<version>1.12.0</version>\n\t\t<authors>Konstantin, const.me</authors>\n\t\t<license type=\"expression\">MPL-2.0</license>\n\t\t<projectUrl>https://github.com/Const-me/Whisper</projectUrl>\n\t\t<description>High-performance GPGPU inference of OpenAI's Whisper automatic speech recognition (ASR) model</description>\n\t\t<releaseNotes>\n\t\t\tUpdated models source URL in the documentation.\n\t\t\tReliability enhancement, microphone capture less likely to transition to “Stalled” state and discard the audio.\n\t\t</releaseNotes>\n\t\t<copyright>Copyright © const.me, 2022-2023</copyright>\n\t\t<tags>whisper, gpgpu, speech recognition</tags>\n\t\t<repository type=\"git\" url=\"https://github.com/Const-me/Whisper.git\" />\n\t\t<dependencies>\n\t\t\t<group targetFramework=\"net6.0\">\n\t\t\t\t<dependency id=\"ComLightInterop\" version=\"1.3.7\" />\n\t\t\t</group>\n\t\t</dependencies>\n\t\t<readme>docs/Readme.md</readme>\n\t</metadata>\n\t<files>\n\t\t<!-- Managed DLL with XML documentation -->\n\t\t<file src=\"bin/Release/WhisperNet.dll\" target=\"lib/net6.0/\" />\n\t\t<file src=\"bin/Release/WhisperNet.xml\" target=\"lib/net6.0/\" />\n\t\t<!-- The C++ DLL -->\n\t\t<file src=\"../x64/Release/Whisper.dll\" target=\"runtimes/win-x64/native/\" />\n\t\t<!-- Readme for the nuget package -->\n\t\t<file src=\"Readme.md\" target=\"docs/\" />\n\t</files>\n</package>"
  },
  {
    "path": "WhisperPS/Commands/ExportBase.cs",
    "content": "﻿using System;\nusing System.IO;\nusing System.Management.Automation;\n\nnamespace Whisper\n{\n\t/// <summary>Base class for commands which export results into some text-based format</summary>\n\tpublic abstract class ExportBase: PSCmdlet\n\t{\n\t\t/// <summary>\n\t\t/// <para type=\"synopsis\">Transcribe result produced by <see cref=\"TranscribeFile\" /></para>\n\t\t/// <para type=\"inputType\">It requires the value of the correct type</para>\n\t\t/// </summary>\n\t\t[Parameter( Mandatory = true, ValueFromPipeline = true )]\n\t\tpublic Transcription source { get; set; }\n\n\t\t/// <summary>\n\t\t/// <para type=\"synopsis\">Output file to write</para>\n\t\t/// </summary>\n\t\t[Parameter( Mandatory = true, Position = 0 ), ValidateNotNullOrEmpty]\n\t\tpublic string path { get; set; }\n\n\t\t/// <summary>Performs execution of the command</summary>\n\t\tprotected override void ProcessRecord()\n\t\t{\n\t\t\tstring path = this.absolutePath( this.path );\n\t\t\tstring dir = Path.GetDirectoryName( path );\n\t\t\tDirectory.CreateDirectory( dir );\n\t\t\tif( File.Exists( path ) )\n\t\t\t\tif( !ShouldContinue( $\"Overwrite \\\"{path}\\\" ?\", \"The output file already exists\" ) )\n\t\t\t\t\treturn;\n\n\t\t\tvar results = source.getResult();\n\t\t\tusing( var stream = File.CreateText( path ) )\n\t\t\t\twrite( stream, results );\n\t\t}\n\n\t\t/// <summary>Actual implementation</summary>\n\t\tprotected abstract void write( StreamWriter stream, TranscribeResult transcribeResult );\n\t}\n}"
  },
  {
    "path": "WhisperPS/Commands/ExportSubrip.cs",
    "content": "﻿using System;\nusing System.Globalization;\nusing System.IO;\nusing System.Management.Automation;\n\nnamespace Whisper\n{\n\t/// <summary>\n\t/// <para type=\"synopsis\">Write transcribe results into SubRip format.</para>\n\t/// <para type=\"description\">The format is documented there: https://en.wikipedia.org/wiki/SubRip#SubRip_file_format</para>\n\t/// </summary>\n\t/// <example><code>Export-SubRip $transcribeResults -path transcript.srt</code></example>\n\t[Cmdlet( VerbsData.Export, \"SubRip\" )]\n\tpublic sealed class ExportSubrip: ExportBase\n\t{\n\t\t/// <summary>\n\t\t/// <para type=\"synopsis\">Optional integer offset to the indices</para>\n\t\t/// </summary>\n\t\t[Parameter]\n\t\tpublic int offset { get; set; } = 0;\n\n\t\tstatic string printTimeWithComma( TimeSpan ts ) =>\n\t\t\tts.ToString( \"hh':'mm':'ss','fff\", CultureInfo.InvariantCulture );\n\n\t\t/// <summary>Write that text</summary>\n\t\tprotected override void write( StreamWriter stream, TranscribeResult transcribeResult )\n\t\t{\n\t\t\tvar segments = transcribeResult.segments;\n\n\t\t\tfor( int i = 0; i < segments.Length; i++ )\n\t\t\t{\n\t\t\t\tstream.WriteLine( i + 1 + offset );\n\t\t\t\tsSegment seg = segments[ i ];\n\t\t\t\tstring begin = printTimeWithComma( seg.time.begin );\n\t\t\t\tstring end = printTimeWithComma( seg.time.end );\n\t\t\t\tstream.WriteLine( \"{0} --> {1}\", begin, end );\n\t\t\t\tstream.WriteLine( seg.text.Trim() );\n\t\t\t\tstream.WriteLine();\n\t\t\t}\n\t\t}\n\t}\n}"
  },
  {
    "path": "WhisperPS/Commands/ExportText.cs",
    "content": "﻿using System;\nusing System.Globalization;\nusing System.IO;\nusing System.Management.Automation;\n\nnamespace Whisper\n{\n\t/// <summary>\n\t/// <para type=\"synopsis\">Write transcribe results into SubRip format.</para>\n\t/// <para type=\"description\">The format is documented there: https://en.wikipedia.org/wiki/SubRip#SubRip_file_format</para>\n\t/// </summary>\n\t/// <example><code>Export-Text $transcribeResults -path transcript.txt -timestamps</code></example>\n\t[Cmdlet( VerbsData.Export, \"Text\" )]\n\tpublic sealed class ExportText: ExportBase\n\t{\n\t\t/// <summary>\n\t\t/// <para type=\"synopsis\">Specify this switch to include timestamps</para>\n\t\t/// </summary>\n\t\t[Parameter]\n\t\tpublic SwitchParameter timestamps { get; set; }\n\n\t\tstatic string printTime( TimeSpan ts ) =>\n\t\t\tts.ToString( \"hh':'mm':'ss'.'fff\", CultureInfo.InvariantCulture );\n\n\t\t/// <summary>Write that text</summary>\n\t\tprotected override void write( StreamWriter stream, TranscribeResult transcribeResult )\n\t\t{\n\t\t\tforeach( sSegment seg in transcribeResult.segments )\n\t\t\t{\n\t\t\t\tif( timestamps )\n\t\t\t\t{\n\t\t\t\t\tstring begin = printTime( seg.time.begin );\n\t\t\t\t\tstring end = printTime( seg.time.end );\n\t\t\t\t\tstream.Write( \"[{0} --> {1}]  \", begin, end );\n\t\t\t\t}\n\t\t\t\tstream.WriteLine( seg.text.Trim() );\n\t\t\t}\n\t\t}\n\t}\n}"
  },
  {
    "path": "WhisperPS/Commands/ExportWebVtt.cs",
    "content": "﻿using System;\nusing System.Globalization;\nusing System.IO;\nusing System.Management.Automation;\n\nnamespace Whisper\n{\n\t/// <summary>\n\t/// <para type=\"synopsis\">Write transcribe results into WebVTT format.</para>\n\t/// <para type=\"description\">The format is documented there: https://en.wikipedia.org/wiki/WebVTT</para>\n\t/// </summary>\n\t/// <example><code>Export-WebVTT $transcribeResults -path transcript.vtt</code></example>\n\t[Cmdlet( VerbsData.Export, \"WebVTT\" )]\n\tpublic sealed class ExportWebVTT: ExportBase\n\t{\n\t\tstatic string printTime( TimeSpan ts ) =>\n\t\t\tts.ToString( \"hh':'mm':'ss'.'fff\", CultureInfo.InvariantCulture );\n\n\t\t/// <summary>Write that text</summary>\n\t\tprotected override void write( StreamWriter stream, TranscribeResult transcribeResult )\n\t\t{\n\t\t\tvar segments = transcribeResult.segments;\n\n\t\t\tstream.WriteLine( \"WEBVTT\" );\n\t\t\tstream.WriteLine();\n\n\t\t\tforeach( sSegment seg in segments )\n\t\t\t{\n\t\t\t\tstring begin = printTime( seg.time.begin );\n\t\t\t\tstring end = printTime( seg.time.end );\n\t\t\t\tstream.WriteLine( \"{0} --> {1}\", begin, end );\n\t\t\t\tstream.WriteLine( seg.text );\n\t\t\t\tstream.WriteLine();\n\t\t\t}\n\t\t}\n\t}\n}"
  },
  {
    "path": "WhisperPS/Commands/FormatSegments.cs",
    "content": "﻿using System.Management.Automation;\n\nnamespace Whisper\n{\n\t/// <summary>\n\t/// <para type=\"synopsis\">Format transcribe results as a sequence of segments.</para>\n\t/// <para type=\"description\">Each segment has a pair of timestamps, and the text</para>\n\t/// </summary>\n\t/// <example><code>Format-Segments $transcribeResults</code></example>\n\t[Cmdlet( VerbsCommon.Format, \"Segments\" )]\n\tpublic sealed class FormatSegments: Cmdlet\n\t{\n\t\t/// <summary>\n\t\t/// <para type=\"synopsis\">Transcribe result produced by <see cref=\"TranscribeFile\" /></para>\n\t\t/// <para type=\"inputType\">It requires the value of the correct type</para>\n\t\t/// </summary>\n\t\t[Parameter( Mandatory = true, ValueFromPipeline = true )]\n\t\tpublic Transcription source { get; set; }\n\n\t\t/// <summary>Performs execution of the command</summary>\n\t\tprotected override void ProcessRecord()\n\t\t{\n\t\t\tvar res = source.getResult();\n\t\t\tforeach( var seg in res.segments )\n\t\t\t{\n\t\t\t\tSegment obj = new Segment( seg );\n\t\t\t\tWriteObject( obj );\n\t\t\t}\n\t\t}\n\t}\n}"
  },
  {
    "path": "WhisperPS/Commands/ListAdapters.cs",
    "content": "﻿using System.Management.Automation;\n\nnamespace Whisper\n{\n\t/// <summary>\n\t/// <para type=\"synopsis\">Produces list of the names of the GPUs available to Direct3D 11</para>\n\t/// <para type=\"description\">You can pass any of these strings to the <c>adapter</c> argument of the <c>Import-WhisperModel</c> cmdlet.</para>\n\t/// </summary>\n\t/// <example><code>Get-Adapters</code></example>\n\n\t[Cmdlet( VerbsCommon.Get, \"Adapters\" )]\n\tpublic sealed class ListAdapters: Cmdlet\n\t{\n\t\t/// <summary>Performs execution of the command</summary>\n\t\tprotected override void ProcessRecord()\n\t\t{\n\t\t\tstring[] arr = Library.listGraphicAdapters();\n\t\t\tforeach( string item in arr )\n\t\t\t\tWriteObject( item );\n\t\t}\n\t}\n}"
  },
  {
    "path": "WhisperPS/Commands/LoadModel.cs",
    "content": "﻿using System;\nusing System.IO;\nusing System.Management.Automation;\n\nnamespace Whisper\n{\n\t/// <summary>\n\t/// <para type=\"synopsis\">Load Whisper model from GGML binary file</para>\n\t/// <para type=\"description\">The command might take a while to complete, depending on the disk speed.</para>\n\t/// </summary>\n\t/// <example>\n\t/// <code>$m = Import-WhisperModel -path D:\\Data\\Whisper\\ggml-medium.bin</code>\n\t/// </example>\n\t/// <para type=\"link\" uri=\"https://huggingface.co/datasets/ggerganov/whisper.cpp\">Download models from Hugging Face</para>\n\n\t[Cmdlet( VerbsData.Import, \"WhisperModel\" )]\n\tpublic sealed class LoadModel: PSCmdlet\n\t{\n\t\t/// <summary>\n\t\t/// <para type=\"synopsis\">Path to the GGML file on disk</para>\n\t\t/// </summary>\n\t\t[Parameter( Mandatory = true, Position = 0 ), ValidateNotNullOrEmpty]\n\t\tpublic string path { get; set; }\n\n\t\t/// <summary>\n\t\t/// <para type=\"synopsis\">Optional name of the graphics adapter to use</para>\n\t\t/// <para type=\"description\">Use <c>Get-Adapters</c> command to list available graphics adapter in this computer</para>\n\t\t/// </summary>\n\t\t[Parameter( Mandatory = false )]\n\t\tpublic string adapter { get; set; }\n\n\t\t/// <summary>\n\t\t/// <para type=\"synopsis\">Advanced GPU flags</para>\n\t\t/// <para type=\"description\">To combine multiple flags, use comma for the separator, example:<br />\n\t\t/// <c>-flags UseReshapedMatMul, Wave64</c></para>\n\t\t/// </summary>\n\t\t[Parameter( Mandatory = false )]\n\t\tpublic eGpuModelFlags flags { get; set; }\n\n\t\tprotected override void BeginProcessing()\n\t\t{\n\t\t\tif( !Environment.Is64BitProcess )\n\t\t\t\tthrow new PSNotSupportedException( \"Whisper cmdlets require 64-bit version of PowerShell \" );\n\t\t\tbase.BeginProcessing();\n\t\t}\n\n\t\tvoid reportProgress( double progressValue ) =>\n\t\t\tthis.writeProgress( progressValue, \"Loading model\" );\n\n\t\t/// <summary>Performs execution of the command</summary>\n\t\tprotected override void ProcessRecord()\n\t\t{\n\t\t\tstring path = this.absolutePath( this.path );\n\t\t\tif( !File.Exists( path ) )\n\t\t\t\tthrow new FileNotFoundException( \"Model file not found\" );\n\n\t\t\tusing( var log = this.setupLog() )\n\t\t\t{\n\t\t\t\tiMediaFoundation mf = Library.initMediaFoundation();\n\t\t\t\tiModel model = Library.loadModel( path, flags, adapter, reportProgress );\n\t\t\t\tWriteObject( new Model( mf, model ) );\n\t\t\t}\n\t\t}\n\t}\n}"
  },
  {
    "path": "WhisperPS/Commands/TranscribeBase.cs",
    "content": "﻿using System;\nusing System.Management.Automation;\nusing Whisper.Internal;\n\nnamespace Whisper\n{\n\t/// <summary>Base class for transcribing cmdlets, it contains a few common parameters</summary>\n\tpublic abstract class TranscribeBase: PSCmdlet\n\t{\n\t\tconst eLanguage defaultLanguage = eLanguage.English;\n\n\t\t/// <summary>\n\t\t/// <para type=\"synopsis\">Whisper model in VRAM</para>\n\t\t/// <para type=\"description\">Use <c>Import-WhisperModel</c> command to load the model from disk</para>\n\t\t/// </summary>\n\t\t[Parameter( Mandatory = true, Position = 0 )]\n\t\tpublic Model model { get; set; }\n\n\t\t/// <summary>\n\t\t/// <para type=\"synopsis\">Language code to use</para>\n\t\t/// </summary>\n\t\t[Parameter( Mandatory = false )]\n\t\tpublic string language { get; set; }\n\n\t\t/// <summary>\n\t\t/// <para type=\"synopsis\">Specify this switch to translate the language into English</para>\n\t\t/// </summary>\n\t\t[Parameter( Mandatory = false )]\n\t\tpublic SwitchParameter translate { get; set; }\n\n\t\t/// <summary>\n\t\t/// <para type=\"synopsis\">Count of CPU threads to use</para>\n\t\t/// <para type=\"description\">Specify 1 to disable multithreading</para>\n\t\t/// </summary>\n\t\t[Parameter( Mandatory = false )]\n\t\tpublic int threads { get; set; } = Environment.ProcessorCount;\n\n\t\t/// <summary>\n\t\t/// <para type=\"synopsis\">Optional initial prompt for the model. For example, &quot;繁體中文&quot; for traditional Chinese, &quot;简体中文&quot; for simplified.</para>\n\t\t/// </summary>\n\t\t[Parameter( Mandatory = false )]\n\t\tpublic string prompt { get; set; }\n\n\t\t/// <summary>Convert the provided text into tokens</summary>\n\t\tinternal static int[] tokenize( iModel model, string text )\n\t\t{\n\t\t\tint[] result = null;\n\t\t\tpfnDecodedTokens pfn = delegate ( int[] arr, int length, IntPtr pv )\n\t\t\t{\n\t\t\t\tresult = arr;\n\t\t\t};\n\t\t\tmodel.tokenize( text, pfn, IntPtr.Zero );\n\t\t\treturn result;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// <para type=\"synopsis\">Maximum segment length in characters</para>\n\t\t/// <para type=\"description\">The default is 60</para>\n\t\t/// </summary>\n\t\t[Parameter( Mandatory = false )]\n\t\tpublic ushort maxSegmentLength { get; set; } = 60;\n\n\t\tinternal eLanguage languageCode { get; private set; } = eLanguage.English;\n\n\t\tstatic eLanguage parseLanguage( string lang )\n\t\t{\n\t\t\t// When no parameter supplied, default to English\n\t\t\tif( string.IsNullOrEmpty( lang ) )\n\t\t\t\treturn defaultLanguage;\n\n\t\t\t// Try human-readable names, such as \"Chinese\" or \"Ukrainian\"\n\t\t\teLanguage res;\n\t\t\tconst bool ignoreCase = true;\n\t\t\tif( Enum.TryParse( lang, ignoreCase, out res ) )\n\t\t\t\treturn res;\n\n\t\t\t// Try OpenAI's language codes, the 1988 version of ISO 639-1\n\t\t\teLanguage? nullable = Library.languageFromCode( lang );\n\t\t\tif( nullable.HasValue )\n\t\t\t\treturn nullable.Value;\n\n\t\t\tthrow new PSArgumentException( $\"Unable to parse the string \\\"{lang}\\\" into language\" );\n\t\t}\n\n\t\tinternal void validateLanguage()\n\t\t{\n\t\t\tlanguageCode = parseLanguage( language );\n\t\t\tif( languageCode == eLanguage.English && translate )\n\t\t\t\tthrow new ArgumentException( \"The translate feature translates speech to English.\\nIt’s not available when the audio language is already English.\", nameof( language ) );\n\t\t}\n\n\t\tinternal void applyParams( ref Parameters parameters )\n\t\t{\n\t\t\tparameters.language = languageCode;\n\t\t\tparameters.cpuThreads = threads;\n\t\t\tif( translate )\n\t\t\t\tparameters.flags |= eFullParamsFlags.Translate;\n\t\t\telse\n\t\t\t\tparameters.flags &= ~eFullParamsFlags.Translate;\n\t\t\tparameters.max_len = maxSegmentLength;\n\t\t}\n\n\t\tinternal sProgressSink makeProgressSink( string what )\n\t\t{\n\t\t\tsProgressSink res = default;\n\n\t\t\tres.pfn = delegate ( double progressValue, IntPtr context, IntPtr pv )\n\t\t\t{\n\t\t\t\ttry\n\t\t\t\t{\n\t\t\t\t\tthis.writeProgress( progressValue, what );\n\t\t\t\t\treturn 0;\n\t\t\t\t}\n\t\t\t\tcatch( Exception ex )\n\t\t\t\t{\n\t\t\t\t\treturn ex.HResult;\n\t\t\t\t}\n\t\t\t};\n\n\t\t\treturn res;\n\t\t}\n\t}\n}"
  },
  {
    "path": "WhisperPS/Commands/TranscribeFile.cs",
    "content": "﻿using System;\nusing System.IO;\nusing System.Management.Automation;\nusing Whisper.Internal;\nusing Whisper.Internals;\n\nnamespace Whisper\n{\n\t/// <summary>\n\t/// <para type=\"synopsis\">Transcribe audio file from disk</para>\n\t/// </summary>\n\t/// <example>\n\t/// <code>$res = Transcribe-File -model $m -path C:\\Temp\\SampleClips\\jfk.wav</code>\n\t/// </example>\n\t[Cmdlet( \"Transcribe\", \"File\" )]\n\tpublic sealed class TranscribeFile: TranscribeBase\n\t{\n\t\t/// <summary>\n\t\t/// <para type=\"synopsis\">Path to the input file</para>\n\t\t/// <para type=\"description\">The command supports most audio and video formats, with the notable exception of Ogg Vorbis.</para>\n\t\t/// </summary>\n\t\t[Parameter( Mandatory = true, Position = 1, ValueFromPipeline = true ), ValidateNotNullOrEmpty]\n\t\tpublic string path { get; set; }\n\n\t\tint[] promptTokens = null;\n\n\t\tprotected override void BeginProcessing()\n\t\t{\n\t\t\tif( null == model )\n\t\t\t\tthrow new PSArgumentNullException( nameof( model ) );\n\n\t\t\tvalidateLanguage();\n\n\t\t\tpromptTokens = null;\n\t\t\tif( !string.IsNullOrEmpty( prompt ) )\n\t\t\t\tpromptTokens = tokenize( model.model, prompt );\n\t\t}\n\n\t\t/// <summary>Performs execution of the command</summary>\n\t\tprotected override void ProcessRecord()\n\t\t{\n\t\t\tstring path = this.absolutePath( this.path );\n\t\t\tif( !File.Exists( path ) )\n\t\t\t\tthrow new FileNotFoundException( \"Input file not found\" );\n\n\t\t\tusing( var log = this.setupLog() )\n\t\t\tusing( iContext context = model.model.createContextInternal() )\n\t\t\tusing( iAudioReader reader = model.mf.openAudioFile( path ) )\n\t\t\t{\n\t\t\t\tsFullParams fullParams = context.fullDefaultParams( eSamplingStrategy.Greedy );\n\t\t\t\tapplyParams( ref fullParams.publicParams );\n\t\t\t\tsProgressSink progressSink = makeProgressSink( $\"Transcribing {path}\" );\n\n\t\t\t\tif( null == promptTokens || promptTokens.Length <= 0 )\n\t\t\t\t\tcontext.runStreamed( ref fullParams, ref progressSink, reader );\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tunsafe\n\t\t\t\t\t{\n\t\t\t\t\t\tfixed( int* p = promptTokens )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfullParams.prompt_tokens = (IntPtr)p;\n\t\t\t\t\t\t\tfullParams.prompt_n_tokens = promptTokens.Length;\n\t\t\t\t\t\t\tcontext.runStreamed( ref fullParams, ref progressSink, reader );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tvar obj = context.getResults( eResultFlags.Tokens | eResultFlags.NewObject );\n\t\t\t\tWriteObject( new Transcription( path, obj ) );\n\t\t\t}\n\t\t}\n\t}\n}"
  },
  {
    "path": "WhisperPS/Internal/MarshalEx.cs",
    "content": "﻿using System;\nusing System.Text;\n\nnamespace Whisper.Internal\n{\n\tstatic class MarshalEx\n\t{\n\t\t/// <summary>Workaround for the missing <c>Marshal.PtrToStringUTF8</c> method</summary>\n\t\tpublic static string PtrToStringUTF8( IntPtr ptr )\n\t\t{\n\t\t\tif( ptr != IntPtr.Zero )\n\t\t\t{\n\t\t\t\tunsafe\n\t\t\t\t{\n\t\t\t\t\tbyte* stringStart = (byte*)ptr;\n\t\t\t\t\tbyte* stringEnd = stringStart;\n\t\t\t\t\twhile( true )\n\t\t\t\t\t{\n\t\t\t\t\t\tif( 0 == *stringEnd )\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tstringEnd++;\n\t\t\t\t\t}\n\n\t\t\t\t\tint len = (int)( stringEnd - stringStart );\n\t\t\t\t\treturn Encoding.UTF8.GetString( stringStart, len );\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\t}\n}"
  },
  {
    "path": "WhisperPS/Internal/NativeLogger.cs",
    "content": "﻿using System;\nusing System.Runtime.CompilerServices;\nusing System.Runtime.ExceptionServices;\nusing System.Runtime.InteropServices;\n\nnamespace Whisper.Internal\n{\n\t/// <summary>Utility class to supply logging function pointer to the C++ library,<br/>\n\t/// and provide custom calling conventions to ComLight runtime to convert error messages printed in C++ into .NET exception messages</summary>\n\tpublic static class NativeLogger\n\t{\n\t\tinternal static void startup() { }\n\n\t\tstatic NativeLogger()\n\t\t{\n\t\t\tsink = logSink;\n\t\t\tsLoggerSetup setup = default;\n\t\t\tsetup.sink = sink;\n\t\t\tsetup.level = eLogLevel.Warning;\n\t\t\tLibrary.setupLogger( ref setup );\n\t\t}\n\n\t\tinternal static void setup( eLogLevel lvl, eLoggerFlags flags, pfnLogMessage pfn )\n\t\t{\n\t\t\tlogMessage = pfn;\n\n\t\t\tsLoggerSetup setup = default;\n\t\t\tsetup.sink = sink;\n\t\t\tsetup.level = lvl;\n\t\t\tsetup.flags = flags;\n\t\t\tLibrary.setupLogger( ref setup );\n\t\t}\n\n\t\t// This field is here to protect the function pointer from being collected by the GC\n\t\tstatic readonly pfnLoggerSink sink;\n\n\t\tstatic void logSink( IntPtr context, eLogLevel lvl, string message )\n\t\t{\n\t\t\tif( lvl == eLogLevel.Error )\n\t\t\t\tstate.setText( message );\n\t\t\tlogMessage?.Invoke( lvl, message );\n\t\t}\n\n\t\tsealed class ThreadState\n\t\t{\n\t\t\tstring errorText = null;\n\t\t\tExceptionDispatchInfo dispatchInfo = null;\n\n\t\t\tpublic void setText( string text ) => errorText = text;\n\t\t\tpublic void capture( Exception ex ) => dispatchInfo = ExceptionDispatchInfo.Capture( ex );\n\n\t\t\tpublic void clear()\n\t\t\t{\n\t\t\t\terrorText = null;\n\t\t\t\tdispatchInfo = null;\n\t\t\t}\n\n\t\t\tpublic void Deconstruct( out string text, out ExceptionDispatchInfo edi )\n\t\t\t{\n\t\t\t\ttext = errorText;\n\t\t\t\tedi = dispatchInfo;\n\t\t\t\terrorText = null;\n\t\t\t\tdispatchInfo = null;\n\t\t\t}\n\t\t}\n\n\t\t[ThreadStatic]\n\t\tstatic ThreadState state = new ThreadState();\n\n\t\tinternal static void captureException( Exception ex ) =>\n\t\t\tstate.capture( ex );\n\n\t\tstatic pfnLogMessage logMessage = null;\n\n\t\t/// <summary>Called internally by ComLight runtime</summary>\n\t\t[MethodImpl( MethodImplOptions.AggressiveInlining )]\n\t\tpublic static void prologue()\n\t\t{\n\t\t\t// https://stackoverflow.com/a/2043505/126995\n\t\t\tif( null != state )\n\t\t\t\tstate.clear();\n\t\t\telse\n\t\t\t\tcreateState();\n\t\t}\n\n\t\t[MethodImpl( MethodImplOptions.NoInlining )]\n\t\tstatic void createState()\n\t\t{\n\t\t\tstate = new ThreadState();\n\t\t}\n\n\t\t/// <summary>Epilogue implementation for unsuccessful status codes</summary>\n\t\t[MethodImpl( MethodImplOptions.NoInlining )]\n\t\tstatic void throwException( int hr )\n\t\t{\n\t\t\t// Move state from the thread local object into local variables, and clear that object\n\t\t\t(string text, ExceptionDispatchInfo edi) = state;\n\n\t\t\tif( null != edi && edi.SourceException.HResult == hr )\n\t\t\t{\n\t\t\t\t// The error comes from a callback, and we have original context of that exception.\n\t\t\t\t// Re-throw the original exception.\n\t\t\t\t// This uses the original error message, and even correctly deals with the stack trace.\n\t\t\t\tedi.Throw();\n\t\t\t}\n\n\t\t\tif( null != text )\n\t\t\t{\n\t\t\t\t// C++ code has printed an error on the current thread, between prologue and epilogue.\n\t\t\t\t// Use that text for the exception message.\n\t\t\t\tException ex = Marshal.GetExceptionForHR( hr );\n\t\t\t\tthrow new ApplicationException( text, ex );\n\t\t\t}\n\n\t\t\t// We don’t have any additional info about the exception.\n\t\t\t// Throw an exception from just the HRESULT code.\n\t\t\tMarshal.ThrowExceptionForHR( hr );\n\t\t}\n\n\t\t/// <summary>Called internally by ComLight runtime</summary>\n\t\t[MethodImpl( MethodImplOptions.AggressiveInlining )]\n\t\tpublic static void throwForHR( int hr )\n\t\t{\n\t\t\tif( hr >= 0 )\n\t\t\t\treturn; // SUCCEEDED\n\t\t\tthrowException( hr );\n\t\t}\n\n\t\t/// <summary>Called internally by ComLight runtime</summary>\n\t\t[MethodImpl( MethodImplOptions.AggressiveInlining )]\n\t\tpublic static bool throwAndReturnBool( int hr )\n\t\t{\n\t\t\tif( hr >= 0 )\n\t\t\t\treturn 0 == hr;\n\t\t\tthrowException( hr );\n\t\t\treturn false;\n\t\t}\n\t}\n}"
  },
  {
    "path": "WhisperPS/Internal/iTranscribeResult.cs",
    "content": "﻿#pragma warning disable CS0649 // Field is never assigned to\nusing ComLight;\nusing System;\nusing System.ComponentModel;\nusing System.Runtime.InteropServices;\nusing Whisper.Internal;\n\nnamespace Whisper.Internal\n{\n\t/// <summary>Size of the buffers owned by the <see cref=\"iTranscribeResult\" /> object</summary>\n\tpublic readonly struct sTranscribeLength\n\t{\n\t\t/// <summary>Count of segments</summary>\n\t\tpublic readonly int countSegments;\n\t\t/// <summary>Total count of tokens, for all segments combined</summary>\n\t\tpublic readonly int countTokens;\n\t}\n\n\t/// <summary>Output data from the model</summary>\n\t[ComInterface( \"2871a73f-5ce3-48f8-8779-6582ee11935e\", eMarshalDirection.ToManaged ), CustomConventions( typeof( NativeLogger ) )]\n\tpublic interface iTranscribeResult: IDisposable\n\t{\n\t\t/// <summary>Get size of the buffers</summary>\n\t\t[RetValIndex, EditorBrowsable( EditorBrowsableState.Never )]\n\t\tsTranscribeLength getSize();\n\n\t\t/// <summary>Pointer to segment data, a vector of <see cref=\"sSegment\" /> structures</summary>\n\t\t[EditorBrowsable( EditorBrowsableState.Never )]\n\t\tIntPtr getSegments();\n\n\t\t/// <summary>Pointer to tokens data, a vector of <see cref=\"sToken\" /> structures</summary>\n\t\t[EditorBrowsable( EditorBrowsableState.Never )]\n\t\tIntPtr getTokens();\n\t}\n}\n\nnamespace Whisper\n{\n\t/// <summary>Start and end times of a segment or token</summary>\n\t/// <remarks>The times are relative to the start of the media</remarks>\n\tpublic readonly struct sTimeInterval\n\t{\n\t\t/// <summary>Start time</summary>\n\t\tpublic readonly TimeSpan begin;\n\t\t/// <summary>End time</summary>\n\t\tpublic readonly TimeSpan end;\n\t}\n\n\t/// <summary>Segment data</summary>\n\tpublic readonly struct sSegment\n\t{\n\t\tinternal readonly IntPtr m_text;\n\t\t/// <summary>Segment text</summary>\n\t\tpublic string text => MarshalEx.PtrToStringUTF8( m_text );\n\t\t/// <summary>Start and end times of the segment</summary>\n\t\tpublic readonly sTimeInterval time;\n\t\t/// <summary>Slice of the tokens</summary>\n\t\tpublic readonly int firstToken, countTokens;\n\t}\n\n\t/// <summary>Token flags</summary>\n\t[Flags]\n\tpublic enum eTokenFlags: uint\n\t{\n\t\t/// <summary>The token is special</summary>\n\t\tSpecial = 1,\n\t}\n\n\t/// <summary>Token data</summary>\n\tpublic readonly struct sToken\n\t{\n\t\tinternal readonly IntPtr m_text;\n\t\t/// <summary>Token text</summary>\n\t\tpublic string text => MarshalEx.PtrToStringUTF8( m_text );\n\t\t/// <summary>Start and end times of the token</summary>\n\t\tpublic readonly sTimeInterval time;\n\t\t/// <summary>Probability of the token</summary>\n\t\tpublic readonly float probability;\n\t\t/// <summary>Probability of the timestamp token</summary>\n\t\tpublic readonly float probabilityTimestamp;\n\t\t/// <summary>Sum of probabilities of all timestamp tokens</summary>\n\t\tpublic readonly float ptsum;\n\t\t/// <summary>Voice length of the token</summary>\n\t\tpublic readonly float vlen;\n\t\t/// <summary>Token id</summary>\n\t\tpublic readonly int id;\n\t\t/// <summary>Token flags</summary>\n\t\treadonly eTokenFlags flags;\n\t\t/// <summary>True if the token flags has the specified bit set</summary>\n\t\tpublic bool hasFlag( eTokenFlags bit ) => flags.HasFlag( bit );\n\t}\n\n\t/// <summary>Output data from the model</summary>\n\tpublic readonly ref struct TranscribeResult\n\t{\n\t\t/// <summary>Segments in the results</summary>\n\t\tpublic readonly ReadOnlySpan<sSegment> segments;\n\t\t/// <summary>Tokens in the results, for all segments</summary>\n\t\tpublic readonly ReadOnlySpan<sToken> tokens;\n\n\t\tinternal TranscribeResult( Internal.iTranscribeResult i )\n\t\t{\n\t\t\tInternal.sTranscribeLength len = i.getSize();\n\t\t\tunsafe\n\t\t\t{\n\t\t\t\t// This does not copy the buffers to managed memory.\n\t\t\t\t// Instead, the C# spans directly reference the native memory stored in these std::vectors\n\t\t\t\tif( len.countSegments > 0 )\n\t\t\t\t\tsegments = new ReadOnlySpan<sSegment>( (void*)i.getSegments(), len.countSegments );\n\t\t\t\telse\n\t\t\t\t\tsegments = ReadOnlySpan<sSegment>.Empty;\n\n\t\t\t\tif( len.countTokens > 0 )\n\t\t\t\t\ttokens = new ReadOnlySpan<sToken>( (void*)i.getTokens(), len.countTokens );\n\t\t\t\telse\n\t\t\t\t\ttokens = ReadOnlySpan<sToken>.Empty;\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>Get tokens for the specified segment</summary>\n\t\tpublic ReadOnlySpan<sToken> getTokens( in sSegment seg ) =>\n\t\t\ttokens.Slice( seg.firstToken, seg.countTokens );\n\t}\n}"
  },
  {
    "path": "WhisperPS/Internal/sCaptureDevice.cs",
    "content": "﻿#pragma warning disable CS0649 // Field is never assigned to\nusing System;\nusing System.Runtime.InteropServices;\n\nnamespace Whisper.Internal\n{\n\t/// <summary>Identifiers for an audio capture device</summary>\n\tpublic struct sCaptureDevice\n\t{\n\t\treadonly IntPtr m_displayName;\n\t\t/// <summary>The display name is suitable for showing to the user, but might not be unique.</summary>\n\t\tpublic string displayName => Marshal.PtrToStringUni( m_displayName );\n\n\t\treadonly IntPtr m_endpoint;\n\t\t/// <summary>Endpoint ID for an audio capture device.<br/>\n\t\t/// It uniquely identifies the device on the system, but is not a readable string.</summary>\n\t\tpublic string endpoint => Marshal.PtrToStringUni( m_endpoint );\n\t}\n\n\t/// <summary>Function pointer to consume a list of audio capture device IDs</summary>\n\t[UnmanagedFunctionPointer( CallingConvention.StdCall )]\n\tpublic delegate int pfnFoundCaptureDevices( int len, [In, MarshalAs( UnmanagedType.LPArray, SizeParamIndex = 0 )] sCaptureDevice[] arr, IntPtr pv );\n}"
  },
  {
    "path": "WhisperPS/Internal/sFullParams.cs",
    "content": "﻿#pragma warning disable CS0649  // Field is never assigned to\n\n// Missing XML comment for publicly visible type or member\n// TODO: remove this line and document them.\n#pragma warning disable CS1591\n\nusing System;\nusing System.Runtime.InteropServices;\n\nnamespace Whisper.Internals\n{\n\t/// <summary>This callback is called on each new segment</summary>\n\t[UnmanagedFunctionPointer( CallingConvention.Cdecl )]\n\tdelegate int pfnNewSegment( IntPtr ctx, int countNew, IntPtr userData );\n\n\t/// <summary>The callback is called before every encoder run. If it returns S_FALSE, the processing is aborted.</summary>\n\t[UnmanagedFunctionPointer( CallingConvention.Cdecl )]\n\tdelegate int pfnEncoderBegin( IntPtr ctx, IntPtr userData );\n\n\t/// <summary>Transcribe parameters</summary>\n\tpublic struct sFullParams\n\t{\n\t\tinternal Parameters publicParams;\n\t\t// The rest of these parameters are not exposed to the user-friendly public API of this DLL\n\n\t\tinternal IntPtr prompt_tokens;\n\t\tinternal int prompt_n_tokens;\n\n\t\t/// <summary>This callback is called on each new segment</summary>\n\t\t[MarshalAs( UnmanagedType.FunctionPtr )]\n\t\tinternal pfnNewSegment newSegmentCallback;\n\t\t/// <summary>Parameter for the above, not needed in C#</summary>\n\t\tinternal IntPtr newSegmentCallbackData;\n\n\t\t/// <summary>The callback is called before every encoder run. If it returns false, the processing is aborted</summary>\n\t\t[MarshalAs( UnmanagedType.FunctionPtr )]\n\t\tinternal pfnEncoderBegin encoderBeginCallback;\n\t\t/// <summary>Parameter for the above, not needed in C#</summary>\n\t\tinternal IntPtr encoderBeginCallbackData;\n\t}\n}"
  },
  {
    "path": "WhisperPS/Internal/sLoadModelCallbacks.cs",
    "content": "﻿using System;\nusing System.Runtime.InteropServices;\nusing System.Threading;\n\nnamespace Whisper.Internal\n{\n\t/// <summary>Function pointer to report model loading progress</summary>\n\t[UnmanagedFunctionPointer( CallingConvention.StdCall )]\n\tdelegate int pfnLoadProgress( double progress, IntPtr pv );\n\n\t/// <summary>Function pointer to implement cooperative cancellation</summary>\n\t[UnmanagedFunctionPointer( CallingConvention.StdCall )]\n\tdelegate int pfnCancel( IntPtr pv );\n\n\t/// <summary>Callback functions for loading models</summary>\n\tpublic struct sLoadModelCallbacks\n\t{\n\t\t/// <summary>Function pointer to report model loading progress</summary>\n\t\t[MarshalAs( UnmanagedType.FunctionPtr )]\n\t\tpfnLoadProgress progress;\n\n\t\t/// <summary>Function pointer to implement cooperative cancellation</summary>\n\t\t[MarshalAs( UnmanagedType.FunctionPtr )]\n\t\tpfnCancel cancel;\n\n\t\t// Not needed in C#, delegates can capture things\n\t\tIntPtr pv;\n\n\t\t/// <summary>Wrap idiomatic C# things into these low-level C callbacks</summary>\n\t\tinternal sLoadModelCallbacks( CancellationToken cancelToken, Action<double> pfnProgress )\n\t\t{\n\t\t\tif( cancelToken != CancellationToken.None )\n\t\t\t{\n\t\t\t\tcancel = delegate ( IntPtr pv )\n\t\t\t\t{\n\t\t\t\t\tif( cancelToken.IsCancellationRequested )\n\t\t\t\t\t\treturn 1;   // S_FALSE\n\t\t\t\t\treturn 0;   // S_OK\n\t\t\t\t};\n\t\t\t}\n\t\t\telse\n\t\t\t\tcancel = null;\n\n\t\t\tif( null != pfnProgress )\n\t\t\t{\n\t\t\t\tprogress = delegate ( double val, IntPtr pv )\n\t\t\t\t{\n\t\t\t\t\ttry\n\t\t\t\t\t{\n\t\t\t\t\t\tpfnProgress( val );\n\t\t\t\t\t\treturn 0;   // S_OK\n\t\t\t\t\t}\n\t\t\t\t\tcatch( Exception ex )\n\t\t\t\t\t{\n\t\t\t\t\t\tNativeLogger.captureException( ex );\n\t\t\t\t\t\treturn ex.HResult;\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t}\n\t\t\telse\n\t\t\t\tprogress = null;\n\n\t\t\tpv = IntPtr.Zero;\n\t\t}\n\t}\n}"
  },
  {
    "path": "WhisperPS/Internal/sModelSetup.cs",
    "content": "﻿using System;\nusing System.Runtime.InteropServices;\n\nnamespace Whisper.Internal\n{\n\tstruct sModelSetup\n\t{\n\t\teModelImplementation impl;\n\t\teGpuModelFlags flags;\n\t\t[MarshalAs( UnmanagedType.LPWStr )]\n\t\tstring adapter;\n\n\t\tpublic sModelSetup( eGpuModelFlags flags, eModelImplementation impl, string adapter )\n\t\t{\n\t\t\tthis.impl = impl;\n\t\t\tthis.flags = flags;\n\t\t\tthis.adapter = adapter;\n\t\t}\n\t}\n\n\t[UnmanagedFunctionPointer( CallingConvention.StdCall )]\n\tdelegate void pfnListAdapters( [In, MarshalAs( UnmanagedType.LPWStr )] string name, IntPtr pv );\n}"
  },
  {
    "path": "WhisperPS/Internal/sProgressSink.cs",
    "content": "﻿#pragma warning disable CS0649  // Field is never assigned to\nusing System;\nusing System.Runtime.InteropServices;\n\nnamespace Whisper.Internal\n{\n\t/// <summary>A callback to get notified about the progress</summary>\n\t[UnmanagedFunctionPointer( CallingConvention.StdCall )]\n\tdelegate int pfnReportProgress( double value, IntPtr context, IntPtr pv );\n\n\t/// <summary>C structure with a progress reporting function pointer</summary>\n\tpublic struct sProgressSink\n\t{\n\t\t/// <summary>A callback to get notified about the progress</summary>\n\t\t[MarshalAs( UnmanagedType.FunctionPtr )]\n\t\tinternal pfnReportProgress pfn;\n\n\t\t/// <summary>Last parameter to the callback</summary>\n\t\tinternal IntPtr pv;\n\t}\n}"
  },
  {
    "path": "WhisperPS/Library.cs",
    "content": "﻿using ComLight;\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Runtime.InteropServices;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing Whisper.Internal;\n\nnamespace Whisper\n{\n\t/// <summary>Factory methods implemented by the C++ DLL</summary>\n\tstatic class Library\n\t{\n\t\tstatic Library()\n\t\t{\n\t\t\tif( Environment.OSVersion.Platform != PlatformID.Win32NT )\n\t\t\t\tthrow new ApplicationException( \"This library requires Windows OS\" );\n\t\t\tif( !Environment.Is64BitProcess )\n\t\t\t\tthrow new ApplicationException( \"This library only works in 64-bit processes\" );\n\t\t\tif( RuntimeInformation.ProcessArchitecture != Architecture.X64 )\n\t\t\t\tthrow new ApplicationException( \"This library requires a processor with AMD64 instruction set\" );\n\t\t\tNativeLogger.startup();\n\t\t}\n\n\t\tconst string dll = \"Whisper.dll\";\n\n\t\t[DllImport( dll, CallingConvention = RuntimeClass.defaultCallingConvention, PreserveSig = false )]\n\t\tinternal static extern void setupLogger( [In] ref sLoggerSetup setup );\n\n\t\t[DllImport( dll, CallingConvention = RuntimeClass.defaultCallingConvention, PreserveSig = true )]\n\t\tstatic extern int loadModel( [MarshalAs( UnmanagedType.LPWStr )] string path,\n\t\t\t[In] ref sModelSetup setup, [In] ref sLoadModelCallbacks callbacks,\n\t\t\t[MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Marshaler<iModel> ) )] out iModel model );\n\n\t\t/// <summary>Load Whisper model from GGML file on disk</summary>\n\t\t/// <remarks>Models are large, depending on user’s disk speed this might take a while, and this function blocks the calling thread.<br/>\n\t\t/// Consider <see cref=\"loadModelAsync\" /> instead.</remarks>\n\t\t/// <seealso href=\"https://huggingface.co/datasets/ggerganov/whisper.cpp\" />\n\t\tpublic static iModel loadModel( string path, eGpuModelFlags flags = eGpuModelFlags.None,\n\t\t\tstring adapter = null, Action<double> progress=null, eModelImplementation impl = eModelImplementation.GPU )\n\t\t{\n\t\t\tiModel model;\n\t\t\tsModelSetup setup = new sModelSetup( flags, impl, adapter );\n\t\t\tsLoadModelCallbacks callbacks = new sLoadModelCallbacks( CancellationToken.None, progress );\n\t\t\tNativeLogger.prologue();\n\t\t\tint hr = loadModel( path, ref setup, ref callbacks, out model );\n\t\t\tNativeLogger.throwForHR( hr );\n\t\t\treturn model;\n\t\t}\n\n\t\t/// <summary>Load Whisper model on a background thread, with optional progress reporting and cancellation</summary>\n\t\tpublic static Task<iModel> loadModelAsync( string path, CancellationToken cancelToken,\n\t\t\teGpuModelFlags flags = eGpuModelFlags.None, string adapter = null,\n\t\t\tAction<double> pfnProgress = null, eModelImplementation impl = eModelImplementation.GPU )\n\t\t{\n\t\t\tTaskCompletionSource<iModel> tcs = new TaskCompletionSource<iModel>();\n\n\t\t\tWaitCallback wcb = delegate ( object state )\n\t\t\t{\n\t\t\t\ttry\n\t\t\t\t{\n\t\t\t\t\tsModelSetup setup = new sModelSetup( flags, impl, adapter );\n\t\t\t\t\tsLoadModelCallbacks callbacks = new sLoadModelCallbacks( cancelToken, pfnProgress );\n\n\t\t\t\t\tiModel model;\n\t\t\t\t\tNativeLogger.prologue();\n\t\t\t\t\tint hr = loadModel( path, ref setup, ref callbacks, out model );\n\t\t\t\t\tNativeLogger.throwForHR( hr );\n\n\t\t\t\t\ttcs.SetResult( model );\n\t\t\t\t}\n\t\t\t\tcatch( Exception ex )\n\t\t\t\t{\n\t\t\t\t\ttcs.SetException( ex );\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tThreadPool.QueueUserWorkItem( wcb );\n\t\t\treturn tcs.Task;\n\t\t}\n\n\t\t[DllImport( dll, CallingConvention = RuntimeClass.defaultCallingConvention, PreserveSig = true )]\n\t\tstatic extern int initMediaFoundation( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Marshaler<iMediaFoundation> ) )] out iMediaFoundation mf );\n\n\t\t/// <summary>Initialize Media Foundation runtime</summary>\n\t\tpublic static iMediaFoundation initMediaFoundation()\n\t\t{\n\t\t\tiMediaFoundation mf;\n\t\t\tNativeLogger.prologue();\n\t\t\tint hr = initMediaFoundation( out mf );\n\t\t\tNativeLogger.throwForHR( hr );\n\t\t\treturn mf;\n\t\t}\n\n\t\t// The .NET runtime uses UTF-16 for the strings, so we only need the Unicode version of this function.\n\t\t// The native DLL exports both Unicode and ASCII versions.\n\t\t[DllImport( dll, CallingConvention = RuntimeClass.defaultCallingConvention, PreserveSig = true )]\n\t\tstatic extern uint findLanguageKeyW( [MarshalAs( UnmanagedType.LPWStr )] string lang );\n\n\t\t/// <summary>Try to resolve language code string like <c>\"en\"</c>, <c>\"pl\"</c> or <c>\"uk\"</c> into the strongly-typed enum.</summary>\n\t\t/// <remarks>The function is case-sensitive, <c>\"EN\"</c> or <c>\"UK\"</c> gonna fail.</remarks>\n\t\tpublic static eLanguage? languageFromCode( string lang )\n\t\t{\n\t\t\tuint key = findLanguageKeyW( lang );\n\t\t\tif( key != uint.MaxValue )\n\t\t\t\treturn (eLanguage)key;\n\t\t\treturn null;\n\t\t}\n\n\t\t/// <summary>Set up delegate to receive log messages from the C++ library</summary>\n\t\tpublic static void setLogSink( eLogLevel lvl, eLoggerFlags flags = eLoggerFlags.SkipFormatMessage, pfnLogMessage pfn = null )\n\t\t{\n\t\t\tNativeLogger.setup( lvl, flags, pfn );\n\t\t}\n\n\t\t[DllImport( dll, CallingConvention = RuntimeClass.defaultCallingConvention, PreserveSig = true )]\n\t\tstatic extern int listGPUs( [MarshalAs( UnmanagedType.FunctionPtr )] pfnListAdapters pfn, IntPtr pv );\n\n\t\t/// <summary>Enumerate graphics adapters on this computer, and return their names.</summary>\n\t\t/// <remarks>To manually select the GPU to use for the inference, pass one of these names to<br/>\n\t\t/// <see cref=\"loadModel(string, eGpuModelFlags, string, eModelImplementation)\" /> or <br/>\n\t\t/// <see cref=\"loadModelAsync(string, CancellationToken, eGpuModelFlags, string, Action{double}, eModelImplementation)\" /> factory function.</remarks>\n\t\tpublic static string[] listGraphicAdapters()\n\t\t{\n\t\t\tList<string> list = new List<string>();\n\t\t\tpfnListAdapters pfn = delegate ( string name, IntPtr pv )\n\t\t\t{\n\t\t\t\tDebug.Assert( pv == IntPtr.Zero );\n\t\t\t\tlist.Add( name );\n\t\t\t};\n\n\t\t\tNativeLogger.prologue();\n\t\t\tint hr = listGPUs( pfn, IntPtr.Zero );\n\t\t\tNativeLogger.throwForHR( hr );\n\n\t\t\treturn list.ToArray();\n\t\t}\n\t}\n}"
  },
  {
    "path": "WhisperPS/Properties/AssemblyTitle.cs",
    "content": "﻿using System.Reflection;\nusing System.Runtime.InteropServices;\n\n[assembly: AssemblyTitle( \"WhisperPS\" )]\n[assembly: AssemblyDescription( \"DirectCompute port of whisper.cpp library, PowerShell 5.1 bindings\" )]\n[assembly: Guid( \"6909b760-ff72-48f5-8493-2e956cbb7cec\" )]"
  },
  {
    "path": "WhisperPS/Readme.md",
    "content": "﻿The `WhisperPS.csproj` project in this folder builds PowerShell wrapper for Whisper.\n\nI wouldn’t call the wrapper particularly great, but it works on my computer.<br/>\nThis should handle use cases like “transcribe all files in a directory” or “export multiple formats”.\n\nThe supported PowerShell version is 5.1, the one I have preinstalled on my Windows 10 computer.<br/>\nI wouldn’t expect it to work with the newer PowerShell Core, the runtime is different.\n\n## Installation\n\nTo install from [PowerShell Gallery](https://www.powershellgallery.com/) for the current user,\nopen Windows PowerShell, and run the following command:<br/>\n`Install-Module -Name WhisperPS -Scope CurrentUser`\n\nTo install from PowerShell Gallery for all users,\nopen Windows PowerShell **as an administrator**, and run the following command:<br/>\n`Install-Module -Name WhisperPS`\n\nTo install manually from github.com,\ndownload WhisperPS.zip from [Releases](https://github.com/Const-me/Whisper/releases) page of this repository,\nand extract into the following folder:<br/>\n`%USERPROFILE%\\Documents\\WindowsPowerShell\\Modules`<br/>\nCreate that folder if you don’t yet have it.\n\n## Models\n\nThe binary files with the models are available for free download on [Hugging Face](https://huggingface.co/ggerganov/whisper.cpp/tree/main).<br/>\nI recommend `ggml-medium.bin` (1.42GB in size, but that web page says 1.53 GB), because I’ve mostly tested the software with that model.<br/>\nCompressed models in ZIP format with `mlmodelc` in the file name are not supported.\n\n## Usage Example\n\n```\nImport-Module WhisperPS -DisableNameChecking\n$Model = Import-WhisperModel D:\\Data\\Whisper\\ggml-medium.bin\ncd C:\\Temp\\2remove\\Whisper\n$Results = dir .\\* -include *.wma, *.wav | Transcribe-File $Model\nforeach ( $i in $Results ) { $txt = $i.SourceName + \".txt\"; $i | Export-Text $txt; }\nforeach ( $i in $Results ) { $txt = $i.SourceName + \".ts.txt\"; $i | Export-Text $txt -timestamps; }\n```\n\n## Commands\n\nHere’s the list of commands implemented by this module.\n\n* `Get-Adapters` prints names of the graphics adapters visible to DirectCompute.<br/>\nYou can use these strings for the `-adapter` argument of the `Import-WhisperModel` command.\n* `Import-WhisperModel` loads the model from disk, returns the object which keeps the model\n* `Transcribe-File` loads audio file from disk, and transcribes the audio into text.\nIt returns the object which keeps both source file name, and transcribed text.\n* `Export-Text` saves transcribed text into a text file, with or without timestamps.\n* `Export-SubRip` saves transcribed text in *.srt format.\n* `Export-WebVTT` saves transcribed text in *.vtt format.\n\nYou can use `man` (an alias for `get-help`) for detailed documentation on specific commands, example:<br/>\n`man Import-WhisperModel -full`\n\n## Miscellaneous\n\nBy default, PowerShell doesn’t print any informational or debug messages.<br/>\nIf you want these messages, run these commands:\n\n```\n$InformationPreference=\"Continue\"\n$DebugPreference=\"Continue\"\n```\n\n[Apparently](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_preference_variables?view=powershell-5.1),\nthe default value for these preference variables is `SilentlyContinue` so by default the messages go nowhere.\n\nUnfortunately, PowerShell 5.1 uses legacy .NET framework 4.8.<br />\nThat’s why I couldn’t simply consume `WhisperNet` library in this project.<br />\nInstead I implemented slightly different C# wrappers for the same C++ DLL.\n\nLuckily, my [ComLightInterop](https://www.nuget.org/packages/ComLightInterop/) library is old,\nand it still supports legacy .NET framework, along with other old versions of the runtime like .NET Core 2.1."
  },
  {
    "path": "WhisperPS/Types/Model.cs",
    "content": "﻿using System;\n\nnamespace Whisper\n{\n\t/// <summary>\n\t/// <para type=\"synopsis\">This object holds a Whisper model, loaded from disk to VRAM on the GPU.</para>\n\t/// <para type=\"description\">For large models, the data size may exceed 4GB of video memory</para>\n\t/// </summary>\n\tpublic sealed class Model: IDisposable\n\t{\n\t\tinternal iMediaFoundation mf { get; private set; }\n\t\tinternal iModel model { get; private set; }\n\n\t\tinternal Model( iMediaFoundation mf, iModel model )\n\t\t{\n\t\t\tthis.mf = mf;\n\t\t\tthis.model = model;\n\t\t}\n\n\t\tpublic void Dispose()\n\t\t{\n\t\t\tmf?.Dispose();\n\t\t\tmf = null;\n\t\t\tmodel?.Dispose();\n\t\t\tmodel = null;\n\t\t\tGC.SuppressFinalize( this );\n\t\t}\n\n\t\t~Model()\n\t\t{\n\t\t\tmf?.Dispose();\n\t\t\tmf = null;\n\t\t\tmodel?.Dispose();\n\t\t\tmodel = null;\n\t\t}\n\t}\n}"
  },
  {
    "path": "WhisperPS/Types/Segment.cs",
    "content": "﻿using System;\n\nnamespace Whisper\n{\n\t/// <summary>One text segment of a transcription</summary>\n\tpublic sealed class Segment\n\t{\n\t\tinternal Segment( in sSegment seg )\n\t\t{\n\t\t\tBegin = seg.time.begin;\n\t\t\tEnd = seg.time.end;\n\t\t\tText = seg.text.Trim();\n\t\t}\n\n\t\t/// <summary>Timestamp of the start of the segment, since the start of the media</summary>\n\t\tpublic TimeSpan Begin { get; }\n\t\t/// <summary>Timestamp of the end of the segment, since the start of the media</summary>\n\t\tpublic TimeSpan End { get; }\n\n\t\t/// <summary>Text of the segment</summary>\n\t\tpublic string Text { get; }\n\n\t\t/// <summary>A string representation of this object</summary>\n\t\tpublic override string ToString() => Text;\n\t}\n}"
  },
  {
    "path": "WhisperPS/Types/Transcription.cs",
    "content": "﻿using System;\nusing System.IO;\nusing System.Text;\nusing Whisper.Internal;\n\nnamespace Whisper\n{\n\t/// <summary>\n\t/// <para type=\"synopsis\">This object holds the results of a transcription.</para>\n\t/// <para type=\"description\">It retains all data produced by the language model, including tokens and probabilities</para>\n\t/// </summary>\n\tpublic sealed class Transcription: IDisposable\n\t{\n\t\tiTranscribeResult result;\n\n\t\tinternal Transcription( string src, iTranscribeResult result )\n\t\t{\n\t\t\tSource = src;\n\t\t\tthis.result = result;\n\t\t}\n\n\t\tpublic void Dispose()\n\t\t{\n\t\t\tresult?.Dispose();\n\t\t\tresult = null;\n\t\t\tGC.SuppressFinalize( this );\n\t\t}\n\n\t\t~Transcription()\n\t\t{\n\t\t\tresult?.Dispose();\n\t\t\tresult = null;\n\t\t}\n\n\t\tinternal TranscribeResult getResult() =>\n\t\t\tnew TranscribeResult( result );\n\n\t\tstring makeText()\n\t\t{\n\t\t\tTranscribeResult res = getResult();\n\t\t\tStringBuilder sb = new StringBuilder();\n\t\t\tbool first = true;\n\t\t\tforeach( var seg in res.segments )\n\t\t\t{\n\t\t\t\tif( first )\n\t\t\t\t\tfirst = false;\n\t\t\t\telse\n\t\t\t\t\tsb.AppendLine();\n\t\t\t\tsb.Append( seg.text.Trim() );\n\t\t\t}\n\t\t\treturn sb.ToString();\n\t\t}\n\n\t\t/// <summary>Return the text</summary>\n\t\tpublic override string ToString() => makeText();\n\n\t\t/// <summary>Source file</summary>\n\t\tpublic string Source { get; }\n\n\t\t/// <summary>Source file name, no extension</summary>\n\t\tpublic string SourceName =>\n\t\t\tPath.GetFileNameWithoutExtension( Source );\n\n\t\t/// <summary>Text of the complete transcription</summary>\n\t\tpublic string Text => makeText();\n\t}\n}"
  },
  {
    "path": "WhisperPS/Utils/CommandLogger.cs",
    "content": "﻿using System;\nusing System.Management.Automation;\n\nnamespace Whisper\n{\n\tstatic class CommandLogger\n\t{\n\t\tstatic CommandLogger()\n\t\t{\n\t\t\tLibrary.setLogSink( eLogLevel.Debug, eLoggerFlags.SkipFormatMessage, sink );\n\t\t}\n\n\t\tstatic void sink( eLogLevel level, string message )\n\t\t{\n\t\t\tswitch( level )\n\t\t\t{\n\t\t\t\tcase eLogLevel.Warning:\n\t\t\t\t\tcmdlet?.WriteWarning( message );\n\t\t\t\t\tbreak;\n\t\t\t\tcase eLogLevel.Info:\n\t\t\t\t\tcmdlet?.WriteInformation( message, null );\n\t\t\t\t\tbreak;\n\t\t\t\tcase eLogLevel.Debug:\n\t\t\t\t\tcmdlet?.WriteDebug( message );\n\t\t\t\t\tbreak;\n\t\t\t\t// Errors usually become C# exceptions\n\t\t\t}\n\t\t}\n\n\t\t[ThreadStatic]\n\t\tstatic Cmdlet cmdlet;\n\n\t\tsealed class Impl: IDisposable\n\t\t{\n\t\t\tpublic Impl( Cmdlet c )\n\t\t\t{\n\t\t\t\tcmdlet = c;\n\t\t\t}\n\n\t\t\tvoid IDisposable.Dispose()\n\t\t\t{\n\t\t\t\tcmdlet = null;\n\t\t\t}\n\t\t}\n\n\t\tpublic static IDisposable setupLog( this Cmdlet cmd ) =>\n\t\t\tnew Impl( cmd );\n\t}\n}"
  },
  {
    "path": "WhisperPS/Utils/MiscUtils.cs",
    "content": "﻿using Microsoft.PowerShell.Commands;\nusing System;\nusing System.IO;\nusing System.Management.Automation;\nusing System.Management.Automation.Runspaces;\n\nnamespace Whisper\n{\n\tstatic class MiscUtils\n\t{\n\t\tpublic static void writeProgress( this Cmdlet cmd, double progressValue, string what )\n\t\t{\n\t\t\tint percents = (int)Math.Round( progressValue * 100.0 );\n\t\t\tpercents = Math.Max( 0, percents );\n\t\t\tpercents = Math.Min( percents, 100 );\n\n\t\t\tProgressRecord rec = new ProgressRecord( 0, what, \"please wait…\" );\n\t\t\trec.PercentComplete = percents;\n\t\t\tcmd.WriteProgress( rec );\n\t\t}\n\n\t\tstatic bool isFullPath( string path )\n\t\t{\n\t\t\tif( string.IsNullOrWhiteSpace( path ) || path.IndexOfAny( Path.GetInvalidPathChars() ) != -1 || !Path.IsPathRooted( path ) )\n\t\t\t\treturn false;\n\n\t\t\tstring pathRoot = Path.GetPathRoot( path );\n\t\t\tif( pathRoot.Length <= 2 && pathRoot != \"/\" ) // Accepts X:\\ and \\\\UNC\\PATH, rejects empty string, \\ and X:, but accepts / to support Linux\n\t\t\t\treturn false;\n\n\t\t\tif( pathRoot[ 0 ] != '\\\\' || pathRoot[ 1 ] != '\\\\' )\n\t\t\t\treturn true; // Rooted and not a UNC path\n\n\t\t\treturn pathRoot.Trim( '\\\\' ).IndexOf( '\\\\' ) != -1; // A UNC server name without a share name (e.g \"\\\\NAME\" or \"\\\\NAME\\\") is invalid\n\t\t}\n\n\t\tstatic string currentDirectory( this PSCmdlet cmd )\n\t\t{\n\t\t\tPathInfo pi = cmd.SessionState.Path.CurrentFileSystemLocation;\n\t\t\tif( pi.Provider.ImplementingType != typeof( FileSystemProvider ) )\n\t\t\t\tthrow new ArgumentException( \"Relative paths only work for file systems\" );\n\t\t\treturn pi.ProviderPath;\n\t\t}\n\n\t\tpublic static string absolutePath( this PSCmdlet cmd, string path )\n\t\t{\n\t\t\tif( isFullPath( path ) )\n\t\t\t\treturn path;\n\t\t\tstring dir = cmd.currentDirectory();\n\t\t\treturn Path.GetFullPath( Path.Combine( dir, path ) );\n\t\t}\n\t}\n}"
  },
  {
    "path": "WhisperPS/WhisperPS.csproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"15.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <Import Project=\"$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props\" Condition=\"Exists('$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props')\" />\n  <PropertyGroup>\n    <Configuration Condition=\" '$(Configuration)' == '' \">Debug</Configuration>\n    <Platform Condition=\" '$(Platform)' == '' \">AnyCPU</Platform>\n    <ProjectGuid>{D6D188BB-237E-43F0-AFE3-8947FFD88FC7}</ProjectGuid>\n    <OutputType>Library</OutputType>\n    <AppDesignerFolder>Properties</AppDesignerFolder>\n    <RootNamespace>Whisper</RootNamespace>\n    <AssemblyName>WhisperPS</AssemblyName>\n    <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>\n    <FileAlignment>512</FileAlignment>\n    <Deterministic>true</Deterministic>\n    <NuGetPackageImportStamp>\n    </NuGetPackageImportStamp>\n    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>\n    <CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>\n    <GenerateAssemblyInfo>false</GenerateAssemblyInfo>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' \">\n    <DebugSymbols>true</DebugSymbols>\n    <DebugType>full</DebugType>\n    <Optimize>false</Optimize>\n    <OutputPath>bin\\Debug\\</OutputPath>\n    <DefineConstants>DEBUG;TRACE</DefineConstants>\n    <ErrorReport>prompt</ErrorReport>\n    <WarningLevel>4</WarningLevel>\n    <DocumentationFile>bin\\Debug\\WhisperPS.xml</DocumentationFile>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' \">\n    <DebugType>pdbonly</DebugType>\n    <Optimize>true</Optimize>\n    <OutputPath>bin\\Release\\</OutputPath>\n    <DefineConstants>TRACE</DefineConstants>\n    <ErrorReport>prompt</ErrorReport>\n    <WarningLevel>4</WarningLevel>\n    <DocumentationFile>bin\\Release\\WhisperPS.xml</DocumentationFile>\n  </PropertyGroup>\n  <ItemGroup>\n    <Reference Include=\"ComLight, Version=1.3.7.0, Culture=neutral, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\ComLightInterop.1.3.7\\lib\\net472\\ComLight.dll</HintPath>\n    </Reference>\n    <Reference Include=\"System\" />\n    <Reference Include=\"System.Buffers, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\System.Buffers.4.5.0\\lib\\netstandard2.0\\System.Buffers.dll</HintPath>\n    </Reference>\n    <Reference Include=\"System.Core\" />\n    <Reference Include=\"System.Management.Automation, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\Microsoft.PowerShell.5.ReferenceAssemblies.1.1.0\\lib\\net4\\System.Management.Automation.dll</HintPath>\n      <Private>False</Private>\n    </Reference>\n    <Reference Include=\"System.Memory, Version=4.0.1.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\System.Memory.4.5.3\\lib\\netstandard2.0\\System.Memory.dll</HintPath>\n    </Reference>\n    <Reference Include=\"System.Numerics\" />\n    <Reference Include=\"System.Runtime.CompilerServices.Unsafe, Version=4.0.4.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\System.Runtime.CompilerServices.Unsafe.4.5.2\\lib\\netstandard2.0\\System.Runtime.CompilerServices.Unsafe.dll</HintPath>\n    </Reference>\n    <Reference Include=\"Microsoft.CSharp\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Compile Include=\"..\\WhisperNet\\API\\CaptureDeviceId.cs\">\n      <Link>API\\CaptureDeviceId.cs</Link>\n    </Compile>\n    <Compile Include=\"..\\WhisperNet\\API\\eCaptureStatus.cs\">\n      <Link>API\\eCaptureStatus.cs</Link>\n    </Compile>\n    <Compile Include=\"..\\WhisperNet\\API\\eGpuModelFlags.cs\">\n      <Link>API\\eGpuModelFlags.cs</Link>\n    </Compile>\n    <Compile Include=\"..\\WhisperNet\\API\\eLanguage.cs\">\n      <Link>API\\eLanguage.cs</Link>\n    </Compile>\n    <Compile Include=\"..\\WhisperNet\\API\\eLogLevel.cs\">\n      <Link>API\\eLogLevel.cs</Link>\n    </Compile>\n    <Compile Include=\"..\\WhisperNet\\API\\eModelImplementation.cs\">\n      <Link>API\\eModelImplementation.cs</Link>\n    </Compile>\n    <Compile Include=\"..\\WhisperNet\\API\\eResultFlags.cs\">\n      <Link>API\\eResultFlags.cs</Link>\n    </Compile>\n    <Compile Include=\"..\\WhisperNet\\API\\eSpeakerChannel.cs\">\n      <Link>API\\eSpeakerChannel.cs</Link>\n    </Compile>\n    <Compile Include=\"..\\WhisperNet\\API\\iAudioBuffer.cs\">\n      <Link>API\\iAudioBuffer.cs</Link>\n    </Compile>\n    <Compile Include=\"..\\WhisperNet\\API\\iAudioReader.cs\">\n      <Link>API\\iAudioReader.cs</Link>\n    </Compile>\n    <Compile Include=\"..\\WhisperNet\\API\\iMediaFoundation.cs\">\n      <Link>API\\iMediaFoundation.cs</Link>\n    </Compile>\n    <Compile Include=\"..\\WhisperNet\\API\\iModel.cs\">\n      <Link>API\\iModel.cs</Link>\n    </Compile>\n    <Compile Include=\"..\\WhisperNet\\API\\Parameters.cs\">\n      <Link>API\\Parameters.cs</Link>\n    </Compile>\n    <Compile Include=\"..\\WhisperNet\\API\\sCaptureParams.cs\">\n      <Link>API\\sCaptureParams.cs</Link>\n    </Compile>\n    <Compile Include=\"..\\WhisperNet\\API\\SpecialTokens.cs\">\n      <Link>API\\SpecialTokens.cs</Link>\n    </Compile>\n    <Compile Include=\"..\\WhisperNet\\AssemblyInfo.cs\">\n      <Link>Properties\\AssemblyInfo.cs</Link>\n    </Compile>\n    <Compile Include=\"..\\WhisperNet\\Internal\\iContext.cs\">\n      <Link>Internal\\iContext.cs</Link>\n    </Compile>\n    <Compile Include=\"..\\WhisperNet\\Internal\\sCaptureCallbacks.cs\">\n      <Link>Internal\\sCaptureCallbacks.cs</Link>\n    </Compile>\n    <Compile Include=\"..\\WhisperNet\\Internal\\sLoggerSetup.cs\">\n      <Link>Internal\\sLoggerSetup.cs</Link>\n    </Compile>\n    <Compile Include=\"Properties\\AssemblyTitle.cs\" />\n    <Compile Include=\"Commands\\ExportBase.cs\" />\n    <Compile Include=\"Commands\\ExportText.cs\" />\n    <Compile Include=\"Commands\\ExportWebVtt.cs\" />\n    <Compile Include=\"Commands\\FormatSegments.cs\" />\n    <Compile Include=\"Commands\\TranscribeBase.cs\" />\n    <Compile Include=\"Commands\\ExportSubrip.cs\" />\n    <Compile Include=\"Internal\\iTranscribeResult.cs\" />\n    <Compile Include=\"Internal\\MarshalEx.cs\" />\n    <Compile Include=\"Internal\\NativeLogger.cs\" />\n    <Compile Include=\"Internal\\sCaptureDevice.cs\" />\n    <Compile Include=\"Internal\\sFullParams.cs\" />\n    <Compile Include=\"Internal\\sLoadModelCallbacks.cs\" />\n    <Compile Include=\"Internal\\sModelSetup.cs\" />\n    <Compile Include=\"Internal\\sProgressSink.cs\" />\n    <Compile Include=\"Library.cs\" />\n    <Compile Include=\"Commands\\ListAdapters.cs\" />\n    <Compile Include=\"Commands\\LoadModel.cs\" />\n    <Compile Include=\"Types\\Model.cs\" />\n    <Compile Include=\"Commands\\TranscribeFile.cs\" />\n    <Compile Include=\"Types\\Segment.cs\" />\n    <Compile Include=\"Types\\Transcription.cs\" />\n    <Compile Include=\"Utils\\CommandLogger.cs\" />\n    <Compile Include=\"Utils\\MiscUtils.cs\" />\n  </ItemGroup>\n  <ItemGroup>\n    <None Include=\"app.config\" />\n    <None Include=\"Readme.md\" />\n    <None Include=\"WhisperPS.psd1\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </None>\n    <None Include=\"packages.config\" />\n  </ItemGroup>\n  <Import Project=\"$(MSBuildToolsPath)\\Microsoft.CSharp.targets\" />\n  <Import Project=\"..\\packages\\ComLightInterop.1.3.7\\build\\ComLightInterop.targets\" Condition=\"Exists('..\\packages\\ComLightInterop.1.3.7\\build\\ComLightInterop.targets')\" />\n  <Target Name=\"EnsureNuGetPackageBuildImports\" BeforeTargets=\"PrepareForBuild\">\n    <PropertyGroup>\n      <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them.  For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>\n    </PropertyGroup>\n    <Error Condition=\"!Exists('..\\packages\\ComLightInterop.1.3.7\\build\\ComLightInterop.targets')\" Text=\"$([System.String]::Format('$(ErrorText)', '..\\packages\\ComLightInterop.1.3.7\\build\\ComLightInterop.targets'))\" />\n    <Error Condition=\"!Exists('..\\packages\\XmlDoc2CmdletDoc.0.3.0\\build\\XmlDoc2CmdletDoc.targets')\" Text=\"$([System.String]::Format('$(ErrorText)', '..\\packages\\XmlDoc2CmdletDoc.0.3.0\\build\\XmlDoc2CmdletDoc.targets'))\" />\n  </Target>\n  <ItemGroup Condition=\" '$(Configuration)' == 'Debug' \">\n    <Content Include=\"..\\x64\\Debug\\Whisper.dll\" Link=\"Whisper.dll\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </Content>\n  </ItemGroup>\n  <ItemGroup Condition=\" '$(Configuration)' == 'Release' \">\n    <Content Include=\"..\\x64\\Release\\Whisper.dll\" Link=\"Whisper.dll\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </Content>\n  </ItemGroup>\n  <Import Project=\"..\\packages\\XmlDoc2CmdletDoc.0.3.0\\build\\XmlDoc2CmdletDoc.targets\" Condition=\"Exists('..\\packages\\XmlDoc2CmdletDoc.0.3.0\\build\\XmlDoc2CmdletDoc.targets')\" />\n</Project>"
  },
  {
    "path": "WhisperPS/WhisperPS.psd1",
    "content": "﻿#\n# Module manifest for module 'myModuleName'\n#\n# Generated by: const.me\n#\n# Generated on: 3/15/2023\n#\n\n@{\n\n# Script module or binary module file associated with this manifest.\nRootModule = 'WhisperPS.dll'\n\n# Version number of this module.\nModuleVersion = '1.12.0'\n\n# Supported PSEditions\nCompatiblePSEditions = @('Desktop')\n\n# ID used to uniquely identify this module\nGUID = '80df9a7f-d9ab-47a6-b783-c6fdccf8d15d'\n\n# Author of this module\nAuthor = 'const.me'\n\n# Company or vendor of this module\nCompanyName = 'const.me'\n\n# Copyright statement for this module\nCopyright = '(c) 2023 const.me. All rights reserved.'\n\n# Description of the functionality provided by this module\nDescription = 'High-performance GPGPU inference of OpenAI''s Whisper automatic speech recognition (ASR) model'\n\n# Minimum version of the Windows PowerShell engine required by this module\nPowerShellVersion = '5.1'\n\n# Name of the Windows PowerShell host required by this module\n# PowerShellHostName = ''\n\n# Minimum version of the Windows PowerShell host required by this module\n# PowerShellHostVersion = ''\n\n# Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only.\nDotNetFrameworkVersion = '4.7.2'\n\n# Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only.\n# CLRVersion = ''\n\n# Processor architecture (None, X86, Amd64) required by this module\nProcessorArchitecture = 'Amd64'\n\n# Modules that must be imported into the global environment prior to importing this module\n# RequiredModules = @()\n\n# Assemblies that must be loaded prior to importing this module\n# RequiredAssemblies = @()\n\n# Script files (.ps1) that are run in the caller's environment prior to importing this module.\n# ScriptsToProcess = @()\n\n# Type files (.ps1xml) to be loaded when importing this module\n# TypesToProcess = @()\n\n# Format files (.ps1xml) to be loaded when importing this module\n# FormatsToProcess = @()\n\n# Modules to import as nested modules of the module specified in RootModule/ModuleToProcess\n# NestedModules = @()\n\n# Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export.\n# FunctionsToExport = @()\n\n# Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export.\nCmdletsToExport = @(\n\t\"Get-Adapters\",\n\t\"Import-WhisperModel\",\n\t\"Transcribe-File\",\n\t\"Format-Segments\",\n\t\"Export-Text\",\n\t\"Export-SubRip\",\n\t\"Export-WebVTT\"\n)\n\n# Variables to export from this module\n# VariablesToExport = '*'\n\n# Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export.\n# AliasesToExport = @()\n\n# DSC resources to export from this module\n# DscResourcesToExport = @()\n\n# List of all modules packaged with this module\n# ModuleList = @()\n\n# List of all files packaged with this module\nFileList = @(\n\t\"ComLight.dll\",\n\t\"System.Memory.dll\",\n\t\"System.Buffers.dll\",\n\t\"System.Runtime.CompilerServices.Unsafe.dll\",\n\t\"Whisper.dll\",\n\t\"WhisperPS.dll\",\n\t\"WhisperPS.dll.config\",\n\t\"WhisperPS.dll-Help.xml\",\n\t\"lz4.txt\"\n)\n\n# Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell.\nPrivateData = @{\n\n    PSData = @{\n\n        # Tags applied to this module. These help with module discovery in online galleries.\n        Tags = @(\"Windows\",\"PSEdition_Desktop\")\n\n        # A URL to the license for this module.\n        LicenseUri = 'https://www.mozilla.org/en-US/MPL/2.0/'\n\n        # A URL to the main website for this project.\n        ProjectUri = 'https://github.com/Const-me/Whisper'\n\n        # A URL to an icon representing this module.\n        # IconUri = ''\n\n        # ReleaseNotes of this module\n        ReleaseNotes = 'When loading models, adapters can be selected with 0-based index, in addition to the name.\nAdded an API method to decode initial prompt into array of tokens.'\n    } # End of PSData hashtable\n\n} # End of PrivateData hashtable\n\n# HelpInfo URI of this module\n# HelpInfoURI = ''\n\n# Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix.\n# DefaultCommandPrefix = ''\n}"
  },
  {
    "path": "WhisperPS/app.config",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<configuration>\n  <runtime>\n    <assemblyBinding xmlns=\"urn:schemas-microsoft-com:asm.v1\">\n      <dependentAssembly>\n        <assemblyIdentity name=\"System.Buffers\" publicKeyToken=\"cc7b13ffcd2ddd51\" culture=\"neutral\" />\n        <bindingRedirect oldVersion=\"0.0.0.0-4.0.3.0\" newVersion=\"4.0.3.0\" />\n      </dependentAssembly>\n    </assemblyBinding>\n  </runtime>\n</configuration>"
  },
  {
    "path": "WhisperPS/packages.config",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<packages>\n  <package id=\"ComLightInterop\" version=\"1.3.7\" targetFramework=\"net472\" />\n  <package id=\"Microsoft.PowerShell.5.ReferenceAssemblies\" version=\"1.1.0\" targetFramework=\"net472\" />\n  <package id=\"System.Buffers\" version=\"4.5.0\" targetFramework=\"net472\" />\n  <package id=\"System.Memory\" version=\"4.5.3\" targetFramework=\"net472\" />\n  <package id=\"System.Numerics.Vectors\" version=\"4.4.0\" targetFramework=\"net472\" />\n  <package id=\"System.Runtime.CompilerServices.Unsafe\" version=\"4.5.2\" targetFramework=\"net472\" />\n  <package id=\"XmlDoc2CmdletDoc\" version=\"0.3.0\" targetFramework=\"net472\" developmentDependency=\"true\" />\n</packages>"
  }
]